Skip to main content
Lightning Jar - Web Studio Lightning Jar Wordmark

Findings No. 76

pdfkit's 14 built-in standard fonts are WinAnsi-encoded and cannot draw ♯, ♭ or ∆, and pdfkit raises no error

pdfkit's standard fonts use WinAnsi, so ♯, ♭ and ∆ print as stray bytes with no error. Embed a font via Vite ?inline and check glyphs with fontkit.

Stem Shovel Tooling verified pdfkit 0.20.2, fontkit 2.0.4, SvelteKit 3.1.1

Stem Shovel builds a song's documentation PDF with pdfkit, chord chart included. Chord symbols set in pdfkit's built-in fonts did not print as written: the sharps, the flats and the ∆ of major seventh chords were lost, and pdfkit raised no error or warning.

The built-in fonts (Helvetica, Times, Courier and the rest of the 14 PDF standard fonts) are embedded with WinAnsi encoding, a single-byte character set. pdfkit 0.20.2 maps a character into that set when it can and otherwise writes its code point as raw bytes. A rerun with Helvetica, rendered by Poppler, shows the result: F♯ prints as "F&o", B♭ as "B&m" and C∆ as C". The degree sign and ø are in WinAnsi and print correctly. Our stem-bench benchmark hit the same limit on its own in Study 5: the accidentals written into chord text were lost on the way into the PDF, every flat chord read as its natural, and an earlier study's published result had to be corrected.

The fix is to embed a font that has the glyphs. Stem Shovel uses Edwin, an open-licensed OTF. In a SvelteKit server module, import url from "./Edwin-Roman.otf?inline" makes Vite emit a base64 data URL at build time, so the font travels inside the function bundle and there is no file path for the deploy's file tracing to miss. doc.registerFont("Edwin", Buffer.from(url.split(",")[1], "base64")) accepts it.

Embedding a font does not guarantee coverage. fontkit, which pdfkit already depends on, answers font.hasGlyphForCodePoint(cp). That check showed Edwin has ♯, ♭, °, ø and ∆ and lacks ♩, the segno, the coda and the fermata, so the chart prints tempos as "124 bpm", directions as words and the fermata as a drawn arc and dot.

Rule: PDF text that can contain characters outside WinAnsi needs an embedded font, checked with hasGlyphForCodePoint for every symbol you plan to print.

Receipt: Stem Shovel commit eebec79a (src/lib/utils/chartToPdf.ts, src/lib/server/documentation.ts; FINDINGS.md, "pdfkit's built-in fonts drop ♯ and ♭; a font ships as a Vite ?inline asset; @barmark/render does not export its chord typesetting").

Ask Eljay

Ask about the studio's work, research, packages, or writing. A few starters:

Answers come from this site's own content and link their sources. For anything that matters, email hello@lightningjar.com.