summaryrefslogtreecommitdiff
path: root/src/image.rs
diff options
context:
space:
mode:
authorMartin Haug <mhaug@live.de>2021-12-07 00:06:54 +0100
committerMartin Haug <mhaug@live.de>2021-12-07 00:06:54 +0100
commit7c829c5c1b67ac8e8fbe4fc4ba01468d100bfb47 (patch)
tree7930718d6dbdf92d1feaa92df97830ef3dfc4ed5 /src/image.rs
parentf15ee7efb68eff188b5993d21d663e2120b5dd08 (diff)
Bump pdf-writer, svg2pdf and error messages
Co-Authored-By: Laurenz <laurmaedje@gmail.com>
Diffstat (limited to 'src/image.rs')
-rw-r--r--src/image.rs133
1 files changed, 59 insertions, 74 deletions
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()
+ }
+}