Skip to main content

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}