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