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}