1use folio_core::Matrix2D;
4
5#[derive(Debug, Clone)]
7pub struct GraphicsState {
8 pub ctm: Matrix2D,
10 pub line_width: f64,
12 pub line_cap: i32,
14 pub line_join: i32,
16 pub miter_limit: f64,
18 pub dash_array: Vec<f64>,
20 pub dash_phase: f64,
22 pub fill_color: Vec<f64>,
24 pub stroke_color: Vec<f64>,
26 pub fill_color_space: Vec<u8>,
28 pub stroke_color_space: Vec<u8>,
30 pub char_spacing: f64,
32 pub word_spacing: f64,
34 pub horiz_scaling: f64,
36 pub text_leading: f64,
38 pub font_name: Vec<u8>,
40 pub font_size: f64,
42 pub text_render_mode: i32,
44 pub text_rise: f64,
46 pub text_matrix: Matrix2D,
48 pub text_line_matrix: Matrix2D,
50 pub fill_opacity: f64,
52 pub stroke_opacity: f64,
54}
55
56impl Default for GraphicsState {
57 fn default() -> Self {
58 Self {
59 ctm: Matrix2D::identity(),
60 line_width: 1.0,
61 line_cap: 0,
62 line_join: 0,
63 miter_limit: 10.0,
64 dash_array: Vec::new(),
65 dash_phase: 0.0,
66 fill_color: vec![0.0],
67 stroke_color: vec![0.0],
68 fill_color_space: b"DeviceGray".to_vec(),
69 stroke_color_space: b"DeviceGray".to_vec(),
70 char_spacing: 0.0,
71 word_spacing: 0.0,
72 horiz_scaling: 100.0,
73 text_leading: 0.0,
74 font_name: Vec::new(),
75 font_size: 0.0,
76 text_render_mode: 0,
77 text_rise: 0.0,
78 text_matrix: Matrix2D::identity(),
79 text_line_matrix: Matrix2D::identity(),
80 fill_opacity: 1.0,
81 stroke_opacity: 1.0,
82 }
83 }
84}
85
86#[derive(Debug)]
88pub struct GraphicsStateStack {
89 current: GraphicsState,
90 stack: Vec<GraphicsState>,
91}
92
93impl GraphicsStateStack {
94 pub fn new() -> Self {
95 Self {
96 current: GraphicsState::default(),
97 stack: Vec::new(),
98 }
99 }
100
101 pub fn current(&self) -> &GraphicsState {
103 &self.current
104 }
105
106 pub fn current_mut(&mut self) -> &mut GraphicsState {
108 &mut self.current
109 }
110
111 pub fn save(&mut self) {
113 self.stack.push(self.current.clone());
114 }
115
116 pub fn restore(&mut self) {
118 if let Some(prev) = self.stack.pop() {
119 self.current = prev;
120 }
121 }
122
123 pub fn depth(&self) -> usize {
125 self.stack.len()
126 }
127}
128
129impl Default for GraphicsStateStack {
130 fn default() -> Self {
131 Self::new()
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn test_save_restore() {
141 let mut stack = GraphicsStateStack::new();
142 stack.current_mut().line_width = 5.0;
143 stack.save();
144 stack.current_mut().line_width = 10.0;
145 assert_eq!(stack.current().line_width, 10.0);
146 stack.restore();
147 assert_eq!(stack.current().line_width, 5.0);
148 }
149
150 #[test]
151 fn test_restore_empty() {
152 let mut stack = GraphicsStateStack::new();
153 stack.current_mut().line_width = 5.0;
154 stack.restore(); assert_eq!(stack.current().line_width, 5.0);
156 }
157
158 #[test]
159 fn test_nested() {
160 let mut stack = GraphicsStateStack::new();
161 stack.current_mut().line_width = 1.0;
162 stack.save();
163 stack.current_mut().line_width = 2.0;
164 stack.save();
165 stack.current_mut().line_width = 3.0;
166 assert_eq!(stack.depth(), 2);
167 stack.restore();
168 assert_eq!(stack.current().line_width, 2.0);
169 stack.restore();
170 assert_eq!(stack.current().line_width, 1.0);
171 }
172}