1use stet_fonts::geometry::{Matrix, PsPath};
8use stet_graphics::color::{DashPattern, DeviceColor, FillRule, LineCap, LineJoin};
9use stet_graphics::device::{
10 BgUcrState, FillParams, HalftoneState, IccColor, SpotColor, StrokeParams, TransferState,
11};
12use stet_graphics::display_list::{DisplayList, SoftMaskSubtype};
13
14#[derive(Clone)]
16pub struct ShadingPatternDL(pub DisplayList);
17
18impl std::fmt::Debug for ShadingPatternDL {
19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20 f.debug_struct("ShadingPatternDL")
21 .field("elements", &self.0.len())
22 .finish()
23 }
24}
25
26#[derive(Clone)]
28pub struct TilingPattern {
29 pub tile: DisplayList,
31 pub bbox: [f64; 4],
33 pub x_step: f64,
35 pub y_step: f64,
37 pub pattern_matrix: Matrix,
39 pub paint_type: i32,
41 pub pattern_id: u32,
43 pub flip_tile_y: bool,
45}
46
47impl std::fmt::Debug for TilingPattern {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 f.debug_struct("TilingPattern")
50 .field("bbox", &self.bbox)
51 .field("x_step", &self.x_step)
52 .field("y_step", &self.y_step)
53 .field("paint_type", &self.paint_type)
54 .field("pattern_id", &self.pattern_id)
55 .finish()
56 }
57}
58
59#[derive(Clone)]
61pub struct SoftMask {
62 pub mask_list: DisplayList,
64 pub subtype: SoftMaskSubtype,
66 pub bbox: [f64; 4],
68 pub backdrop_color: Option<[f64; 3]>,
70 pub transfer_invert: bool,
72 pub has_nested_mask_scope: bool,
77}
78
79impl std::fmt::Debug for SoftMask {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 f.debug_struct("SoftMask")
82 .field("subtype", &self.subtype)
83 .field("bbox", &self.bbox)
84 .finish()
85 }
86}
87
88#[derive(Clone, Debug)]
90pub enum ColorSpaceRef {
91 DeviceGray,
92 DeviceRGB,
93 DeviceCMYK,
94 Named(Vec<u8>),
96}
97
98impl ColorSpaceRef {
99 pub fn num_components(&self) -> Option<usize> {
101 match self {
102 Self::DeviceGray => Some(1),
103 Self::DeviceRGB => Some(3),
104 Self::DeviceCMYK => Some(4),
105 Self::Named(_) => None,
106 }
107 }
108}
109
110#[derive(Clone, Debug)]
112pub struct PdfGraphicsState {
113 pub ctm: Matrix,
114 pub fill_color: DeviceColor,
115 pub stroke_color: DeviceColor,
116 pub line_width: f64,
117 pub line_cap: LineCap,
118 pub line_join: LineJoin,
119 pub miter_limit: f64,
120 pub dash_pattern: DashPattern,
121 pub rendering_intent: u8,
122 pub stroke_adjust: bool,
123 pub overprint: bool,
124 pub overprint_stroke: bool,
125 pub overprint_mode: i32,
127 pub opm_paired: bool,
134 pub fill_painted_channels: u8,
136 pub fill_is_device_cmyk: bool,
138 pub stroke_painted_channels: u8,
140 pub stroke_is_device_cmyk: bool,
142 pub flatness: f64,
143 pub fill_color_space: ColorSpaceRef,
144 pub stroke_color_space: ColorSpaceRef,
145 pub pending_clip: Option<(PsPath, FillRule)>,
147 pub clip_path: Option<PsPath>,
149 pub clip_stack: Vec<(PsPath, FillRule)>,
153 pub clip_path_version: u32,
155 pub fill_alpha: f64,
156 pub stroke_alpha: f64,
157 pub fill_spot_color: Option<SpotColor>,
160 pub stroke_spot_color: Option<SpotColor>,
163 pub fill_icc_color: Option<IccColor>,
166 pub stroke_icc_color: Option<IccColor>,
168 pub fill_is_none: bool,
170 pub stroke_is_none: bool,
172 pub blend_mode: u8,
174 pub alpha_is_shape: bool,
176 pub text_knockout: bool,
178 pub text_matrix: Matrix,
180 pub text_line_matrix: Matrix,
181 pub font_size: f64,
182 pub char_spacing: f64,
183 pub word_spacing: f64,
184 pub text_leading: f64,
185 pub text_rise: f64,
186 pub horizontal_scaling: f64,
188 pub text_rendering_mode: i32,
189 pub text_font_name: Vec<u8>,
190 pub fill_pattern: Option<TilingPattern>,
192 pub fill_shading_pattern: Option<Box<ShadingPatternDL>>,
196 pub stroke_pattern: Option<TilingPattern>,
198 pub stroke_shading_pattern: Option<Box<ShadingPatternDL>>,
200 pub next_pattern_id: u32,
202 pub transfer: TransferState,
204 pub soft_mask: Option<SoftMask>,
206 pub smask_gen: u64,
209}
210
211impl PdfGraphicsState {
212 pub fn new(initial_ctm: Matrix) -> Self {
214 Self {
215 ctm: initial_ctm,
216 fill_color: DeviceColor::black(),
217 stroke_color: DeviceColor::black(),
218 line_width: 1.0,
219 line_cap: LineCap::Butt,
220 line_join: LineJoin::Miter,
221 miter_limit: 10.0,
222 dash_pattern: DashPattern::solid(),
223 rendering_intent: 0,
224 stroke_adjust: false,
225 overprint: false,
226 overprint_stroke: false,
227 overprint_mode: 0,
228 opm_paired: false,
229 fill_painted_channels: 0,
230 fill_is_device_cmyk: false,
231 stroke_painted_channels: 0,
232 stroke_is_device_cmyk: false,
233 flatness: 1.0,
234 fill_color_space: ColorSpaceRef::DeviceGray,
235 stroke_color_space: ColorSpaceRef::DeviceGray,
236 pending_clip: None,
237 clip_path: None,
238 clip_stack: Vec::new(),
239 clip_path_version: 0,
240 fill_alpha: 1.0,
241 stroke_alpha: 1.0,
242 fill_spot_color: None,
243 stroke_spot_color: None,
244 fill_icc_color: None,
245 stroke_icc_color: None,
246 fill_is_none: false,
247 stroke_is_none: false,
248 blend_mode: 0,
249 alpha_is_shape: false,
250 text_knockout: true,
251 text_matrix: Matrix::identity(),
252 text_line_matrix: Matrix::identity(),
253 font_size: 0.0,
254 char_spacing: 0.0,
255 word_spacing: 0.0,
256 text_leading: 0.0,
257 text_rise: 0.0,
258 horizontal_scaling: 1.0,
259 text_rendering_mode: 0,
260 text_font_name: Vec::new(),
261 fill_pattern: None,
262 fill_shading_pattern: None,
263 stroke_pattern: None,
264 stroke_shading_pattern: None,
265 next_pattern_id: 0,
266 transfer: TransferState::default(),
267 soft_mask: None,
268 smask_gen: 0,
269 }
270 }
271
272 pub fn fill_params(&self, fill_rule: FillRule) -> FillParams {
274 let color = if self.transfer.has_functions() {
275 super::apply_transfer_to_color(&self.fill_color, &self.transfer)
276 } else {
277 self.fill_color.clone()
278 };
279 FillParams {
280 color,
281 fill_rule,
282 ctm: Matrix::identity(),
283 is_text_glyph: false,
284 overprint: self.overprint,
285 overprint_mode: self.overprint_mode,
286 opm_paired: self.opm_paired,
287 painted_channels: self.fill_painted_channels,
288 is_device_cmyk: self.fill_is_device_cmyk,
289 spot_color: self.fill_spot_color.clone(),
290 icc_color: self.fill_icc_color.clone(),
291 rendering_intent: self.rendering_intent,
292 transfer: self.transfer.clone(),
293 halftone: HalftoneState::default(),
294 bg_ucr: BgUcrState::default(),
295 alpha: if self.fill_is_none {
296 0.0
297 } else {
298 self.fill_alpha
299 },
300 blend_mode: self.blend_mode,
301 alpha_is_shape: self.alpha_is_shape,
302 }
303 }
304
305 pub fn stroke_params(&self) -> StrokeParams {
307 let scale = self.ctm_scale_factor();
308 let scaled_dash = DashPattern {
309 array: self.dash_pattern.array.iter().map(|d| d * scale).collect(),
310 offset: self.dash_pattern.offset * scale,
311 };
312 let color = if self.transfer.has_functions() {
313 super::apply_transfer_to_color(&self.stroke_color, &self.transfer)
314 } else {
315 self.stroke_color.clone()
316 };
317 StrokeParams {
318 color,
319 line_width: self.line_width * scale,
320 line_cap: self.line_cap,
321 line_join: self.line_join,
322 miter_limit: self.miter_limit,
323 dash_pattern: scaled_dash,
324 ctm: Matrix::identity(),
325 stroke_adjust: self.stroke_adjust,
326 is_text_glyph: false,
327 overprint: self.overprint_stroke,
328 overprint_mode: self.overprint_mode,
329 opm_paired: self.opm_paired,
330 painted_channels: self.stroke_painted_channels,
331 is_device_cmyk: self.stroke_is_device_cmyk,
332 spot_color: self.stroke_spot_color.clone(),
333 icc_color: self.stroke_icc_color.clone(),
334 rendering_intent: self.rendering_intent,
335 transfer: self.transfer.clone(),
336 halftone: HalftoneState::default(),
337 bg_ucr: BgUcrState::default(),
338 alpha: if self.stroke_is_none {
339 0.0
340 } else {
341 self.stroke_alpha
342 },
343 blend_mode: self.blend_mode,
344 alpha_is_shape: self.alpha_is_shape,
345 }
346 }
347
348 pub fn stroke_params_with_ctm(&self) -> StrokeParams {
351 let color = if self.transfer.has_functions() {
352 super::apply_transfer_to_color(&self.stroke_color, &self.transfer)
353 } else {
354 self.stroke_color.clone()
355 };
356 StrokeParams {
357 color,
358 line_width: self.line_width,
359 line_cap: self.line_cap,
360 line_join: self.line_join,
361 miter_limit: self.miter_limit,
362 dash_pattern: self.dash_pattern.clone(),
363 ctm: Matrix::identity(), stroke_adjust: self.stroke_adjust,
365 is_text_glyph: false,
366 overprint: self.overprint_stroke,
367 overprint_mode: self.overprint_mode,
368 opm_paired: self.opm_paired,
369 painted_channels: self.stroke_painted_channels,
370 is_device_cmyk: self.stroke_is_device_cmyk,
371 spot_color: self.stroke_spot_color.clone(),
372 icc_color: self.stroke_icc_color.clone(),
373 rendering_intent: self.rendering_intent,
374 transfer: self.transfer.clone(),
375 halftone: HalftoneState::default(),
376 bg_ucr: BgUcrState::default(),
377 alpha: if self.stroke_is_none {
378 0.0
379 } else {
380 self.stroke_alpha
381 },
382 blend_mode: self.blend_mode,
383 alpha_is_shape: self.alpha_is_shape,
384 }
385 }
386
387 pub fn ctm_scale_factor(&self) -> f64 {
389 (self.ctm.a * self.ctm.a + self.ctm.b * self.ctm.b).sqrt()
390 }
391}