Skip to main content

rust_pdf_box/content/
stream.rs

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
16/// Content stream builder for rendering text, paths, graphics states and images on a page.
17pub 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    // --- Graphics State ---
40
41    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        // Translate to origin, rotate, translate back:
134        // cm = [cos sin -sin cos (cx - cx*cos + cy*sin) (cy - cx*sin - cy*cos)]
135        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    // --- Path Construction & Painting ---
141
142    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    // --- Text Rendering ---
218
219    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    // --- Image Drawing ---
322
323    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    /// Flushes and applies the content stream to the document page.
339    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    /// Returns the raw byte buffer of this content stream.
369    pub fn finish_to_bytes(self) -> Vec<u8> {
370        self.buffer
371    }
372}