1use crate::bindgen::FS_MATRIX;
5use crate::error::PdfiumError;
6use crate::pdf::points::PdfPoints;
7use crate::{create_transform_getters, create_transform_setters};
8use std::hash::{Hash, Hasher};
9use std::ops::{Add, Mul, Sub};
10use vecmath::{Matrix3, mat3_add, mat3_det, mat3_inv, mat3_sub, mat3_transposed, row_mat3_mul};
11
12pub type PdfMatrixValue = f32;
14
15#[derive(Debug, Copy, Clone)]
33pub struct PdfMatrix {
34 matrix: Matrix3<PdfMatrixValue>,
35}
36
37impl PdfMatrix {
38 pub const ZERO: PdfMatrix = Self::zero();
40
41 pub const IDENTITY: PdfMatrix = Self::identity();
44
45 #[inline]
46 pub(crate) fn from_pdfium(matrix: FS_MATRIX) -> Self {
47 Self::new(matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f)
48 }
49
50 #[inline]
52 pub const fn new(
53 a: PdfMatrixValue,
54 b: PdfMatrixValue,
55 c: PdfMatrixValue,
56 d: PdfMatrixValue,
57 e: PdfMatrixValue,
58 f: PdfMatrixValue,
59 ) -> Self {
60 Self {
61 matrix: [[a, b, 0.0], [c, d, 0.0], [e, f, 1.0]],
62 }
63 }
64
65 #[inline]
69 pub const fn zero() -> Self {
70 Self::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
71 }
72
73 #[inline]
78 pub const fn identity() -> Self {
79 Self::new(1.0, 0.0, 0.0, 1.0, 0.0, 0.0)
80 }
81
82 #[inline]
85 pub fn a(&self) -> PdfMatrixValue {
86 self.matrix[0][0]
87 }
88
89 #[inline]
92 pub fn set_a(&mut self, a: PdfMatrixValue) {
93 self.matrix[0][0] = a;
94 }
95
96 #[inline]
99 pub fn b(&self) -> PdfMatrixValue {
100 self.matrix[0][1]
101 }
102
103 #[inline]
106 pub fn set_b(&mut self, b: PdfMatrixValue) {
107 self.matrix[0][1] = b;
108 }
109
110 #[inline]
113 pub fn c(&self) -> PdfMatrixValue {
114 self.matrix[1][0]
115 }
116
117 #[inline]
120 pub fn set_c(&mut self, c: PdfMatrixValue) {
121 self.matrix[1][0] = c;
122 }
123
124 #[inline]
127 pub fn d(&self) -> PdfMatrixValue {
128 self.matrix[1][1]
129 }
130
131 #[inline]
134 pub fn set_d(&mut self, d: PdfMatrixValue) {
135 self.matrix[1][1] = d;
136 }
137
138 #[inline]
141 pub fn e(&self) -> PdfMatrixValue {
142 self.matrix[2][0]
143 }
144
145 #[inline]
148 pub fn set_e(&mut self, e: PdfMatrixValue) {
149 self.matrix[2][0] = e;
150 }
151
152 #[inline]
155 pub fn f(&self) -> PdfMatrixValue {
156 self.matrix[2][1]
157 }
158
159 #[inline]
162 pub fn set_f(&mut self, f: PdfMatrixValue) {
163 self.matrix[2][1] = f;
164 }
165
166 #[inline]
167 pub(crate) fn as_pdfium(&self) -> FS_MATRIX {
168 FS_MATRIX {
169 a: self.a(),
170 b: self.b(),
171 c: self.c(),
172 d: self.d(),
173 e: self.e(),
174 f: self.f(),
175 }
176 }
177
178 #[inline]
180 pub fn invert(&self) -> PdfMatrix {
181 Self {
182 matrix: mat3_inv(self.matrix),
183 }
184 }
185
186 #[inline]
188 pub fn transpose(&self) -> PdfMatrix {
189 Self {
190 matrix: mat3_transposed(self.matrix),
191 }
192 }
193
194 #[inline]
196 pub fn determinant(&self) -> PdfMatrixValue {
197 mat3_det(self.matrix)
198 }
199
200 #[inline]
202 pub fn add(&self, other: PdfMatrix) -> PdfMatrix {
203 Self {
204 matrix: mat3_add(self.matrix, other.matrix),
205 }
206 }
207
208 #[inline]
210 pub fn subtract(&self, other: PdfMatrix) -> PdfMatrix {
211 Self {
212 matrix: mat3_sub(self.matrix, other.matrix),
213 }
214 }
215
216 #[inline]
218 pub fn multiply(&self, other: PdfMatrix) -> PdfMatrix {
219 Self {
220 matrix: row_mat3_mul(self.matrix, other.matrix),
221 }
222 }
223
224 #[inline]
227 pub fn apply_to_points(&self, x: PdfPoints, y: PdfPoints) -> (PdfPoints, PdfPoints) {
228 (
229 PdfPoints::new(self.a() * x.value + self.c() * y.value + self.e()),
230 PdfPoints::new(self.b() * x.value + self.d() * y.value + self.f()),
231 )
232 }
233
234 create_transform_setters!(
235 Self,
236 Result<Self, PdfiumError>,
237 "this [PdfMatrix]",
238 "this [PdfMatrix].",
239 "this [PdfMatrix],"
240 );
241
242 fn transform_impl(
243 mut self,
244 a: PdfMatrixValue,
245 b: PdfMatrixValue,
246 c: PdfMatrixValue,
247 d: PdfMatrixValue,
248 e: PdfMatrixValue,
249 f: PdfMatrixValue,
250 ) -> Result<Self, PdfiumError> {
251 let result = row_mat3_mul(self.matrix, [[a, b, 0.0], [c, d, 0.0], [e, f, 1.0]]);
252
253 if mat3_det(result) == 0.0 {
254 Err(PdfiumError::InvalidTransformationMatrix)
255 } else {
256 self.matrix = result;
257
258 Ok(self)
259 }
260 }
261
262 fn reset_matrix_impl(mut self, matrix: PdfMatrix) -> Result<Self, PdfiumError> {
263 self.set_a(matrix.a());
264 self.set_b(matrix.b());
265 self.set_c(matrix.c());
266 self.set_d(matrix.d());
267 self.set_e(matrix.e());
268 self.set_f(matrix.f());
269
270 Ok(self)
271 }
272
273 create_transform_getters!("this [PdfMatrix]", "this [PdfMatrix].", "this [PdfMatrix],");
274
275 #[inline]
276 fn get_matrix_impl(&self) -> Result<PdfMatrix, PdfiumError> {
277 Ok(*self)
278 }
279}
280
281impl PartialEq for PdfMatrix {
282 fn eq(&self, other: &Self) -> bool {
283 (self.a() - other.a()).abs() < 0.0001
284 && (self.b() - other.b()).abs() < 0.0001
285 && (self.c() - other.c()).abs() < 0.0001
286 && (self.d() - other.d()).abs() < 0.0001
287 && (self.e() - other.e()).abs() < 0.0001
288 && (self.f() - other.f()).abs() < 0.0001
289 }
290}
291
292impl Eq for PdfMatrix {}
293
294impl Hash for PdfMatrix {
295 fn hash<H: Hasher>(&self, state: &mut H) {
296 state.write_u32(self.a().to_bits());
297 state.write_u32(self.b().to_bits());
298 state.write_u32(self.c().to_bits());
299 state.write_u32(self.d().to_bits());
300 state.write_u32(self.e().to_bits());
301 state.write_u32(self.f().to_bits());
302 }
303}
304
305impl Add for PdfMatrix {
306 type Output = PdfMatrix;
307
308 #[inline]
309 fn add(self, rhs: Self) -> Self::Output {
310 Self::add(&self, rhs)
311 }
312}
313
314impl Sub for PdfMatrix {
315 type Output = PdfMatrix;
316
317 #[inline]
318 fn sub(self, rhs: Self) -> Self::Output {
319 self.subtract(rhs)
320 }
321}
322
323impl Mul for PdfMatrix {
324 type Output = PdfMatrix;
325
326 #[inline]
327 fn mul(self, rhs: Self) -> Self::Output {
328 self.multiply(rhs)
329 }
330}
331
332#[cfg(test)]
333mod tests {
334 use crate::prelude::*;
335
336 #[test]
337 fn test_matrix_apply_to_points() {
338 let delta_x = PdfPoints::new(50.0);
339 let delta_y = PdfPoints::new(-25.0);
340
341 let matrix = PdfMatrix::identity().translate(delta_x, delta_y).unwrap();
342
343 let x = PdfPoints::new(300.0);
344 let y = PdfPoints::new(400.0);
345
346 let result = matrix.apply_to_points(x, y);
347
348 assert_eq!(result.0, x + delta_x);
349 assert_eq!(result.1, y + delta_y);
350 }
351}