Repository navigation
BUG: Map suffixed and ligature glyph names in font encodings - #4310
michaelkillgta wants to merge 2 commits into
Conversation
Signed-off-by: michaelkillgta <269775689+michaelkillgta@users.noreply.github.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4310 +/- ##
==========================================
+ Coverage 98.55% 98.63% +0.08%
==========================================
Files 59 59
Lines 11992 11976 -16
Branches 2268 2275 +7
==========================================
- Hits 11819 11813 -6
+ Misses 93 86 -7
+ Partials 80 77 -3 ☔ 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 check the coverage and provide exact references to the official specification.
Signed-off-by: michaelkillgta <269775689+michaelkillgta@users.noreply.github.com>
| code_point = glyph_name[2:] | ||
| else: | ||
| # Adobe Glyph List Specification, section 2, step 1. | ||
| base_name = glyph_name.lstrip("/").split(".", 1)[0] |
There was a problem hiding this comment.
| base_name = glyph_name.lstrip("/").split(".", 1)[0] | |
| base_name = glyph_name.removeprefix("/").split(".", maxsplit=1)[0] |
| base_name = glyph_name.lstrip("/").split(".", 1)[0] | ||
| if not base_name: | ||
| return None | ||
| if f"/{base_name}" in adobe_glyphs: |
There was a problem hiding this comment.
Consider using the Walrus operator with a dedicated variable to avoid repeated conversion.
| return None | ||
| if f"/{base_name}" in adobe_glyphs: | ||
| return adobe_glyphs[f"/{base_name}"] | ||
| result = "" |
There was a problem hiding this comment.
Prefer a list and "".join over repeated string concatenation.
| See the Adobe Glyph List Specification, version 2.9, section 2, step 3. | ||
| Surrogates (D800-DFFF) and values above 10FFFF are rejected. | ||
| """ | ||
| if component.startswith("uni") and len(component) > 3 and len(component) % 4 == 3: |
There was a problem hiding this comment.
You are calculating len(component) quite often. Consider using a dedicated variable.
| """ | ||
| Map a ``uniXXXX[XXXX...]`` or ``uXXXX[XX]`` glyph name component. | ||
|
|
||
| See the Adobe Glyph List Specification, version 2.9, section 2, step 3. |
There was a problem hiding this comment.
Do you have a link to the specification?
| code_points = [component[1:]] | ||
| else: | ||
| return None | ||
| result = "" |
There was a problem hiding this comment.
See above for concatenating.
| return None | ||
| result = "" | ||
| for code_point in code_points: | ||
| if not all(c in "0123456789ABCDEFabcdef" for c in code_point): |
There was a problem hiding this comment.
| if not all(c in "0123456789ABCDEFabcdef" for c in code_point): | |
| if not all(c in string.hexdigits for c in code_point): |
| if not all(c in "0123456789ABCDEFabcdef" for c in code_point): | ||
| return None | ||
| value = int(code_point, 16) | ||
| if 0xD800 <= value <= 0xDFFF or value > 0x10FFFF: |
There was a problem hiding this comment.
Please define these range limits as named constants for better readability/documentation.
| return result | ||
|
|
||
|
|
||
| def _glyph_component_code_points(component: str) -> str | None: |
There was a problem hiding this comment.
Function names should start with an action.
|
|
||
| @pytest.mark.parametrize( | ||
| ("glyph_name", "expected"), | ||
| [ |
There was a problem hiding this comment.
Are you able to provide meaningful parameter IDs to document what each line is trying to ensure?
Closes #4309.
_glyph_name_to_unicodenow follows the Adobe Glyph List mapping rules.Specification references
/Differencesarray uses standard character names, map the code to a character name and then "look up the character name in the Adobe Glyph List" to get the Unicode value.a.sc->a,one.oldstyle->1).f_i->f,i).unifollowed by groups of four hex digits, each in 0000-D7FF or E000-FFFF (uni00660069->fi), orufollowed by four to six hex digits, in 0000-D7FF or E000-10FFFF. This is why/u1no longer turns into"\x01"and surrogates are rejected.Lcommaaccent_uni20AC0308_u1040C.alternateanduniD801DC0Care used as test cases.Deviations from the specification
Each of these keeps pypdf's current behavior:
/two.superior, andf_f_i.altstill maps toffi./uni00e9.Noneinstead, so callers keep the raw glyph name as before (/f_unknownglyph,/.sc).The same helper is used for CFF and Type 1 font program encodings, so those get the same handling.
Tests
tests/test_cmap.py. It fails on main and passes here._glyph_name_to_unicodeand_glyph_component_code_pointsis covered:pytest tests/test_cmap.py --cov=pypdf._cmap --cov-branch.ruff check pypdf testsandmypy pypdfpass locally.