Repository navigation
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4345 +/- ##
=======================================
Coverage 98.81% 98.81%
=======================================
Files 59 59
Lines 12139 12161 +22
Branches 2310 2316 +6
=======================================
+ Hits 11995 12017 +22
Misses 70 70
Partials 74 74 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
stefan6419846
left a comment
There was a problem hiding this comment.
Please review the inline comments and the coverage.
| return None | ||
|
|
||
|
|
||
| def _differences_codes(encoding: DictionaryObject) -> set[int]: |
There was a problem hiding this comment.
Function names should start with an action.
| return codes | ||
| code = 0 | ||
| for entry in differences: | ||
| if isinstance(entry, int): |
There was a problem hiding this comment.
Please explain this logic.
|
I'm currently unable to test or contribute. That being said, I contributed the CFF font program parser, in a way analogous to the already existing code for conventional Type1 font programs. At the time, I was already wondering why we parsed the embedded font to add a character_map. With the code here, I'm starting to wonder even more whether parsing the embedded font's encoding should be part of the _parse_to_unicode() method (the current case) or the _parse_encoding() method instead. I'm wondering, @Ak4shh , whether you considered this. This is not to suggest that you should change this PR, but it might be something to keep in mind. I was somewhat out of my depth in this regard when I added the CFF font program parser, which is why I only added the clearly missing functionality and did not reconsider the existing implementation for parsing the Type1 font. In fact, I was working locally on the final missing piece here for simple font encodings, that is, parsing an embedded TTF program, and while I'm pretty sure that my local code does what it should, it produced gibberish for one of the files in our test suite... Might be the same issue. |
…ding For a Type1 font that embeds a CFF program, ISO 32000-2:2020, 9.6.6.1 makes the program's built-in encoding only the default base encoding: a named /Encoding or a /BaseEncoding replaces it, and /Differences override the codes they list. The map derived with fontTools was handed to get_encoding() as if it came from a /ToUnicode CMap. Every code the program defines became chr(code) and was then mapped through the program's glyph names, so the font dictionary's own /Encoding was ignored and documents relying on /BaseEncoding + /Differences were extracted as garbage. _rank_font_program_encoding() now drops the derived entries that the font dictionary overrides, before the identity rule in get_encoding() applies. Fixes py-pdf#4343. Signed-off-by: Ak4shh <akashposa40@gmail.com>
420b856 to
7c86078
Compare
Fixes #4343.
The bug
When a Type1 font embeds a CFF program (
/FontFile3with/Subtype /Type1C),the encoding read from the program with fontTools was treated like a
/ToUnicodeCMap: every code the program defines becamechr(code)and wasthen mapped through the program's glyph names. The font dictionary's own
/Encodinglost, whatever it contained.ISO 32000-2:2020, 9.6.6.1 ranks it the other way round. The built-in encoding
of a font program is only the default base encoding:
/Encoding/BaseEncoding/BaseEncoding/Differencesoverride the listed codes in every case.The fix
_rank_font_program_encoding()applies those rules to the map derived from thefont program, before
get_encoding()sees it:/Encodingor a/BaseEncodingreplaces the built-in encoding, sothe derived entries are dropped;
/Differences-only/Encoding, the derived entries for the codes the/Differenceslist are dropped, so they fall back to the font dictionary;/Encoding, nothing is dropped.The map is cached per font program stream (#4156), so the filtering runs on the
copy returned for each font dictionary and leaves the cache intact.
Tests
test_get_encoding__cff_type1_font_program_is_ranked_below_font_encodingbuildsa CFF program that maps the codes
0x41/0x42toZ/Y, puts(AB)in thecontent stream, and covers every row of the table plus a null
/Encodingand an/Encodingdictionary without/Differences:Full suite
python -m pytest tests -qon Windows / Python 3.13: 2403 passed, 11 skipped,10 xfailed, 1 failed, 99 errors — the same failure and errors as unmodified
main(2397 passed). Both are environmental: the sandbox temp directory is notwritable (
PermissionError/OSError) and thesample-filessubmodule isempty.
passedrises by exactly the 6 new cases.Scope
Two files:
pypdf/_cmap.py,tests/test_cmap.py.AI assistance
Written and reviewed together with DeepSeek Harness (
deepseek-flash): itdrafted the patch and the tests, and I reviewed and verified them.