Skip to content

ENH: AppearanceStream: Consider more encodings for Type1 core fonts#3905

Open
PJBrs wants to merge 14 commits into
py-pdf:mainfrom
PJBrs:3318
Open

ENH: AppearanceStream: Consider more encodings for Type1 core fonts#3905
PJBrs wants to merge 14 commits into
py-pdf:mainfrom
PJBrs:3318

Conversation

@PJBrs

@PJBrs PJBrs commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

The unembedded Adobe core fonts contain around 315 characters and therefore support more encodings than only cp1252 (WinAnsi). I've found that the following encodings appear to be supported:
"cp1252", # Western European (WinAnsi base)
"cp1250", # Central / Eastern European
"cp1254", # Turkish
"cp1257", # Baltic Rim
"iso8859_15", # Western European ISO Alternate

When we encounter form field text with unencodable characters with one of the 14 Adobe core fonts, we try one of the above encodings instead. If successful, we make a new Font instance with that encoding, add it to the appearance stream, and add it to the document's font resources with a differences encoding and an associated ToUnicode cmap.

Fixes #3318

Achieves full coverage for _font.py.

This PR does include a small bug fix very similar in character to the added code, and refactors one test.

Assisted by Google Gemini, especially the changes for generating the ToUnicode cmap.

EDIT

I added some patches to always fall back to StandardEncoding for simple fonts when /Encoding is not defined in the font resource (and when a Type1 font does not provide its own encoding). This simplifies my original PR (don't need to deal with charmap encoding anymore. I've changed code elsewhere accordingly.

@codecov

codecov Bot commented Jun 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.90%. Comparing base (efc511b) to head (dd2ace8).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3905      +/-   ##
==========================================
+ Coverage   97.86%   97.90%   +0.03%     
==========================================
  Files          57       57              
  Lines       10738    10777      +39     
  Branches     2006     2017      +11     
==========================================
+ Hits        10509    10551      +42     
+ Misses        127      125       -2     
+ Partials      102      101       -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@PJBrs
PJBrs marked this pull request as draft June 27, 2026 14:28
PJBrs added 4 commits July 5, 2026 11:04
The unembedded Adobe core fonts contain around 315 characters and therefore
support more encodings than only cp1252 (WinAnsi). I've found that the
following encodings appear to be supported:
    "cp1252",       # Western European (WinAnsi base)
    "cp1250",       # Central / Eastern European
    "cp1254",       # Turkish
    "cp1257",       # Baltic Rim
    "iso8859_15",   # Western European ISO Alternate

Test:

import unicodedata

from pypdf._codecs import fill_from_encoding
from pypdf._codecs.core_font_metrics import CORE_FONT_METRICS

test_encodings = (
    "cp1250", "cp1252", "cp1254", "cp1257", "iso8859_15",
)

for encoding in test_encodings:
    encoding_list = fill_from_encoding(encoding)
    for font in CORE_FONT_METRICS:
        if CORE_FONT_METRICS[font].font_descriptor.name not in {"Symbol", "ZapfDingbats"}:
            for char in encoding_list:
                if unicodedata.category(char).startswith("C") or char.isspace():
                    continue
                if char not in CORE_FONT_METRICS[font].character_widths.keys():
                    print (f"Unmatched: {encoding}: {char} ({ascii(char)})")
When generating a font resource for one of the 14 Type1 fonts,
add a Differences encoding when applicable.
…_file

test_font_from_font_file performs a series of tests that mimic a damaged
font file in some way or other. One such test was the test for zero units
per em. Just integrate it with the others.
@PJBrs
PJBrs force-pushed the 3318 branch 2 times, most recently from 3f029ba to 5c4aeec Compare July 5, 2026 12:00
@PJBrs
PJBrs marked this pull request as ready for review July 5, 2026 12:26
PJBrs added 10 commits July 8, 2026 08:53
The PDF specification of font subtypes can be somewhat confusing,
because the font subtypes do not align exactly with font file types.

First, the specification uses "Simple Fonts", which can consist of
Type1 or TrueType font files (or Type3 fonts where "glyphs" are PDF
objects). These fonts are 8-bit encoded. Then there are composite
fonts (Type0), which are 16-bit encoded.

When we initialise a font from a font file, we should set font.sub_type
in line with the PDF font type, not with the file type. This patch
therefore uses Type0 for fonts that we initialise from TrueType font
files, in accordance with the fact the we 16-bit encode them.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Form rendering issues with special characters in input text

1 participant