diff options
| author | Martin Haug <mhaug@live.de> | 2021-12-07 00:06:54 +0100 |
|---|---|---|
| committer | Martin Haug <mhaug@live.de> | 2021-12-07 00:06:54 +0100 |
| commit | 7c829c5c1b67ac8e8fbe4fc4ba01468d100bfb47 (patch) | |
| tree | 7930718d6dbdf92d1feaa92df97830ef3dfc4ed5 | |
| parent | f15ee7efb68eff188b5993d21d663e2120b5dd08 (diff) | |
Bump pdf-writer, svg2pdf and error messages
Co-Authored-By: Laurenz <laurmaedje@gmail.com>
| -rw-r--r-- | Cargo.toml | 6 | ||||
| -rw-r--r-- | NOTICE | 6 | ||||
| -rw-r--r-- | src/export/pdf.rs | 5 | ||||
| -rw-r--r-- | src/image.rs | 133 | ||||
| -rw-r--r-- | tests/res/monkey.svg | 2 | ||||
| -rw-r--r-- | tests/res/pattern.svg | 36 |
6 files changed, 89 insertions, 99 deletions
@@ -23,10 +23,10 @@ fxhash = "0.2.1" image = { version = "0.23", default-features = false, features = ["png", "jpeg"] } itertools = "0.10" miniz_oxide = "0.4" -pdf-writer = { git = "https://github.com/typst/pdf-writer", rev = "e1ec200" } +pdf-writer = "0.4" rustybuzz = "0.4" serde = { version = "1", features = ["derive", "rc"] } -svg2pdf = { git = "https://github.com/typst/svg2pdf", rev = "a127d6f", default-features = false, features = ["text", "png", "jpeg"] } +svg2pdf = { version = "0.1", default-features = false, features = ["text", "png", "jpeg"] } ttf-parser = "0.12" unicode-bidi = "0.3.5" unicode-segmentation = "1.8" @@ -46,7 +46,7 @@ walkdir = { version = "2", optional = true } filedescriptor = "0.8" iai = { git = "https://github.com/reknih/iai" } resvg = { version = "0.19", default-features = false, features = ["text"] } -tiny-skia = "0.6.1" +tiny-skia = "0.6" walkdir = "2" [[bin]] @@ -16,6 +16,12 @@ The SIL Open Font License Version 1.1 applies to: Copyright (c) 2010, ParaType Ltd. (http://www.paratype.com/public), with Reserved Font Names "PT Sans" and "ParaType". +* Monkey emoji in tests/res/monkey.svg + Copyright 2018 Vincent Le Moign, Streamline Emoji Project + Via Wikimedia Commons + (https://commons.wikimedia.org/wiki/File:440-monkey.svg) + Partially minified using SVGO + ----------------------------------------------------------- SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ----------------------------------------------------------- diff --git a/src/export/pdf.rs b/src/export/pdf.rs index 100a53f9..b5f4c009 100644 --- a/src/export/pdf.rs +++ b/src/export/pdf.rs @@ -10,7 +10,6 @@ use pdf_writer::types::{ ActionType, AnnotationType, CidFontType, FontFlags, SystemInfo, UnicodeCmap, }; use pdf_writer::{Content, Filter, Finish, Name, PdfWriter, Rect, Ref, Str, TextStr}; -use svg2pdf::{convert_tree_into, Options}; use ttf_parser::{name_id, GlyphId, Tag}; use super::subset; @@ -263,9 +262,9 @@ impl<'a> PdfExporter<'a> { } } Image::Svg(img) => { - let next_ref = convert_tree_into( + let next_ref = svg2pdf::convert_tree_into( &img.0, - Options::default(), + svg2pdf::Options::default(), &mut self.writer, image_ref, ); diff --git a/src/image.rs b/src/image.rs index f6574045..a7b62503 100644 --- a/src/image.rs +++ b/src/image.rs @@ -1,6 +1,7 @@ //! Image handling. use std::collections::{hash_map::Entry, HashMap}; +use std::ffi::OsStr; use std::fmt::{self, Debug, Formatter}; use std::io; use std::path::Path; @@ -9,7 +10,6 @@ use std::rc::Rc; use image::io::Reader as ImageReader; use image::{DynamicImage, GenericImageView, ImageFormat}; use serde::{Deserialize, Serialize}; -use usvg::{Error as USvgError, Tree}; use crate::loading::{FileHash, Loader}; @@ -66,7 +66,8 @@ impl ImageStore { Entry::Occupied(entry) => entry.into_mut(), Entry::Vacant(entry) => { let buffer = self.loader.load(path)?; - let image = Image::parse(&buffer)?; + let ext = path.extension().and_then(OsStr::to_str).unwrap_or_default(); + let image = Image::parse(&buffer, &ext)?; let id = ImageId(self.images.len() as u32); if let Some(callback) = &self.on_load { callback(id, &image); @@ -91,21 +92,32 @@ impl ImageStore { /// A loaded image. #[derive(Debug)] pub enum Image { + /// A pixel raster format, like PNG or JPEG. Raster(RasterImage), + /// An SVG vector graphic. Svg(Svg), } impl Image { - /// Parse an image from raw data. This will prioritize SVG images and then - /// try to decode a supported raster format. - pub fn parse(data: &[u8]) -> io::Result<Self> { + /// Parse an image from raw data. The file extension is used as a hint for + /// which error message describes the problem best. + pub fn parse(data: &[u8], ext: &str) -> io::Result<Self> { match Svg::parse(data) { - Ok(svg) => Ok(Self::Svg(svg)), - Err(e) if e.kind() == io::ErrorKind::InvalidData => { - Ok(Self::Raster(RasterImage::parse(data)?)) - } - Err(e) => Err(e), + Ok(svg) => return Ok(Self::Svg(svg)), + Err(err) if matches!(ext, "svg" | "svgz") => return Err(err), + Err(_) => {} } + + match RasterImage::parse(data) { + Ok(raster) => return Ok(Self::Raster(raster)), + Err(err) if matches!(ext, "png" | "jpg" | "jpeg") => return Err(err), + Err(_) => {} + } + + Err(io::Error::new( + io::ErrorKind::InvalidData, + "unknown image format", + )) } /// The width of the image in pixels. @@ -123,67 +135,6 @@ impl Image { Self::Svg(image) => image.height(), } } - - pub fn is_vector(&self) -> bool { - match self { - Self::Raster(_) => false, - Self::Svg(_) => true, - } - } -} - -/// An SVG image, supported through the usvg crate. -pub struct Svg(pub Tree); - -impl Svg { - /// Parse an SVG file from a data buffer. This also handles `.svgz` - /// compressed files. - pub fn parse(data: &[u8]) -> io::Result<Self> { - let usvg_opts = usvg::Options::default(); - let tree = Tree::from_data(data, &usvg_opts.to_ref()).map_err(|e| match e { - USvgError::NotAnUtf8Str => { - io::Error::new(io::ErrorKind::InvalidData, "file is not valid utf-8") - } - USvgError::MalformedGZip => io::Error::new( - io::ErrorKind::InvalidData, - "could not extract gzipped SVG", - ), - USvgError::ElementsLimitReached => io::Error::new( - io::ErrorKind::Other, - "SVG file has more than 1 million elements", - ), - USvgError::InvalidSize => io::Error::new( - io::ErrorKind::InvalidData, - "SVG width or height not greater than zero", - ), - USvgError::ParsingFailed(error) => io::Error::new( - io::ErrorKind::InvalidData, - format!("SVG parsing error: {}", error.to_string()), - ), - })?; - - Ok(Self(tree)) - } - - /// The width of the image in rounded-up nominal SVG pixels. - pub fn width(&self) -> u32 { - self.0.svg_node().size.width().ceil() as u32 - } - - /// The height of the image in rounded-up nominal SVG pixels. - pub fn height(&self) -> u32 { - self.0.svg_node().size.height().ceil() as u32 - } -} - -impl Debug for Svg { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.debug_struct("Svg") - .field("width", &self.0.svg_node().size.width()) - .field("height", &self.0.svg_node().size.height()) - .field("viewBox", &self.0.svg_node().view_box) - .finish() - } } /// A raster image, supported through the image crate. @@ -201,9 +152,9 @@ impl RasterImage { pub fn parse(data: &[u8]) -> io::Result<Self> { let cursor = io::Cursor::new(data); let reader = ImageReader::new(cursor).with_guessed_format()?; - let format = reader.format().ok_or_else(|| { - io::Error::new(io::ErrorKind::InvalidData, "unknown image format") - })?; + let format = reader + .format() + .ok_or_else(|| io::Error::from(io::ErrorKind::InvalidData))?; let buf = reader .decode() @@ -233,3 +184,37 @@ impl Debug for RasterImage { .finish() } } + +/// An SVG image, supported through the usvg crate. +pub struct Svg(pub usvg::Tree); + +impl Svg { + /// Parse an SVG file from a data buffer. This also handles `.svgz` + /// compressed files. + pub fn parse(data: &[u8]) -> io::Result<Self> { + let usvg_opts = usvg::Options::default(); + usvg::Tree::from_data(data, &usvg_opts.to_ref()) + .map(Self) + .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err)) + } + + /// The width of the image in rounded-up nominal SVG pixels. + pub fn width(&self) -> u32 { + self.0.svg_node().size.width().ceil() as u32 + } + + /// The height of the image in rounded-up nominal SVG pixels. + pub fn height(&self) -> u32 { + self.0.svg_node().size.height().ceil() as u32 + } +} + +impl Debug for Svg { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.debug_struct("Svg") + .field("width", &self.0.svg_node().size.width()) + .field("height", &self.0.svg_node().size.height()) + .field("viewBox", &self.0.svg_node().view_box) + .finish() + } +} diff --git a/tests/res/monkey.svg b/tests/res/monkey.svg index 3cd5da4a..0e8c1124 100644 --- a/tests/res/monkey.svg +++ b/tests/res/monkey.svg @@ -36,7 +36,7 @@ <path class="st3" d="m6.1 26.1-.7-1.8"/> <path class="st3" d="m7.1 23.6-3.5 1.5"/> <path class="st34" d="M30.7 19.4c-.5.9-.2 2.1.7 2.6.9.5 2.1.2 2.6-.7.9-1.7 3-2.3 4.7-1.6 3.5 1.5 5.4 5.4 4.4 9.1-1.7 6.7-7.7 11.3-14.6 11.3h-8l-.6 3.9h8.8c8.7 0 16.4-5.9 18.5-14.4 1.3-5.4-1.3-10.9-6.3-13.3-3.7-1.8-8.1-.5-10.2 3.1z"/> - <g > + <g> <path class="st35" d="M43.4 27.9c0 .3-.1.6-.2.8C41.5 35.4 35.5 40 28.6 40h-8l-.4 2.2h8.4c6.9 0 12.9-4.6 14.6-11.3.3-1 .3-2 .2-3zM30.7 19.4c-.4.7-.3 1.5.1 2.1 2.1-3.4 6.5-4.7 10.1-3 3.8 1.8 6.2 5.4 6.6 9.4.4-4.8-2.1-9.4-6.6-11.6-3.7-1.8-8.1-.5-10.2 3.1z"/> </g> <path class="st3" d="M30.7 19.4c-.5.9-.2 2.1.7 2.6.9.5 2.1.2 2.6-.7.9-1.7 3-2.3 4.7-1.6 3.5 1.5 5.4 5.4 4.4 9.1-1.7 6.7-7.7 11.3-14.6 11.3h-8l-.6 3.9h8.8c8.7 0 16.4-5.9 18.5-14.4 1.3-5.4-1.3-10.9-6.3-13.3-3.7-1.8-8.1-.5-10.2 3.1z"/> diff --git a/tests/res/pattern.svg b/tests/res/pattern.svg index b3bf4847..0a46c0b6 100644 --- a/tests/res/pattern.svg +++ b/tests/res/pattern.svg @@ -1,22 +1,22 @@ <?xml version="1.0" encoding="utf-8"?> <!-- Adapted from - https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Patterns under - CC0 / Public Domain Licensing --> + https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Patterns under + CC0 / Public Domain Licensing --> <svg width="200" height="150" xmlns="http://www.w3.org/2000/svg"> - <defs> - <linearGradient id="Gradient1"> - <stop offset="5%" stop-color="white"/> - <stop offset="95%" stop-color="blue"/> - </linearGradient> - <linearGradient id="Gradient2" x1="0" x2="0" y1="0" y2="1"> - <stop offset="5%" stop-color="red"/> - <stop offset="95%" stop-color="orange"/> - </linearGradient> - <pattern id="Pattern" x="40" y="10" width="50" height="50" patternUnits="userSpaceOnUse"> - <rect x="0" y="0" width="50" height="50" fill="skyblue"/> - <rect x="0" y="0" width="25" height="25" fill="url(#Gradient2)"/> - <circle cx="25" cy="25" r="20" fill="url(#Gradient1)" fill-opacity="0.5"/> - </pattern> - </defs> - <rect fill="url(#Pattern)" stroke="black" width="200" height="150"/> + <defs> + <linearGradient id="Gradient1"> + <stop offset="5%" stop-color="white"/> + <stop offset="95%" stop-color="blue"/> + </linearGradient> + <linearGradient id="Gradient2" x1="0" x2="0" y1="0" y2="1"> + <stop offset="5%" stop-color="red"/> + <stop offset="95%" stop-color="orange"/> + </linearGradient> + <pattern id="Pattern" x="40" y="10" width="50" height="50" patternUnits="userSpaceOnUse"> + <rect x="0" y="0" width="50" height="50" fill="skyblue"/> + <rect x="0" y="0" width="25" height="25" fill="url(#Gradient2)"/> + <circle cx="25" cy="25" r="20" fill="url(#Gradient1)" fill-opacity="0.5"/> + </pattern> + </defs> + <rect fill="url(#Pattern)" stroke="black" width="200" height="150"/> </svg> |
