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 writes_file_per_page(&self) -> bool {
76 true
77 }
78
79 fn page_size(&self) -> (u32, u32);
81
82 fn replay_and_show(&mut self, list: DisplayList, output_path: &str) -> Result<(), String> {
84 for element in list.elements() {
85 match element {
86 DisplayElement::Fill { path, params } => self.fill_path(path, params),
87 DisplayElement::Stroke { path, params } => self.stroke_path(path, params),
88 DisplayElement::Clip { path, params } => self.clip_path(path, params),
89 DisplayElement::InitClip => self.init_clip(),
90 DisplayElement::Image {
91 sample_data,
92 params,
93 } => self.draw_image(sample_data, params),
94 DisplayElement::ErasePage => self.erase_page(),
95 DisplayElement::AxialShading { params } => self.paint_axial_shading(params),
96 DisplayElement::RadialShading { params } => self.paint_radial_shading(params),
97 DisplayElement::MeshShading { params } => self.paint_mesh_shading(params),
98 DisplayElement::PatchShading { params } => self.paint_patch_shading(params),
99 DisplayElement::PatternFill { params } => self.paint_pattern_fill(params),
100 DisplayElement::Text { .. } => {} DisplayElement::Group { .. } | DisplayElement::SoftMasked { .. } => {
102 }
104 DisplayElement::OcgGroup {
105 elements,
106 visibility,
107 } => {
108 let visible = visibility.default_visible();
112 for elem in elements.elements() {
113 match elem {
114 DisplayElement::Clip { path, params } => self.clip_path(path, params),
115 DisplayElement::InitClip => self.init_clip(),
116 _ if !visible => {}
117 DisplayElement::Fill { path, params } => self.fill_path(path, params),
118 DisplayElement::Stroke { path, params } => {
119 self.stroke_path(path, params)
120 }
121 DisplayElement::Image {
122 sample_data,
123 params,
124 } => self.draw_image(sample_data, params),
125 DisplayElement::ErasePage => self.erase_page(),
126 DisplayElement::AxialShading { params } => {
127 self.paint_axial_shading(params)
128 }
129 DisplayElement::RadialShading { params } => {
130 self.paint_radial_shading(params)
131 }
132 DisplayElement::MeshShading { params } => {
133 self.paint_mesh_shading(params)
134 }
135 DisplayElement::PatchShading { params } => {
136 self.paint_patch_shading(params)
137 }
138 DisplayElement::PatternFill { params } => {
139 self.paint_pattern_fill(params)
140 }
141 _ => {}
142 }
143 }
144 }
145 _ => {}
146 }
147 }
148 self.show_page(output_path)
149 }
150
151 fn finish(&mut self) -> Result<(), String> {
153 Ok(())
154 }
155
156 fn finish_with_context(&mut self, _ctx: &crate::context::Context) -> Result<(), String> {
158 self.finish()
159 }
160
161 fn as_any(&self) -> &dyn std::any::Any {
164 &()
166 }
167}
168
169pub struct NullDevice {
171 width: u32,
172 height: u32,
173}
174
175impl NullDevice {
176 pub fn new(width: u32, height: u32) -> Self {
177 Self { width, height }
178 }
179}
180
181impl OutputDevice for NullDevice {
182 fn fill_path(&mut self, _path: &PsPath, _params: &FillParams) {}
183 fn stroke_path(&mut self, _path: &PsPath, _params: &StrokeParams) {}
184 fn clip_path(&mut self, _path: &PsPath, _params: &ClipParams) {}
185 fn init_clip(&mut self) {}
186 fn erase_page(&mut self) {}
187 fn show_page(&mut self, _output_path: &str) -> Result<(), String> {
188 Ok(())
189 }
190 fn draw_image(&mut self, _sample_data: &[u8], _params: &ImageParams) {}
191 fn page_size(&self) -> (u32, u32) {
192 (self.width, self.height)
193 }
194}
195
196pub fn replay_to_device(list: &DisplayList, device: &mut dyn OutputDevice) {
198 for element in list.elements() {
199 match element {
200 DisplayElement::Fill { path, params } => {
201 device.fill_path(path, params);
202 }
203 DisplayElement::Stroke { path, params } => {
204 device.stroke_path(path, params);
205 }
206 DisplayElement::Clip { path, params } => {
207 device.clip_path(path, params);
208 }
209 DisplayElement::InitClip => {
210 device.init_clip();
211 }
212 DisplayElement::Image {
213 sample_data,
214 params,
215 } => {
216 device.draw_image(sample_data, params);
217 }
218 DisplayElement::ErasePage => {
219 device.erase_page();
220 }
221 DisplayElement::AxialShading { params } => {
222 device.paint_axial_shading(params);
223 }
224 DisplayElement::RadialShading { params } => {
225 device.paint_radial_shading(params);
226 }
227 DisplayElement::MeshShading { params } => {
228 device.paint_mesh_shading(params);
229 }
230 DisplayElement::PatchShading { params } => {
231 device.paint_patch_shading(params);
232 }
233 DisplayElement::PatternFill { params } => {
234 device.paint_pattern_fill(params);
235 }
236 DisplayElement::Text { .. } => {}
237 DisplayElement::Group { elements, .. } => {
238 replay_to_device(elements, device);
239 }
240 DisplayElement::SoftMasked { content, .. } => {
241 replay_to_device(content, device);
242 }
243 DisplayElement::OcgGroup {
244 elements,
245 visibility,
246 } => {
247 if visibility.default_visible() {
248 replay_to_device(elements, device);
249 } else {
250 for elem in elements.elements() {
254 match elem {
255 DisplayElement::Clip { path, params } => {
256 device.clip_path(path, params);
257 }
258 DisplayElement::InitClip => {
259 device.init_clip();
260 }
261 _ => {}
262 }
263 }
264 }
265 }
266 _ => {}
267 }
268 }
269}