use serde::{Deserialize, Serialize};
use super::{Element, RenderContext};
use crate::compliance::ua::StructTag;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub enum ImageAlignment {
Left,
#[default]
Center,
Right,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImageElement {
pub data: Vec<u8>,
pub width_mm: Option<f64>,
pub height_mm: Option<f64>,
pub alignment: ImageAlignment,
pub caption: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub alt: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub width_percent: Option<f64>,
}
impl ImageElement {
pub fn new(data: Vec<u8>) -> Self {
Self {
data,
width_mm: None,
height_mm: None,
alignment: ImageAlignment::Center,
caption: None,
alt: None,
width_percent: None,
}
}
pub fn width(mut self, mm: f64) -> Self {
self.width_mm = Some(mm);
self
}
pub fn width_mm(mut self, mm: f64) -> Self {
self.width_mm = Some(mm);
self
}
pub fn height(mut self, mm: f64) -> Self {
self.height_mm = Some(mm);
self
}
pub fn align(mut self, alignment: ImageAlignment) -> Self {
self.alignment = alignment;
self
}
pub fn caption(mut self, caption: impl Into<String>) -> Self {
self.caption = Some(caption.into());
self
}
pub fn alt(mut self, text: impl Into<String>) -> Self {
self.alt = Some(text.into());
self
}
}
impl Element for ImageElement {
fn estimated_height_mm(&self) -> f64 {
let img_h = self.height_mm.unwrap_or(50.0);
img_h + if self.caption.is_some() { 5.0 } else { 0.0 }
}
fn render(&self, ctx: &mut RenderContext) -> crate::Result<super::RenderResult> {
if ctx.ua_enabled() {
match &self.alt {
Some(alt_text) => {
let mcid = ctx.ua_tag_element(StructTag::Figure, Some(alt_text.clone()));
ctx.backend.begin_tagged_content(b"Figure", mcid);
}
None => {
if ctx.ua_config.warn_missing_alt {
eprintln!(
"PDF/UA-2 WARNING: ImageElement without alt text — \
marking as Artifact. Add .alt() for accessible images."
);
}
ctx.backend.begin_artifact_content();
}
}
}
ctx.flow.advance(self.estimated_height_mm());
if ctx.ua_enabled() {
ctx.backend.end_tagged_content();
}
Ok(super::RenderResult::done())
}
}