A PDF has no words in it: 'edit one word' is not a string operation
One developer's PDF editor build: the visible word exists only as kerned glyph fragments, and an invisible replacement means estimating font, size, baseline and colour the file never states.
The Engineer · Build desk
What happened
- In a months-long PDF editor build, the feature that sounded easiest, changing one word and leaving the rest alone, consumed most of the schedule.
- A visible word is emitted as a TJ array of kerned fragments, so the string a user searches for is not present in the file at all.
- An invisible replacement needs the original font, size, baseline and colour, and none of the four can be read out reliably.
- Embedded fonts are usually subsets, so a replacement needing a glyph the page never used cannot be drawn in the document's own font.
Why it matters
- cost An estimate written as find and replace prices the attribute estimator and its fallback path at zero, and that fallback is where nearly every bug in this build lived.
- constraint The input space belongs to other people's exporters, so the font-name conventions your classifier keys on are not yours to define and will keep arriving abbreviated and undocumented.
- decision The interface decides how much is guessed. Offer a draw-a-box tool and you infer font, size and baseline from a rectangle; require the user to select a detected run and those values are known.
- exposure A slim container becomes a fidelity liability: with one font family installed, every fallback lands on it regardless of what the document was set in, so output quality depends on the image build...
The order of operations is what breaks the estimate. Nothing gets replaced until the logical text has been rebuilt from positioned glyphs, the target located inside that reconstruction, the specific drawing operations that produced those glyphs identified and removed, and something new drawn so that it looks like it was always there [3]. Four passes over the page before one character changes.
Then the attributes, each with its own way of going wrong. Size is the clearest trap: the number after Tf is not the rendered size, because the text matrix scales it, so a heading written as Tf 1 inside a matrix scaled by 24 renders at 24pt while a naive read returns 1 [5]. Baseline has almost no tolerance; a couple of points high is visible to any reader even when font and size are exact [6]. Colour fails specifically on scans, where sampling one page-wide background colour eventually paints white blocks onto cream paper [7]. Add the availability check, since a PDF normally embeds only the glyphs the page already used [15], and the floor for replacing a single word is five determinations, four of them estimated rather than read [22].
The bug the developer describes is the part to carry into your own planning meeting. Serif detection was a substring test for "times" or "roman". LaTeX embeds a Times clone named NimbusRomNo9L: it contains Rom, not roman, so serif documents were classified as sans and replaced with Helvetica [10]. Computer Modern's CMR10 and CMBX12 fail the same test [11]. The same naming assumption broke weight detection, because Nimbus spells its bold weight -Medi [12]. Two comparisons, two separately visible defects in the output [23].
What kept it alive was the corpus. The generated test files came from a library that emits clean TrueType with tidy names, so they could not produce the failing case, and the bug was declared fixed three times against them [9][13]. One real paper downloaded from arXiv found more than a week of synthetic cases, according to the same account [19].
This generalises past one codebase because the format itself records no paragraphs and no lines, and nothing in it marks a set of glyphs as belonging together; the structure a human sees is inferred by whatever software reads the file [2]. The estimator is not a shortcut someone took under deadline. It is the permanent centre of the feature, and its accuracy is the feature's accuracy.
Which is why the acceptance criterion matters more than the API surface. "Did the edit apply" is the wrong question [21]. The question is what the page looks like afterwards, and answering it needs a renderer or a person, not a return code.
What to watch
- A shared corpus of real exporter output would let font classifiers be tested against names nobody wrote them for.
- Whether classification moves off name substrings and onto the embedded font program's own metrics, which do not depend on vendor abbreviations.
- Whether teams scoping redaction treat it as covering pixels or as deleting the operators that drew the glyphs.
Clarity's read
What the record supports and how the coverage leans. The claims behind it follow.
Reality
- Evidence58
- Adoption
- Insufficient
- Hype gap−5
- Incentives62
- Confidence55
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
In a PDF, a visual word is typically emitted as a TJ array of fragments with kerning adjustments between them; the string "Welcome" does not exist anywhere in the file.
- [2]
A PDF contains no paragraph or line structure and no notion that particular glyphs belong together; everything a human reads as structure is inferred by whatever software reads the file.
- [3]
Find and replace in a PDF requires reconstructing the logical text from positioned glyphs, locating the target inside it, working out which drawing operations produced those glyphs, removing them, and drawing something new so it looks like it was always there.
- [4]
Making a replacement invisible requires the original font, size, baseline and colour, and none of the four is reliably available.
- [5]
Rendered size is not the number after Tf, because the text matrix can scale it: a heading declared as Tf 1 inside a matrix scaled by 24 renders at 24pt, while reading the Tf value alone returns 1.
- [6]
Baseline error is highly visible: a couple of points too high is caught instantly by the eye even when font and size are correct.
- [7]
Sampling a single page-wide background colour on scanned documents will eventually paint white blocks onto cream paper.
- [8]
The developer estimated size, font and baseline from a weighted median of neighbouring runs when the exact run could not be matched, and that fallback path is where nearly every bug lived.
- [9]
Test PDFs built to match the failing case (embedded fonts, subsetted, bold headings) all passed, and the bug was "fixed" three times against those files while continuing to fail on real documents.
- [10]
Serif detection used a substring test for "times" or "roman"; LaTeX embeds a Times clone named NimbusRomNo9L, which contains Rom but not roman, so the font was classified as sans-serif and replaced with Helvetica.
- [11]
Computer Modern font names CMR10 and CMBX12 also never contain the word "roman", so they fail the same substring test.
- [12]
The same naming assumption broke weight detection, because Nimbus calls its bold weight -Medi rather than -Bold, so headings were replaced at regular weight.
- [13]
The generated test files were written by a library that emits clean TrueType with tidy names, and could not produce the documents on which the two string comparisons failed.
- [14]
Real documents come from LaTeX, from Word and from twenty-year-old scanners, and are full of abbreviations nobody documents.
- [15]
A PDF usually embeds only a subset of each font, containing just the glyphs already used on the page, which is why file sizes stay sane.
- [16]
If the original text never contained a z, the embedded subset font has no z, so a replacement word needing that glyph cannot be drawn with the document's own font at all.
- [17]
A fallback is always needed and must look close, which requires real font families installed on the server; a container shipped with only one font family means every fallback lands on it whatever the document looked like.
- [18]
Letting a user draw a box and drop text into it means guessing font, size and baseline from a rectangle, whereas detecting text runs first and letting the user pick one makes those values known rather than inferred.
- [19]
One real paper downloaded from arXiv found more than a week of synthetic test cases.
- [20]
The developer spent several months building a PDF editor, and the feature that sounded easiest, changing a word and leaving everything else alone, ate most of the time.
- [21]
The developer's conclusion on testing: measure the output, not the process, because "did the edit apply" is not the question.
- [22]
The minimum work to replace one word is five determinations per edit, four of them estimates rather than direct reads.
- [23]
The two font-name substring comparisons produced two separately visible defects in rendered output: wrong typeface and wrong weight.
Get this ledger inside ChatGPT. Ask about any of our stories and the answer brings its figure, each claim’s verdict and the sources. Free, no account.
Sources
1 independent publisher whose own reporting we read for this story.
- dev.toWhy editing one word in a PDF is so much harder than it looks
1 article · August 23, 2026
Topics and entities
Follow any of these and your For You feed starts watching them — no settings page required.