mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-14 11:17:10 -04:00
A font the document does not embed was replaced by whatever the caller happened to pick, and nothing told the user. `doc_info` said only `embedded: false`, which is why "my Chinese PDF shows nothing" has been a mystery rather than a sentence. `crates/fonts/src/fallback.rs` applies one chain, in a documented order, and returns what it chose with a reason: 1. embedded -> nothing substituted; 2. /BaseFont names a standard-14 face or a known alias (exact metrics); 3. craft-fonts holds that exact family; 4. /CIDSystemInfo /Ordering names a script we have a face for; 5. the ordering names a script we have no licensed face for -- report none, so nothing is drawn rather than the wrong glyphs; 6. otherwise the nearest of the fourteen by descriptor flags. It delegates to the existing per-script selectors instead of restating their order, so each keeps one source of truth. It answers the dictionary question only; "which face has a glyph for this character?" depends on the string being drawn and stays where the drawing happens. `doc_info` gains `substitute` and `substitution` per font. Both null means the document's own glyphs are shown; a null `substitute` with a reason means no face was available at all. That is Acrobat's Actual Font, minus the interface, which `parity/` now records as still missing. Two findings on the way: `Std14::from_base_font` matches loosely, so "SomeHouseSerif" resolved to Times by its name containing "serif". The face is a fair guess but reporting it as "names one of the standard 14" would have been untrue of a font nobody has heard of. `Std14::recognise` now says whether a name matched an alias or only a family hint, and the two report differently. `from_base_font` is unchanged. The generated table moved to `crates/fonts/src/std14_metrics.rs`, data alone, and the ATTRIBUTION entry and AGENTS.md 1.1 line follow it. F1 put the hash on a file holding both the table and the code reading it, so this commit's ordinary edits tripped the gate -- and a hash that churns on unrelated changes gets re-attested reflexively, which is the protection gone. Verified both directions after the split: editing std14.rs passes the gate, changing one width in std14_metrics.rs fails it. Every new test was watched failing first: removing the script step breaks the two unit tests and the doc_info integration test. Co-Authored-By: Claude <[email protected]>