1use pdf_render::pdf_syntax::object::dict::keys;
11use pdf_render::pdf_syntax::object::Rect;
12use pdf_render::pdf_syntax::page::Page;
13
14#[derive(Debug, Clone, Copy, PartialEq)]
16pub struct PageBox {
17 pub x0: f64,
19 pub y0: f64,
21 pub x1: f64,
23 pub y1: f64,
25}
26
27impl PageBox {
28 pub fn width(&self) -> f64 {
30 (self.x1 - self.x0).abs()
31 }
32
33 pub fn height(&self) -> f64 {
35 (self.y1 - self.y0).abs()
36 }
37
38 pub fn pixels(&self, dpi: f64) -> (u32, u32) {
40 let scale = dpi / 72.0;
41 (
42 (self.width() * scale).ceil() as u32,
43 (self.height() * scale).ceil() as u32,
44 )
45 }
46}
47
48impl From<Rect> for PageBox {
49 fn from(r: Rect) -> Self {
50 Self {
51 x0: r.x0,
52 y0: r.y0,
53 x1: r.x1,
54 y1: r.y1,
55 }
56 }
57}
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub enum PageRotation {
62 None,
64 Rotate90,
66 Rotate180,
68 Rotate270,
70}
71
72impl PageRotation {
73 pub fn degrees(&self) -> u32 {
75 match self {
76 Self::None => 0,
77 Self::Rotate90 => 90,
78 Self::Rotate180 => 180,
79 Self::Rotate270 => 270,
80 }
81 }
82}
83
84#[derive(Debug, Clone)]
86pub struct PageGeometry {
87 pub media_box: PageBox,
89 pub crop_box: PageBox,
91 pub trim_box: Option<PageBox>,
93 pub bleed_box: Option<PageBox>,
95 pub art_box: Option<PageBox>,
97 pub rotation: PageRotation,
99}
100
101impl PageGeometry {
102 pub fn effective_dimensions(&self) -> (f64, f64) {
104 let w = self.crop_box.width();
105 let h = self.crop_box.height();
106 match self.rotation {
107 PageRotation::Rotate90 | PageRotation::Rotate270 => (h, w),
108 _ => (w, h),
109 }
110 }
111
112 pub fn pixel_dimensions(&self, dpi: f64) -> (u32, u32) {
114 let (w, h) = self.effective_dimensions();
115 let scale = dpi / 72.0;
116 ((w * scale).ceil() as u32, (h * scale).ceil() as u32)
117 }
118}
119
120pub(crate) fn extract_geometry(page: &Page<'_>) -> PageGeometry {
122 let media_box = PageBox::from(page.media_box());
123 let crop_box = PageBox::from(page.crop_box());
124
125 let rotation = match page.rotation() {
126 pdf_render::pdf_syntax::page::Rotation::None => PageRotation::None,
127 pdf_render::pdf_syntax::page::Rotation::Horizontal => PageRotation::Rotate90,
128 pdf_render::pdf_syntax::page::Rotation::Flipped => PageRotation::Rotate180,
129 pdf_render::pdf_syntax::page::Rotation::FlippedHorizontal => PageRotation::Rotate270,
130 };
131
132 let raw = page.raw();
133 let trim_box = raw.get::<Rect>(keys::TRIM_BOX).map(PageBox::from);
134 let bleed_box = raw.get::<Rect>(keys::BLEED_BOX).map(PageBox::from);
135 let art_box = raw.get::<Rect>(keys::ART_BOX).map(PageBox::from);
136
137 PageGeometry {
138 media_box,
139 crop_box,
140 trim_box,
141 bleed_box,
142 art_box,
143 rotation,
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn page_box_dimensions() {
153 let b = PageBox {
154 x0: 0.0,
155 y0: 0.0,
156 x1: 612.0,
157 y1: 792.0,
158 };
159 assert!((b.width() - 612.0).abs() < f64::EPSILON);
160 assert!((b.height() - 792.0).abs() < f64::EPSILON);
161 }
162
163 #[test]
164 fn page_box_pixels() {
165 let b = PageBox {
166 x0: 0.0,
167 y0: 0.0,
168 x1: 72.0,
169 y1: 72.0,
170 };
171 assert_eq!(b.pixels(72.0), (72, 72));
172 assert_eq!(b.pixels(144.0), (144, 144));
173 }
174
175 #[test]
176 fn rotation_degrees() {
177 assert_eq!(PageRotation::None.degrees(), 0);
178 assert_eq!(PageRotation::Rotate90.degrees(), 90);
179 assert_eq!(PageRotation::Rotate180.degrees(), 180);
180 assert_eq!(PageRotation::Rotate270.degrees(), 270);
181 }
182
183 #[test]
184 fn geometry_effective_dimensions() {
185 let g = PageGeometry {
186 media_box: PageBox {
187 x0: 0.0,
188 y0: 0.0,
189 x1: 612.0,
190 y1: 792.0,
191 },
192 crop_box: PageBox {
193 x0: 0.0,
194 y0: 0.0,
195 x1: 612.0,
196 y1: 792.0,
197 },
198 trim_box: None,
199 bleed_box: None,
200 art_box: None,
201 rotation: PageRotation::Rotate90,
202 };
203 let (w, h) = g.effective_dimensions();
204 assert!((w - 792.0).abs() < f64::EPSILON);
205 assert!((h - 612.0).abs() < f64::EPSILON);
206 }
207}