Repository navigation
The pdf exporter needs pdfmake's font VFS registered, and a missing font is a hang rather than an error #1
Description
Found by Shawn 2026-09-04, consuming this package as an ESM git dependency from a new Astro app (MichalAFerber/wizard-web#98). Worked around app-side to unblock; filing here because this package owns the pdf exporter and the next consumer will rediscover it.
The defect
src/exporters/pdf.js does import pdfMake from 'pdfmake' and calls pdfMake.createPdf(dd). Nothing registers pdfmake's embedded Roboto faces, so through a bundler the PDF export fails:
File 'Roboto-Medium.ttf' not found in virtual file system
The standalone markdownwizard page never hit this because it loaded js/vendor/vfs_fonts.js as a <script>, and that file self-registers by looking for a global:
if (typeof _global.pdfMake !== 'undefined' && typeof _global.pdfMake.addVirtualFileSystem !== 'undefined') {
_global.pdfMake.addVirtualFileSystem(vfs);
}
if (typeof module !== 'undefined') { module.exports = vfs; }Under a bundler there is no global to attach to, so the first branch is skipped and nothing registers the fonts. The README says the ESM entry is "what an Astro app wants", and it is — this is the one thing missing to make that true for fmt: 'pdf'.
The part that cost real time: it hangs, it does not throw
pdfMake.createPdf(dd).getBlob(cb) never invokes the callback when a font is missing. The Promise the exporter returns therefore never settles. await exportAs({ fmt: 'pdf', … }) does not reject — it waits forever. A probe that exported all eight formats in a loop sat for thirty minutes before an external timeout killed it, with the only clue a console.warn in the page.
So the failure mode is: seven formats work, the eighth never returns, and the error text appears as a warning rather than a rejection. Worth guarding against on its own terms, separately from the VFS fix.
Suggested fix
Register the fonts inside the package, where the dependency on them lives:
import pdfMake from 'pdfmake';
import vfs from 'pdfmake/build/vfs_fonts';
// pdfmake resolves fonts through a virtual file system that its <script> build
// self-registers against a global. Through a bundler there is no global, so the
// package that imports pdfmake must register them itself — otherwise every
// createPdf fails on "Roboto-Medium.ttf not found", and getBlob never calls
// back, so the caller hangs rather than erroring.
if (typeof pdfMake.addVirtualFileSystem === 'function') {
pdfMake.addVirtualFileSystem(vfs);
}Notes for whoever takes it:
- Verified against pdfmake 0.2.23. Its
package.jsonhasmain: src/printer.js(the Node printer) andbrowser: build/pdfmake.js, so a bundler honouringbrowsergets the browser build, which is the one exposingaddVirtualFileSystem.vfs_fonts.jsis CommonJS exporting the vfs object directly. vfs_fontsis roughly 768 KB of base64 font data, so importing it unconditionally at module scope adds that to any consumer bundling this package — including one that only ever exports.txt. If that matters, doing it lazily inside the pdf exporter's function body, or exporting aregisterPdfFonts()the app calls once, are both better than the current silence. Whichever way, it should not stay the app's job by accident.- The IIFE build is unaffected: the offline page loads
vfs_fonts.jsas a script and the self-registration works there, which is exactly why this went unnoticed. - Consider making the hang loud regardless of where the fonts get registered — wrapping
getBlobwith a timeout that rejects, or checking the VFS is populated beforecreatePdf. A missing font should be an error, not a stall.
Verified after the app-side workaround
With pdfMake.addVirtualFileSystem(vfs) called once before use, all eight formats produce real blobs from the same document in a browser: md 85B, txt 84B, html 1579B, doc 1866B, dot 1866B, rtf 572B, docx 8990B, pdf 13580B. So the exporter itself is correct — only the font registration was missing.
Activity
MichalAFerber commentedon Sep 5, 2026
Grok is taking this. Do not start a parallel implementation.
shawn/pdf-vfs-register~/GitHub/.worktrees/markdownwizard-tools/shawn-pdf-vfs-register.txtconsumers do not eat 768 KB of Roboto), and make a missing font an error rather than a hang.PR will open as a draft (Sr Dev → DevOps marks ready → Michal merges).
MichalAFerber commentedon Sep 5, 2026
Draft PR: #2
Fonts are registered lazily inside
pdf()viapdfmake/build/vfs_fonts.js+addVirtualFileSystem. A missing font no longer hangs:getBlobis bounded at 15s and rejects with the Roboto-Medium message. IIFE still uses the page'svfs_fonts.jsscript (stub alias, no 768 KB indist/).Run the suite in CI, and make all four tests fail when their subject …