hayro_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    // These are hidden since clients are supposed to call get the
97    // width/height from `LumaData` instead.
98    #[doc(hidden)]
99    pub fn width(&self) -> u32 {
100        self.0.width()
101    }
102
103    #[doc(hidden)]
104    pub fn height(&self) -> u32 {
105        self.0.height()
106    }
107}
108
109impl CacheKey for RasterImage<'_> {
110    fn cache_key(&self) -> u128 {
111        self.0.cache_key()
112    }
113}
114
115/// A type of image.
116pub enum Image<'a, 'b> {
117    /// A stencil image.
118    Stencil(StencilImage<'a, 'b>),
119    /// A normal raster image.
120    Raster(RasterImage<'b>),
121}
122
123impl Image<'_, '_> {
124    // These are hidden since clients are supposed to call get the
125    // width/height from `LumaData/RgbData` instead.
126    #[doc(hidden)]
127    pub fn width(&self) -> u32 {
128        match self {
129            Image::Stencil(s) => s.width(),
130            Image::Raster(r) => r.width(),
131        }
132    }
133
134    // These are hidden since clients are supposed to call get the
135    // width/height from `LumaData/RgbData` instead.
136    #[doc(hidden)]
137    pub fn height(&self) -> u32 {
138        match self {
139            Image::Stencil(s) => s.height(),
140            Image::Raster(r) => r.height(),
141        }
142    }
143}
144
145impl CacheKey for Image<'_, '_> {
146    fn cache_key(&self) -> u128 {
147        match self {
148            Image::Stencil(i) => i.cache_key(),
149            Image::Raster(i) => i.cache_key(),
150        }
151    }
152}
153
154/// A structure holding 3-channel RGB data.
155#[derive(Clone)]
156pub struct RgbData {
157    /// The actual data. It is guaranteed to have the length width * height * 3.
158    pub data: Vec<u8>,
159    /// The width.
160    pub width: u32,
161    /// The height.
162    pub height: u32,
163    /// Whether the image should be interpolated.
164    pub interpolate: bool,
165    /// Additional scaling factors to apply to the image.
166    ///
167    /// In most cases, those factors will just be 1.0, and you can
168    /// ignore them. There are two situations in which they will not be equal
169    /// to 1:
170    /// 1) The PDF provided wrong metadata about the width/height of the image,
171    ///    which needs to be corrected
172    /// 2) A lower resolution of the image was requested, in which case it needs
173    ///    to be scaled up so that it still covers the same area.
174    ///
175    /// The first number indicates the x scaling factor, the second number the
176    /// y scaling factor.
177    pub scale_factors: (f32, f32),
178}
179
180/// A structure holding 1-channel luma data.
181#[derive(Clone)]
182pub struct LumaData {
183    /// The actual data. It is guaranteed to have the length width * height.
184    pub data: Vec<u8>,
185    /// The width.
186    pub width: u32,
187    /// The height.
188    pub height: u32,
189    /// Whether the image should be interpolated.
190    pub interpolate: bool,
191    /// Additional scaling factors to apply to the image.
192    ///
193    /// In most cases, those factors will just be 1.0, and you can
194    /// ignore them. There are two situations in which they will not be equal
195    /// to 1:
196    /// 1) The PDF provided wrong metadata about the width/height of the image,
197    ///    which needs to be corrected
198    /// 2) A lower resolution of the image was requested, in which case it needs
199    ///    to be scaled up so that it still covers the same area.
200    ///
201    /// The first number indicates the x scaling factor, the second number the
202    /// y scaling factor.
203    pub scale_factors: (f32, f32),
204}
205
206/// A type of paint.
207#[derive(Clone, Debug)]
208pub enum Paint<'a> {
209    /// A solid RGBA color.
210    Color(Color),
211    /// A PDF pattern.
212    Pattern(Box<Pattern<'a>>),
213}
214
215impl CacheKey for Paint<'_> {
216    fn cache_key(&self) -> u128 {
217        match self {
218            Paint::Color(c) => {
219                // TODO: We should actually cache the color with color space etc., not just the
220                // RGBA8 version.
221                hash128(&c.to_rgba().to_rgba8())
222            }
223            Paint::Pattern(p) => p.cache_key(),
224        }
225    }
226}
227
228/// The draw mode that should be used for a path.
229#[derive(Clone, Debug)]
230pub enum PathDrawMode {
231    /// Draw using a fill.
232    Fill(FillRule),
233    /// Draw using a stroke.
234    Stroke(StrokeProps),
235}
236
237/// The draw mode that should be used for a glyph.
238#[derive(Clone, Debug)]
239pub enum GlyphDrawMode {
240    /// Draw using a fill.
241    Fill,
242    /// Draw using a stroke.
243    Stroke(StrokeProps),
244    /// Invisible text (for text extraction but not visual rendering).
245    Invisible,
246}
247
248/// Stroke properties.
249#[derive(Clone, Debug)]
250pub struct StrokeProps {
251    /// The line width.
252    pub line_width: f32,
253    /// The line cap.
254    pub line_cap: Cap,
255    /// The line join.
256    pub line_join: Join,
257    /// The miter limit.
258    pub miter_limit: f32,
259    /// The dash array.
260    pub dash_array: SmallVec<[f32; 4]>,
261    /// The dash offset.
262    pub dash_offset: f32,
263}
264
265impl Default for StrokeProps {
266    fn default() -> Self {
267        Self {
268            line_width: 1.0,
269            line_cap: Cap::Butt,
270            line_join: Join::Miter,
271            miter_limit: 10.0,
272            dash_array: smallvec![],
273            dash_offset: 0.0,
274        }
275    }
276}
277
278/// A fill rule.
279#[derive(Clone, Debug, Copy, Hash, PartialEq, Eq)]
280pub enum FillRule {
281    /// Non-zero filling.
282    NonZero,
283    /// Even-odd filling.
284    EvenOdd,
285}
286
287/// A blend mode.
288#[derive(Clone, Debug, Copy, Hash, PartialEq, Eq, Default)]
289pub enum BlendMode {
290    /// Normal blend mode (default).
291    #[default]
292    Normal,
293    /// Multiply blend mode.
294    Multiply,
295    /// Screen blend mode.
296    Screen,
297    /// Overlay blend mode.
298    Overlay,
299    /// Darken blend mode.
300    Darken,
301    /// Lighten blend mode.
302    Lighten,
303    /// `ColorDodge` blend mode.
304    ColorDodge,
305    /// `ColorBurn` blend mode.
306    ColorBurn,
307    /// `HardLight` blend mode.
308    HardLight,
309    /// `SoftLight` blend mode.
310    SoftLight,
311    /// Difference blend mode.
312    Difference,
313    /// Exclusion blend mode.
314    Exclusion,
315    /// Hue blend mode.
316    Hue,
317    /// Saturation blend mode.
318    Saturation,
319    /// Color blend mode.
320    Color,
321    /// Luminosity blend mode.
322    Luminosity,
323}