pdf_interpret/types.rs
1use crate::CacheKey;
2use crate::color::Color;
3use crate::pattern::Pattern;
4use crate::util::hash128;
5use crate::x_object::ImageXObject;
6use kurbo::{BezPath, Cap, Join};
7use smallvec::{SmallVec, smallvec};
8
9/// A clip path.
10#[derive(Debug, Clone)]
11pub struct ClipPath {
12 /// The clipping path.
13 pub path: BezPath,
14 /// The fill rule.
15 pub fill: FillRule,
16}
17
18impl CacheKey for ClipPath {
19 fn cache_key(&self) -> u128 {
20 hash128(&(&self.path.to_svg(), &self.fill))
21 }
22}
23
24/// A stencil image.
25pub struct StencilImage<'a, 'b> {
26 pub(crate) paint: Paint<'a>,
27 pub(crate) image_xobject: ImageXObject<'b>,
28}
29
30impl<'a, 'b> StencilImage<'a, 'b> {
31 /// Perform some operation with the stencil data of the image.
32 ///
33 /// The second argument allows you to give the image decoder a hint for
34 /// what resolution of the image you want to have. Note that this does not
35 /// mean that the resulting image will have that dimension. Instead, it allows
36 /// the image decoder to extract a lower-resolution version of the image in
37 /// certain cases.
38 pub fn with_stencil(
39 &self,
40 func: impl FnOnce(LumaData, &Paint<'a>),
41 target_dimension: Option<(u32, u32)>,
42 ) {
43 if let Some(luma) = self
44 .image_xobject
45 .decoded_object(target_dimension)
46 .and_then(|d| d.luma_data)
47 {
48 func(luma, &self.paint);
49 }
50 }
51
52 // These are hidden since clients are supposed to call get the
53 // width/height from `LumaData` instead.
54 #[doc(hidden)]
55 pub fn width(&self) -> u32 {
56 self.image_xobject.width()
57 }
58
59 #[doc(hidden)]
60 pub fn height(&self) -> u32 {
61 self.image_xobject.height()
62 }
63}
64
65impl CacheKey for StencilImage<'_, '_> {
66 fn cache_key(&self) -> u128 {
67 self.image_xobject.cache_key()
68 }
69}
70
71/// A raster image.
72pub struct RasterImage<'a>(pub(crate) ImageXObject<'a>);
73
74impl RasterImage<'_> {
75 /// Perform some operation with the RGB and alpha channel of the image.
76 ///
77 /// The second argument allows you to give the image decoder a hint for
78 /// what resolution of the image you want to have. Note that this does not
79 /// mean that the resulting image will have that dimension. Instead, it allows
80 /// the image decoder to extract a lower-resolution version of the image in
81 /// certain cases.
82 pub fn with_rgba(
83 &self,
84 func: impl FnOnce(RgbData, Option<LumaData>),
85 target_dimension: Option<(u32, u32)>,
86 ) {
87 let decoded = self.0.decoded_object(target_dimension);
88
89 if let Some(decoded) = decoded
90 && let Some(rgb) = decoded.rgb_data
91 {
92 func(rgb, decoded.luma_data);
93 }
94 }
95
96 /// Perform some operation with the original CMYK bytes of a DeviceCMYK image.
97 pub fn with_device_cmyk(
98 &self,
99 func: impl FnOnce(CmykData, Option<LumaData>),
100 target_dimension: Option<(u32, u32)>,
101 ) {
102 let decoded = self.0.decoded_object(target_dimension);
103
104 if let Some(decoded) = decoded
105 && let Some(cmyk) = decoded.cmyk_data
106 {
107 func(cmyk, decoded.luma_data);
108 }
109 }
110
111 // These are hidden since clients are supposed to call get the
112 // width/height from `LumaData` instead.
113 #[doc(hidden)]
114 pub fn width(&self) -> u32 {
115 self.0.width()
116 }
117
118 #[doc(hidden)]
119 pub fn height(&self) -> u32 {
120 self.0.height()
121 }
122}
123
124impl CacheKey for RasterImage<'_> {
125 fn cache_key(&self) -> u128 {
126 self.0.cache_key()
127 }
128}
129
130/// A type of image.
131pub enum Image<'a, 'b> {
132 /// A stencil image.
133 Stencil(StencilImage<'a, 'b>),
134 /// A normal raster image.
135 Raster(RasterImage<'b>),
136}
137
138impl Image<'_, '_> {
139 // These are hidden since clients are supposed to call get the
140 // width/height from `LumaData/RgbData` instead.
141 #[doc(hidden)]
142 pub fn width(&self) -> u32 {
143 match self {
144 Image::Stencil(s) => s.width(),
145 Image::Raster(r) => r.width(),
146 }
147 }
148
149 // These are hidden since clients are supposed to call get the
150 // width/height from `LumaData/RgbData` instead.
151 #[doc(hidden)]
152 pub fn height(&self) -> u32 {
153 match self {
154 Image::Stencil(s) => s.height(),
155 Image::Raster(r) => r.height(),
156 }
157 }
158}
159
160impl CacheKey for Image<'_, '_> {
161 fn cache_key(&self) -> u128 {
162 match self {
163 Image::Stencil(i) => i.cache_key(),
164 Image::Raster(i) => i.cache_key(),
165 }
166 }
167}
168
169/// A structure holding 3-channel RGB data.
170#[derive(Clone)]
171pub struct RgbData {
172 /// The actual data. It is guaranteed to have the length width * height * 3.
173 pub data: Vec<u8>,
174 /// The width.
175 pub width: u32,
176 /// The height.
177 pub height: u32,
178 /// Whether the image should be interpolated.
179 pub interpolate: bool,
180 /// Additional scaling factors to apply to the image.
181 ///
182 /// In most cases, those factors will just be 1.0, and you can
183 /// ignore them. There are two situations in which they will not be equal
184 /// to 1:
185 /// 1) The PDF provided wrong metadata about the width/height of the image,
186 /// which needs to be corrected
187 /// 2) A lower resolution of the image was requested, in which case it needs
188 /// to be scaled up so that it still covers the same area.
189 ///
190 /// The first number indicates the x scaling factor, the second number the
191 /// y scaling factor.
192 pub scale_factors: (f32, f32),
193}
194
195/// A structure holding 4-channel CMYK data.
196#[derive(Clone)]
197pub struct CmykData {
198 /// The actual data. It is guaranteed to have the length width * height * 4.
199 pub data: Vec<u8>,
200 /// The width.
201 pub width: u32,
202 /// The height.
203 pub height: u32,
204 /// Whether the image should be interpolated.
205 pub interpolate: bool,
206 /// Additional scaling factors to apply to the image.
207 pub scale_factors: (f32, f32),
208}
209
210/// A structure holding 1-channel luma data.
211#[derive(Clone)]
212pub struct LumaData {
213 /// The actual data. It is guaranteed to have the length width * height.
214 pub data: Vec<u8>,
215 /// The width.
216 pub width: u32,
217 /// The height.
218 pub height: u32,
219 /// Whether the image should be interpolated.
220 pub interpolate: bool,
221 /// Additional scaling factors to apply to the image.
222 ///
223 /// In most cases, those factors will just be 1.0, and you can
224 /// ignore them. There are two situations in which they will not be equal
225 /// to 1:
226 /// 1) The PDF provided wrong metadata about the width/height of the image,
227 /// which needs to be corrected
228 /// 2) A lower resolution of the image was requested, in which case it needs
229 /// to be scaled up so that it still covers the same area.
230 ///
231 /// The first number indicates the x scaling factor, the second number the
232 /// y scaling factor.
233 pub scale_factors: (f32, f32),
234}
235
236/// A type of paint.
237#[derive(Clone, Debug)]
238pub enum Paint<'a> {
239 /// A solid RGBA color.
240 Color(Color),
241 /// A PDF pattern.
242 Pattern(Box<Pattern<'a>>),
243}
244
245impl CacheKey for Paint<'_> {
246 fn cache_key(&self) -> u128 {
247 match self {
248 Paint::Color(c) => {
249 // TODO: We should actually cache the color with color space etc., not just the
250 // RGBA8 version.
251 hash128(&c.to_rgba().to_rgba8())
252 }
253 Paint::Pattern(p) => p.cache_key(),
254 }
255 }
256}
257
258/// The draw mode that should be used for a path.
259#[derive(Clone, Debug)]
260pub enum PathDrawMode {
261 /// Draw using a fill.
262 Fill(FillRule),
263 /// Draw using a stroke.
264 Stroke(StrokeProps),
265}
266
267/// The draw mode that should be used for a glyph.
268#[derive(Clone, Debug)]
269pub enum GlyphDrawMode {
270 /// Draw using a fill.
271 Fill,
272 /// Draw using a stroke.
273 Stroke(StrokeProps),
274 /// Invisible text (for text extraction but not visual rendering).
275 Invisible,
276}
277
278/// Stroke properties.
279#[derive(Clone, Debug)]
280pub struct StrokeProps {
281 /// The line width.
282 pub line_width: f32,
283 /// The line cap.
284 pub line_cap: Cap,
285 /// The line join.
286 pub line_join: Join,
287 /// The miter limit.
288 pub miter_limit: f32,
289 /// The dash array.
290 pub dash_array: SmallVec<[f32; 4]>,
291 /// The dash offset.
292 pub dash_offset: f32,
293}
294
295impl Default for StrokeProps {
296 fn default() -> Self {
297 Self {
298 line_width: 1.0,
299 line_cap: Cap::Butt,
300 line_join: Join::Miter,
301 miter_limit: 10.0,
302 dash_array: smallvec![],
303 dash_offset: 0.0,
304 }
305 }
306}
307
308/// A fill rule.
309#[derive(Clone, Debug, Copy, Hash, PartialEq, Eq)]
310pub enum FillRule {
311 /// Non-zero filling.
312 NonZero,
313 /// Even-odd filling.
314 EvenOdd,
315}
316
317/// A blend mode.
318#[derive(Clone, Debug, Copy, Hash, PartialEq, Eq, Default)]
319pub enum BlendMode {
320 /// Normal blend mode (default).
321 #[default]
322 Normal,
323 /// Multiply blend mode.
324 Multiply,
325 /// Screen blend mode.
326 Screen,
327 /// Overlay blend mode.
328 Overlay,
329 /// Darken blend mode.
330 Darken,
331 /// Lighten blend mode.
332 Lighten,
333 /// `ColorDodge` blend mode.
334 ColorDodge,
335 /// `ColorBurn` blend mode.
336 ColorBurn,
337 /// `HardLight` blend mode.
338 HardLight,
339 /// `SoftLight` blend mode.
340 SoftLight,
341 /// Difference blend mode.
342 Difference,
343 /// Exclusion blend mode.
344 Exclusion,
345 /// Hue blend mode.
346 Hue,
347 /// Saturation blend mode.
348 Saturation,
349 /// Color blend mode.
350 Color,
351 /// Luminosity blend mode.
352 Luminosity,
353}