1mod color;
2mod image;
3mod path;
4
5pub use color::Color;
6pub use image::{ColorSpace as ImageColorSpace, Image, ImageFormat};
7pub use path::{LineCap, LineJoin, PathBuilder};
8
9use crate::error::Result;
10use std::fmt::Write;
11
12#[derive(Clone)]
13pub struct GraphicsContext {
14 operations: String,
15 current_color: Color,
16 stroke_color: Color,
17 line_width: f64,
18}
19
20impl Default for GraphicsContext {
21 fn default() -> Self {
22 Self::new()
23 }
24}
25
26impl GraphicsContext {
27 pub fn new() -> Self {
28 Self {
29 operations: String::new(),
30 current_color: Color::black(),
31 stroke_color: Color::black(),
32 line_width: 1.0,
33 }
34 }
35
36 pub fn move_to(&mut self, x: f64, y: f64) -> &mut Self {
37 writeln!(&mut self.operations, "{x:.2} {y:.2} m").unwrap();
38 self
39 }
40
41 pub fn line_to(&mut self, x: f64, y: f64) -> &mut Self {
42 writeln!(&mut self.operations, "{x:.2} {y:.2} l").unwrap();
43 self
44 }
45
46 pub fn curve_to(&mut self, x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64) -> &mut Self {
47 writeln!(
48 &mut self.operations,
49 "{x1:.2} {y1:.2} {x2:.2} {y2:.2} {x3:.2} {y3:.2} c"
50 )
51 .unwrap();
52 self
53 }
54
55 pub fn rect(&mut self, x: f64, y: f64, width: f64, height: f64) -> &mut Self {
56 writeln!(
57 &mut self.operations,
58 "{x:.2} {y:.2} {width:.2} {height:.2} re"
59 )
60 .unwrap();
61 self
62 }
63
64 pub fn circle(&mut self, cx: f64, cy: f64, radius: f64) -> &mut Self {
65 let k = 0.552284749831;
66 let r = radius;
67
68 self.move_to(cx + r, cy);
69 self.curve_to(cx + r, cy + k * r, cx + k * r, cy + r, cx, cy + r);
70 self.curve_to(cx - k * r, cy + r, cx - r, cy + k * r, cx - r, cy);
71 self.curve_to(cx - r, cy - k * r, cx - k * r, cy - r, cx, cy - r);
72 self.curve_to(cx + k * r, cy - r, cx + r, cy - k * r, cx + r, cy);
73 self.close_path()
74 }
75
76 pub fn close_path(&mut self) -> &mut Self {
77 self.operations.push_str("h\n");
78 self
79 }
80
81 pub fn stroke(&mut self) -> &mut Self {
82 self.apply_stroke_color();
83 self.operations.push_str("S\n");
84 self
85 }
86
87 pub fn fill(&mut self) -> &mut Self {
88 self.apply_fill_color();
89 self.operations.push_str("f\n");
90 self
91 }
92
93 pub fn fill_stroke(&mut self) -> &mut Self {
94 self.apply_fill_color();
95 self.apply_stroke_color();
96 self.operations.push_str("B\n");
97 self
98 }
99
100 pub fn set_stroke_color(&mut self, color: Color) -> &mut Self {
101 self.stroke_color = color;
102 self
103 }
104
105 pub fn set_fill_color(&mut self, color: Color) -> &mut Self {
106 self.current_color = color;
107 self
108 }
109
110 pub fn set_line_width(&mut self, width: f64) -> &mut Self {
111 self.line_width = width;
112 writeln!(&mut self.operations, "{width:.2} w").unwrap();
113 self
114 }
115
116 pub fn set_line_cap(&mut self, cap: LineCap) -> &mut Self {
117 writeln!(&mut self.operations, "{} J", cap as u8).unwrap();
118 self
119 }
120
121 pub fn set_line_join(&mut self, join: LineJoin) -> &mut Self {
122 writeln!(&mut self.operations, "{} j", join as u8).unwrap();
123 self
124 }
125
126 pub fn save_state(&mut self) -> &mut Self {
127 self.operations.push_str("q\n");
128 self
129 }
130
131 pub fn restore_state(&mut self) -> &mut Self {
132 self.operations.push_str("Q\n");
133 self
134 }
135
136 pub fn translate(&mut self, tx: f64, ty: f64) -> &mut Self {
137 writeln!(&mut self.operations, "1 0 0 1 {tx:.2} {ty:.2} cm").unwrap();
138 self
139 }
140
141 pub fn scale(&mut self, sx: f64, sy: f64) -> &mut Self {
142 writeln!(&mut self.operations, "{sx:.2} 0 0 {sy:.2} 0 0 cm").unwrap();
143 self
144 }
145
146 pub fn rotate(&mut self, angle: f64) -> &mut Self {
147 let cos = angle.cos();
148 let sin = angle.sin();
149 writeln!(
150 &mut self.operations,
151 "{:.6} {:.6} {:.6} {:.6} 0 0 cm",
152 cos, sin, -sin, cos
153 )
154 .unwrap();
155 self
156 }
157
158 pub fn transform(&mut self, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) -> &mut Self {
159 writeln!(
160 &mut self.operations,
161 "{a:.2} {b:.2} {c:.2} {d:.2} {e:.2} {f:.2} cm"
162 )
163 .unwrap();
164 self
165 }
166
167 pub fn rectangle(&mut self, x: f64, y: f64, width: f64, height: f64) -> &mut Self {
168 self.rect(x, y, width, height)
169 }
170
171 pub fn draw_image(
172 &mut self,
173 image_name: &str,
174 x: f64,
175 y: f64,
176 width: f64,
177 height: f64,
178 ) -> &mut Self {
179 self.save_state();
181
182 writeln!(
185 &mut self.operations,
186 "{width:.2} 0 0 {height:.2} {x:.2} {y:.2} cm"
187 )
188 .unwrap();
189
190 writeln!(&mut self.operations, "/{image_name} Do").unwrap();
192
193 self.restore_state();
195
196 self
197 }
198
199 fn apply_stroke_color(&mut self) {
200 match self.stroke_color {
201 Color::Rgb(r, g, b) => {
202 writeln!(&mut self.operations, "{r:.3} {g:.3} {b:.3} RG").unwrap();
203 }
204 Color::Gray(g) => {
205 writeln!(&mut self.operations, "{g:.3} G").unwrap();
206 }
207 Color::Cmyk(c, m, y, k) => {
208 writeln!(&mut self.operations, "{c:.3} {m:.3} {y:.3} {k:.3} K").unwrap();
209 }
210 }
211 }
212
213 fn apply_fill_color(&mut self) {
214 match self.current_color {
215 Color::Rgb(r, g, b) => {
216 writeln!(&mut self.operations, "{r:.3} {g:.3} {b:.3} rg").unwrap();
217 }
218 Color::Gray(g) => {
219 writeln!(&mut self.operations, "{g:.3} g").unwrap();
220 }
221 Color::Cmyk(c, m, y, k) => {
222 writeln!(&mut self.operations, "{c:.3} {m:.3} {y:.3} {k:.3} k").unwrap();
223 }
224 }
225 }
226
227 pub(crate) fn generate_operations(&self) -> Result<Vec<u8>> {
228 Ok(self.operations.as_bytes().to_vec())
229 }
230}