1use crate::bindgen::{FPDF_BOOL, FS_QUADPOINTSF};
5use crate::bindings::PdfiumLibraryBindings;
6use crate::error::{PdfiumError, PdfiumInternalError};
7use crate::pdf::matrix::PdfMatrix;
8use crate::pdf::points::PdfPoints;
9use crate::pdf::rect::PdfRect;
10use std::fmt::{Display, Formatter};
11use std::hash::{Hash, Hasher};
12
13#[cfg(doc)]
14use crate::pdf::document::page::PdfPage;
15
16#[derive(Debug, Copy, Clone)]
30pub struct PdfQuadPoints {
31 x1: PdfPoints,
32 y1: PdfPoints,
33 x2: PdfPoints,
34 y2: PdfPoints,
35 x3: PdfPoints,
36 y3: PdfPoints,
37 x4: PdfPoints,
38 y4: PdfPoints,
39}
40
41impl PdfQuadPoints {
42 pub const ZERO: PdfQuadPoints = PdfQuadPoints::zero();
44
45 #[inline]
46 pub(crate) fn from_pdfium(points: FS_QUADPOINTSF) -> Self {
47 PdfQuadPoints::new_from_values(
48 points.x1, points.y1, points.x2, points.y2, points.x3, points.y3, points.x4, points.y4,
49 )
50 }
51
52 #[inline]
53 pub(crate) fn from_pdfium_as_result(
54 result: FPDF_BOOL,
55 points: FS_QUADPOINTSF,
56 bindings: &dyn PdfiumLibraryBindings,
57 ) -> Result<PdfQuadPoints, PdfiumError> {
58 if !bindings.is_true(result) {
59 Err(PdfiumError::PdfiumLibraryInternalError(
60 PdfiumInternalError::Unknown,
61 ))
62 } else {
63 Ok(PdfQuadPoints::from_pdfium(points))
64 }
65 }
66
67 #[inline]
73 #[allow(clippy::too_many_arguments)]
74 pub const fn new(
75 x1: PdfPoints,
76 y1: PdfPoints,
77 x2: PdfPoints,
78 y2: PdfPoints,
79 x3: PdfPoints,
80 y3: PdfPoints,
81 x4: PdfPoints,
82 y4: PdfPoints,
83 ) -> Self {
84 Self {
85 x1,
86 y1,
87 x2,
88 y2,
89 x3,
90 y3,
91 x4,
92 y4,
93 }
94 }
95
96 #[inline]
102 #[allow(clippy::too_many_arguments)]
103 pub const fn new_from_values(
104 x1: f32,
105 y1: f32,
106 x2: f32,
107 y2: f32,
108 x3: f32,
109 y3: f32,
110 x4: f32,
111 y4: f32,
112 ) -> Self {
113 Self::new(
114 PdfPoints::new(x1),
115 PdfPoints::new(y1),
116 PdfPoints::new(x2),
117 PdfPoints::new(y2),
118 PdfPoints::new(x3),
119 PdfPoints::new(y3),
120 PdfPoints::new(x4),
121 PdfPoints::new(y4),
122 )
123 }
124
125 #[inline]
130 pub const fn zero() -> Self {
131 Self::new_from_values(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
132 }
133
134 #[inline]
136 pub fn from_rect(rect: &PdfRect) -> Self {
137 PdfQuadPoints::new(
138 rect.left(),
139 rect.bottom(),
140 rect.right(),
141 rect.bottom(),
142 rect.right(),
143 rect.top(),
144 rect.left(),
145 rect.top(),
146 )
147 }
148
149 #[inline]
151 pub fn x1(&self) -> PdfPoints {
152 self.x1
153 }
154
155 #[inline]
157 pub fn y1(&self) -> PdfPoints {
158 self.y1
159 }
160
161 #[inline]
163 pub fn x2(&self) -> PdfPoints {
164 self.x2
165 }
166
167 #[inline]
169 pub fn y2(&self) -> PdfPoints {
170 self.y2
171 }
172
173 #[inline]
175 pub fn x3(&self) -> PdfPoints {
176 self.x3
177 }
178
179 #[inline]
181 pub fn y3(&self) -> PdfPoints {
182 self.y3
183 }
184
185 #[inline]
187 pub fn x4(&self) -> PdfPoints {
188 self.x4
189 }
190
191 #[inline]
193 pub fn y4(&self) -> PdfPoints {
194 self.y4
195 }
196
197 pub fn left(&self) -> PdfPoints {
199 *vec![self.x1, self.x2, self.x3, self.x4]
200 .iter()
201 .min()
202 .unwrap()
203 }
204
205 pub fn right(&self) -> PdfPoints {
207 *vec![self.x1, self.x2, self.x3, self.x4]
208 .iter()
209 .max()
210 .unwrap()
211 }
212
213 pub fn bottom(&self) -> PdfPoints {
215 *vec![self.y1, self.y2, self.y3, self.y4]
216 .iter()
217 .min()
218 .unwrap()
219 }
220
221 pub fn top(&self) -> PdfPoints {
223 *vec![self.y1, self.y2, self.y3, self.y4]
224 .iter()
225 .max()
226 .unwrap()
227 }
228
229 #[inline]
231 pub fn width(&self) -> PdfPoints {
232 self.right() - self.left()
233 }
234
235 #[inline]
237 pub fn height(&self) -> PdfPoints {
238 self.top() - self.bottom()
239 }
240
241 #[inline]
244 pub fn transform(&self, matrix: PdfMatrix) -> PdfQuadPoints {
245 let (x1, y1) = matrix.apply_to_points(self.x1, self.y1);
246 let (x2, y2) = matrix.apply_to_points(self.x2, self.y2);
247 let (x3, y3) = matrix.apply_to_points(self.x3, self.y3);
248 let (x4, y4) = matrix.apply_to_points(self.x4, self.y4);
249
250 PdfQuadPoints::new(x1, y1, x2, y2, x3, y3, x4, y4)
251 }
252
253 pub fn to_rect(&self) -> PdfRect {
256 let xs = [self.x1, self.x2, self.x3, self.x4];
257 let ys = [self.y1, self.y2, self.y3, self.y4];
258
259 PdfRect::new(
260 *ys.iter().min().unwrap(),
261 *xs.iter().min().unwrap(),
262 *ys.iter().max().unwrap(),
263 *xs.iter().max().unwrap(),
264 )
265 }
266
267 #[inline]
268 pub(crate) fn as_pdfium(&self) -> FS_QUADPOINTSF {
269 FS_QUADPOINTSF {
270 x1: self.x1.value,
271 y1: self.y1.value,
272 x2: self.x2.value,
273 y2: self.y2.value,
274 x3: self.x3.value,
275 y3: self.y3.value,
276 x4: self.x4.value,
277 y4: self.y4.value,
278 }
279 }
280}
281
282impl PartialEq for PdfQuadPoints {
286 fn eq(&self, other: &Self) -> bool {
287 self.x1 == other.x1
288 && self.y1 == other.y1
289 && self.x2 == other.x2
290 && self.y2 == other.y2
291 && self.x3 == other.x3
292 && self.y3 == other.y3
293 && self.x4 == other.x4
294 && self.y4 == other.y4
295 }
296}
297
298impl Eq for PdfQuadPoints {}
302
303impl Hash for PdfQuadPoints {
304 fn hash<H: Hasher>(&self, state: &mut H) {
305 state.write_u32(self.x1.value.to_bits());
306 state.write_u32(self.y1.value.to_bits());
307 state.write_u32(self.x2.value.to_bits());
308 state.write_u32(self.y2.value.to_bits());
309 state.write_u32(self.x3.value.to_bits());
310 state.write_u32(self.y3.value.to_bits());
311 state.write_u32(self.x4.value.to_bits());
312 state.write_u32(self.y4.value.to_bits());
313 }
314}
315
316impl Display for PdfQuadPoints {
317 #[inline]
318 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
319 f.write_fmt(format_args!(
320 "PdfQuadPoints(x1: {}, y1: {}, x2: {}, y2: {}, x3: {}, y3: {}, x4: {}, y4: {})",
321 self.x1.value,
322 self.y1.value,
323 self.x2.value,
324 self.y2.value,
325 self.x3.value,
326 self.y3.value,
327 self.x4.value,
328 self.y4.value
329 ))
330 }
331}
332
333#[cfg(test)]
334mod tests {
335 use crate::prelude::*;
336
337 #[test]
338 fn test_quadpoints_extents() {
339 let r = PdfRect::new_from_values(50.0, 100.0, 300.0, 200.0);
340
341 assert_eq!(r.to_quad_points().left().value, 100.0);
342 assert_eq!(r.to_quad_points().right().value, 200.0);
343 assert_eq!(r.to_quad_points().top().value, 300.0);
344 assert_eq!(r.to_quad_points().bottom().value, 50.0);
345
346 assert_eq!(r.to_quad_points().width().value, 100.0);
347 assert_eq!(r.to_quad_points().height().value, 250.0);
348 }
349
350 #[test]
351 fn test_quadpoints_to_rect() {
352 let r = PdfRect::new_from_values(100.0, 100.0, 200.0, 200.0);
353 assert_eq!(r.to_quad_points().to_rect(), r);
354
355 let m = PdfMatrix::identity()
356 .rotate_clockwise_degrees(45.0)
357 .unwrap();
358 let r45 = r.transform(m);
359
360 let q = r.to_quad_points();
361 let q45 = q.transform(m);
362
363 assert_eq!(q.to_rect(), r);
364 assert_eq!(q45.to_rect(), r45);
365
366 let s = q45.transform(m.invert()).to_rect();
375 let threshold = PdfPoints::new(0.001);
376 assert!((s.top() - r.top()).abs() < threshold);
377 assert!((s.bottom() - r.bottom()).abs() < threshold);
378 assert!((s.left() - r.left()).abs() < threshold);
379 assert!((s.right() - r.right()).abs() < threshold);
380 }
381}