oxidize_pdf/graphics/
mod.rs

1mod path;
2mod color;
3mod image;
4
5pub use path::{PathBuilder, LineJoin, LineCap};
6pub use color::Color;
7pub use image::{Image, ImageFormat, ColorSpace as ImageColorSpace};
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 GraphicsContext {
21    pub fn new() -> Self {
22        Self {
23            operations: String::new(),
24            current_color: Color::black(),
25            stroke_color: Color::black(),
26            line_width: 1.0,
27        }
28    }
29    
30    pub fn move_to(&mut self, x: f64, y: f64) -> &mut Self {
31        write!(&mut self.operations, "{:.2} {:.2} m\n", x, y).unwrap();
32        self
33    }
34    
35    pub fn line_to(&mut self, x: f64, y: f64) -> &mut Self {
36        write!(&mut self.operations, "{:.2} {:.2} l\n", x, y).unwrap();
37        self
38    }
39    
40    pub fn curve_to(&mut self, x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64) -> &mut Self {
41        write!(&mut self.operations, "{:.2} {:.2} {:.2} {:.2} {:.2} {:.2} c\n", 
42               x1, y1, x2, y2, x3, y3).unwrap();
43        self
44    }
45    
46    pub fn rect(&mut self, x: f64, y: f64, width: f64, height: f64) -> &mut Self {
47        write!(&mut self.operations, "{:.2} {:.2} {:.2} {:.2} re\n", 
48               x, y, width, height).unwrap();
49        self
50    }
51    
52    pub fn circle(&mut self, cx: f64, cy: f64, radius: f64) -> &mut Self {
53        let k = 0.552284749831;
54        let r = radius;
55        
56        self.move_to(cx + r, cy);
57        self.curve_to(cx + r, cy + k * r, cx + k * r, cy + r, cx, cy + r);
58        self.curve_to(cx - k * r, cy + r, cx - r, cy + k * r, cx - r, cy);
59        self.curve_to(cx - r, cy - k * r, cx - k * r, cy - r, cx, cy - r);
60        self.curve_to(cx + k * r, cy - r, cx + r, cy - k * r, cx + r, cy);
61        self.close_path()
62    }
63    
64    pub fn close_path(&mut self) -> &mut Self {
65        self.operations.push_str("h\n");
66        self
67    }
68    
69    pub fn stroke(&mut self) -> &mut Self {
70        self.apply_stroke_color();
71        self.operations.push_str("S\n");
72        self
73    }
74    
75    pub fn fill(&mut self) -> &mut Self {
76        self.apply_fill_color();
77        self.operations.push_str("f\n");
78        self
79    }
80    
81    pub fn fill_stroke(&mut self) -> &mut Self {
82        self.apply_fill_color();
83        self.apply_stroke_color();
84        self.operations.push_str("B\n");
85        self
86    }
87    
88    pub fn set_stroke_color(&mut self, color: Color) -> &mut Self {
89        self.stroke_color = color;
90        self
91    }
92    
93    pub fn set_fill_color(&mut self, color: Color) -> &mut Self {
94        self.current_color = color;
95        self
96    }
97    
98    pub fn set_line_width(&mut self, width: f64) -> &mut Self {
99        self.line_width = width;
100        write!(&mut self.operations, "{:.2} w\n", width).unwrap();
101        self
102    }
103    
104    pub fn set_line_cap(&mut self, cap: LineCap) -> &mut Self {
105        write!(&mut self.operations, "{} J\n", cap as u8).unwrap();
106        self
107    }
108    
109    pub fn set_line_join(&mut self, join: LineJoin) -> &mut Self {
110        write!(&mut self.operations, "{} j\n", join as u8).unwrap();
111        self
112    }
113    
114    pub fn save_state(&mut self) -> &mut Self {
115        self.operations.push_str("q\n");
116        self
117    }
118    
119    pub fn restore_state(&mut self) -> &mut Self {
120        self.operations.push_str("Q\n");
121        self
122    }
123    
124    pub fn translate(&mut self, tx: f64, ty: f64) -> &mut Self {
125        write!(&mut self.operations, "1 0 0 1 {:.2} {:.2} cm\n", tx, ty).unwrap();
126        self
127    }
128    
129    pub fn scale(&mut self, sx: f64, sy: f64) -> &mut Self {
130        write!(&mut self.operations, "{:.2} 0 0 {:.2} 0 0 cm\n", sx, sy).unwrap();
131        self
132    }
133    
134    pub fn rotate(&mut self, angle: f64) -> &mut Self {
135        let cos = angle.cos();
136        let sin = angle.sin();
137        write!(&mut self.operations, "{:.6} {:.6} {:.6} {:.6} 0 0 cm\n", 
138               cos, sin, -sin, cos).unwrap();
139        self
140    }
141    
142    pub fn transform(&mut self, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) -> &mut Self {
143        write!(&mut self.operations, "{:.2} {:.2} {:.2} {:.2} {:.2} {:.2} cm\n", 
144               a, b, c, d, e, f).unwrap();
145        self
146    }
147    
148    pub fn rectangle(&mut self, x: f64, y: f64, width: f64, height: f64) -> &mut Self {
149        self.rect(x, y, width, height)
150    }
151    
152    pub fn draw_image(&mut self, image_name: &str, x: f64, y: f64, width: f64, height: f64) -> &mut Self {
153        // Save graphics state
154        self.save_state();
155        
156        // Set up transformation matrix for image placement
157        // PDF coordinate system has origin at bottom-left, so we need to translate and scale
158        write!(&mut self.operations, "{:.2} 0 0 {:.2} {:.2} {:.2} cm\n", 
159               width, height, x, y).unwrap();
160        
161        // Draw the image XObject
162        write!(&mut self.operations, "/{} Do\n", image_name).unwrap();
163        
164        // Restore graphics state
165        self.restore_state();
166        
167        self
168    }
169    
170    fn apply_stroke_color(&mut self) {
171        match self.stroke_color {
172            Color::Rgb(r, g, b) => {
173                write!(&mut self.operations, "{:.3} {:.3} {:.3} RG\n", r, g, b).unwrap();
174            }
175            Color::Gray(g) => {
176                write!(&mut self.operations, "{:.3} G\n", g).unwrap();
177            }
178            Color::Cmyk(c, m, y, k) => {
179                write!(&mut self.operations, "{:.3} {:.3} {:.3} {:.3} K\n", c, m, y, k).unwrap();
180            }
181        }
182    }
183    
184    fn apply_fill_color(&mut self) {
185        match self.current_color {
186            Color::Rgb(r, g, b) => {
187                write!(&mut self.operations, "{:.3} {:.3} {:.3} rg\n", r, g, b).unwrap();
188            }
189            Color::Gray(g) => {
190                write!(&mut self.operations, "{:.3} g\n", g).unwrap();
191            }
192            Color::Cmyk(c, m, y, k) => {
193                write!(&mut self.operations, "{:.3} {:.3} {:.3} {:.3} k\n", c, m, y, k).unwrap();
194            }
195        }
196    }
197    
198    pub(crate) fn generate_operations(&self) -> Result<Vec<u8>> {
199        Ok(self.operations.as_bytes().to_vec())
200    }
201}