Skip to content

Strip TrueType hinting from the packaged fonts whose licences allow it - #853

Merged
jakejackson1 merged 3 commits into
gravitypdffrom
chore/dehint-fonts
Nov 5, 2026
Merged

jakejackson1 merged 3 commits into
gravitypdffrom
chore/dehint-fonts

Conversation

@gravitypdf-bot

@gravitypdf-bot gravitypdf-bot commented Nov 5, 2026 •

Copy link
Copy Markdown

Summary

Ports #850 from gravitypdf-v6 to gravitypdf. The packaged fonts carry TrueType hinting that mPDF copies into every PDF embedding them. This strips it from the 51 fonts with real hinting and a licence that allows the change, which takes 10.94 MB (24.3%) off those files and about 24% off sampled PDFs using them.

  • Removes the hinting tables, glyph instructions and stale DSIG from 51 fonts in packages/: Aboriginal Sans, Ayar, DejaVu, GNU FreeFont, Garuda, Khmer OS, Lohit Kannada, MPH 2B Damase, Pothana 2000, UnBatang and XB Riyaz.
  • Adds utils/dehint_fonts.py, which holds the vetted font list by package and the reason the others are left out.
  • Updates the 17 ranges files whose size changed. Their character ranges are the same.
  • Refreshes 218 snapshot fixtures, which Strip TrueType hinting from the bundled fonts whose licences allow it #850 didn't need to do. See "Why the fixtures changed" below.
  • Makes AddFontTest compare length1 with the font's real size instead of a hardcoded number.
  • Updates the disk space figures in composer.json and the bundle READMEs.
  • Adds an entry to CHANGELOG-GRAVITYPDF.md, and a line to the README of each changed package saying its fonts are modified.

Check before merging: a family registered from a font package whose font is then loaded as a hinted copy from elsewhere now logs a size warning. A family in plain fontdata doesn't. This matters only if the plugin moves to this branch and registers those fonts as packages. See "Ranges warning" below.

Try it

Rebuilds the fonts and ranges from the script. The tree should stay clean.

V=$(mktemp -d)
python3 -m venv "$V/fonttools" && "$V/fonttools/bin/pip" install fonttools==4.68.0
"$V/fonttools/bin/python" -I utils/dehint_fonts.py
composer ranges:update
git status --short

Test plan

  • CI is green, including the snapshot job
  • The one fixture whose pages changed in Ghostscript, unicode-zero-width-space, looks acceptable
  • A PDF using DejaVu Sans and FreeSerif looks right in Acrobat and a browser viewer, which haven't been tested
  • A decision is made on the ranges warning and the Aboriginal Serif licence question below
More info

How this differs from #850

gravitypdf-v6 (#850) gravitypdf (this PR)
Fonts ttfonts/ packages/<package>/fonts/
Ranges files data/character-ranges/ packages/<package>/fonts/<font file>.ranges.php, written by composer ranges:update from the registered packages
Fonts changed 47, with the follow-up v6 PR 51
Size saved 7.81 MB, with the follow-up v6 PR 10.94 MB (24.3%)
Fixtures None changed 218 refreshed
Changelog CHANGELOG.md CHANGELOG-GRAVITYPDF.md, because CHANGELOG.md is kept identical to upstream's
  • Font list. 43 of Strip TrueType hinting from the bundled fonts whose licences allow it #850's 44 files are here. 42 are byte-identical to v6's originals, including Garuda.ttf.
  • AboriginalSansREGULAR.ttf is a newer build here (9.602 against 9.601, 1,445,592 bytes against 800,480). Same licence, so it's kept on the list.
  • UnBatang_0613.ttf isn't here. UnBatang.ttf and UnBatangBold.ttf take its place, both GPL, so both are stripped. They give 4.2 MB of the saving.
  • Aboriginal Sans Bold, Italic and Bold Italic aren't on v6. Their name tables and the package licence say GPLv3, so they're stripped.
  • Aboriginal Serif (4 files, also not on v6) is left byte-identical. See "Licence questions".
  • Other fonts not on v6 have a Reserved Font Name (Abyssinica, Jomolhari, Dai Banna, Tai Heritage) or no hinting (Noto Emoji, OCR-A, OCR-B), so they're left byte-identical. Quivira.otf differs from v6's but has no hinting either.
  • Packages are Composer path packages at version 0.1, not published yet and export-ignored from archives. No manifest, size or hash other than the ranges files records the fonts.
  • Modification notice. The GPL fonts need to record that they were modified. On v6 the changelog and script do that because they ship beside the fonts. A package published on its own would carry neither, so the note is in each package's README too.
  • AGENTS.md doesn't exist on gravitypdf, so I followed v6's copy and CHANGELOG-GRAVITYPDF.md's own conventions.

Why the fixtures changed

On v6, snapshot tests compare pixels, so the old fixtures keep passing. Here Snapshot compares bytes, then objects, then pages. composer test runs the snapshots too, and tests.yml installs no Imagick, so a fixture whose font stream changed fails there: with CI=1 and no Imagick, TableBackgroundSnapshotTest fails with "Imagick is not installed".

So 218 of the 235 snapshot tests needed new fixtures, and the suite's own notice asks for that (composer snapshot:update). They were checked against the old fixtures before being replaced:

  • Across the first 217 diffs, the changed lines are /Length1, the font streams' size and hash, and, in documents with XMP metadata, the document UUID that follows from them. No page content, widths or font dictionaries moved.
  • With Imagick and Ghostscript at 120 dpi, 216 documents have identical pages.
  • unicode-zero-width-space differs (RMSE 0.0075). Only its rotated textcircle words in DejaVu Sans change: Ghostscript drew them thinner with the hints. Poppler gives the same pixels for the old and new document. That change is the only one a reader of the fixtures could see, and I looked at it enlarged: the text is correct, and slightly heavier.

khmer-coeng is the 218th, added with the second commit. Poppler renders it pixel-identically, and regenerating every fixture afterwards changed no other file. The refreshed fixtures are 1.43 MB smaller in total. exif-orientation, exif-orientation-greyscale and png-transparency go through GD and were written on macOS. They were byte-identical on this machine before this change, so they should behave on CI as they did.

Verification

fontTools 4.68.0, Ghostscript and poppler pdftoppm, PHP 8.5.12 on macOS.

Check Result
Outlines, glyph bounding boxes, glyph order, advance widths, character maps Identical in all 51 fonts
Other tables Byte-identical apart from the removed tables, glyf, loca
head and maxp Only checkSumAdjustment, maxSizeOfInstructions and, in one font, indexToLocFormat differ
Hinting left None
The other 34 fonts Byte-identical
Re-running the script and composer ranges:update No changes
Ranges diff Only size lines, 17 files
Poppler at 72, 150 and 300 dpi Pixel-identical on all 51
vendor/bin/phpunit --no-coverage, with Imagick 13488 tests, 31 skipped, as before this change
The same with CI=1 and no Imagick, as tests.yml runs it 13488 tests, 34 skipped (before the changelog entry)
Each snapshot test file on its own All 235 pass, none with a "bytes differ" notice
phpcs on AddFontTest.php Clean
  • Assertion count. It falls by 4, all in TableChecksumTest::testMatchesTheChecksumsAFontStates, which asserts once per table of the font. The font has lost cvt , fpgm, prep and gasp. Per-test JUnit output on both trees shows no other change.
  • PDF size. 300 sampled glyphs per font with useOTL off: 3,101,313 to 2,367,633 bytes (23.7%). Real documents will vary, and complex-script shaping isn't covered by that comparison.
  • Not run: PHP versions other than 8.5 and Windows locally, PHPStan (no src/ changes), Acrobat or browser viewers.

Ranges warning

I ran this with a fontFileFinder handing back the hinted originals:

Case Result
garuda from its registered package, as a backup font, loaded from outside the package Warns: ... may have moved text into the wrong font: it was written for a font of 39092 bytes, but the "garuda" font loaded is 55756 bytes. Write the file again with vendor/bin/mpdf-font-ranges.
The same fonts in plain fontdata, with no package No warning. No shipped ranges file is read, and the cache's ranges come from the font on disk.
Registered package, dehinted fonts No warning

The ranges are the same either way, so the warning is noise, but its wording suggests a real problem. It differs from v6, where any shipped family and file name match data/character-ranges/. Here it applies only to families registered from a package.

The plugin requires dev-gravitypdf-v6 today, so nothing changes for it yet. When it moves to this branch, this depends on how it registers its fonts. Its fonts/core-fonts.json also records mpdf-core-fonts v1.0.0's sizes and hashes, which stay hinted and aren't touched here. I've left the decision open.

Licence questions

  • Aboriginal Serif. The package licence says GPLv3, but the fonts' name tables say "You may not copy or distribute this software" without permission. I left them byte-identical, and that conflict is worth a look regardless of hinting.
  • Khmer OS. Stripped. Its name table and LICENSE.txt here say LGPL 2.1, with a trademark notice and no Reserved Font Name. The v6 branch also has a KhmerOFL.txt reserving the name "Khmer", dated 2010, which is why Strip TrueType hinting from the bundled fonts whose licences allow it #850 excluded it. This file is version 3.01 from 2005 and states LGPL itself, so I read that text as belonging to a different font. That's an inference, and the maintainer has accepted it.
  • Ayar. Stripped. Its licence here is OFL 1.1 and names no Reserved Font Name. Its name table says "All rights reserved" and it has no licence text of its own on v6, so this rests on that licence file.
  • MPH 2B Damase. Stripped. Its licence file and name table both say public domain.

All three files match mpdf-core-fonts v1.1.0, which still has them hinted.

Size figures

composer.json and the bundle READMEs now give the installed size of each package's files: Font Bundle All goes from 110MB to 99MB, Font Bundle Lite from 30MB to 26MB, and DejaVu from 10MB to 9MB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gravitypdf-bot gravitypdf-bot added the full-ci Run the full PHP and OS test matrix on this pull request label Nov 5, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jakejackson1
jakejackson1 merged commit 80048fb into gravitypdf Nov 5, 2026
@jakejackson1
jakejackson1 deleted the chore/dehint-fonts branch November 5, 2026 03:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

full-ci Run the full PHP and OS test matrix on this pull request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants