M5: an indirect value the builder can't draw keeps the plain drawing (view.render-annotation-appearances)

Following references can turn up a value the appearance builder leaves
alone, such as a cloudy /BE on a square. The comment then keeps the
drawing made without the references followed, instead of none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn
This commit is contained in:
mtlcl410andClaude Opus 5.5 committed 2026-11-07 12:01:00 +08:00
1 parent e69b433156
commit 4bbd3a2626
2 files changed
+25 -1

No files matched your search

+9 -1
View File
@@ -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<Stream> {
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| {
+16
View File
@@ -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.