fix(render-pdf): draw Latin text as letters rather than ligatures - #601
Merged
Merged
Conversation
PDFBox applies a font's GSUB substitutions itself: a PDType0Font carrying them rewrites every shown string from characters into glyph identifiers, and in most of the bundled families `ti`, `tf` and `ft` each became one glyph. The ToUnicode map is built by reading the font's character map backwards, where a ligature is reachable from no character at all, so the entry was absent and both letters were lost on extraction — `Platform` came back as `Pla orm`. The page looked right, which is why nothing showed it, and the text layer is what search, copy-and-paste, screen readers and applicant tracking systems read. A Latin face is now handed to PDFBox with nothing to substitute. That is what the engine already assumed: layout measures a string ligature-blind, so a line drawn with ligatures was narrower than the box measured for it, and the DOCX and PPTX backends never substituted. Faces whose active script is one PDFBox shapes — Devanagari, Bengali, Gujarati — keep their substitutions, because there they are how the script renders. PDFBox applies ccmp, liga and clig together with no way to keep one without the others; in the bundled families the Latin ccmp changes nothing, so only the ligature pairs move. Tests: PdfLatinLigatureTextLayerTest sweeps all 35 bundled families and CvPresetTextLayerTest all 16 CV presets, both asserting the probe words come back whole; without the fix 10 of the 16 presets go red. Full reactor gate green (762 qa tests); the layered CV and cover-letter visual baselines and the 25 drifted committed previews were re-recorded.
This was referenced Aug 25, 2026
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.
Rebase of #576 onto develop. Same commit, same content; the only change is the CHANGELOG entry, which now sits inside the existing
### Fixedsection of v2.2.1 instead of opening a second one. #576 conflicted only on that file after #596 / #598 / #599 / #600 landed. Opened as a new branch rather than force-pushingfix/pdf-latin-text-layer, because #577 is stacked on it — #576 can be closed as superseded, and #577 retargeted todevelop.Why (from #576, re-verified on today's develop)
The defect is live. Rendering
"Platform notification fifty software"and reading the text layer back:It is not a corner case.
Typography(headlineFont, bodyFont, …)puts Lato in the bodyFont slot of 10 of the 17 typography presets, so it is the body text of most shipped templates. Counting the pairs in three committed CV previews:titfftZero across three résumés of English prose — every one of those pairs was dropped. That is what an ATS parser reads.
Verification of this rebase
Full reactor gate on the rebased branch:
clean verifyover the eight modules —BUILD SUCCESS, qa 785 tests, 0 failures. That matters more than usual here: the 26 visual baselines this PR re-recorded were blessed before #596 (SVG opacity), #598, #599 and #600 landed, and they still match — nothing that landed in between moved those pages.After installing this branch, the same probe reads back clean on every family, Lato and Carlito included.
Unchanged from #576
64 lines of production in
PdfFontLoader.java;PdfLatinLigatureTextLayerTest(render-pdf) andCvPresetTextLayerTest(qa) as the guards; 26 visual baselines and 25 committed example previews re-recorded by the ligature change alone.