normordis_pdf/elements/
fixed_image.rs1use super::{Element, LayoutMode, RenderContext};
2use crate::layout::FixedBox;
3
4#[derive(Debug, Clone, Copy, Default, PartialEq)]
6pub enum ImageFit {
7 #[default]
9 Contain,
10 Cover,
12 Stretch,
14 Original,
16}
17
18#[derive(Debug, Clone)]
22pub struct FixedImageBox {
23 pub image_box: FixedBox,
24 pub data: Vec<u8>,
26 pub fit: ImageFit,
27}
28
29impl FixedImageBox {
30 pub fn new(image_box: FixedBox, data: Vec<u8>) -> Self {
31 Self {
32 image_box,
33 data,
34 fit: ImageFit::Contain,
35 }
36 }
37
38 pub fn fit(mut self, fit: ImageFit) -> Self {
39 self.fit = fit;
40 self
41 }
42}
43
44impl Element for FixedImageBox {
45 fn layout_mode(&self) -> LayoutMode {
46 LayoutMode::Fixed(self.image_box.clone())
47 }
48
49 fn estimated_height_mm(&self) -> f64 {
50 0.0
51 }
52
53 fn render(&self, ctx: &mut RenderContext) -> crate::Result<super::RenderResult> {
54 let ua = ctx.ua_config.enabled;
55 if ua {
56 match &self.image_box.ua_role {
57 Some(tag) => {
58 let mcid = ctx.ua_tag_element(tag.clone(), self.image_box.ua_alt.clone());
59 ctx.backend.begin_tagged_content(tag.pdf_name().as_bytes(), mcid);
60 }
61 None => {
62 ctx.backend.begin_artifact_content();
63 }
64 }
65 }
66
67 if !self.data.is_empty() {
68 if let Ok(img) = image::load_from_memory(&self.data) {
69 let (px_w, px_h) = (img.width() as f64, img.height() as f64);
70 let aspect = if px_w > 0.0 { px_h / px_w } else { 1.0 };
71 let box_w = self.image_box.width_mm;
72 let box_h = self.image_box.height_mm;
73
74 let (render_w, render_h, x_off, y_off) = match self.fit {
75 ImageFit::Contain => {
76 let h_by_w = box_w * aspect;
77 if h_by_w <= box_h {
78 (box_w, h_by_w, 0.0, (box_h - h_by_w) / 2.0)
79 } else {
80 let rw = box_h / aspect;
81 (rw, box_h, (box_w - rw) / 2.0, 0.0)
82 }
83 }
84 ImageFit::Cover => {
85 let h_by_w = box_w * aspect;
86 if h_by_w >= box_h {
87 (box_w, h_by_w, 0.0, 0.0)
88 } else {
89 (box_h / aspect, box_h, 0.0, 0.0)
90 }
91 }
92 ImageFit::Stretch => (box_w, box_h, 0.0, 0.0),
93 ImageFit::Original => {
94 let w = (px_w * 25.4 / 96.0).min(box_w);
95 let h = (px_h * 25.4 / 96.0).min(box_h);
96 (w, h, 0.0, 0.0)
97 }
98 };
99
100 if render_w > 0.0 && render_h > 0.0 {
101 let img_ref = ctx.backend.embed_image(&self.data)?;
102 ctx.backend.draw_image(
103 img_ref,
104 self.image_box.x_mm + x_off,
105 self.image_box.y_mm + y_off,
106 render_w,
107 render_h,
108 );
109 }
110 }
111 }
112
113 if ua { ctx.backend.end_tagged_content(); }
114 Ok(super::RenderResult::done())
115 }
116}