Skip to content

Recover Type 3 character codes when Unicode mappings are absent #437

Closed

Description

@yhay81

Context

DESIGN.md sections 1, 3, 6 and 12 require engine-faithful text extraction with documented provenance. The PyMuPDF comparison #419 compares tests/assets/real_world/pdfium-type3.pdf: PyMuPDF 2.0 extracts A\nA\nA\n, while current pyropdf skips three glyphs and warns; unmapped="glyph_id" produces replacement characters for Type 3. The Type 3 font in this PDF has no /ToUnicode, and its /Encoding /Differences maps character code 65 to the custom glyph name /C1. PyMuPDF's result is a fallback to investigate, not proven ground truth: arbitrary Type 3 character codes do not guarantee Unicode semantics. tests/test_text.py::test_type3_glyphs_without_unicode_have_no_glyph_id documents current behavior. #247 lists unknown-Unicode options.

Goal

Expose enough original Type 3 character-code and font-encoding information to let callers recover or choose a documented best-effort fallback when Unicode mappings are absent, without silently claiming the inferred character is correct.

Scope

In: inspect hayro's Type 3 glyph callbacks, propose and implement upstream support if necessary, adopt a release, and expose a bounded, explicit fallback policy or original code through pyropdf. Honor valid /ToUnicode and standard glyph-name mappings when available. Preserve or deliberately document default skip-and-warning behavior. Out: inventing Unicode for arbitrary custom glyph names or treating PyMuPDF as semantic ground truth.

Paths

  • Upstream hayro as needed; local proposals and evidence under evidence/
  • crates/pyropdf-engine/src/extract/, crates/pyropdf-python/src/text.rs, python/pyropdf/_text.py, python/pyropdf/_page.py
  • tests/test_text.py and Type 3 fixtures
  • docs/en/api/text-and-content.md and docs/en/api/pages.md (generated from the public option documentation and verified by tests/test_docs.py)

Acceptance

  • Document the information hayro provides for Type 3 glyphs and any upstream design and release needed; obtain owner approval before posting upstream.
  • On the corpus fixture, the original character code 65 is recoverable or a documented opt-in fallback returns A; default behavior and its warning are explicitly tested.
  • Valid /ToUnicode, standard names, a custom non-Latin glyph name, and an invalid or ambiguous mapping are tested; no ambiguous Unicode is silently asserted as known.
  • Text, words, and character results have consistent mappings; limits and malformed input remain bounded.

Notes

Compare with PyMuPDF 2.0 and at least one independent reference, and label heuristic output as such. The current implementation handles Glyph::Type3(_) as a replacement when unmapped="glyph_id".

Activity

  1. added this to the Phase 1: Reading milestone on Oct 30, 2026
  2. added
    upstreamWork that lands in an engine or dependency repository
    adoptionpyropdf adopts a released engine change
    area:enginecrates/pyropdf-engine: interpretation, rendering, writing, understanding
    area:pythoncrates/pyropdf-python and python/pyropdf: the binding and the Python API
    engine:hayroEngine or dependency: hayro
    on Oct 30, 2026
  3. added
    upstream:draftUpstream: prepared here, not yet published upstream; waits on the owner's go-ahead
    upstream:todoUpstream: no draft yet; an agent can investigate and prepare one
    in-progressClaimed; the latest comment names the branch
    and removed
    upstream:draftUpstream: prepared here, not yet published upstream; waits on the owner's go-ahead
    on Nov 2, 2026
  4. yhay81 commented on Nov 2, 2026

    @yhay81
    OwnerAuthor

    Claimed by claude on branch claude/437-type3-char-codes: investigating what hayro 0.8.0 and main provide for Type 3 glyphs (first acceptance item).

  5. added
    upstream:mergedUpstream: merged upstream; waits on a release to adopt
    and removed
    in-progressClaimed; the latest comment names the branch
    upstream:todoUpstream: no draft yet; an agent can investigate and prepare one
    on Nov 2, 2026
  6. yhay81 commented on Nov 2, 2026

    @yhay81
    OwnerAuthor

    Handoff for the first acceptance item. PR: #563 (claude/437-type3-char-codes, evidence only, awaiting owner review).

    What hayro gives (details and probe output in evidence/type3-char-codes/README.md on that branch):

    Decision: no new upstream change or draft is needed. The required change is merged and waits on a hayro release. I moved the label from upstream:todo to upstream:merged (not removing upstream, since pyropdf cannot build on it until a release). I also released the claim.

    Next action: when a hayro release contains #1512, drop upstream and upstream:merged, label adoption, and implement the remaining acceptance items on Glyph::char_code(). Interpreting code 65 as A stays an opt-in, labeled pyropdf policy. The encoding's glyph name is not exposed by hayro, but #437 does not need it.

  7. removed
    upstreamWork that lands in an engine or dependency repository
    upstream:mergedUpstream: merged upstream; waits on a release to adopt
    on Nov 3, 2026
  8. yhay81 commented on Nov 3, 2026

    @yhay81
    OwnerAuthor

    Triage, 2026-11-03: hayro 0.9.0 (released 2026-11-02) contains the upstream fixes this issue waited on, so I replaced upstream and upstream:merged with adoption. pyropdf cannot adopt 0.9 yet: it drops OutlineGlyph::font_cache_key() and SoftMask::id(), which the engine reads. #584 tracks putting them back (LaurenzV/hayro#1734, awaiting review), so this issue is now blocked by #584. The 0.9 bump itself resumes on #18's branch claude/18-hayro-0.9-u32.

    Next action: once a hayro release contains LaurenzV/hayro#1734 and the bump lands, adopt this issue's change.

  9. yhay81 commented on Nov 5, 2026

    @yhay81
    OwnerAuthor

    Dependency triage, 2026-11-06: removed the obsolete blocked-by link to #584. PR #609 already merged the owner-approved hayro 0.9-based commit 394e4a0d37baf8af513f6ebda15f4bada85d6f23, and both the workspace and fuzz manifests use it. This issue's 0.9 capability is available now; #603 separately retains the #584 dependency for returning to published crates. This does not close #584 or waive any acceptance criterion here.

    Next action: Implement the remaining explicit Type 3 character-code recovery policy and tests using the already available Glyph::char_code API.

    No implementation claim is held by this triage. Recheck the live queue, claim a branch and complete the existing acceptance before closing.

  10. added
    in-progressClaimed; the latest comment names the branch
    on Nov 6, 2026
  11. yhay81 commented on Nov 6, 2026

    @yhay81
    OwnerAuthor

    Claimed on codex/437-type3-char-code. Implement the opt-in Type 3 fallback and verify text, words, characters, mapping precedence and limits against the corpus and synthetic cases.

  12. yhay81 commented on Nov 6, 2026

    @yhay81
    OwnerAuthor

    Implemented on codex/437-type3-char-code in #712, head 0b305bb. The shared extraction path recovers Type 3 source codes on explicit request. Tests cover the corpus and synthetic mappings, budget refusals, and consistent text/word/character results. Compared PyMuPDF 2.0 and independent PDFium 156, and updated the generated public reference. All Rust and static checks passed. After correcting the stale generated reference, all 180 documentation and text tests passed. Next: verify current-head CI, review threads and acceptance, then squash-merge.

  13. removed
    in-progressClaimed; the latest comment names the branch
    on Nov 6, 2026
  14. yhay81 commented on Nov 6, 2026

    @yhay81
    OwnerAuthor

    Merged #712 at 742fea1 after Python, Rust, audit, documentation build and all nine fuzz jobs passed. The default warning, opt-in code recovery, mapping precedence, three output forms and extraction limits are covered. Closed and released the claim.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    adoptionpyropdf adopts a released engine changearea:enginecrates/pyropdf-engine: interpretation, rendering, writing, understandingarea:pythoncrates/pyropdf-python and python/pyropdf: the binding and the Python APIengine:hayroEngine or dependency: hayro

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions