1use crate::display_list::{DisplayElement, DisplayList};
8use crate::graphics_state::PsPath;
9
10pub use stet_graphics::device::{
13 AxialShadingParams, BgUcrState, CMYK_ALL, CMYK_C, CMYK_K, CMYK_M, CMYK_Y, ClipParams,
14 ColorStop, FillParams, HalftoneScreen, HalftoneState, ImageColorSpace, ImageParams,
15 MeshShadingParams, PatchShadingParams, PatternFillParams, RadialShadingParams,
16 ShadingColorSpace, ShadingPatch, ShadingTriangle, ShadingVertex, SimpleColorSpace, SpotColor,
17 SpotColorSpace, StrokeParams, TextParams, TintLookupTable, TransferState, TransferTable,
18 cmyk_channel_for_name,
19};
20pub use stet_graphics::device::{PageSink, PageSinkFactory};
21
22pub trait OutputDevice {
27 fn fill_path(&mut self, path: &PsPath, params: &FillParams);
29
30 fn stroke_path(&mut self, path: &PsPath, params: &StrokeParams);
32
33 fn clip_path(&mut self, path: &PsPath, params: &ClipParams);
35
36 fn init_clip(&mut self);
38
39 fn erase_page(&mut self);
41
42 fn show_page(&mut self, output_path: &str) -> Result<(), String>;
44
45 fn draw_image(&mut self, sample_data: &[u8], params: &ImageParams);
47
48 fn paint_axial_shading(&mut self, _params: &AxialShadingParams) {}
50
51 fn paint_radial_shading(&mut self, _params: &RadialShadingParams) {}
53
54 fn paint_mesh_shading(&mut self, _params: &MeshShadingParams) {}
56
57 fn paint_patch_shading(&mut self, _params: &PatchShadingParams) {}
59
60 fn paint_pattern_fill(&mut self, _params: &PatternFillParams) {}
62
63 fn set_trim_box(&mut self, _llx: f64, _lly: f64, _urx: f64, _ury: f64) {}
66
67 fn page_size(&self) -> (u32, u32);
69
70 fn replay_and_show(&mut self, list: DisplayList, output_path: &str) -> Result<(), String> {
72 for element in list.elements() {
73 match element {
74 DisplayElement::Fill { path, params } => self.fill_path(path, params),
75 DisplayElement::Stroke { path, params } => self.stroke_path(path, params),
76 DisplayElement::Clip { path, params } => self.clip_path(path, params),
77 DisplayElement::InitClip => self.init_clip(),
78 DisplayElement::Image {
79 sample_data,
80 params,
81 } => self.draw_image(sample_data, params),
82 DisplayElement::ErasePage => self.erase_page(),
83 DisplayElement::AxialShading { params } => self.paint_axial_shading(params),
84 DisplayElement::RadialShading { params } => self.paint_radial_shading(params),
85 DisplayElement::MeshShading { params } => self.paint_mesh_shading(params),
86 DisplayElement::PatchShading { params } => self.paint_patch_shading(params),
87 DisplayElement::PatternFill { params } => self.paint_pattern_fill(params),
88 DisplayElement::Text { .. } => {} DisplayElement::Group { .. } | DisplayElement::SoftMasked { .. } => {
90 }
92 DisplayElement::OcgGroup {
93 elements,
94 default_visible,
95 ..
96 } => {
97 let visible = *default_visible;
101 for elem in elements.elements() {
102 match elem {
103 DisplayElement::Clip { path, params } => self.clip_path(path, params),
104 DisplayElement::InitClip => self.init_clip(),
105 _ if !visible => {}
106 DisplayElement::Fill { path, params } => self.fill_path(path, params),
107 DisplayElement::Stroke { path, params } => {
108 self.stroke_path(path, params)
109 }
110 DisplayElement::Image {
111 sample_data,
112 params,
113 } => self.draw_image(sample_data, params),
114 DisplayElement::ErasePage => self.erase_page(),
115 DisplayElement::AxialShading { params } => {
116 self.paint_axial_shading(params)
117 }
118 DisplayElement::RadialShading { params } => {
119 self.paint_radial_shading(params)
120 }
121 DisplayElement::MeshShading { params } => {
122 self.paint_mesh_shading(params)
123 }
124 DisplayElement::PatchShading { params } => {
125 self.paint_patch_shading(params)
126 }
127 DisplayElement::PatternFill { params } => {
128 self.paint_pattern_fill(params)
129 }
130 _ => {}
131 }
132 }
133 }
134 }
135 }
136 self.show_page(output_path)
137 }
138
139 fn finish(&mut self) -> Result<(), String> {
141 Ok(())
142 }
143
144 fn finish_with_context(&mut self, _ctx: &crate::context::Context) -> Result<(), String> {
146 self.finish()
147 }
148
149 fn as_any(&self) -> &dyn std::any::Any {
152 &()
154 }
155}
156
157pub struct NullDevice {
159 width: u32,
160 height: u32,
161}
162
163impl NullDevice {
164 pub fn new(width: u32, height: u32) -> Self {
165 Self { width, height }
166 }
167}
168
169impl OutputDevice for NullDevice {
170 fn fill_path(&mut self, _path: &PsPath, _params: &FillParams) {}
171 fn stroke_path(&mut self, _path: &PsPath, _params: &StrokeParams) {}
172 fn clip_path(&mut self, _path: &PsPath, _params: &ClipParams) {}
173 fn init_clip(&mut self) {}
174 fn erase_page(&mut self) {}
175 fn show_page(&mut self, _output_path: &str) -> Result<(), String> {
176 Ok(())
177 }
178 fn draw_image(&mut self, _sample_data: &[u8], _params: &ImageParams) {}
179 fn page_size(&self) -> (u32, u32) {
180 (self.width, self.height)
181 }
182}
183
184pub fn replay_to_device(list: &DisplayList, device: &mut dyn OutputDevice) {
186 for element in list.elements() {
187 match element {
188 DisplayElement::Fill { path, params } => {
189 device.fill_path(path, params);
190 }
191 DisplayElement::Stroke { path, params } => {
192 device.stroke_path(path, params);
193 }
194 DisplayElement::Clip { path, params } => {
195 device.clip_path(path, params);
196 }
197 DisplayElement::InitClip => {
198 device.init_clip();
199 }
200 DisplayElement::Image {
201 sample_data,
202 params,
203 } => {
204 device.draw_image(sample_data, params);
205 }
206 DisplayElement::ErasePage => {
207 device.erase_page();
208 }
209 DisplayElement::AxialShading { params } => {
210 device.paint_axial_shading(params);
211 }
212 DisplayElement::RadialShading { params } => {
213 device.paint_radial_shading(params);
214 }
215 DisplayElement::MeshShading { params } => {
216 device.paint_mesh_shading(params);
217 }
218 DisplayElement::PatchShading { params } => {
219 device.paint_patch_shading(params);
220 }
221 DisplayElement::PatternFill { params } => {
222 device.paint_pattern_fill(params);
223 }
224 DisplayElement::Text { .. } => {}
225 DisplayElement::Group { elements, .. } => {
226 replay_to_device(elements, device);
227 }
228 DisplayElement::SoftMasked { content, .. } => {
229 replay_to_device(content, device);
230 }
231 DisplayElement::OcgGroup {
232 elements,
233 default_visible,
234 ..
235 } => {
236 if *default_visible {
237 replay_to_device(elements, device);
238 } else {
239 for elem in elements.elements() {
243 match elem {
244 DisplayElement::Clip { path, params } => {
245 device.clip_path(path, params);
246 }
247 DisplayElement::InitClip => {
248 device.init_clip();
249 }
250 _ => {}
251 }
252 }
253 }
254 }
255 }
256 }
257}