Widgets are drawn in a second pass over /Annots, holding nothing per entry; comment order with a widget among them in the test

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn
This commit is contained in:
mtlcl410
2026-11-06 09:22:11 +08:00
co-authored by Claude Opus 5.5
parent 2bace2144f
commit 3dca7f3b01
2 changed files with 17 additions and 4 deletions
@@ -47,10 +47,15 @@ fn pdf(order: &str) -> Vec<u8> {
/// The colour at the page's centre.
fn centre(order: &str) -> [u8; 3] {
at(order, 100, 100)
}
/// The colour at (`x`, `y`) px from the top left.
fn at(order: &str, x: usize, y: usize) -> [u8; 3] {
let mut renderer = PageRenderer::new(Arc::new(pdf(order)), RenderConfig::default());
let out = renderer.render(RenderRequest { page: 0, scale: 1.0, ..Default::default() });
assert!(out.error.is_none(), "{:?}", out.error);
let at = (100 * out.width as usize + 100) * 4;
let at = (y * out.width as usize + x) * 4;
let px = out.rgba.get(at..at + 3).expect("the centre lies on the page");
[px[0], px[1], px[2]]
}
@@ -62,4 +67,8 @@ fn a_widget_is_drawn_above_the_comments_on_its_page() {
// Comments keep their `/Annots` order among themselves.
assert_eq!(centre("RS"), [0, 0, 0], "the black square listed last is on top");
assert_eq!(centre("SR"), [255, 0, 0], "the red square listed last is on top");
// And with a widget among them: (45, 45) is inside both squares, outside the check box.
assert_eq!(at("WRS", 45, 45), [0, 0, 0], "a widget first: the black square listed last is on top");
assert_eq!(at("WSR", 45, 45), [255, 0, 0], "a widget first: the red square listed last is on top");
assert_eq!(centre("WSR"), [0, 255, 0], "and the check box above both");
}
+7 -3
View File
@@ -203,9 +203,12 @@ pub fn interpret_page<'a>(
// PdfCraft patch (86): form fields' widgets are drawn after the page's other
// annotations, each group in `/Annots` order, as other readers draw them: a filled
// comment listed after a check box covered the box. ISO 32000-2 doesn't order the two.
let mut annots: Vec<Dict<'_>> = annot_arr.iter::<Dict<'_>>().collect();
annots.sort_by_key(|a| a.get::<hayro_syntax::object::Name<'_>>(SUBTYPE).as_deref() == Some(b"Widget"));
for annot in annots {
// Two passes over the array, so nothing is held for every entry of a long one.
for widgets in [false, true] {
for annot in annot_arr.iter::<Dict<'_>>() {
if (annot.get::<hayro_syntax::object::Name<'_>>(SUBTYPE).as_deref() == Some(b"Widget")) != widgets {
continue;
}
let flags = annot.get::<u32>(F).unwrap_or(0);
// Annotation should be hidden (Hidden = bit 2, NoView = bit 6).
@@ -321,6 +324,7 @@ pub fn interpret_page<'a>(
draw_highlight_without_appearance(&annot, context, device);
}
}
}
device.end_annotations();
}
}