mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-09 02:33:51 +00:00
M11: a font named through a chain of references is the font PDF/A checks
A /Font entry can point at an object that is nothing but a reference to another (ISO 32000-1 7.3.10). The fonts something draws with were recorded by the entry's own reference, so the font dictionary at the end of such a chain went unchecked. Follow the chain, as resolve does, up to 32 references. (CodeRabbit review on #6172.) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
2cf0554b4a
commit
abb409bac6
@@ -341,12 +341,25 @@ fn drawing_fonts(doc: &Document, objs: &[(ObjRef, std::sync::Arc<Object>)]) -> s
|
||||
let Some(resources) = dict_of(o).and_then(|d| d.get(b"Resources")).map(|r| doc.resolve(r)) else { continue };
|
||||
let Some(fonts) = resources.as_dict().and_then(|r| r.get(b"Font")).map(|f| doc.resolve(f)) else { continue };
|
||||
if let Some(fonts) = fonts.as_dict() {
|
||||
out.extend(fonts.iter().filter_map(|(_, f)| f.as_ref()));
|
||||
out.extend(fonts.iter().filter_map(|(_, f)| f.as_ref()).map(|r| font_object(doc, r)));
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// How many references [`font_object`] follows, as the document's own `resolve` does.
|
||||
const MAX_REFERENCE_HOPS: usize = 32;
|
||||
|
||||
/// The object a font entry's reference `r` ends at: an object can be nothing but a reference to
|
||||
/// another (ISO 32000-1 7.3.10), and the font dictionary is the last one in that chain.
|
||||
fn font_object(doc: &Document, mut r: ObjRef) -> ObjRef {
|
||||
for _ in 0..MAX_REFERENCE_HOPS {
|
||||
let Object::Ref(next) = &*doc.get(r) else { break };
|
||||
r = *next;
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
/// Whether a stream's own dictionary says it holds content: a form XObject, or a stream with a
|
||||
/// `/BBox` and no `/Subtype` (a tiling pattern, or an appearance stream that leaves out
|
||||
/// `/Subtype /Form`). Mesh shadings may carry a `/BBox` too, but their data isn't content.
|
||||
|
||||
@@ -507,12 +507,16 @@ fn only_fonts_something_draws_with_must_be_embedded() {
|
||||
"<< /Type /Font /Subtype /Type1 /BaseFont /ZapfDingbats >>",
|
||||
"<< /Type /Annot /Subtype /Widget /FT /Tx /T (name) /F 4 /Rect [10 10 90 30] /P 3 0 R /AP << /N 7 0 R >> >>",
|
||||
&format!("<< /Type /XObject /Subtype /Form /BBox [0 0 80 20] /Resources << {appearance_fonts} >> /Length 0 >>\nstream\n\nendstream"),
|
||||
// An object that is nothing but a reference to the Helvetica font (ISO 32000-1 7.3.10).
|
||||
"4 0 R",
|
||||
],
|
||||
"",
|
||||
)
|
||||
};
|
||||
assert!(fonts(&form("")).is_empty(), "{:?}", fonts(&form("")));
|
||||
assert_eq!(fonts(&form("/Font << /Helv 4 0 R >>")), ["The font Helvetica isn't embedded"]);
|
||||
// Named through a reference to it, it is still the font something draws with.
|
||||
assert_eq!(fonts(&form("/Font << /Helv 8 0 R >>")), ["The font Helvetica isn't embedded"]);
|
||||
// A page's own font is checked as before.
|
||||
assert_eq!(fonts(&messy()), ["The font Helvetica isn't embedded"]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user