I make presentation decks for a living — Chinese business reviews, thesis defences, funding requests. Last week I audited the artifacts I had already delivered. Four failures, none of which my own checks caught, because every check I owned asked is it present and none asked does it correspond.
◷ U+25F7 absent from the CJK font — printed as □ on the speaker cards“Static” was a wider claim than my measurement. I counted <p:transition> and called the
result “no animation”. Three different things live in a slide package: the slide transition
(<p:transition>), the in-page animation tree (<p:timing>), and
timed advance (advTm). Zero transitions rules out only the first.
Recomputed across the 120 .pptx files in my tree, per slide:
transition present : 13 files timing present : 0 files advTm present : 0 files neither transition nor timing : 107 files
So the conclusion survives for this corpus, but only because the second and third counts are zero
everywhere — not because zero transitions implies it. A file with in-page animations and no transition would have
been misfiled, silently. The check now prints the three numbers separately and refuses to collapse them; a file
with timing > 0 and no transition is reported as “in-page animation, no slide transition”.
What the package establishes is that there is no animation data for the renderer to play; what F5 does
in a given application I cannot verify from here, and I no longer claim it.
| What I tell the client | The check that can be wrong about it |
|---|---|
| “It is animated — press F5.” | Count <p:transition> per slide in the delivered file, not in the module that was supposed to write it. A module with no caller passes its own unit tests. |
| “The numbers are your numbers.” | Diff every figure on the pages against the inputs you were given. Recompute after each edit; the cover usually repeats the arithmetic, and that copy is the one that goes stale. |
| “It fits your minutes.” | Pages = minutes ÷ seconds-per-page for this kind of talk, and the arithmetic printed on the cover so the mismatch is visible to them rather than hidden in your notes. |
| “It is readable when projected.” | Real pixel contrast per run against the pixels actually behind it — not the colour values you chose. Body text 4.5:1, large text 3:1. A colour reserved for hairlines must never carry words. |
| “Every character will print.” | Glyph coverage of the exact font the deck is set in. This one bites last, on paper, in the room. |
| “It opens.” | Unpack the ZIP, open the XML, count slides. Anything else measures the file you meant to send. |
unzip -p deck.pptx 'ppt/slides/slide*.xml' | grep -c '<p:transition' # 0 = “animated” was about intent unzip -p deck.pptx 'ppt/slides/slide1.xml' | grep -o 'sz="[0-9]*"' # font sizes, in 1/100 pt pdfinfo deck.pdf | grep Pages # matches the deck, or it does not pdffonts deck.pdf | grep -c 'no' # fonts not embedded
Drag a .pptx into the unpack page and it reports, per slide:
real text vs. full-slide image, runs that overflow their box, the smallest content-area font size, speaker notes present,
leftover placeholders, and the true contrast of every run against the pixels behind it. Nothing is uploaded — the ZIP is
parsed in your browser. The interface is in Chinese because that is who I build for; the checks are the six above.
This is a corpus of my own decks for Chinese-language talks, not a benchmark of the field. Where I state a number, it comes from unpacking the delivered file; where I state a hope, it is labelled as one.
1 — A promise about behaviour, verified only against intent.
My site said “download the animated version — press F5 and it plays”. Across 286 delivered files, 285 had zero pages
containing <p:transition>. The animation module existed, was unit-tested, and its own
validate() passed. It had no caller: grep -rn "pptx_motion" . returned nothing.
A module with no caller is worse than an absent one, because green unit tests make the next reader believe the
feature is running.
2 — A criterion that measured a different set than the sentence it printed. I had a metric called “pages with nothing but text” and printed “N pages have no image, no table, no chart”. The code counted anchor type, not visual presence. A page with one small image and a large heading was counted both as “has an image” and as one of the “remaining N pages”. The two sets were never equal; the number I published was one page too large. The general shape: a sentence and the set it reports on were computed from different inputs, and nothing compared them.
3 — The same number copied into two places, only one of which updated.
I added two full-page photographs to a 12-page deck (making it 14) and, ten minutes later, my front page still read
“12 pages: 0 images”. The fix was not care, it was removing the copy: the number now lives between markers and is
recomputed from the .pptx at publish time, with a --check that fails when the page and the
artifact disagree. Any static check is silent about a second hand-written copy, because both copies are
individually correct.
4 — Text that is present, positioned correctly, and unreadable.
A slide’s source line — the only thing saying where this number comes from — was drawn in the colour reserved
for hairlines: 1.40:1 against paper, across five themes, for eight days. One caption at 1.02:1.
One glyph absent from the CJK font the deck was set in, printed as □ on the speaker cards.
Readable is ≥4.5:1 for body text and ≥3:1 for large text. A colour that exists to draw rules must never carry
words — and that is a judgement about use, not about the colour value, so the check has to read the source
that assigns colours, not only the palette.