1use crate::fontref::FontRef;
2use crate::fontsource::{BuiltinFont, FontSource};
3use crate::graphicsstate::*;
4use crate::outline::OutlineItem;
5use crate::textobject::TextObject;
6use std::collections::HashMap;
7use std::io::{self, Write};
8use std::sync::Arc;
9
10pub struct Canvas<'a> {
18 output: &'a mut dyn Write,
19 fonts: &'a mut HashMap<BuiltinFont, FontRef>,
20 outline_items: &'a mut Vec<OutlineItem>,
21}
22
23impl<'a> Canvas<'a> {
24 pub(crate) fn new(
26 output: &'a mut dyn Write,
27 fonts: &'a mut HashMap<BuiltinFont, FontRef>,
28 outline_items: &'a mut Vec<OutlineItem>,
29 ) -> Self {
30 Canvas {
31 output,
32 fonts,
33 outline_items,
34 }
35 }
36
37 pub fn rectangle(
40 &mut self,
41 x: f32,
42 y: f32,
43 width: f32,
44 height: f32,
45 ) -> io::Result<()> {
46 writeln!(self.output, "{} {} {} {} re", x, y, width, height)
47 }
48 pub fn set_line_join_style(
50 &mut self,
51 style: JoinStyle,
52 ) -> io::Result<()> {
53 writeln!(
54 self.output,
55 "{} j",
56 match style {
57 JoinStyle::Miter => 0,
58 JoinStyle::Round => 1,
59 JoinStyle::Bevel => 2,
60 }
61 )
62 }
63 pub fn set_line_cap_style(&mut self, style: CapStyle) -> io::Result<()> {
65 writeln!(
66 self.output,
67 "{} J",
68 match style {
69 CapStyle::Butt => 0,
70 CapStyle::Round => 1,
71 CapStyle::ProjectingSquare => 2,
72 }
73 )
74 }
75 pub fn set_line_width(&mut self, w: f32) -> io::Result<()> {
77 writeln!(self.output, "{} w", w)
78 }
79 pub fn set_stroke_color(&mut self, color: Color) -> io::Result<()> {
81 let norm = |c| f32::from(c) / 255.0;
82 match color {
83 Color::RGB { red, green, blue } => writeln!(
84 self.output,
85 "{} {} {} SC",
86 norm(red),
87 norm(green),
88 norm(blue),
89 ),
90 Color::Gray { gray } => writeln!(self.output, "{} G", norm(gray)),
91 }
92 }
93 pub fn set_fill_color(&mut self, color: Color) -> io::Result<()> {
95 let norm = |c| f32::from(c) / 255.0;
96 match color {
97 Color::RGB { red, green, blue } => writeln!(
98 self.output,
99 "{} {} {} sc",
100 norm(red),
101 norm(green),
102 norm(blue),
103 ),
104 Color::Gray { gray } => writeln!(self.output, "{} g", norm(gray)),
105 }
106 }
107
108 pub fn concat(&mut self, m: Matrix) -> io::Result<()> {
111 writeln!(self.output, "{} cm", m)
112 }
113
114 pub fn line(
116 &mut self,
117 x1: f32,
118 y1: f32,
119 x2: f32,
120 y2: f32,
121 ) -> io::Result<()> {
122 self.move_to(x1, y1)?;
123 self.line_to(x2, y2)
124 }
125 pub fn move_to(&mut self, x: f32, y: f32) -> io::Result<()> {
127 write!(self.output, "{} {} m ", x, y)
128 }
129 pub fn line_to(&mut self, x: f32, y: f32) -> io::Result<()> {
132 write!(self.output, "{} {} l ", x, y)
133 }
134 pub fn curve_to(
137 &mut self,
138 x1: f32,
139 y1: f32,
140 x2: f32,
141 y2: f32,
142 x3: f32,
143 y3: f32,
144 ) -> io::Result<()> {
145 writeln!(self.output, "{} {} {} {} {} {} c", x1, y1, x2, y2, x3, y3)
146 }
147 pub fn circle(&mut self, x: f32, y: f32, r: f32) -> io::Result<()> {
151 let top = y - r;
152 let bottom = y + r;
153 let left = x - r;
154 let right = x + r;
155 #[allow(clippy::excessive_precision)]
156 let c = 0.551_915_024_494;
157 let dist = r * c;
158 let up = y - dist;
159 let down = y + dist;
160 let leftp = x - dist;
161 let rightp = x + dist;
162 self.move_to(x, top)?;
163 self.curve_to(leftp, top, left, up, left, y)?;
164 self.curve_to(left, down, leftp, bottom, x, bottom)?;
165 self.curve_to(rightp, bottom, right, down, right, y)?;
166 self.curve_to(right, up, rightp, top, x, top)
167 }
168 pub fn stroke(&mut self) -> io::Result<()> {
170 writeln!(self.output, "S")
171 }
172 pub fn close_and_stroke(&mut self) -> io::Result<()> {
174 writeln!(self.output, "s")
175 }
176 pub fn fill(&mut self) -> io::Result<()> {
178 writeln!(self.output, "f")
179 }
180 pub fn get_font(&mut self, font: BuiltinFont) -> FontRef {
182 let next_n = self.fonts.len();
183 self.fonts
184 .entry(font)
185 .or_insert_with(|| {
186 FontRef::new(
187 next_n,
188 font.get_encoding().clone(),
189 Arc::new(font.get_metrics()),
190 )
191 })
192 .clone()
193 }
194
195 pub fn text<F, T>(&mut self, render_text: F) -> io::Result<T>
202 where
203 F: FnOnce(&mut TextObject) -> io::Result<T>,
204 {
205 writeln!(self.output, "BT")?;
206 let result = render_text(&mut TextObject::new(self.output))?;
207 writeln!(self.output, "ET")?;
208 Ok(result)
209 }
210 pub fn left_text(
212 &mut self,
213 x: f32,
214 y: f32,
215 font: BuiltinFont,
216 size: f32,
217 text: &str,
218 ) -> io::Result<()> {
219 let font = self.get_font(font);
220 self.text(|t| {
221 t.set_font(&font, size)?;
222 t.pos(x, y)?;
223 t.show(text)
224 })
225 }
226 pub fn right_text(
228 &mut self,
229 x: f32,
230 y: f32,
231 font: BuiltinFont,
232 size: f32,
233 text: &str,
234 ) -> io::Result<()> {
235 let font = self.get_font(font);
236 self.text(|t| {
237 let text_width = font.get_width(size, text);
238 t.set_font(&font, size)?;
239 t.pos(x - text_width, y)?;
240 t.show(text)
241 })
242 }
243 pub fn center_text(
245 &mut self,
246 x: f32,
247 y: f32,
248 font: BuiltinFont,
249 size: f32,
250 text: &str,
251 ) -> io::Result<()> {
252 let font = self.get_font(font);
253 self.text(|t| {
254 let text_width = font.get_width(size, text);
255 t.set_font(&font, size)?;
256 t.pos(x - text_width / 2.0, y)?;
257 t.show(text)
258 })
259 }
260
261 pub fn add_outline(&mut self, title: &str) {
269 self.outline_items.push(OutlineItem::new(title));
270 }
271
272 pub fn gsave(&mut self) -> io::Result<()> {
275 writeln!(self.output, "q")
276 }
277 pub fn grestore(&mut self) -> io::Result<()> {
280 writeln!(self.output, "Q")
281 }
282}