1use crate::content::color::Color;
2use crate::cos::object::{COSDictionary, COSStream};
3use crate::error::PdfResult;
4use crate::font::truetype::PDTrueTypeFont;
5use crate::font::type1::PDType1Font;
6use crate::pd::document::PDDocument;
7use crate::pd::page::PDPage;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum AppendMode {
11 Overwrite,
12 Append,
13 Prepend,
14}
15
16pub struct PDPageContentStream {
18 buffer: Vec<u8>,
19 pub mode: AppendMode,
20}
21
22impl PDPageContentStream {
23 pub fn new(mode: AppendMode) -> Self {
24 Self {
25 buffer: Vec::new(),
26 mode,
27 }
28 }
29
30 pub fn write_str(&mut self, s: &str) {
31 self.buffer.extend_from_slice(s.as_bytes());
32 }
33
34 pub fn write_line(&mut self, s: &str) {
35 self.write_str(s);
36 self.buffer.push(b'\n');
37 }
38
39 pub fn save_graphics_state(&mut self) -> &mut Self {
42 self.write_line("q");
43 self
44 }
45
46 pub fn restore_graphics_state(&mut self) -> &mut Self {
47 self.write_line("Q");
48 self
49 }
50
51 pub fn set_line_width(&mut self, width: f64) -> &mut Self {
52 self.write_line(&format!("{:.3} w", width));
53 self
54 }
55
56 pub fn set_line_cap(&mut self, cap: i32) -> &mut Self {
57 self.write_line(&format!("{} J", cap));
58 self
59 }
60
61 pub fn set_line_join(&mut self, join: i32) -> &mut Self {
62 self.write_line(&format!("{} j", join));
63 self
64 }
65
66 pub fn set_stroke_color(&mut self, color: Color) -> &mut Self {
67 match color {
68 Color::Rgb(r, g, b) => self.write_line(&format!("{:.3} {:.3} {:.3} RG", r, g, b)),
69 Color::Cmyk(c, m, y, k) => {
70 self.write_line(&format!("{:.3} {:.3} {:.3} {:.3} K", c, m, y, k))
71 }
72 Color::Gray(g) => self.write_line(&format!("{:.3} G", g)),
73 }
74 self
75 }
76
77 pub fn set_fill_color(&mut self, color: Color) -> &mut Self {
78 match color {
79 Color::Rgb(r, g, b) => self.write_line(&format!("{:.3} {:.3} {:.3} rg", r, g, b)),
80 Color::Cmyk(c, m, y, k) => {
81 self.write_line(&format!("{:.3} {:.3} {:.3} {:.3} k", c, m, y, k))
82 }
83 Color::Gray(g) => self.write_line(&format!("{:.3} g", g)),
84 }
85 self
86 }
87
88 pub fn set_opacity_on_page(
89 &mut self,
90 page: &mut PDPage,
91 stroke_alpha: f64,
92 fill_alpha: f64,
93 ) -> &mut Self {
94 let gs_name = page.resources.add_ext_gstate(stroke_alpha, fill_alpha);
95 self.write_line(&format!("/{} gs", gs_name));
96 self
97 }
98
99 pub fn set_opacity(
100 &mut self,
101 doc: &mut PDDocument,
102 page_index: usize,
103 stroke_alpha: f64,
104 fill_alpha: f64,
105 ) -> &mut Self {
106 if let Ok(page) = doc.get_page_mut(page_index) {
107 let gs_name = page.resources.add_ext_gstate(stroke_alpha, fill_alpha);
108 self.write_line(&format!("/{} gs", gs_name));
109 }
110 self
111 }
112
113 pub fn transform(&mut self, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) -> &mut Self {
114 self.write_line(&format!(
115 "{:.4} {:.4} {:.4} {:.4} {:.4} {:.4} cm",
116 a, b, c, d, e, f
117 ));
118 self
119 }
120
121 pub fn translate(&mut self, tx: f64, ty: f64) -> &mut Self {
122 self.transform(1.0, 0.0, 0.0, 1.0, tx, ty)
123 }
124
125 pub fn scale(&mut self, sx: f64, sy: f64) -> &mut Self {
126 self.transform(sx, 0.0, 0.0, sy, 0.0, 0.0)
127 }
128
129 pub fn rotate(&mut self, degrees: f64, cx: f64, cy: f64) -> &mut Self {
130 let rad = degrees.to_radians();
131 let cos_val = rad.cos();
132 let sin_val = rad.sin();
133 let e = cx - cx * cos_val + cy * sin_val;
136 let f = cy - cx * sin_val - cy * cos_val;
137 self.transform(cos_val, sin_val, -sin_val, cos_val, e, f)
138 }
139
140 pub fn move_to(&mut self, x: f64, y: f64) -> &mut Self {
143 self.write_line(&format!("{:.3} {:.3} m", x, y));
144 self
145 }
146
147 pub fn line_to(&mut self, x: f64, y: f64) -> &mut Self {
148 self.write_line(&format!("{:.3} {:.3} l", x, y));
149 self
150 }
151
152 pub fn curve_to(&mut self, x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64) -> &mut Self {
153 self.write_line(&format!(
154 "{:.3} {:.3} {:.3} {:.3} {:.3} {:.3} c",
155 x1, y1, x2, y2, x3, y3
156 ));
157 self
158 }
159
160 pub fn rect(&mut self, x: f64, y: f64, width: f64, height: f64) -> &mut Self {
161 self.write_line(&format!("{:.3} {:.3} {:.3} {:.3} re", x, y, width, height));
162 self
163 }
164
165 pub fn circle(&mut self, cx: f64, cy: f64, r: f64) -> &mut Self {
166 let k = 0.552284749831 * r;
167 self.move_to(cx + r, cy);
168 self.curve_to(cx + r, cy + k, cx + k, cy + r, cx, cy + r);
169 self.curve_to(cx - k, cy + r, cx - r, cy + k, cx - r, cy);
170 self.curve_to(cx - r, cy - k, cx - k, cy - r, cx, cy - r);
171 self.curve_to(cx + k, cy - r, cx + r, cy - k, cx + r, cy);
172 self.close_path();
173 self
174 }
175
176 pub fn round_rect(&mut self, x: f64, y: f64, w: f64, h: f64, r: f64) -> &mut Self {
177 let r = r.min(w / 2.0).min(h / 2.0);
178 let k = 0.552284749831 * r;
179 self.move_to(x + r, y);
180 self.line_to(x + w - r, y);
181 self.curve_to(x + w - r + k, y, x + w, y + r - k, x + w, y + r);
182 self.line_to(x + w, y + h - r);
183 self.curve_to(x + w, y + h - r + k, x + w - r + k, y + h, x + w - r, y + h);
184 self.line_to(x + r, y + h);
185 self.curve_to(x + r - k, y + h, x, y + h - r + k, x, y + h - r);
186 self.line_to(x, y + r);
187 self.curve_to(x, y + r - k, x + r - k, y, x + r, y);
188 self.close_path();
189 self
190 }
191
192 pub fn close_path(&mut self) -> &mut Self {
193 self.write_line("h");
194 self
195 }
196
197 pub fn stroke(&mut self) -> &mut Self {
198 self.write_line("S");
199 self
200 }
201
202 pub fn fill(&mut self) -> &mut Self {
203 self.write_line("f");
204 self
205 }
206
207 pub fn fill_and_stroke(&mut self) -> &mut Self {
208 self.write_line("B");
209 self
210 }
211
212 pub fn clip(&mut self) -> &mut Self {
213 self.write_line("W n");
214 self
215 }
216
217 pub fn begin_text(&mut self) -> &mut Self {
220 self.write_line("BT");
221 self
222 }
223
224 pub fn end_text(&mut self) -> &mut Self {
225 self.write_line("ET");
226 self
227 }
228
229 pub fn set_font(&mut self, font_name: &str, size: f64) -> &mut Self {
230 self.write_line(&format!("/{} {:.3} Tf", font_name, size));
231 self
232 }
233
234 pub fn set_font_type1(
235 &mut self,
236 doc: &mut PDDocument,
237 page_index: usize,
238 font: PDType1Font,
239 size: f64,
240 ) -> &mut Self {
241 let font_id = doc.cos_doc.add_object(font.to_dict());
242 if let Ok(page) = doc.get_page_mut(page_index) {
243 let font_name = page.resources.add_font(font_id);
244 self.write_line(&format!("/{} {:.3} Tf", font_name, size));
245 }
246 self
247 }
248
249 pub fn set_truetype_font(
250 &mut self,
251 doc: &mut PDDocument,
252 page_index: usize,
253 font: &PDTrueTypeFont,
254 size: f64,
255 ) -> &mut Self {
256 if let Ok(page) = doc.get_page_mut(page_index) {
257 let font_name = page.resources.add_font(font.font_id);
258 self.write_line(&format!("/{} {:.3} Tf", font_name, size));
259 }
260 self
261 }
262
263 pub fn set_text_matrix(&mut self, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) -> &mut Self {
264 self.write_line(&format!(
265 "{:.4} {:.4} {:.4} {:.4} {:.4} {:.4} Tm",
266 a, b, c, d, e, f
267 ));
268 self
269 }
270
271 pub fn set_leading(&mut self, leading: f64) -> &mut Self {
272 self.write_line(&format!("{:.3} TL", leading));
273 self
274 }
275
276 pub fn move_text_position(&mut self, tx: f64, ty: f64) -> &mut Self {
277 self.write_line(&format!("{:.3} {:.3} Td", tx, ty));
278 self
279 }
280
281 pub fn show_text(&mut self, text: &str) -> &mut Self {
282 let mut escaped = String::with_capacity(text.len());
283 for c in text.chars() {
284 match c {
285 '(' => escaped.push_str(r"\("),
286 ')' => escaped.push_str(r"\)"),
287 '\\' => escaped.push_str(r"\\"),
288 '\r' => escaped.push_str(r"\r"),
289 '\n' => escaped.push_str(r"\n"),
290 _ => escaped.push(c),
291 }
292 }
293 self.write_line(&format!("({}) Tj", escaped));
294 self
295 }
296
297 pub fn show_text_at(&mut self, text: &str, x: f64, y: f64) -> &mut Self {
298 self.set_text_matrix(1.0, 0.0, 0.0, 1.0, x, y);
299 self.show_text(text);
300 self
301 }
302
303 pub fn show_text_multiline(
304 &mut self,
305 lines: &[&str],
306 x: f64,
307 y: f64,
308 leading: f64,
309 ) -> &mut Self {
310 self.set_leading(leading);
311 self.set_text_matrix(1.0, 0.0, 0.0, 1.0, x, y);
312 for (i, line) in lines.iter().enumerate() {
313 if i > 0 {
314 self.write_line("T*");
315 }
316 self.show_text(line);
317 }
318 self
319 }
320
321 pub fn draw_image(
324 &mut self,
325 image_name: &str,
326 x: f64,
327 y: f64,
328 width: f64,
329 height: f64,
330 ) -> &mut Self {
331 self.save_graphics_state();
332 self.transform(width, 0.0, 0.0, height, x, y);
333 self.write_line(&format!("/{} Do", image_name));
334 self.restore_graphics_state();
335 self
336 }
337
338 pub fn finish(self, doc: &mut PDDocument, page_index: usize) -> PdfResult<()> {
340 let page = doc.get_page_mut(page_index)?;
341 page.sync_resources();
342
343 let mut stream = COSStream::new(COSDictionary::new(), self.buffer);
344 let _ = stream.encode_flate();
345 let stream_id = doc.cos_doc.add_object(stream);
346
347 let page = doc.get_page_mut(page_index)?;
348 let mut current_ids = page.content_stream_ids();
349
350 match self.mode {
351 AppendMode::Overwrite => {
352 page.set_contents(stream_id);
353 }
354 AppendMode::Append => {
355 current_ids.push(stream_id);
356 page.set_contents_array(current_ids);
357 }
358 AppendMode::Prepend => {
359 current_ids.insert(0, stream_id);
360 page.set_contents_array(current_ids);
361 }
362 }
363
364 page.sync_resources();
365 Ok(())
366 }
367
368 pub fn finish_to_bytes(self) -> Vec<u8> {
370 self.buffer
371 }
372}