Skip to main content

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 hayro_syntax::object::Stream;
7use kurbo::{BezPath, Cap, Join};
8use smallvec::{SmallVec, smallvec};
9
10/// A clip path.
11#[derive(Debug, Clone)]
12pub struct ClipPath {
13    /// The clipping path.
14    pub path: BezPath,
15    /// The fill rule.
16    pub fill: FillRule,
17}
18
19impl CacheKey for ClipPath {
20    fn cache_key(&self) -> u128 {
21        hash128(&(&self.path.to_svg(), &self.fill))
22    }
23}
24
25/// A stencil image.
26pub struct StencilImage<'a, 'b> {
27    pub(crate) paint: Paint<'a>,
28    pub(crate) image_xobject: ImageXObject<'b>,
29}
30
31impl<'a, 'b> StencilImage<'a, 'b> {
32    /// Perform some operation with the stencil data of the image.
33    ///
34    /// The second argument allows you to give the image decoder a hint for
35    /// what resolution of the image you want to have. Note that this does not
36    /// mean that the resulting image will have that dimension. Instead, it allows
37    /// the image decoder to extract a lower-resolution version of the image in
38    /// certain cases.
39    pub fn with_stencil(
40        &self,
41        func: impl FnOnce(LumaData, &Paint<'a>),
42        target_dimension: Option<(u32, u32)>,
43    ) {
44        if let Some(decoded) = self.image_xobject.decoded_mask(target_dimension) {
45            func(decoded.luma, &self.paint);
46        }
47    }
48
49    // These are hidden since clients are supposed to call get the
50    // width/height from `LumaData` instead.
51    #[doc(hidden)]
52    pub fn width(&self) -> u32 {
53        self.image_xobject.width()
54    }
55
56    #[doc(hidden)]
57    pub fn height(&self) -> u32 {
58        self.image_xobject.height()
59    }
60}
61
62impl CacheKey for StencilImage<'_, '_> {
63    fn cache_key(&self) -> u128 {
64        self.image_xobject.cache_key()
65    }
66}
67
68/// A raster image.
69pub struct RasterImage<'a>(pub(crate) ImageXObject<'a>);
70
71impl RasterImage<'_> {
72    /// Perform some operation with the RGB and alpha channel of the image.
73    ///
74    /// The second argument allows you to give the image decoder a hint for
75    /// what resolution of the image you want to have. Note that this does not
76    /// mean that the resulting image will have that dimension. Instead, it allows
77    /// the image decoder to extract a lower-resolution version of the image in
78    /// certain cases.
79    pub fn with_rgba(
80        &self,
81        func: impl FnOnce(ImageData, Option<LumaData>),
82        target_dimension: Option<(u32, u32)>,
83    ) {
84        if let Some(decoded) = self.0.decoded_raster(target_dimension) {
85            func(decoded.image, decoded.alpha);
86        }
87    }
88
89    /// Return the underlying stream object.
90    ///
91    /// This allows you to get access to the raw encoded image data, without doing any decoding.
92    pub fn stream(&self) -> &Stream<'_> {
93        self.0.stream()
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/// The color data of a raster image, either 3-channel RGB or 1-channel luma.
207#[derive(Clone)]
208pub enum ImageData {
209    /// 3-channel RGB data.
210    Rgb(RgbData),
211    /// 1-channel grayscale data.
212    Luma(LumaData),
213}
214
215impl ImageData {
216    /// The width of the image.
217    pub fn width(&self) -> u32 {
218        match self {
219            Self::Rgb(d) => d.width,
220            Self::Luma(d) => d.width,
221        }
222    }
223
224    /// The height of the image.
225    pub fn height(&self) -> u32 {
226        match self {
227            Self::Rgb(d) => d.height,
228            Self::Luma(d) => d.height,
229        }
230    }
231
232    /// Whether the image should be interpolated.
233    pub fn interpolate(&self) -> bool {
234        match self {
235            Self::Rgb(d) => d.interpolate,
236            Self::Luma(d) => d.interpolate,
237        }
238    }
239
240    /// The scaling factors of the image.
241    pub fn scale_factors(&self) -> (f32, f32) {
242        match self {
243            Self::Rgb(d) => d.scale_factors,
244            Self::Luma(d) => d.scale_factors,
245        }
246    }
247}
248
249/// A type of paint.
250#[derive(Clone, Debug)]
251pub enum Paint<'a> {
252    /// A solid RGBA color.
253    Color(Color),
254    /// A PDF pattern.
255    Pattern(Box<Pattern<'a>>),
256}
257
258impl CacheKey for Paint<'_> {
259    fn cache_key(&self) -> u128 {
260        match self {
261            Paint::Color(c) => {
262                // TODO: We should actually cache the color with color space etc., not just the
263                // RGBA8 version.
264                hash128(&c.to_rgba().to_rgba8())
265            }
266            Paint::Pattern(p) => p.cache_key(),
267        }
268    }
269}
270
271/// The draw mode that should be used for a path.
272#[derive(Clone, Debug)]
273pub enum PathDrawMode {
274    /// Draw using a fill.
275    Fill(FillRule),
276    /// Draw using a stroke.
277    Stroke(StrokeProps),
278}
279
280/// The draw mode that should be used for a glyph.
281#[derive(Clone, Debug)]
282pub enum GlyphDrawMode {
283    /// Draw using a fill.
284    Fill,
285    /// Draw using a stroke.
286    Stroke(StrokeProps),
287    /// Invisible text (for text extraction but not visual rendering).
288    Invisible,
289}
290
291/// Stroke properties.
292#[derive(Clone, Debug)]
293pub struct StrokeProps {
294    /// The line width.
295    pub line_width: f32,
296    /// The line cap.
297    pub line_cap: Cap,
298    /// The line join.
299    pub line_join: Join,
300    /// The miter limit.
301    pub miter_limit: f32,
302    /// The dash array.
303    pub dash_array: SmallVec<[f32; 4]>,
304    /// The dash offset.
305    pub dash_offset: f32,
306}
307
308impl Default for StrokeProps {
309    fn default() -> Self {
310        Self {
311            line_width: 1.0,
312            line_cap: Cap::Butt,
313            line_join: Join::Miter,
314            miter_limit: 10.0,
315            dash_array: smallvec![],
316            dash_offset: 0.0,
317        }
318    }
319}
320
321/// A fill rule.
322#[derive(Clone, Debug, Copy, Hash, PartialEq, Eq)]
323pub enum FillRule {
324    /// Non-zero filling.
325    NonZero,
326    /// Even-odd filling.
327    EvenOdd,
328}
329
330/// A blend mode.
331#[derive(Clone, Debug, Copy, Hash, PartialEq, Eq, Default)]
332pub enum BlendMode {
333    /// Normal blend mode (default).
334    #[default]
335    Normal,
336    /// Multiply blend mode.
337    Multiply,
338    /// Screen blend mode.
339    Screen,
340    /// Overlay blend mode.
341    Overlay,
342    /// Darken blend mode.
343    Darken,
344    /// Lighten blend mode.
345    Lighten,
346    /// `ColorDodge` blend mode.
347    ColorDodge,
348    /// `ColorBurn` blend mode.
349    ColorBurn,
350    /// `HardLight` blend mode.
351    HardLight,
352    /// `SoftLight` blend mode.
353    SoftLight,
354    /// Difference blend mode.
355    Difference,
356    /// Exclusion blend mode.
357    Exclusion,
358    /// Hue blend mode.
359    Hue,
360    /// Saturation blend mode.
361    Saturation,
362    /// Color blend mode.
363    Color,
364    /// Luminosity blend mode.
365    Luminosity,
366}