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}