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#[derive(Debug, Copy, Clone)]
27pub struct PdfQuadPoints {
28 pub x1: PdfPoints,
29 pub y1: PdfPoints,
30 pub x2: PdfPoints,
31 pub y2: PdfPoints,
32 pub x3: PdfPoints,
33 pub y3: PdfPoints,
34 pub x4: PdfPoints,
35 pub y4: PdfPoints,
36 #[deprecated(
41 since = "0.8.28",
42 note = "Use the PdfQuadPoints::bottom() function instead of direct field access. Direct field access will be removed in release 0.9.0."
43 )]
44 pub bottom: PdfPoints,
45
46 #[deprecated(
47 since = "0.8.28",
48 note = "Use the PdfQuadPoints::left() function instead of direct field access. Direct field access will be removed in release 0.9.0."
49 )]
50 pub left: PdfPoints,
51
52 #[deprecated(
53 since = "0.8.28",
54 note = "Use the PdfQuadPoints::top() function instead of direct field access. Direct field access will be removed in release 0.9.0."
55 )]
56 pub top: PdfPoints,
57
58 #[deprecated(
59 since = "0.8.28",
60 note = "Use the PdfQuadPoints::right() function instead of direct field access. Direct field access will be removed in release 0.9.0."
61 )]
62 pub right: PdfPoints,
63}
64
65impl PdfQuadPoints {
66 #[allow(deprecated)]
68 pub const ZERO: PdfQuadPoints = PdfQuadPoints {
69 x1: PdfPoints::zero(),
72 y1: PdfPoints::zero(),
73 x2: PdfPoints::zero(),
74 y2: PdfPoints::zero(),
75 x3: PdfPoints::zero(),
76 y3: PdfPoints::zero(),
77 x4: PdfPoints::zero(),
78 y4: PdfPoints::zero(),
79 bottom: PdfPoints::zero(),
80 left: PdfPoints::zero(),
81 top: PdfPoints::zero(),
82 right: PdfPoints::zero(),
83 };
84
85 #[inline]
86 pub(crate) fn from_pdfium(points: FS_QUADPOINTSF) -> Self {
87 PdfQuadPoints::new_from_values(
88 points.x1, points.y1, points.x2, points.y2, points.x3, points.y3, points.x4, points.y4,
89 )
90 }
91
92 #[inline]
93 pub(crate) fn from_pdfium_as_result(
94 result: FPDF_BOOL,
95 points: FS_QUADPOINTSF,
96 bindings: &dyn PdfiumLibraryBindings,
97 ) -> Result<PdfQuadPoints, PdfiumError> {
98 if !bindings.is_true(result) {
99 Err(PdfiumError::PdfiumLibraryInternalError(
100 PdfiumInternalError::Unknown,
101 ))
102 } else {
103 Ok(PdfQuadPoints::from_pdfium(points))
104 }
105 }
106
107 #[inline]
113 #[allow(clippy::too_many_arguments)]
114 pub fn new(
117 x1: PdfPoints,
118 y1: PdfPoints,
119 x2: PdfPoints,
120 y2: PdfPoints,
121 x3: PdfPoints,
122 y3: PdfPoints,
123 x4: PdfPoints,
124 y4: PdfPoints,
125 ) -> Self {
126 #[allow(deprecated)]
127 Self {
128 x1,
129 y1,
130 x2,
131 y2,
132 x3,
133 y3,
134 x4,
135 y4,
136 left: *[x1, x2, x3, x4].iter().min().unwrap(),
137 right: *[x1, x2, x3, x4].iter().max().unwrap(),
138 bottom: *[y1, y2, y3, y4].iter().min().unwrap(),
139 top: *[y1, y2, y3, y4].iter().max().unwrap(),
140 }
141 }
142
143 #[inline]
149 #[allow(clippy::too_many_arguments)]
150 pub fn new_from_values(
153 x1: f32,
154 y1: f32,
155 x2: f32,
156 y2: f32,
157 x3: f32,
158 y3: f32,
159 x4: f32,
160 y4: f32,
161 ) -> Self {
162 Self::new(
163 PdfPoints::new(x1),
164 PdfPoints::new(y1),
165 PdfPoints::new(x2),
166 PdfPoints::new(y2),
167 PdfPoints::new(x3),
168 PdfPoints::new(y3),
169 PdfPoints::new(x4),
170 PdfPoints::new(y4),
171 )
172 }
173
174 #[inline]
179 pub fn zero() -> Self {
182 Self::new_from_values(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
183 }
184
185 #[inline]
187 pub fn from_rect(rect: &PdfRect) -> Self {
188 PdfQuadPoints::new(
189 rect.left(),
190 rect.bottom(),
191 rect.right(),
192 rect.bottom(),
193 rect.right(),
194 rect.top(),
195 rect.left(),
196 rect.top(),
197 )
198 }
199
200 pub fn left(&self) -> PdfPoints {
202 #[allow(deprecated)]
203 self.left
205 }
210
211 pub fn right(&self) -> PdfPoints {
213 #[allow(deprecated)]
214 self.right
216 }
221
222 pub fn bottom(&self) -> PdfPoints {
224 #[allow(deprecated)]
225 self.bottom
227 }
232
233 pub fn top(&self) -> PdfPoints {
235 #[allow(deprecated)]
236 self.top
238 }
243
244 #[inline]
246 pub fn width(&self) -> PdfPoints {
247 self.right() - self.left()
248 }
249
250 #[inline]
252 pub fn height(&self) -> PdfPoints {
253 self.top() - self.bottom()
254 }
255
256 #[inline]
259 pub fn transform(&self, matrix: PdfMatrix) -> PdfQuadPoints {
260 let (x1, y1) = matrix.apply_to_points(self.x1, self.y1);
261 let (x2, y2) = matrix.apply_to_points(self.x2, self.y2);
262 let (x3, y3) = matrix.apply_to_points(self.x3, self.y3);
263 let (x4, y4) = matrix.apply_to_points(self.x4, self.y4);
264
265 PdfQuadPoints::new(x1, y1, x2, y2, x3, y3, x4, y4)
266 }
267
268 pub fn to_rect(&self) -> PdfRect {
271 let xs = [self.x1, self.x2, self.x3, self.x4];
272 let ys = [self.y1, self.y2, self.y3, self.y4];
273
274 PdfRect::new(
275 *ys.iter().min().unwrap(),
276 *xs.iter().min().unwrap(),
277 *ys.iter().max().unwrap(),
278 *xs.iter().max().unwrap(),
279 )
280 }
281
282 #[inline]
283 pub(crate) fn as_pdfium(&self) -> FS_QUADPOINTSF {
284 FS_QUADPOINTSF {
285 x1: self.x1.value,
286 y1: self.y1.value,
287 x2: self.x2.value,
288 y2: self.y2.value,
289 x3: self.x3.value,
290 y3: self.y3.value,
291 x4: self.x4.value,
292 y4: self.y4.value,
293 }
294 }
295}
296
297impl PartialEq for PdfQuadPoints {
301 fn eq(&self, other: &Self) -> bool {
302 self.x1 == other.x1
303 && self.y1 == other.y1
304 && self.x2 == other.x2
305 && self.y2 == other.y2
306 && self.x3 == other.x3
307 && self.y3 == other.y3
308 && self.x4 == other.x4
309 && self.y4 == other.y4
310 }
311}
312
313impl Eq for PdfQuadPoints {}
317
318impl Hash for PdfQuadPoints {
319 fn hash<H: Hasher>(&self, state: &mut H) {
320 state.write_u32(self.x1.value.to_bits());
321 state.write_u32(self.y1.value.to_bits());
322 state.write_u32(self.x2.value.to_bits());
323 state.write_u32(self.y2.value.to_bits());
324 state.write_u32(self.x3.value.to_bits());
325 state.write_u32(self.y3.value.to_bits());
326 state.write_u32(self.x4.value.to_bits());
327 state.write_u32(self.y4.value.to_bits());
328 }
329}
330
331impl Display for PdfQuadPoints {
332 #[inline]
333 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
334 f.write_fmt(format_args!(
335 "PdfQuadPoints(x1: {}, y1: {}, x2: {}, y2: {}, x3: {}, y3: {}, x4: {}, y4: {})",
336 self.x1.value,
337 self.y1.value,
338 self.x2.value,
339 self.y2.value,
340 self.x3.value,
341 self.y3.value,
342 self.x4.value,
343 self.y4.value
344 ))
345 }
346}
347
348#[cfg(test)]
349mod tests {
350 use crate::prelude::*;
351
352 #[test]
353 fn test_quadpoints_extents() {
354 let r = PdfRect::new_from_values(50.0, 100.0, 300.0, 200.0);
355
356 assert_eq!(r.to_quad_points().left().value, 100.0);
357 assert_eq!(r.to_quad_points().right().value, 200.0);
358 assert_eq!(r.to_quad_points().top().value, 300.0);
359 assert_eq!(r.to_quad_points().bottom().value, 50.0);
360
361 assert_eq!(r.to_quad_points().width().value, 100.0);
362 assert_eq!(r.to_quad_points().height().value, 250.0);
363 }
364
365 #[test]
366 fn test_quadpoints_to_rect() {
367 let r = PdfRect::new_from_values(100.0, 100.0, 200.0, 200.0);
368 assert_eq!(r.to_quad_points().to_rect(), r);
369
370 let m = PdfMatrix::identity()
371 .rotate_clockwise_degrees(45.0)
372 .unwrap();
373 let r45 = r.transform(m);
374
375 let q = r.to_quad_points();
376 let q45 = q.transform(m);
377
378 assert_eq!(q.to_rect(), r);
379 assert_eq!(q45.to_rect(), r45);
380
381 let s = q45.transform(m.invert()).to_rect();
390 let threshold = PdfPoints::new(0.001);
391 assert!((s.top() - r.top()).abs() < threshold);
392 assert!((s.bottom() - r.bottom()).abs() < threshold);
393 assert!((s.left() - r.left()).abs() < threshold);
394 assert!((s.right() - r.right()).abs() < threshold);
395 }
396}