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::{mat3_add, mat3_det, mat3_inv, mat3_sub, mat3_transposed, row_mat3_mul, Matrix3};
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        // The formula for applying transform to coordinates is provided in
229        // The PDF Reference Manual, version 1.7, on page 208.
230
231        (
232            PdfPoints::new(self.a() * x.value + self.c() * y.value + self.e()),
233            PdfPoints::new(self.b() * x.value + self.d() * y.value + self.f()),
234        )
235    }
236
237    create_transform_setters!(
238        Self,
239        Result<Self, PdfiumError>,
240        "this [PdfMatrix]",
241        "this [PdfMatrix].",
242        "this [PdfMatrix],"
243    );
244
245    // The internal implementation of the transform() function used by the create_transform_setters!() macro.
246    fn transform_impl(
247        mut self,
248        a: PdfMatrixValue,
249        b: PdfMatrixValue,
250        c: PdfMatrixValue,
251        d: PdfMatrixValue,
252        e: PdfMatrixValue,
253        f: PdfMatrixValue,
254    ) -> Result<Self, PdfiumError> {
255        let result = row_mat3_mul(self.matrix, [[a, b, 0.0], [c, d, 0.0], [e, f, 1.0]]);
256
257        if mat3_det(result) == 0.0 {
258            Err(PdfiumError::InvalidTransformationMatrix)
259        } else {
260            self.matrix = result;
261
262            Ok(self)
263        }
264    }
265
266    // The internal implementation of the reset_matrix() function used by the create_transform_setters!() macro.
267    fn reset_matrix_impl(mut self, matrix: PdfMatrix) -> Result<Self, PdfiumError> {
268        self.set_a(matrix.a());
269        self.set_b(matrix.b());
270        self.set_c(matrix.c());
271        self.set_d(matrix.d());
272        self.set_e(matrix.e());
273        self.set_f(matrix.f());
274
275        Ok(self)
276    }
277
278    create_transform_getters!("this [PdfMatrix]", "this [PdfMatrix].", "this [PdfMatrix],");
279
280    // The internal implementation of the get_matrix_impl() function used by the create_transform_getters!() macro.
281    #[inline]
282    fn get_matrix_impl(&self) -> Result<PdfMatrix, PdfiumError> {
283        Ok(*self)
284    }
285}
286
287// We could derive PartialEq automatically, but it's good practice to implement PartialEq
288// by hand when implementing Hash.
289
290impl PartialEq for PdfMatrix {
291    fn eq(&self, other: &Self) -> bool {
292        (self.a() - other.a()).abs() < 0.0001
293            && (self.b() - other.b()).abs() < 0.0001
294            && (self.c() - other.c()).abs() < 0.0001
295            && (self.d() - other.d()).abs() < 0.0001
296            && (self.e() - other.e()).abs() < 0.0001
297            && (self.f() - other.f()).abs() < 0.0001
298    }
299}
300
301// The PdfMatrixValue values inside PdfMatrix will never be NaN or Infinity, so these implementations
302// of Eq and Hash are safe.
303
304impl Eq for PdfMatrix {}
305
306impl Hash for PdfMatrix {
307    fn hash<H: Hasher>(&self, state: &mut H) {
308        state.write_u32(self.a().to_bits());
309        state.write_u32(self.b().to_bits());
310        state.write_u32(self.c().to_bits());
311        state.write_u32(self.d().to_bits());
312        state.write_u32(self.e().to_bits());
313        state.write_u32(self.f().to_bits());
314    }
315}
316
317impl Add for PdfMatrix {
318    type Output = PdfMatrix;
319
320    #[inline]
321    fn add(self, rhs: Self) -> Self::Output {
322        // Add::add() shadows Self::add(), so we must be explicit about which function to call.
323        Self::add(&self, rhs)
324    }
325}
326
327impl Sub for PdfMatrix {
328    type Output = PdfMatrix;
329
330    #[inline]
331    fn sub(self, rhs: Self) -> Self::Output {
332        self.subtract(rhs)
333    }
334}
335
336impl Mul for PdfMatrix {
337    type Output = PdfMatrix;
338
339    #[inline]
340    fn mul(self, rhs: Self) -> Self::Output {
341        self.multiply(rhs)
342    }
343}
344
345#[cfg(test)]
346mod tests {
347    use crate::prelude::*;
348
349    #[test]
350    fn test_matrix_apply_to_points() {
351        let delta_x = PdfPoints::new(50.0);
352        let delta_y = PdfPoints::new(-25.0);
353
354        let matrix = PdfMatrix::identity().translate(delta_x, delta_y).unwrap();
355
356        let x = PdfPoints::new(300.0);
357        let y = PdfPoints::new(400.0);
358
359        let result = matrix.apply_to_points(x, y);
360
361        assert_eq!(result.0, x + delta_x);
362        assert_eq!(result.1, y + delta_y);
363    }
364}