pdf_canvas/
graphicsstate.rs

1//! Types for representing details in the graphics state.
2
3use std::f32::consts::PI;
4use std::fmt::{self, Display};
5use std::ops::Mul;
6
7/// Line join styles, as described in section 8.4.3.4 of the PDF
8/// specification.
9#[derive(Clone, Copy, Debug)]
10pub enum JoinStyle {
11    /// The outer edges continues until they meet.
12    Miter,
13    /// The lines are joined by a circle of line-width diameter.
14    Round,
15    /// End the lines as with `CapStyle::Butt` and fill the resulting
16    /// gap with a triangle.
17    Bevel,
18}
19
20/// Line cap styles, as described in section 8.4.3.4 of the PDF
21/// specification.
22#[derive(Clone, Copy, Debug)]
23pub enum CapStyle {
24    /// Truncate the line squarely through the endpoint.
25    Butt,
26    /// Include a circle of line-width diameter around the endpoint.
27    Round,
28    /// Include a square around the endpoint, so the line continues for half
29    /// a line-width through the endpoint.
30    ProjectingSquare,
31}
32
33/// Any color (or grayscale) value that this library can make PDF represent.
34#[derive(Clone, Copy, Debug)]
35pub enum Color {
36    #[doc(hidden)]
37    RGB { red: u8, green: u8, blue: u8 },
38    #[doc(hidden)]
39    Gray { gray: u8 },
40}
41
42impl Color {
43    /// Return a color from a RGB colorspace.
44
45    /// # Example
46    /// ````
47    /// # use pdf_canvas::graphicsstate::Color;
48    /// let white  = Color::rgb(255, 255, 255);
49    /// let black  = Color::rgb(0, 0, 0);
50    /// let red    = Color::rgb(255, 0, 0);
51    /// let yellow = Color::rgb(255, 255, 0);
52    /// ````
53    pub fn rgb(red: u8, green: u8, blue: u8) -> Self {
54        Color::RGB { red, green, blue }
55    }
56
57    /// Return a grayscale color value.
58
59    /// # Example
60    /// ````
61    /// # use pdf_canvas::graphicsstate::Color;
62    /// let white = Color::gray(255);
63    /// let gray  = Color::gray(128);
64    /// ````
65    pub fn gray(gray: u8) -> Self {
66        Color::Gray { gray }
67    }
68}
69
70/// A transformation matrix for the pdf graphics state.
71///
72/// Matrixes can be created with numerous named constructors and
73/// combined by multiplication.
74///
75/// # Examples
76///
77/// ```
78/// # use pdf_canvas::{Pdf, BuiltinFont, FontSource};
79/// # use pdf_canvas::graphicsstate::Matrix;
80/// # let mut document = Pdf::create("foo.pdf").unwrap();
81/// # document.render_page(180.0, 240.0, |canvas| {
82/// canvas.concat(Matrix::translate(10.0, 24.0))?;
83///
84/// // Matrixes can be combined by multiplication:
85/// canvas.concat(Matrix::translate(7.0, 0.0) * Matrix::rotate_deg(45.0))?;
86/// // ... will be visualy identical to:
87/// canvas.concat(Matrix::translate(7.0, 0.0))?;
88/// canvas.concat(Matrix::rotate_deg(45.0))?;
89/// # Ok(())
90/// # }).unwrap();
91/// # document.finish().unwrap();
92/// ```
93pub struct Matrix {
94    v: [f32; 6],
95}
96
97impl Matrix {
98    /// Construct a matrix for translation
99    pub fn translate(dx: f32, dy: f32) -> Self {
100        Matrix {
101            v: [1., 0., 0., 1., dx, dy],
102        }
103    }
104    /// Construct a matrix for rotating by `a` radians.
105    pub fn rotate(a: f32) -> Self {
106        Matrix {
107            v: [a.cos(), a.sin(), -a.sin(), a.cos(), 0., 0.],
108        }
109    }
110    /// Construct a matrix for rotating by `a` degrees.
111    pub fn rotate_deg(a: f32) -> Self {
112        Self::rotate(a * PI / 180.)
113    }
114    /// Construct a matrix for scaling by factor `sx` in x-direction
115    /// and by `sy` in y-direction.
116    pub fn scale(sx: f32, sy: f32) -> Self {
117        Matrix {
118            v: [sx, 0., 0., sy, 0., 0.],
119        }
120    }
121    /// Construct a matrix for scaling by the same factor, `s` in both
122    /// directions.
123    pub fn uniform_scale(s: f32) -> Self {
124        Self::scale(s, s)
125    }
126    /// Construct a matrix for skewing.
127    pub fn skew(a: f32, b: f32) -> Self {
128        Matrix {
129            v: [1., a.tan(), b.tan(), 1., 0., 0.],
130        }
131    }
132}
133
134impl Display for Matrix {
135    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
136        let v = self.v;
137        write!(f, "{} {} {} {} {} {}", v[0], v[1], v[2], v[3], v[4], v[5])
138    }
139}
140
141impl Mul for Matrix {
142    type Output = Self;
143    fn mul(self, b: Self) -> Self {
144        let a = self.v;
145        let b = b.v;
146        Matrix {
147            v: [
148                a[0] * b[0] + a[1] * b[2],
149                a[0] * b[1] + a[1] * b[3],
150                a[2] * b[0] + a[3] * b[2],
151                a[2] * b[1] + a[3] * b[3],
152                a[4] * b[0] + a[5] * b[2] + b[4],
153                a[4] * b[1] + a[5] * b[3] + b[5],
154            ],
155        }
156    }
157}
158
159#[cfg(test)]
160mod tests {
161    use super::Matrix;
162    use std::f32::consts::PI;
163    #[test]
164    fn test_matrix_mul_a() {
165        assert_unit(Matrix::rotate_deg(45.) * Matrix::rotate_deg(-45.));
166    }
167    #[test]
168    fn test_matrix_mul_b() {
169        assert_unit(Matrix::uniform_scale(2.) * Matrix::uniform_scale(0.5));
170    }
171    #[test]
172    fn test_matrix_mul_c() {
173        assert_unit(Matrix::rotate(2. * PI));
174    }
175    #[test]
176    fn test_matrix_mul_d() {
177        assert_unit(Matrix::rotate(PI) * Matrix::uniform_scale(-1.));
178    }
179
180    fn assert_unit(m: Matrix) {
181        assert_eq!(None, diff(&[1., 0., 0., 1., 0., 0.], &m.v));
182    }
183
184    fn diff(a: &[f32; 6], b: &[f32; 6]) -> Option<String> {
185        let large_a = a.iter().fold(0f32, |x, &y| x.max(y));
186        let large_b = b.iter().fold(0f32, |x, &y| x.max(y));
187        let epsilon = 1e-6 * large_a.max(large_b);
188        for i in 0..6 {
189            if (a[i] - b[i]).abs() > epsilon {
190                return Some(format!("{:?} != {:?}", a, b));
191            }
192        }
193        None
194    }
195}