diff --git a/crates/annot/src/draw.rs b/crates/annot/src/draw.rs index 27f38f43f6..e36bf3a534 100644 --- a/crates/annot/src/draw.rs +++ b/crates/annot/src/draw.rs @@ -40,7 +40,15 @@ pub(crate) fn draw_appearance(doc: &mut Document, r: ObjRef) -> Result<(), Annot /// Draw the appearance for an annotation dictionary: the expensive half of [`set_appearance`]. /// Pure from the dictionary and the objects it points at, so [`AppearanceCache`] can reuse it. fn build_appearance(doc: &Document, d: &Dict) -> Option { - let mut stream = appearance::build(&with_direct_values(doc, d))?; + // References followed first. Where that turns up something the builder doesn't draw (a + // cloudy `/BE` on a square), the drawing made without them stays, rather than none. + let direct = with_direct_values(doc, d); + let built = appearance::build(&direct); + let mut stream = match (built, direct) { + (Some(stream), _) => stream, + (None, std::borrow::Cow::Owned(_)) => appearance::build(d)?, + (None, std::borrow::Cow::Borrowed(_)) => return None, + }; // Picture-stamp restyling must retain the placement's page-axis correction. if d.get(b"PCPicture").is_some() { let matrix = d.get(b"AP").and_then(|ap| { diff --git a/crates/annot/src/tests.rs b/crates/annot/src/tests.rs index 6acb8ce742..28ab7e8fa7 100644 --- a/crates/annot/src/tests.rs +++ b/crates/annot/src/tests.rs @@ -1487,6 +1487,22 @@ fn indirect_border_style_and_colour_are_drawn() { assert_eq!(props.width, Some(6.0), "the properties show the width drawn"); } +/// A followed reference the builder can't draw (a cloudy border effect on a square, which it +/// leaves alone) keeps the drawing made without it instead of none. +#[test] +fn an_undrawable_indirect_value_keeps_the_plain_drawing() { + let doc = document_of(&[ + b"<< /Type /Catalog /Pages 2 0 R >>", + b"<< /Type /Pages /Kids [3 0 R] /Count 1 /MediaBox [0 0 200 200] >>", + b"<< /Type /Page /Parent 2 0 R /Annots [4 0 R] >>", + b"<< /Type /Annot /Subtype /Square /Rect [20 20 180 180] /BE 5 0 R /C [1 0 0] >>", + b"<< /S /C /I 1 >>", + ]); + let display = with_missing_appearances(&doc).expect("the square is still drawn"); + let shown = list(&display, 0).remove(0); + assert!(ap_content(&display, &shown).contains("1 0 0 RG\n1 w\n")); +} + /// Following references copies a bounded number of values: an indirect array past the budget /// stays a reference (and the comment is not drawn from it), a chain that never ends is not /// followed, and links to other objects are never copied.