Skip to content

Font packages (mirrors mpdf/mpdf#2161) - #2

Merged
jakejackson1 merged 9 commits into
gravitypdffrom
decouple-fonts
Sep 7, 2026
Merged

Font packages (mirrors mpdf/mpdf#2161)#2
jakejackson1 merged 9 commits into
gravitypdffrom
decouple-fonts

Conversation

@jakejackson1

@jakejackson1 jakejackson1 commented Sep 7, 2026

Copy link
Copy Markdown
Member

Summary

This is the branch behind mpdf#2161, mirrored here so the whole fork delta is reviewable in one place before Gravity PDF 7.0 depends on it. gravitypdf is plain upstream development (389e19e); merging this makes it "upstream + font packages" and nothing else.

The upstream PR replaces mPDF's built-in font handling with registrable font packages: Mpdf\Fonts\FontRegistry, FontRegistrationInterface, an abstract FontRegistration, and Mpdf\Language\LanguageToFontRegistry. ttfonts/ is deleted, the font defaults in ConfigVariables/FontVariables are emptied, and the fonts move into packages/* Composer path repositories.

Seven commits follow the two original ones; the last six are ours, added while working through Phase 0 of the 7.0 plan.

Try it

composer install
composer test   # 998 tests
composer cs

composer install pulls mpdf/font-bundle-all from require-dev, so the suite has fonts to register. Without that the suite goes red — see the note below.

Test plan

  • composer test — 998 tests, 2289 assertions, green.
  • composer cs — clean.
  • Downstream: the Gravity PDF plugin builds against this branch with composer prefix; Test_Vendor_Prefixing (11 tests) and the full 1637-test suite pass.
More info

The commits

Commit Change Origin
70b90cc Font Packages upstream PR
40aec72 Add mpdf as a dependency in the font packages upstream PR
80aa688 Line-break dictionaries become font-package data. getLineBreakDictionaries() on the interface, merged in initFontRegistry() with the same first-wins precedence as fontdata, plus a seaLineBreaking() early return when no dictionary is registered; linebrdict{T,K,L}.dat move from data/ into packages/{Garuda,Khmer-OS,Dhyana}/fonts/ ours
28bf7d8 FontRegistry no longer throws when composer.lock is absent — logs through an optional PSR-3 logger and registers nothing ours
caae349 fontFileFinder is container-resolvable in ServiceFactory::getServices() ours
3cb1647 mpdf/font-bundle-all in require-dev ours
6415052 The default font sorts first in fontdata, so it heads available_unifonts ours
1afecbc FontRegistry keys by an overridable getId(), defaulting to the class name ours
6e45465 packages/ export-ignore in .gitattributes, so a Composer zipball install does not drag ~100 MB of font binaries into every vendor/ ours

Why require-dev matters

packages/* was declared as a path repository but nothing required it, so a plain composer install registered no fonts, mPDF dropped into core-font mode, and the suite reported 41 failures and 15 errors. One line in require-dev makes it green.

Behaviour changes for existing consumers

  • Line-break dictionaries. A package that wants Thai/Khmer/Lao dictionary line breaking now declares getLineBreakDictionaries(). Without one, seaLineBreaking() returns early and mPDF falls back to ordinary line breaking rather than failing.
  • getId(). FontRegistry::add() keys on $class->getId() instead of get_class($class). The base class returns get_class($this), so every existing package keeps its key; overriding it lets one class register twice under different ids.

Not in this PR

The MarkGlyphSets fix is #1, and the fork-only branch-alias config is #3. Both are independent of font packages.

Thai, Khmer and Lao word boundaries come from linebrdict{T,K,L}.dat, which
Otl loaded from a hard-coded src/../data path. Now that the fonts they pair
with live in packages, the dictionaries move with them: Garuda carries T,
Khmer-OS carries K and Dhyana carries L, each declared through the new
FontRegistrationInterface::getLineBreakDictionaries().

initFontRegistry() merges the declarations with the same first-wins
precedence it applies to fontdata, and seaLineBreaking() returns early when
no dictionary is registered for the shaper. Without that early return the
$dict =& $this->lbdicts[...] reference is null and ord($dict[$dictptr])
emits a notice per character; now a document with no dictionary simply falls
back to ordinary line breaking.
Autoloading font packages needs composer.lock, and distributions that strip
it (a plugin release zip, say) made every `new Mpdf()` without an explicit
fontRegistry fatal. Log and register nothing instead, which leaves mPDF in
the core-font mode it already falls into when no package registers a font.
getServices() already resolves httpClient, localContentLoader and
assetFetcher this way. Consumers that need to recover from a font file
that has gone missing on disk can now supply their own finder.
The packages/* path repositories are declared but nothing pulls them in, so
a plain `composer install` leaves mPDF with no registered fonts and 56 of
its own tests fail in core-font mode.
available_unifonts is built in fontdata order, and it is what an
unresolvable font-family falls back to. Without this the fallback is
whichever package or config entry happened to register first, which the
caller does not control once several packages are in play.
add() keyed the register by get_class(), so two instances of one class
collapsed into one entry. A consumer whose packages differ only by where
their data comes from — a constant map versus rows from a database, say —
had to write one class per package to work around it.

FontRegistrationInterface::getId() defaults to the class name in the
FontRegistration base, so every existing package keeps the key it had, and
remove() keeps taking the same argument.
Composer installs this fork from a zipball, and packages/ is ~100MB of font
files no consumer of the library itself needs.
@jakejackson1
jakejackson1 merged commit ba9a902 into gravitypdf Sep 7, 2026
23 of 27 checks passed
@jakejackson1 jakejackson1 added the enhancement New feature or request label Sep 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

create-upstream-pr enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant