use utils::mm_to_pt;
use crate::{utils::pt_to_mm, *};
pub struct Svg<'a> {
pub data: &'a usvg::Tree,
}
impl<'a> Element for Svg<'a> {
fn first_location_usage(&self, ctx: FirstLocationUsageCtx) -> FirstLocationUsage {
let (height, _) = calculate_size(self.data, ctx.width);
if ctx.break_appropriate_for_min_height(height) {
FirstLocationUsage::WillSkip
} else {
FirstLocationUsage::WillUse
}
}
fn measure(&self, mut ctx: MeasureCtx) -> ElementSize {
let (height, element_size) = calculate_size(self.data, ctx.width);
ctx.break_if_appropriate_for_min_height(height);
element_size
}
fn draw(&self, mut ctx: DrawCtx) -> ElementSize {
let (height, element_size) = calculate_size(self.data, ctx.width);
ctx.break_if_appropriate_for_min_height(height);
let pos = ctx.location.pos;
let (svg_chunk, svg_id) = svg2pdf::to_chunk(
self.data,
svg2pdf::ConversionOptions {
compress: false,
raster_scale: 1.5,
embed_text: false,
pdfa: true,
},
)
.unwrap();
let offset = ctx.pdf.alloc.get();
let mut max = offset;
svg_chunk.renumber_into(&mut ctx.pdf.pdf, |old| {
let val = offset + old.get();
max = max.max(val);
pdf_writer::Ref::new(val)
});
let svg_id = pdf_writer::Ref::new(offset + svg_id.get());
ctx.pdf.alloc = pdf_writer::Ref::new(max + 1);
let x_object = ctx.pdf.pages[ctx.location.page_idx].add_x_object(svg_id);
let layer = ctx.location.layer(ctx.pdf);
layer
.save_state()
.transform([
mm_to_pt(element_size.width.unwrap()),
0.,
0.,
mm_to_pt(element_size.height.unwrap()),
mm_to_pt(pos.0),
mm_to_pt(pos.1 - element_size.height.unwrap()),
])
.x_object(Name(x_object.as_bytes()))
.restore_state();
element_size
}
}
#[inline]
fn calculate_size(data: &usvg::Tree, width: WidthConstraint) -> (f32, ElementSize) {
let svg = data;
let svg_size = svg.size();
let svg_width = pt_to_mm(svg_size.width() as f32);
let svg_height = pt_to_mm(svg_size.height() as f32);
let width = width.constrain(svg_width);
let scale_factor = width / svg_width;
let height = svg_height * scale_factor;
(
height,
ElementSize {
width: Some(width),
height: Some(height),
},
)
}
#[cfg(test)]
mod tests {
use super::*;
use insta::*;
use test_utils::binary_snapshots::*;
#[test]
fn test() {
const SVG: &str = "\
<svg
width=\"512\"
height=\"512\"
viewBox=\"0 0 135.46666 135.46667\"
version=\"1.1\"
id=\"svg1\"
xmlns=\"http://www.w3.org/2000/svg\"
xmlns:svg=\"http://www.w3.org/2000/svg\">
<defs
id=\"defs1\" />
<g
id=\"layer1\">
<rect
style=\"fill:#000f80;fill-opacity:1;stroke:none;stroke-width:1.3386\"
id=\"rect1\"
width=\"108.92857\"
height=\"72.85714\"
x=\"18.571426\"
y=\"11.428572\" />
<ellipse
style=\"fill:#008080;fill-opacity:1;stroke:none;stroke-width:2.07092\"
id=\"path1\"
cx=\"84.107147\"
cy=\"84.107132\"
rx=\"51.964283\"
ry=\"46.250004\" />
</g>
</svg>
";
let tree = usvg::Tree::from_str(
SVG,
&usvg::Options {
..Default::default()
},
)
.unwrap();
let bytes = test_element_bytes(TestElementParams::breakable(), |callback| {
callback.call(
&Svg { data: &tree }
.debug(0)
.show_max_width()
.show_last_location_max_height(),
);
});
assert_binary_snapshot!(".pdf", bytes);
}
}