Skip to content

fix(stamp): font-path hardening — unresolvable /Resources references and direct-dict /Font entries - #11

Merged
alexjcoles merged 5 commits into
mainfrom
alexc/amphtt-1234-stamprs-font-path-hardening-unresolvable-resources
Jul 9, 2026
Merged

alexjcoles merged 5 commits into
mainfrom
alexc/amphtt-1234-stamprs-font-path-hardening-unresolvable-resources

Conversation

@alexjcoles

Copy link
Copy Markdown
Contributor

Summary

Fixes two pre-existing gaps in the stamp.rs font path, surfaced (not introduced) by the AMPHTT-1232 review — AMPHTT-1234:

  • ensure_font errors on unresolvable /Resources references — previously a dangling reference silently fell through to the Missing branch (replacing the page's corrupt entry with a fresh dict) or stopped the inheritance walk short. Now a /Resources reference that doesn't resolve to a dictionary is a hard Err on the page and on any /Parent ancestor consulted during the walk — uniform with the /Font (AMPHTT-1232) and /Annots (AMPHTT-1157) contracts. Callers discard the document on error.
  • find_base_font_in matches direct-dictionary /Font entries (legal per ISO 32000 §7.8.3) — previously only Reference-shaped entries were found, so an existing direct-dict font was duplicated as a fresh F_PS_* object on every stamp pass.
  • Documented and pinned the malformed-direct-/Font repair in add_font_to_resources (the crate's direct-repair/indirect-error asymmetry, mirroring annotation.rs). Behaviour unchanged.

Follow-up commits from an xhigh /code-review on this branch: extracted require_dict (immutable sibling of require_dict_mut) and the shared Phase-1 tail; added a #[track_caller] page_font_dict test helper and tightened the walk-Err pins (page untouched + no orphan objects).

One review finding was deliberately not fixed here: /Font resource keys flow unescaped/UTF-8-lossy into Tf operators — pre-existing on main, filed as AMPHTT-1235.

Plan (decision record incl. the uniform-Err vs walk-past-corrupt-ancestor argument): docs/plans/pdf-native-library/amphtt-1234-stamprs-font-path-hardening.md in the Rails repo.

Test plan

  • cargo test --workspace — 99 tests green after every commit
  • New tests: dangling leaf /Resources reference → Err (two shapes, page untouched, no orphan objects); unresolvable intermediate /Resources with a valid grandparent → Err (pins the uniform contract); direct-dict /Font entry found with no duplicate registration; malformed direct /Font value repaired

AMPHTT-1234

🤖 Generated with Claude Code

alexjcoles and others added 5 commits July 8, 2026 14:46
Pre-existing in the old pdf-stamp binary and carried through the port:
an unresolvable /Resources reference broke out of the /Parent walk as
Missing, so a dangling leaf reference was silently replaced with a
fresh direct dict and a corrupt intermediate node stopped inheritance
short. The nearest /Resources is Phase 2's write target, so the walk
now fails loudly there — uniform with the /Font (AMPHTT-1232) and
/Annots (AMPHTT-1157) reference contracts; callers discard the
document on error.

AMPHTT-1234
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
find_base_font_in only matched entries stored as Object::Reference, so
a font embedded as a direct dictionary (legal per ISO 32000 §7.8.3)
was never found and ensure_font registered a duplicate F_PS_* object
on every stamp pass. Bounded and rendering-neutral, but needless —
extract the dictionary from either entry shape. Unresolvable entry
references stay lookup-misses: this function is pure lookup and never
becomes a write target, so the AMPHTT-1232 lookup/registration split
is unchanged.

AMPHTT-1234
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…etry

add_font_to_resources silently replaces a non-dict, non-reference
direct /Font value — the crate's established direct-repair /
indirect-error convention (annotation.rs states it for /Annots), but
undocumented and untested here. Comment the arm and pin it with a
test; behaviour is unchanged.

AMPHTT-1234
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s tail

Code-review follow-up on the AMPHTT-1234 branch: the new Phase-1 chain
was the second hand-rolled immutable copy of the resolve-id-to-dict-or-
error idiom (the other in the Inherited branch), and the two Phase-1
match arms ended in a token-identical lookup/location/break tail. Name
the immutable sibling of require_dict_mut and run the shared tail once;
error strings and behaviour are unchanged.

AMPHTT-1234
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Code-review follow-up on the AMPHTT-1234 branch. The branch had doubled
(2 to 4, plus a split-syntax fifth) the copies of the page -> /Resources
-> /Font match-panic pyramid; a #[track_caller] page_font_dict helper
collapses them in the module's parent_pages_id/set_page_font style.
Also closes two pin gaps the review sweep found: the non-dict leaf
/Resources shape now re-checks the page was left untouched, and all
three walk-Err tests assert the object count is unchanged, pinning the
"Err from the /Parent walk adds no orphan objects" half of the
ensure_font contract.

AMPHTT-1234
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@linear

linear Bot commented Jul 8, 2026

Copy link
Copy Markdown

AMPHTT-1234

@alexjcoles
alexjcoles merged commit 883667a into main Jul 9, 2026
1 check passed
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.

1 participant