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}