laser_pdf/elements/
image.rs1use ::image::{DynamicImage, GenericImageView};
2use miniz_oxide::deflate::{CompressionLevel, compress_to_vec_zlib};
3use pdf_writer::Filter;
4use utils::mm_to_pt;
5
6use crate::{image::Image, *};
7
8use super::svg::Svg;
9
10const INCH_TO_MM: f32 = 25.4;
11
12pub struct ImageElement<'a> {
17 pub image: &'a Image,
19}
20
21impl<'a> Element for ImageElement<'a> {
22 fn first_location_usage(&self, ctx: FirstLocationUsageCtx) -> FirstLocationUsage {
23 match self.image {
24 Image::Svg(svg) => Svg { data: svg }.first_location_usage(ctx),
25 Image::Pixel(image) => {
26 let (height, _) = calculate_size(image, ctx.width);
27
28 if ctx.break_appropriate_for_min_height(height) {
29 FirstLocationUsage::WillSkip
30 } else {
31 FirstLocationUsage::WillUse
32 }
33 }
34 }
35 }
36
37 fn measure(&self, mut ctx: MeasureCtx) -> ElementSize {
38 match self.image {
39 Image::Svg(svg) => Svg { data: svg }.measure(ctx),
40 Image::Pixel(image) => {
41 let (height, element_size) = calculate_size(image, ctx.width);
42
43 ctx.break_if_appropriate_for_min_height(height);
44
45 element_size
46 }
47 }
48 }
49
50 fn draw(&self, mut ctx: DrawCtx) -> ElementSize {
51 match self.image {
52 Image::Svg(svg) => Svg { data: svg }.draw(ctx),
53 Image::Pixel(image) => {
54 let (height, element_size) = calculate_size(image, ctx.width);
55
56 ctx.break_if_appropriate_for_min_height(height);
57
58 let image_id = ctx.pdf.alloc();
63 let s_mask_id = ctx.pdf.alloc();
64 let image_name = ctx.pdf.pages[ctx.location.page_idx].add_x_object(image_id);
65
66 let dynamic = image;
67
68 let level = CompressionLevel::DefaultLevel as u8;
78 let encoded = compress_to_vec_zlib(dynamic.to_rgb8().as_raw(), level);
79
80 let mask = dynamic.color().has_alpha().then(|| {
82 let alphas: Vec<_> = dynamic.pixels().map(|p| (p.2).0[3]).collect();
83 compress_to_vec_zlib(&alphas, level)
84 });
85 let (filter, encoded, mask) = (Filter::FlateDecode, encoded, mask);
86
87 let mut image = ctx.pdf.pdf.image_xobject(image_id, &encoded);
89 image.filter(filter);
90 image.width(dynamic.width() as i32);
91 image.height(dynamic.height() as i32);
92 image.color_space().device_rgb();
93 image.bits_per_component(8);
94 if mask.is_some() {
95 image.s_mask(s_mask_id);
96 }
97 drop(image);
98
99 if let Some(encoded) = &mask {
101 let mut s_mask = ctx.pdf.pdf.image_xobject(s_mask_id, encoded);
102 s_mask.filter(filter);
103 s_mask.width(dynamic.width() as i32);
104 s_mask.height(dynamic.height() as i32);
105 s_mask.color_space().device_gray();
106 s_mask.bits_per_component(8);
107 }
108
109 ctx.location
110 .layer(ctx.pdf)
111 .save_state()
112 .transform([
113 mm_to_pt(element_size.width.unwrap()),
114 0.,
115 0.,
116 mm_to_pt(element_size.height.unwrap()),
117 mm_to_pt(ctx.location.pos.0),
118 mm_to_pt(ctx.location.pos.1 - element_size.height.unwrap()),
119 ])
120 .x_object(Name(image_name.as_bytes()))
121 .restore_state();
122
123 element_size
124 }
125 }
126 }
127}
128
129#[inline]
130fn calculate_size(image: &DynamicImage, width: WidthConstraint) -> (f32, ElementSize) {
131 let dimensions = {
132 let (x, y) = image.dimensions();
133 (x as f32 * INCH_TO_MM, y as f32 * INCH_TO_MM)
134 };
135
136 let width = width.constrain(dimensions.0);
137
138 let size = (width, dimensions.1 * width / dimensions.0);
139
140 (
141 size.1,
142 ElementSize {
143 width: Some(size.0),
144 height: Some(size.1),
145 },
146 )
147}