You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fixes a glyph-lookup bug in simple fonts (/Type1 with embedded /FontFile
or bare-CFF /FontFile3 /Type1C programs) that custom-encode low character
codes via a /Encoding/Differences array. The synthetic codepoint-to-glyph
lookup built for these embedded font programs was keyed off a generic,
document-independent Adobe-glyph-name guess table, completely disconnected
from the font dictionary's own /Differences-declared glyph names. As a
result, any glyph reachable only through a /Differences override whose
name wasn't in the generic table (for example the AGL uniXXXX convention,
e.g. /uni03BC, or /thinspace, which was missing from the table
entirely), or whose name the embedded font's own CFF charset spelled
differently than the generic guess, silently failed to render.
Root-caused against a real-world reproducer PDF (two subsetted Gotham
Type1C fonts with /Differences remapping codes to /thinspace, /uni03BC, /fi, /fl, /f_f) and fixed by:
Adding an AGL uniXXXX/uXXXX hex-codepoint parsing fallback
(TryResolveGlyphNameToCodepoint), used by ApplyDifferences.
Adding the missing "thinspace" entry to StandardGlyphNames.
Reordering BuildResolvedSimpleFont to resolve /Encodingbefore
loading the embedded font program, then building a per-font-dictionary
enriched codepoint-to-glyph-name map (BuildEmbeddedFontGlyphNameMap)
that lets the document's own literal /Differences-declared name win
over the generic guess, passed into LoadType1Font/LoadType1CFont.
Correcting a stale doc comment that contradicted ApplyDifferences's
actual (and unchanged) tolerant handling of unrecognized names.
A real-world regression fixture (text-type1c-differences-agl-ligatures.pdf,
extracted and trimmed from the reporter's own PDF) plus targeted thinspace/uni03BC tests were added. The f_f ligature glyph name
remains an accepted, documented scope boundary (true underscore-ligature
decomposition is a separate concern).
Type of Change
Bug fix (non-breaking change which fixes an issue)
New feature (non-breaking change which adds functionality)
Breaking change (fix or feature that would cause existing functionality to not work as expected)
Documentation update
Code quality improvement
Related Issues
Closes #
Pre-Submission Checklist
Before submitting this pull request, ensure you have completed the following:
Build and Test
Code builds successfully and all tests pass: pwsh ./build.ps1 (7422/7422 passing)
Code produces zero warnings
Code Quality
New code has appropriate XML documentation comments
Static analyzer warnings have been addressed
Quality Checks
Please run the following checks before submitting:
All linters pass: pwsh ./lint.ps1 (exit code 0)
Testing
Added unit tests for new functionality
Updated existing tests if behavior changed
All tests follow the AAA (Arrange, Act, Assert) pattern
Test coverage is maintained or improved
Documentation
Updated README.md (if applicable)
Updated docs/ documentation (if applicable)
Added code examples for new features (if applicable)
Also independently verified against an external multi-corpus PDF rendering
harness (pdftest/pypdf/pdf20/payloads/user4, 115 real-world files): 0
Timeout / 0 Crashed, consistent with the pre-fix baseline, and the original
reporter's full reproducer PDF now renders its previously-failing pages
with zero exceptions (remaining exceptions on unrelated pages are a
pre-existing, out-of-scope JPXDecode/JPEG2000 limitation).
Simple fonts with embedded Type1/Type1C (bare CFF) programs built their
synthetic codepoint-to-glyph lookup from a generic, document-independent
Adobe-glyph-name guess table, ignoring the font dictionary's own
/Encoding/Differences-declared glyph names. When a /Differences array
named a glyph via the AGL uniXXXX convention (e.g. /uni03BC) or a name
missing from the generic table (e.g. /thinspace), or when the embedded
font's own CFF charset spelled a glyph differently than the generic
guess, the glyph silently failed to resolve.
- Add TryResolveGlyphNameToCodepoint with an AGL uniXXXX/uXXXX hex
fallback, used by ApplyDifferences.
- Add the missing "thinspace" entry to StandardGlyphNames.
- Resolve /Encoding before loading the embedded font program in
BuildResolvedSimpleFont, and build a per-font-dictionary enriched
codepoint-to-glyph-name map (BuildEmbeddedFontGlyphNameMap) that lets
the document's own /Differences-declared name win over the generic
guess, passed into LoadType1Font/LoadType1CFont.
- Correct a stale doc comment claiming unrecognized /Differences names
throw, matching ApplyDifferences' actual tolerant behavior.
- Add a real-world regression fixture (text-type1c-differences-agl-ligatures.pdf)
plus targeted thinspace/uni03BC tests.
Verified against the external corpus harness: 0 Timeout/0 Crashed across
all corpora, and the original reporter's reproducer PDF now renders its
previously-failing pages with zero exceptions.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Reject syntactically-valid-looking u-prefixed glyph names whose parsed
value exceeds 0x10FFFF (the highest valid Unicode codepoint), e.g.
/uFFFFFF, instead of treating them as resolved and overwriting the base
encoding. TryParseUppercaseHexDigits now range-checks the parsed value.
- Add a classic Type 1 (/FontFile) regression test mirroring the existing
bare-CFF (/FontFile3) /uni03BC coverage, proving the /Differences
enrichment map also reaches LoadType1Font, not only LoadType1CFont.
BuildEmbeddedType1FontResources gained a customGlyphName parameter to
support this.
- Add a boundary regression test for the out-of-range u-prefixed name
rejection above.
- Correct a test description and a fixture README rationale that both
overstated which /Differences names fall outside StandardGlyphNames:
/thinspace is now a direct entry in that table (added by this fix) -
only /uni03BC needs the AGL uniXXXX hex fallback.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
These new paragraphs describe the PDF as an exception, but the README's opening still says every fixture is hand-authored and the fixture table above does not list this filename. Please qualify the opening provenance statement and add text-type1c-differences-agl-ligatures.pdf to the table so the fixture catalog is internally consistent.
…ME provenance
Adds a Theory covering u1234/u10000/u10FFFF (4/5/6-digit AGL hex glyph names)
resolving end-to-end via an embedded Type1C font, addressing review feedback that
only the out-of-range rejection path was previously tested.
Also qualifies the PdfFixtures README's opening and third-party-corpus provenance
statements and adds the missing text-type1c-differences-agl-ligatures.pdf row to
the fixture table.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The new classic /FontFile regression test PdfDocument_Fonts_Differences_Uni03BCName_ResolvesViaEnrichedEmbeddedType1FontGlyphMap is not present in this requirement's Type1 tests list. The requirement prose now covers the enriched map, but this path is not traceable to verification; add the test to the Type1FontResolution list and its verification section.
Add classic FontFile test to Type1 verification coverage
This verification section lists only the Type1C test and the real-world fixture, but the PR also adds PdfDocument_Fonts_Differences_Uni03BCName_ResolvesViaEnrichedEmbeddedType1FontGlyphMap for the classic /FontFile path. Add that test to the Type1FontResolution verification list; otherwise the newly documented classic-path coverage is omitted from the verification traceability.
…e provenance
Adds PdfDocument_Fonts_Differences_Uni03BCName_ResolvesViaEnrichedEmbeddedType1FontGlyphMap
to the Type1FontResolution reqstream tests list and verification document, which
previously traced only the Type1C/CFF variant.
Corrects the PdfFixtures README's provenance/licensing overview: it previously
claimed text-type1c-differences-agl-ligatures.pdf was the only third-party
exception and that the entire corpus is MIT-licensed, overlooking that
text-embedded-truetype-font.pdf and text-composite-truetype-identity-h.pdf also
embed real, SIL Open Font License-licensed Open Sans bytes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The new parser reports /uni0000 and /u0000 as successfully resolved, but ResolveEncoding uses codepoint 0 as its undefined-slot sentinel and drops any entry whose value is zero (lines 474-480). Thus these newly accepted AGL names silently remain unmapped instead of reaching the embedded glyph. Either support an explicit defined/undefined state in the encoding table or reject zero consistently in this parser.
Correct PdfFixtures summary to reflect real-world fixture provenance
The class-level XML summary still says the entire PdfFixtures corpus is hand-authored, while this newly added test deliberately opens the real-world excerpt described here. Update that summary so the test documentation does not contradict the fixture provenance.
The class-level summary still claimed the entire PdfFixtures corpus is
hand-authored, contradicting the test immediately below that deliberately opens
the real-world text-type1c-differences-agl-ligatures.pdf excerpt.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This parser accepts uni0000/u0000 as a valid codepoint (the parsed value is 0), but ResolveEncoding later uses 0 as the undefined sentinel and drops table entries whose value is 0. Consequently the new fallback silently fails for a valid U+0000 glyph name; either preserve an explicit mapped-zero state through encoding resolution or reject U+0000 consistently with the representation.
Third-party fixture lacks verifiable provenance and licensing
Unlike the repository's other third-party fixture documentation, this entry identifies only a reporter's document and provides no source URL, authorship, license text, or sidecar provenance record. Since the PDF bytes are redistributed in the test package, add verifiable provenance/licensing here (as SvgFixtures/README.md does) or keep the regression fixture synthetic.
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
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.
Pull Request
Description
Fixes a glyph-lookup bug in simple fonts (
/Type1with embedded/FontFileor bare-CFF
/FontFile3 /Type1Cprograms) that custom-encode low charactercodes via a
/Encoding/Differencesarray. The synthetic codepoint-to-glyphlookup built for these embedded font programs was keyed off a generic,
document-independent Adobe-glyph-name guess table, completely disconnected
from the font dictionary's own
/Differences-declared glyph names. As aresult, any glyph reachable only through a
/Differencesoverride whosename wasn't in the generic table (for example the AGL
uniXXXXconvention,e.g.
/uni03BC, or/thinspace, which was missing from the tableentirely), or whose name the embedded font's own CFF charset spelled
differently than the generic guess, silently failed to render.
Root-caused against a real-world reproducer PDF (two subsetted Gotham
Type1C fonts with
/Differencesremapping codes to/thinspace,/uni03BC,/fi,/fl,/f_f) and fixed by:uniXXXX/uXXXXhex-codepoint parsing fallback(
TryResolveGlyphNameToCodepoint), used byApplyDifferences."thinspace"entry toStandardGlyphNames.BuildResolvedSimpleFontto resolve/Encodingbeforeloading the embedded font program, then building a per-font-dictionary
enriched codepoint-to-glyph-name map (
BuildEmbeddedFontGlyphNameMap)that lets the document's own literal
/Differences-declared name winover the generic guess, passed into
LoadType1Font/LoadType1CFont.ApplyDifferences'sactual (and unchanged) tolerant handling of unrecognized names.
A real-world regression fixture (
text-type1c-differences-agl-ligatures.pdf,extracted and trimmed from the reporter's own PDF) plus targeted
thinspace/uni03BCtests were added. Thef_fligature glyph nameremains an accepted, documented scope boundary (true underscore-ligature
decomposition is a separate concern).
Type of Change
Related Issues
Closes #
Pre-Submission Checklist
Before submitting this pull request, ensure you have completed the following:
Build and Test
pwsh ./build.ps1(7422/7422 passing)Code Quality
Quality Checks
Please run the following checks before submitting:
pwsh ./lint.ps1(exit code 0)Testing
Documentation
Additional Notes
Also independently verified against an external multi-corpus PDF rendering
harness (pdftest/pypdf/pdf20/payloads/user4, 115 real-world files): 0
Timeout / 0 Crashed, consistent with the pre-fix baseline, and the original
reporter's full reproducer PDF now renders its previously-failing pages
with zero exceptions (remaining exceptions on unrelated pages are a
pre-existing, out-of-scope JPXDecode/JPEG2000 limitation).