1use crate::WarningSinkFn;
4use crate::cache::Cache;
5use crate::color::{Color, ColorSpace};
6use crate::context::Context;
7use crate::device::Device;
8use crate::font::Glyph;
9use crate::interpret::state::{ActiveTransferFunction, State};
10use crate::shading::Shading;
11use crate::soft_mask::SoftMask;
12use crate::util::{Float32Ext, RectExt, decode_or_warn, hash128};
13use crate::{BlendMode, CacheKey, ClipPath, GlyphDrawMode, Image, PathDrawMode};
14use crate::{FillRule, InterpreterSettings, Paint, interpret};
15use kurbo::{Affine, BezPath, Rect, Shape};
16use log::warn;
17use pdf_syntax::content::TypedIter;
18use pdf_syntax::object::Dict;
19use pdf_syntax::object::Stream;
20use pdf_syntax::object::dict::keys::{
21 BBOX, EXT_G_STATE, MATRIX, PAINT_TYPE, RESOURCES, SHADING, X_STEP, Y_STEP,
22};
23use pdf_syntax::object::{Object, dict_or_stream};
24use pdf_syntax::page::Resources;
25use pdf_syntax::xref::XRef;
26use std::fmt::{Debug, Formatter};
27use std::sync::Arc;
28
29#[derive(Debug, Clone)]
31pub enum Pattern<'a> {
32 Shading(ShadingPattern),
34 Tiling(Box<TilingPattern<'a>>),
36}
37
38impl<'a> Pattern<'a> {
39 pub(crate) fn new(object: Object<'a>, ctx: &Context<'a>) -> Option<Self> {
40 if let Some(dict) = object.clone().into_dict() {
41 Some(Self::Shading(ShadingPattern::new(
42 &dict,
43 &ctx.object_cache,
44 ctx.get().graphics_state.non_stroke_alpha,
45 &ctx.settings.warning_sink,
46 )?))
47 } else if let Some(stream) = object.clone().into_stream() {
48 Some(Self::Tiling(Box::new(TilingPattern::new(stream, ctx)?)))
49 } else {
50 None
51 }
52 }
53
54 pub(crate) fn pre_concat_transform(&mut self, transform: Affine) {
55 match self {
56 Self::Shading(p) => {
57 p.matrix = transform * p.matrix;
58 let transformed_clip_path = p.shading.clip_path.clone().map(|r| p.matrix * r);
59 Arc::make_mut(&mut p.shading).clip_path = transformed_clip_path;
60 }
61 Self::Tiling(p) => p.matrix = transform * p.matrix,
62 }
63 }
64
65 pub(crate) fn set_transfer_function(&mut self, tf: ActiveTransferFunction) {
66 if let Self::Shading(p) = self {
67 p.transfer_function = Some(tf);
68 }
69 }
70}
71
72impl CacheKey for Pattern<'_> {
73 fn cache_key(&self) -> u128 {
74 match self {
75 Self::Shading(p) => p.cache_key(),
76 Self::Tiling(p) => p.cache_key(),
77 }
78 }
79}
80
81#[derive(Clone, Debug)]
83pub struct ShadingPattern {
84 pub shading: Arc<Shading>,
86 pub matrix: Affine,
88 pub opacity: f32,
90 pub transfer_function: Option<ActiveTransferFunction>,
92}
93
94impl ShadingPattern {
95 pub(crate) fn new(
96 dict: &Dict<'_>,
97 cache: &Cache,
98 opacity: f32,
99 warning_sink: &WarningSinkFn,
100 ) -> Option<Self> {
101 let shading = dict.get::<Object<'_>>(SHADING).and_then(|o| {
102 let (dict, stream) = dict_or_stream(&o)?;
103
104 Shading::new(&dict, stream.as_ref(), cache, warning_sink)
105 })?;
106 let matrix = dict
107 .get::<[f64; 6]>(MATRIX)
108 .map(Affine::new)
109 .unwrap_or_default();
110
111 if dict.contains_key(EXT_G_STATE) {
112 warn!("shading patterns with ext_g_state are not supported yet");
113 }
114
115 Some(Self {
116 shading: Arc::new(shading),
117 opacity,
118 matrix,
119 transfer_function: None,
120 })
121 }
122}
123
124impl CacheKey for ShadingPattern {
125 fn cache_key(&self) -> u128 {
126 hash128(&(self.shading.cache_key(), self.matrix.cache_key()))
127 }
128}
129
130#[derive(Clone)]
132pub struct TilingPattern<'a> {
133 nesting_depth: u32,
137 cache_key: u128,
138 ctx_bbox: Rect,
139 pub bbox: Rect,
141 pub x_step: f32,
143 pub y_step: f32,
145 pub matrix: Affine,
147 stream: Stream<'a>,
148 pub is_color: bool,
150 pub opacity: f32,
155 pub(crate) stroke_paint: Color,
156 pub(crate) non_stroking_paint: Color,
157 resources: Resources<'a>,
158 pub(crate) cache: Cache,
159 pub(crate) settings: InterpreterSettings,
160 pub(crate) xref: &'a XRef,
161}
162
163impl Debug for TilingPattern<'_> {
164 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
165 f.write_str("TilingPattern")
166 }
167}
168
169impl<'a> TilingPattern<'a> {
170 pub(crate) fn new(stream: Stream<'a>, ctx: &Context<'a>) -> Option<Self> {
171 let cache_key = stream.cache_key();
172 let dict = stream.dict();
173
174 let bbox = dict.get::<pdf_syntax::object::Rect>(BBOX)?.to_kurbo();
175 let x_step = dict.get::<f32>(X_STEP)?;
176 let y_step = dict.get::<f32>(Y_STEP)?;
177
178 if x_step.is_nearly_zero() || y_step.is_nearly_zero() || bbox.is_zero_area() {
179 return None;
180 }
181
182 let is_color = dict.get::<u8>(PAINT_TYPE)? == 1;
183 let matrix = dict
184 .get::<[f64; 6]>(MATRIX)
185 .map(Affine::new)
186 .unwrap_or_default();
187
188 let state = ctx.get().clone();
189 let ctx_bbox = ctx.bbox();
190
191 let fill_cs = state
192 .graphics_state
193 .none_stroke_cs
194 .pattern_cs()
195 .unwrap_or(ColorSpace::device_gray());
196 let stroke_cs = state
197 .graphics_state
198 .stroke_cs
199 .pattern_cs()
200 .unwrap_or(ColorSpace::device_gray());
201
202 let non_stroke_alpha = state.graphics_state.non_stroke_alpha;
203 let stroke_alpha = state.graphics_state.stroke_alpha;
204
205 let non_stroking_paint = Color::new(
206 fill_cs,
207 state.graphics_state.non_stroke_color.clone(),
208 non_stroke_alpha,
209 );
210 let stroke_paint = Color::new(
211 stroke_cs,
212 state.graphics_state.stroke_color.clone(),
213 stroke_alpha,
214 );
215
216 let opacity = non_stroke_alpha;
220 let resources = Resources::new(dict.get(RESOURCES).unwrap_or_default());
221
222 Some(Self {
223 nesting_depth: ctx.nesting_depth(),
224 cache_key,
225 bbox,
226 x_step,
227 y_step,
228 matrix,
229 ctx_bbox,
230 is_color,
231 stream,
232 opacity,
233 stroke_paint,
234 non_stroking_paint,
235 settings: ctx.settings.clone(),
236 resources,
237 cache: ctx.object_cache.clone(),
238 xref: ctx.xref,
239 })
240 }
241
242 pub fn interpret(
244 &self,
245 device: &mut impl Device<'a>,
246 initial_transform: Affine,
247 is_stroke: bool,
248 ) -> Option<()> {
249 let state = State::new(initial_transform);
250
251 let mut context = Context::new_with(
252 state.ctm,
253 (initial_transform * self.ctx_bbox.to_path(0.1)).bounding_box(),
255 self.cache.clone(),
256 self.xref,
257 self.settings.clone(),
258 state,
259 self.nesting_depth + 1,
264 );
265
266 let decoded = decode_or_warn(&self.stream, &self.settings.warning_sink)?;
267 let iter = TypedIter::new(decoded.as_ref());
268
269 let clip_path = ClipPath {
270 path: initial_transform * self.bbox.to_path(0.1),
271 fill: FillRule::NonZero,
272 };
273 device.push_clip_path(&clip_path);
274
275 if self.is_color {
276 interpret(iter, &self.resources, &mut context, device);
277 } else {
278 let paint = if !is_stroke {
279 Paint::Color(self.non_stroking_paint.clone())
280 } else {
281 Paint::Color(self.stroke_paint.clone())
282 };
283
284 let mut device = StencilPatternDevice::new(device, paint.clone());
285 interpret(iter, &self.resources, &mut context, &mut device);
286 }
287
288 device.pop_clip_path();
289
290 Some(())
291 }
292}
293
294impl CacheKey for TilingPattern<'_> {
295 fn cache_key(&self) -> u128 {
296 self.cache_key
297 }
298}
299
300struct StencilPatternDevice<'a, 'b, T: Device<'a>> {
301 inner: &'b mut T,
302 paint: Paint<'a>,
303}
304
305impl<'a, 'b, T: Device<'a>> StencilPatternDevice<'a, 'b, T> {
306 pub(crate) fn new(device: &'b mut T, paint: Paint<'a>) -> Self {
307 Self {
308 inner: device,
309 paint,
310 }
311 }
312}
313
314impl<'a, T: Device<'a>> Device<'a> for StencilPatternDevice<'a, '_, T> {
316 fn draw_path(
317 &mut self,
318 path: &BezPath,
319 transform: Affine,
320 _: &Paint<'_>,
321 draw_mode: &PathDrawMode,
322 ) {
323 self.inner
324 .draw_path(path, transform, &self.paint, draw_mode);
325 }
326
327 fn set_soft_mask(&mut self, _: Option<SoftMask<'_>>) {}
328
329 fn push_clip_path(&mut self, clip_path: &ClipPath) {
330 self.inner.push_clip_path(clip_path);
331 }
332
333 fn push_transparency_group(&mut self, _: f32, _: Option<SoftMask<'_>>, _: BlendMode) {}
334
335 fn draw_glyph(
336 &mut self,
337 g: &Glyph<'a>,
338 transform: Affine,
339 glyph_transform: Affine,
340 p: &Paint<'a>,
341 draw_mode: &GlyphDrawMode,
342 ) {
343 self.inner
344 .draw_glyph(g, transform, glyph_transform, p, draw_mode);
345 }
346
347 fn draw_image(&mut self, image: Image<'a, '_>, transform: Affine) {
348 if let Image::Stencil(mut s) = image {
349 s.paint = self.paint.clone();
350 self.inner.draw_image(Image::Stencil(s), transform);
351 }
352 }
353
354 fn pop_clip_path(&mut self) {
355 self.inner.pop_clip_path();
356 }
357
358 fn pop_transparency_group(&mut self) {}
359
360 fn set_blend_mode(&mut self, _: BlendMode) {}
361}