From f15ee7efb68eff188b5993d21d663e2120b5dd08 Mon Sep 17 00:00:00 2001 From: Martin Haug Date: Mon, 6 Dec 2021 14:58:57 +0100 Subject: Add SVG capabilities --- tests/ref/elements/image.png | Bin 211019 -> 186196 bytes tests/res/monkey.svg | 57 +++++++++++++++++++++++++++++++++++++++++++ tests/res/pattern.svg | 22 +++++++++++++++++ tests/typ/elements/image.typ | 8 ++++-- tests/typeset.rs | 47 +++++++++++++++++++++++++---------- 5 files changed, 119 insertions(+), 15 deletions(-) create mode 100644 tests/res/monkey.svg create mode 100644 tests/res/pattern.svg (limited to 'tests') diff --git a/tests/ref/elements/image.png b/tests/ref/elements/image.png index 92c68e29..bdcfd200 100644 Binary files a/tests/ref/elements/image.png and b/tests/ref/elements/image.png differ diff --git a/tests/res/monkey.svg b/tests/res/monkey.svg new file mode 100644 index 00000000..3cd5da4a --- /dev/null +++ b/tests/res/monkey.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/res/pattern.svg b/tests/res/pattern.svg new file mode 100644 index 00000000..b3bf4847 --- /dev/null +++ b/tests/res/pattern.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/tests/typ/elements/image.typ b/tests/typ/elements/image.typ index 5fd121e3..7fddb12d 100644 --- a/tests/typ/elements/image.typ +++ b/tests/typ/elements/image.typ @@ -18,7 +18,7 @@ #image("../../res/rhino.png", height: 30pt) // Set width and height explicitly and force stretching. -#image("../../res/tiger.jpg", width: 100%, height: 20pt, fit: "stretch") +#image("../../res/monkey.svg", width: 100%, height: 20pt, fit: "stretch") // Make sure the bounding-box of the image is correct. #align(bottom + right, image("../../res/tiger.jpg", width: 40pt)) @@ -32,7 +32,7 @@ gutter: 3pt, image("../../res/tiger.jpg", width: 100%, height: 100%, fit: "contain"), image("../../res/tiger.jpg", width: 100%, height: 100%, fit: "cover"), - image("../../res/tiger.jpg", width: 100%, height: 100%, fit: "stretch"), + image("../../res/monkey.svg", width: 100%, height: 100%, fit: "stretch"), ) --- @@ -46,6 +46,10 @@ Stuff // Test baseline. A #image("../../res/tiger.jpg", height: 1cm, width: 80%) B +--- +// Test advanced SVG features. +#image("../../res/pattern.svg") + --- // Error: 8-29 file not found #image("path/does/not/exist") diff --git a/tests/typeset.rs b/tests/typeset.rs index 3011a351..792251c3 100644 --- a/tests/typeset.rs +++ b/tests/typeset.rs @@ -8,6 +8,7 @@ use filedescriptor::{FileDescriptor, StdioDescriptor::*}; use image::{GenericImageView, Rgba}; use tiny_skia as sk; use ttf_parser::{GlyphId, OutlineBuilder}; +use usvg::FitTo; use walkdir::WalkDir; use typst::diag::Error; @@ -17,7 +18,7 @@ use typst::frame::{Element, Frame, Geometry, Group, Shape, Stroke, Text}; use typst::geom::{ self, Color, Length, Paint, PathElement, RgbaColor, Sides, Size, Transform, }; -use typst::image::Image; +use typst::image::{Image, RasterImage, Svg}; use typst::layout::layout; #[cfg(feature = "layout-cache")] use typst::library::DocumentNode; @@ -513,7 +514,9 @@ fn draw_text( let viewbox = format!("viewBox=\"0 0 {0} {0}\" xmlns", units_per_em); svg.replace("xmlns", &viewbox) }) - .and_then(|s| usvg::Tree::from_str(&s, &usvg::Options::default()).ok()) + .and_then(|s| { + usvg::Tree::from_str(&s, &usvg::Options::default().to_ref()).ok() + }) { for child in tree.root().children() { if let usvg::NodeKind::Path(node) = &*child.borrow() { @@ -535,13 +538,13 @@ fn draw_text( // TODO: Vertical alignment isn't quite right for Apple Color Emoji, // and maybe also for Noto Color Emoji. And: Is the size calculation // correct? - let img = Image::parse(&raster.data).unwrap(); + let img = RasterImage::parse(&raster.data).unwrap(); let h = text.size; let w = (img.width() as f64 / img.height() as f64) * h; let dx = (raster.x as f32) / (img.width() as f32) * size; let dy = (raster.y as f32) / (img.height() as f32) * size; let ts = ts.pre_translate(dx, -size - dy); - draw_image(canvas, ts, mask, &img, Size::new(w, h)); + draw_image(canvas, ts, mask, &Image::Raster(img), Size::new(w, h)); } else { // Otherwise, draw normal outline. let mut builder = WrappedPathBuilder(sk::PathBuilder::new()); @@ -608,16 +611,34 @@ fn draw_image( img: &Image, size: Size, ) { - let mut pixmap = sk::Pixmap::new(img.buf.width(), img.buf.height()).unwrap(); - for ((_, _, src), dest) in img.buf.pixels().zip(pixmap.pixels_mut()) { - let Rgba([r, g, b, a]) = src; - *dest = sk::ColorU8::from_rgba(r, g, b, a).premultiply(); - } - let view_width = size.x.to_f32(); let view_height = size.y.to_f32(); - let scale_x = view_width as f32 / pixmap.width() as f32; - let scale_y = view_height as f32 / pixmap.height() as f32; + + let pixmap = match img { + Image::Raster(img) => { + let w = img.buf.width(); + let h = img.buf.height(); + let mut pixmap = sk::Pixmap::new(w, h).unwrap(); + for ((_, _, src), dest) in img.buf.pixels().zip(pixmap.pixels_mut()) { + let Rgba([r, g, b, a]) = src; + *dest = sk::ColorU8::from_rgba(r, g, b, a).premultiply(); + } + pixmap + } + Image::Svg(Svg(tree)) => { + let size = tree.svg_node().size; + let aspect = (size.width() / size.height()) as f32; + let scale = ts.sx.max(ts.sy); + let w = (scale * view_width.max(aspect * view_height)).ceil() as u32; + let h = ((w as f32) / aspect).ceil() as u32; + let mut pixmap = sk::Pixmap::new(w, h).unwrap(); + resvg::render(&tree, FitTo::Size(w, h), pixmap.as_mut()); + pixmap + } + }; + + let scale_x = view_width / pixmap.width() as f32; + let scale_y = view_height / pixmap.height() as f32; let mut paint = sk::Paint::default(); paint.shader = sk::Pattern::new( @@ -689,7 +710,7 @@ fn convert_usvg_fill(fill: &usvg::Fill) -> (sk::Paint<'static>, sk::FillRule) { let mut paint = sk::Paint::default(); paint.anti_alias = true; - if let usvg::Paint::Color(usvg::Color { red, green, blue }) = fill.paint { + if let usvg::Paint::Color(usvg::Color { red, green, blue, alpha: _ }) = fill.paint { paint.set_color_rgba8(red, green, blue, fill.opacity.to_u8()) } -- cgit v1.2.3