From e73b2b29b049bc399b865ee3f047bd39eccb49bd Mon Sep 17 00:00:00 2001 From: Velli20 Date: Tue, 13 Oct 2026 21:56:19 +0300 Subject: [PATCH] Add optional_rect and required_rect dictionary readers Read four-number rectangle entries by key and use them for /CropBox, /RD, /BBox and /MediaBox instead of repeating optional_array_of::. Co-Authored-By: Claude Opus 5.5 --- .../src/source_decode_subtypes.rs | 18 +++------ crates/pdf-document/src/document.rs | 3 +- crates/pdf-document/src/page.rs | 4 +- crates/pdf-object-reader/src/dictionary.rs | 38 +++++++++++++++---- 4 files changed, 38 insertions(+), 25 deletions(-) diff --git a/crates/pdf-annotation-core/src/source_decode_subtypes.rs b/crates/pdf-annotation-core/src/source_decode_subtypes.rs index b1aefca9..c02533ed 100644 --- a/crates/pdf-annotation-core/src/source_decode_subtypes.rs +++ b/crates/pdf-annotation-core/src/source_decode_subtypes.rs @@ -9,7 +9,7 @@ use crate::pdf_data::{ StampAnnotation, StrikeOutAnnotation, TextAnnotation, UnderlineAnnotation, WidgetAnnotation, }; use crate::source_decode::{DecodeResult, color}; -use pdf_graphics::{polyline::Polyline, rect::Rect}; +use pdf_graphics::polyline::Polyline; use pdf_object_reader::{ dictionary::Dictionary, object_lookup::ObjectLookupExt, object_resolver::ObjectResolver, object_variant::ObjectVariant, @@ -141,9 +141,7 @@ impl FreeTextAnnotation { .get(b"CL") .map(|value| callout_line(value, objects)) .transpose()?; - let difference_rect = dictionary - .optional_array_of::(b"RD", objects)? - .map(Rect::from); + let difference_rect = dictionary.optional_rect(b"RD", objects)?; let intent = dictionary.optional_bytes(b"IT", objects)?.map(Vec::from); let border_effect = BorderEffect::from_dictionary(dictionary, b"BE", objects)?; @@ -197,9 +195,7 @@ impl SquareAnnotation { let border_style = BorderStyle::from_dictionary(dictionary, b"BS", objects)?; let interior_color = color(dictionary, b"IC", objects)?; let border_effect = BorderEffect::from_dictionary(dictionary, b"BE", objects)?; - let difference_rect = dictionary - .optional_array_of::(b"RD", objects)? - .map(Rect::from); + let difference_rect = dictionary.optional_rect(b"RD", objects)?; Ok(Self { border_style, @@ -218,9 +214,7 @@ impl CircleAnnotation { let border_style = BorderStyle::from_dictionary(dictionary, b"BS", objects)?; let interior_color = color(dictionary, b"IC", objects)?; let border_effect = BorderEffect::from_dictionary(dictionary, b"BE", objects)?; - let difference_rect = dictionary - .optional_array_of::(b"RD", objects)? - .map(Rect::from); + let difference_rect = dictionary.optional_rect(b"RD", objects)?; Ok(Self { border_style, @@ -362,9 +356,7 @@ impl CaretAnnotation { dictionary: &Dictionary, objects: &dyn ObjectResolver, ) -> DecodeResult { - let difference_rect = dictionary - .optional_array_of::(b"RD", objects)? - .map(Rect::from); + let difference_rect = dictionary.optional_rect(b"RD", objects)?; let style = dictionary .get(b"Sy") .map(|value| value.try_bytes(objects).map(CaretSymbolStyle::from)) diff --git a/crates/pdf-document/src/document.rs b/crates/pdf-document/src/document.rs index 444e5716..28e52764 100644 --- a/crates/pdf-document/src/document.rs +++ b/crates/pdf-document/src/document.rs @@ -29,8 +29,7 @@ impl FromPdfObject for PdfDocument { let media_box = context.dictionary().optional_media_box(context.source())?; let crop_box = context .dictionary() - .optional_array_of::(b"CropBox", context.source())? - .map(Rect::from); + .optional_rect(b"CropBox", context.source())?; let rotation = context .dictionary() .optional_number::(b"Rotate", context.source())?; diff --git a/crates/pdf-document/src/page.rs b/crates/pdf-document/src/page.rs index 313bb5d6..8b429768 100644 --- a/crates/pdf-document/src/page.rs +++ b/crates/pdf-document/src/page.rs @@ -178,9 +178,7 @@ impl FromPdfObject for PdfPage { context.optional::(b"Contents")? }; let media_box = dictionary.optional_media_box(context.source())?; - let crop_box = dictionary - .optional_array_of::(b"CropBox", context.source())? - .map(Rect::from); + let crop_box = dictionary.optional_rect(b"CropBox", context.source())?; let rotation = dictionary.optional_number::(b"Rotate", context.source())?; let resources = context .optional_shared::(b"Resources")? diff --git a/crates/pdf-object-reader/src/dictionary.rs b/crates/pdf-object-reader/src/dictionary.rs index e9904f89..159f1a08 100644 --- a/crates/pdf-object-reader/src/dictionary.rs +++ b/crates/pdf-object-reader/src/dictionary.rs @@ -116,22 +116,48 @@ impl Dictionary { }) } + /// Reads the optional rectangle entry with the given key. + /// + /// Missing entries and explicit PDF `null` values are treated as absent. + /// The coordinates are returned in their original order. + pub fn optional_rect( + &self, + key: &[u8], + objects: &dyn ObjectResolver, + ) -> Result, ObjectError> { + Ok(self + .optional_array_of::(key, objects)? + .map(Rect::from)) + } + + /// Reads the required rectangle entry with the given key. + /// + /// The coordinates are returned in their original order. + pub fn required_rect( + &self, + key: &[u8], + objects: &dyn ObjectResolver, + ) -> Result { + self.required_array_of::(key, objects) + .map(Rect::from) + } + /// Reads the optional `/BBox` entry as a rectangle. /// /// Missing entries and explicit PDF `null` values are treated as absent. /// The rectangle is normalized so that `left <= right` and `top <= bottom`. pub fn optional_bbox(&self, objects: &dyn ObjectResolver) -> Result, ObjectError> { Ok(self - .optional_array_of::(b"BBox", objects)? - .map(|bbox| Rect::from(bbox).normalized())) + .optional_rect(b"BBox", objects)? + .map(|bbox| bbox.normalized())) } /// Reads the required `/BBox` entry as a rectangle. /// /// The rectangle is normalized so that `left <= right` and `top <= bottom`. pub fn required_bbox(&self, objects: &dyn ObjectResolver) -> Result { - self.required_array_of::(b"BBox", objects) - .map(|bbox| Rect::from(bbox).normalized()) + self.required_rect(b"BBox", objects) + .map(|bbox| bbox.normalized()) } /// Reads optional `/Width` and `/Height` entries as an origin-based integer rectangle. @@ -175,9 +201,7 @@ impl Dictionary { &self, objects: &dyn ObjectResolver, ) -> Result, ObjectError> { - Ok(self - .optional_array_of::(b"MediaBox", objects)? - .map(Rect::from)) + self.optional_rect(b"MediaBox", objects) } /// Reads the optional `/Matrix` entry as an affine transform.