Apply the metrics and the collection font a fontdata entry sets to the font it registers (#321) - #322
Merged
Conversation
…e font it registers (#321) Since 93515f7 the Ascent, Descent and Leading overrides were written to a local $desc that nothing read, while the font was registered with the metrics from the cache. They now go into $font['desc'], as they went into $desc before v7.1.8, so line heights, the baseline and the embedded FontDescriptor follow them again. The cache is not rewritten, so another document reading it gets the font's own metrics. The same commit set TTCfontID to the result of isset(), so every configured font of a TrueType Collection was read as its first. It is the configured index again, and metrics cached for another font of the collection, including the ones cached while every font was read as the first, are regenerated rather than reused. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
jakejackson1
force-pushed
the
fix/321-fontdata-metric-overrides
branch
from
September 22, 2026 07:20
07d587a to
6f321eb
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two
fontdatasettings have been read wrongly since v7.1.8, both broken by the same commit: theAscent,DescentandLeadingoverrides have no effect, andTTCfontIDalways selects the first font of a TrueType Collection. This fixes both.Cause: the metric overrides
AddFont()loaded the font cache byrequire-ing a PHP file that defined$desc. The override block changed$desc, and the font was registered with'desc' => $desc.$fontand registers'desc' => $font['desc']. The override block was left writing to$desc, which nothing reads any more.Fix: the metric overrides
The three assignments now write to
$font['desc']instead of$desc.isset(...) && $this->fontdata[$family][...]test is unchanged from before v7.1.8, so an override of0is still ignored. ForLeading, 0 is what most fonts declare anyway. ForAscent,_getNormalLineheight()and_setLineYpos()treat 0 as "no metrics" and use the fallback, so 0 could never have been a useful override.$fontis this document's copy of what was read from.mtx.json, and nothing writes the cache after this point. The cache keeps the font's own metrics, and another document sharing thetempDirgets them.What the overrides affect
The override now reaches the registered font's
desc, which is read in two places:_getNormalLineheight()builds the normal line height fromAscent - Descent + Leading._setLineYpos()usesAscentandDescentfor the box top and bottom, which place the baseline, and addsLeadingagain as a line gap.FontWriterwrites every key ofdescinto it, so/Ascent,/Descentand/Leadingcarry the overrides.Both match how the overrides behaved before v7.1.8.
Measured
This is the example from #321, with the overrides set through the
fontdataconfig so they are in place before the default font is added. DejaVu Sans at 11pt, in<p>Line one<br>Line two<br>Line three</p>:descAscent / Descent / Leading/Ascent 928 /Descent -236 /Leading 0/Ascent 928 /Descent -236 /Leading 0/Ascent 1500 /Descent -900 /Leading 400Before this PR, the document with overrides was byte-identical to the one without. With it, 39.512 = (1.14 × 2.8 + 0.4) × 11:
adjustFontDescLineheighttimes the overridden metrics, plus the line gap. A document that sets no overrides renders as before, and the snapshot suite passes unchanged.(The issue's own snippet sets
$mpdf->fontdata[...]after construction, when the default font is already registered, so it shows no change even with this fix. A font that is first used after the assignment does take the overrides.)The TTCfontID fix
93515f7 also rewrote the
TTCfontIDlookup asso every configured font of a collection became
true.TTFontFile::selectFont()indexes its offsets with it, andtruereads as 1, so the first font was registered and embedded whichever one was asked for. Before v7.1.8 it was$TTCfontID = $this->fontdata[$family]['TTCfontID'][$stylekey];. It is the configured value again.That fix alone would leave caches wrong:
.mtx.jsonis keyed by font key only, and nothing checked the face it was generated from. A cache written before this PR for aTTCfontIDof 2 or more holds the first font's metrics, and the same happens when someone editsTTCfontID.fontMetrics()now regenerates when the cachedTTCfontIDdiffers from the configured one. The comparison is between integers, because the old caches holdtrue, andtrue == 2. A plain font caches 0 and is configured as 0, so nothing else is regenerated.Measured with a collection of two fixture fonts,
TTCfontIDset to['R' => 2]:fonts[...]['TTCfontID']fonts[...]['name']trueMPDFAA+NotoSans-Regular2MPDFAA+Carlito-RegularTests
tests/Mpdf/FontdataMetricOverrideTest.php, with each test using its owntempDir:$mpdf->fonts['dejavusans']['desc']. Metrics that are not overridden keep the font's values.dejavusans.mtx.jsonstill holds 928 / -236 / 0, and a second document without overrides gets those values.Six of the seven cases fail on
gravitypdfwithout the fix. The cache case passes either way, because it guards against the fix leaking into the cache.tests/Mpdf/TtcFontIdTest.phphas no binary fixture, because the repo has no.ttc. Its set-up packs two fixture fonts fromtests/data/ttf/into a collection, rewriting each table offset to where the font now starts.TTCfontIDandname, and the embedded FontName, match the font configured.tempDirgives Carlito for the second, so a cache for another font of the collection is not reused.All three cases fail on
gravitypdf. With only the one-line fix, the shared-cache case still fails.Notes
registerFont(). Thempdf/mpdfmirror makes the same change inAddFont(). It needs theTTCfontIDfix and its cache check too, sincedevelopmenthas the same code.mpdf/mpdfPR needs a changelog entry covering both fixes. Documents that set the overrides will render differently: their line heights, their baselines and their FontDescriptor all change. Documents that use any font of a collection but the first will switch to the font they asked for.Fixes #321
🤖 Generated with Claude Code