Skip to main content

pdfium_render/pdf/
matrix.rs

1//! Defines the [PdfMatrix] struct, a container for six floating-point values that represent
2//! the six configurable elements of a nine-element 3x3 PDF transformation matrix.
3
4use 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
12/// The floating-point data type used internally by a [PdfMatrix].
13pub type PdfMatrixValue = f32;
14
15/// Six floating-point values, labelled `a`, `b`, `c`, `d`, `e`, and `f`, that represent
16/// the six configurable elements of a nine-element 3x3 PDF transformation matrix.
17///
18/// Applying the matrix to any transformable object containing a `set_matrix()` function - such as
19/// a page, clip path, individual page object, or page object group - will result in a
20/// transformation of that object. Depending on the values specified in the matrix, the object
21/// can be moved, scaled, rotated, or skewed.
22///
23/// **It is rare that a matrix needs to be used directly.** All transformable objects provide
24/// convenient and expressive access to the most commonly used transformation operations without
25/// requiring a matrix.
26///
27/// However, a matrix can be convenient when the same transformation values need to be applied
28/// to a large set of transformable objects.
29///
30/// An overview of PDF transformation matrices can be found in the PDF Reference Manual
31/// version 1.7 on page 204; a detailed description can be founded in section 4.2.3 on page 207.
32#[derive(Debug, Copy, Clone)]
33pub struct PdfMatrix {
34    matrix: Matrix3<PdfMatrixValue>,
35}
36
37impl PdfMatrix {
38    /// A [PdfMatrix] object with all matrix values set to 0.0.
39    pub const ZERO: PdfMatrix = Self::zero();
40
41    /// A [PdfMatrix] object with matrix values a and d set to 1.0
42    /// and all other values set to 0.0.
43    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    /// Creates a new [PdfMatrix] with the given matrix values.
51    #[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    /// Creates a new [PdfMatrix] object with all matrix values set to 0.0.
66    ///
67    /// The return value of this function is identical to the constant [PdfMatrix::ZERO].
68    #[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    /// Creates a new [PdfMatrix] object with matrix values a and d set to 1.0
74    /// and all other values set to 0.0.
75    ///
76    /// The return value of this function is identical to the constant [PdfMatrix::IDENTITY].
77    #[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    /// Returns the value of `a`, the first of six floating-point values that represent
83    /// the six configurable elements of a nine-element 3x3 PDF transformation matrix.
84    #[inline]
85    pub fn a(&self) -> PdfMatrixValue {
86        self.matrix[0][0]
87    }
88
89    /// Sets the value of `a`, the first of six floating-point values that represent
90    /// the six configurable elements of a nine-element 3x3 PDF transformation matrix.
91    #[inline]
92    pub fn set_a(&mut self, a: PdfMatrixValue) {
93        self.matrix[0][0] = a;
94    }
95
96    /// Returns the value of `b`, the second of six floating-point values that represent
97    /// the six configurable elements of a nine-element 3x3 PDF transformation matrix.
98    #[inline]
99    pub fn b(&self) -> PdfMatrixValue {
100        self.matrix[0][1]
101    }
102
103    /// Sets the value of `b`, the second of six floating-point values that represent
104    /// the six configurable elements of a nine-element 3x3 PDF transformation matrix.
105    #[inline]
106    pub fn set_b(&mut self, b: PdfMatrixValue) {
107        self.matrix[0][1] = b;
108    }
109
110    /// Returns the value of `c`, the third of six floating-point values that represent
111    /// the six configurable elements of a nine-element 3x3 PDF transformation matrix.
112    #[inline]
113    pub fn c(&self) -> PdfMatrixValue {
114        self.matrix[1][0]
115    }
116
117    /// Sets the value of `c`, the third of six floating-point values that represent
118    /// the six configurable elements of a nine-element 3x3 PDF transformation matrix.
119    #[inline]
120    pub fn set_c(&mut self, c: PdfMatrixValue) {
121        self.matrix[1][0] = c;
122    }
123
124    /// Returns the value of `d`, the fourth of six floating-point values that represent
125    /// the six configurable elements of a nine-element 3x3 PDF transformation matrix.
126    #[inline]
127    pub fn d(&self) -> PdfMatrixValue {
128        self.matrix[1][1]
129    }
130
131    /// Sets the value of `d`, the fourth of six floating-point values that represent
132    /// the six configurable elements of a nine-element 3x3 PDF transformation matrix.
133    #[inline]
134    pub fn set_d(&mut self, d: PdfMatrixValue) {
135        self.matrix[1][1] = d;
136    }
137
138    /// Returns the value of `e`, the fifth of six floating-point values that represent
139    /// the six configurable elements of a nine-element 3x3 PDF transformation matrix.
140    #[inline]
141    pub fn e(&self) -> PdfMatrixValue {
142        self.matrix[2][0]
143    }
144
145    /// Sets the value of `e`, the fifth of six floating-point values that represent
146    /// the six configurable elements of a nine-element 3x3 PDF transformation matrix.
147    #[inline]
148    pub fn set_e(&mut self, e: PdfMatrixValue) {
149        self.matrix[2][0] = e;
150    }
151
152    /// Returns the value of `f`, the sixth of six floating-point values that represent
153    /// the six configurable elements of a nine-element 3x3 PDF transformation matrix.
154    #[inline]
155    pub fn f(&self) -> PdfMatrixValue {
156        self.matrix[2][1]
157    }
158
159    /// Sets the value of `f`, the sixth of six floating-point values that represent
160    /// the six configurable elements of a nine-element 3x3 PDF transformation matrix.
161    #[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    /// Returns the inverse of this [PdfMatrix].
179    #[inline]
180    pub fn invert(&self) -> PdfMatrix {
181        Self {
182            matrix: mat3_inv(self.matrix),
183        }
184    }
185
186    /// Returns the transpose of this [PdfMatrix].
187    #[inline]
188    pub fn transpose(&self) -> PdfMatrix {
189        Self {
190            matrix: mat3_transposed(self.matrix),
191        }
192    }
193
194    /// Returns the determinant of this [PdfMatrix].
195    #[inline]
196    pub fn determinant(&self) -> PdfMatrixValue {
197        mat3_det(self.matrix)
198    }
199
200    /// Returns the result of adding the given [PdfMatrix] to this [PdfMatrix].
201    #[inline]
202    pub fn add(&self, other: PdfMatrix) -> PdfMatrix {
203        Self {
204            matrix: mat3_add(self.matrix, other.matrix),
205        }
206    }
207
208    /// Returns the result of subtracting the given [PdfMatrix] from this [PdfMatrix].
209    #[inline]
210    pub fn subtract(&self, other: PdfMatrix) -> PdfMatrix {
211        Self {
212            matrix: mat3_sub(self.matrix, other.matrix),
213        }
214    }
215
216    /// Returns the result of multiplying this [PdfMatrix] by the given [PdfMatrix].
217    #[inline]
218    pub fn multiply(&self, other: PdfMatrix) -> PdfMatrix {
219        Self {
220            matrix: row_mat3_mul(self.matrix, other.matrix),
221        }
222    }
223
224    /// Returns the result of applying this [PdfMatrix] to the given coordinate pair expressed
225    /// as [PdfPoints].
226    #[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}