Skip to content

Why HappyPDF

HappyPDF is a fork of pdf-lib (by way of @cantoo/pdf-lib) that replaces fontkit with HarfBuzz.

The problem

pdf-lib shapes text with fontkit. That works for Latin and other simple scripts, but scripts that need reordering, mark attachment, or contextual substitution come out wrong — Khmer, Thai, Lao, Arabic, Devanagari and the other Indic scripts.

A Khmer word like សួស្តី is not a sequence of independent letters. Consonants stack into orthographic clusters, and vowels are reordered around them. Rendering the code points in logical order produces text that is, at best, unreadable.

What changed

HarfBuzz does the shaping. It is compiled to WebAssembly and inlined into the bundle, so there is no extra download and nothing to install.

Every glyph is positioned individually. Rather than emitting one Tj operator per line and trusting the viewer, HappyPDF writes each shaped glyph at the position HarfBuzz computed. Reordering, ligatures and mark placement survive into the file.

No registerFontkit. Custom fonts embed out of the box. The method is kept as a no-op so pdf-lib code keeps working.

Variable fonts. variations instances a font on its axes, and the instanced outlines are what gets embedded.

Wrapping and alignment. maxWidth wraps, and align supports left, center, right and justify. Break points come from Intl.Segmenter, so scripts without spaces wrap at real word boundaries.

What did not change

Everything else. Forms, SVG, PDF/A, encryption, page manipulation, embedding images — the API is pdf-lib's, so existing code and existing knowledge carry over.

Differences from pdf-lib

pdf-libHappyPDF
Shaping enginefontkitHarfBuzz (bundled)
Setup for custom fontsregisterFontkit(fontkit)none
Complex scriptsbrokencorrect
Variable fontsnovariations
Text alignmentnoleft/center/right/justify
Word breakingwhitespace onlyIntl.Segmenter
Node14+22+

Removed APIs

These were superseded by segmentation and no longer exist:

  • breakTextIntoLines()
  • PDFDocument.defaultWordBreaks
  • the wordBreaks option on drawText

Use wrapText instead.

MIT Licensed. A fork of pdf-lib.