Skip to content

Build Not yet confirmed elsewhere1 publisher 2 min read Published

Silencing pdf-lib's WinAnsi error deletes the characters it was warning about

The fourteen standard PDF fonts are Windows-1252 underneath, so a Polish company name throws an encoder error, and the one-line regex that stops the crash ships a valid invoice with the letters quietly gone.

The Engineer · Build desk

How we use AI Send a correction

Illustration accompanying Silencing pdf-lib's WinAnsi error deletes the characters it was warning about
Generated illustration

What happened

  • An invoice generated with pdf-lib in a Node serverless function printed the client name Lodz Sp. z o.o. with two letters missing, threw nothing, logged nothing, and opened perfectly in a reader.
  • The fourteen standard PDF fonts that pdf-lib exposes as StandardFonts are encoded as WinAnsi, roughly Windows-1252, and that glyph set covers Western European Latin and nothing else.
  • pdf-lib refuses an unencodable character with an explicit error naming its hex code point, and the author stopped the crash with a one-line regex strip after which the whole suite passed.
  • Embedding a TrueType face lifts the encoding restriction, but the byte-array path first needs a registerFontkit call whose absence produces an error message pointing somewhere else.

Why it matters

  • exposure The field being shortened is a counterparty's legal name on a document the sender forwards to its own customer, so the defect surfaces at the recipient rather than in the system that produced it.
  • constraint Embedding buys coverage only as far as the face reaches: Archivo carries no CJK, so the first Japanese company name throws inside widthOfTextAtSize and a fallback policy is still owed.
  • decision The choice is now explicit between a name that silently closes up and a substituted space that a human proofreading the page has some chance of catching.
  • cost Licence review joins the engineering fix, because embedding is a redistribution question: Archivo ships under SIL OFL and allows it, and many commercial faces do not.

The strip is a range test, and the range is not WinAnsi's. `[^\x20-\xFF]` removes every code point above U+00FF [8]. The character that started this, Ł, is U+0141, so it goes [7]. The euro sign at U+20AC and the curly quotes at U+2018 and above also go, and WinAnsi encodes all of those without complaint [6][20]. So one regex deletes two disjoint sets: characters the standard font genuinely cannot draw, and characters it can. It is doing more work than it was asked to do.

Nothing downstream sees either deletion. The file is a valid PDF, no exception is raised, and the logs stay clean [9]. A generator suite asserts on the input string and on the absence of a throw, and neither of those changes when a letter vanishes between `drawText` and the page. pdf-lib's own error was the useful artifact here, naming both the character and its hex code point [7]. Trading it for a green test run swaps a stack trace for a document defect. The author of the write-up is blunt about the only check that catches it: render a page with non-Latin-1 text in it and look at it with your eyes [9].

The figures most likely to be copied out of this are the font sizes, so it is worth saying what they rest on. One unsubsetted Archivo weight is about 180 KB, which is why three weights add roughly half a megabyte to every document generated [12][21]. With `subset: true`, the same three weights inside the author's four-page invoice land at 35 KB, because only the glyphs actually drawn get embedded [13]. Call it fifteen times smaller [21]. That ratio transfers to documents that draw a comparable glyph set and no further. Subset size follows distinct glyphs used, so a customer base heavy on diacritics will not compress like one Polish name in an English template, and caching the font files in a module-level variable shortens the read on warm invocations rather than the subset [14].

With a rasteriser already pointed at the output, two layout defects turned up that reading the code would not have surfaced [18]. The totals block right-aligned its figure at 20pt Black in a column sized for four digits: $2,160.00 fit, and 86,832.81 PLN ran back over the words TOTAL DUE [19]. That is the same failure class as the missing letter, and the same instrument catches both, which is the argument for keeping a rendered-page check inside the pipeline instead of waiting for it to arrive as a bug report.

What to watch

  • Whether pdf-lib ever reports a coverage miss as something other than a throw from widthOfTextAtSize, which is what forces the per-character probe today.
  • Whether rendered-page snapshot checks become normal in document-generation CI, given that no assertion on strings or exit codes detects a dropped glyph.
  • Whether teams vendoring commercial faces for embedding read the EULA before shipping, now that the encoding fix requires font bytes in the PDF.

Clarity's read

What the record supports and how the coverage leans. The claims behind it follow.

Reality

Evidence62
Adoption22
Hype gap+18
Incentives45
Confidence66
Why these scores

Claim ledger

Ranked by verification strength, evidence, and original report placement.

  1. [1]

    The author generates invoices in a Node serverless function using pdf-lib.

    Reported Supported Source: dev.to write-up by the authorView cited source
  2. [2]

    A client company name, Lodz Sp. z o.o. written as Łódź Sp. z o.o., was rendered on the invoice as "ód Sp. z o.o."; nothing was thrown, nothing was logged, and the PDF opened perfectly.

    Reported Supported Source: dev.to write-up by the authorView cited source
  3. [3]

    PDF has fourteen fonts every reader is guaranteed to have, usable without embedding anything, and pdf-lib exposes them as StandardFonts.

    Reported SupportedView cited source

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.

Add to ChatGPTAdd to Claude

Sources

1 independent publisher whose own reporting we read for this story.

  1. dev.to

    1 article · August 30, 2026

    pdf-lib is silently deleting characters from your users' data

Share your take

Let Clarity write the post for you.

Signed-in readers get a short post drafted on this story in the register they choose — narrative, analytical, or a direct position — editable to the last word before it goes anywhere. The share buttons at the top of this story work without an account.

Topics and entities

Follow any of these and your For You feed starts watching them — no settings page required.

Topics

Entities

Loading related stories