From 5e31a321666c1984f5b9da1c21e1074bacdcfc76 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 18 Jun 2020 11:36:11 +0200 Subject: [PATCH] Add support for SVG files to `get_image_metadata` (Fixes #769) (#1063) * Add support for SVG files to `get_image_metadata` * Add support for SVG files to `get_image_metadata` * Update documentation after adding SVG support --- CHANGELOG.md | 1 + Cargo.lock | 34 +++++++++++++++++++ components/templates/Cargo.toml | 1 + components/templates/src/global_fns/mod.rs | 26 +++++++++++--- .../documentation/templates/overview.md | 3 +- 5 files changed, 60 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7514bad..7dd956a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Add 2 syntax highlighting themes: `green` and `railsbase16-green-screen-dark` - Enable task lists in Markdown +- Add support for SVG in `get_image_metadata` ## 0.11.0 (2020-05-25) diff --git a/Cargo.lock b/Cargo.lock index 0ee978f6..8852d0e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -369,6 +369,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "dtoa" version = "0.4.5" @@ -1756,6 +1762,15 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "roxmltree" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5001f134077069d87f77c8b9452b690df2445f7a43f1c7ca4a1af8dd505789d" +dependencies = [ + "xmlparser", +] + [[package]] name = "rust-stemmers" version = "1.2.0" @@ -2042,6 +2057,18 @@ dependencies = [ "syn", ] +[[package]] +name = "svg_metadata" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fe5b1fbd62339f055704951dcaf2e757c460b9f6abe17f6de0d2563da821c57" +dependencies = [ + "doc-comment", + "lazy_static", + "regex", + "roxmltree", +] + [[package]] name = "syn" version = "1.0.31" @@ -2117,6 +2144,7 @@ dependencies = [ "reqwest", "serde_json", "sha2", + "svg_metadata", "tera", "toml", "url", @@ -2668,6 +2696,12 @@ dependencies = [ "time", ] +[[package]] +name = "xmlparser" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccb4240203dadf40be2de9369e5c6dec1bf427528115b030baca3334c18362d7" + [[package]] name = "yaml-rust" version = "0.4.4" diff --git a/components/templates/Cargo.toml b/components/templates/Cargo.toml index 24189765..e0664fa3 100644 --- a/components/templates/Cargo.toml +++ b/components/templates/Cargo.toml @@ -21,6 +21,7 @@ utils = { path = "../utils" } library = { path = "../library" } config = { path = "../config" } imageproc = { path = "../imageproc" } +svg_metadata = "0.4.1" [dependencies.reqwest] version = "0.10" diff --git a/components/templates/src/global_fns/mod.rs b/components/templates/src/global_fns/mod.rs index 563be571..76af26a6 100644 --- a/components/templates/src/global_fns/mod.rs +++ b/components/templates/src/global_fns/mod.rs @@ -2,9 +2,11 @@ use std::collections::HashMap; use std::path::PathBuf; use std::sync::{Arc, Mutex, RwLock}; use std::{fs, io, result}; +use std::ffi::OsStr; use sha2::{Digest, Sha256, Sha384, Sha512}; use tera::{from_value, to_value, Error, Function as TeraFn, Result, Value}; +use svg_metadata as svg; use config::Config; use image; @@ -279,15 +281,31 @@ impl TeraFn for GetImageMeta { if !src_path.exists() { return Err(format!("`get_image_metadata`: Cannot find path: {}", path).into()); } - let img = image::open(&src_path) - .map_err(|e| Error::chain(format!("Failed to process image: {}", path), e))?; + let (height, width) = image_dimensions(&src_path)?; let mut map = tera::Map::new(); - map.insert(String::from("height"), Value::Number(tera::Number::from(img.height()))); - map.insert(String::from("width"), Value::Number(tera::Number::from(img.width()))); + map.insert(String::from("height"), Value::Number(tera::Number::from(height))); + map.insert(String::from("width"), Value::Number(tera::Number::from(width))); Ok(Value::Object(map)) } } +// Try to read the image dimensions for a given image +fn image_dimensions(path: &PathBuf) -> Result<(u32, u32)> { + if let Some("svg") = path.extension().and_then(OsStr::to_str) { + let img = svg::Metadata::parse_file(&path) + .map_err(|e| Error::chain(format!("Failed to process SVG: {}", path.display()), e))?; + match (img.height(), img.width(), img.view_box()) { + (Some(h), Some(w), _) => Ok((h as u32, w as u32)), + (_, _, Some(view_box)) => Ok((view_box.height as u32, view_box.width as u32)), + _ => Err("Invalid dimensions: SVG width/height and viewbox not set.".into()) + } + } else { + let img = image::open(&path) + .map_err(|e| Error::chain(format!("Failed to process image: {}", path.display()), e))?; + Ok((img.height(), img.width())) + } +} + #[derive(Debug)] pub struct GetTaxonomyUrl { taxonomies: HashMap>, diff --git a/docs/content/documentation/templates/overview.md b/docs/content/documentation/templates/overview.md index 257eb4cc..83ba23ce 100644 --- a/docs/content/documentation/templates/overview.md +++ b/docs/content/documentation/templates/overview.md @@ -165,7 +165,8 @@ Whenever hashing files, whether using `get_file_hash` or `get_url(..., cachebust ### `get_image_metadata` -Gets metadata for an image. Currently, the only supported keys are `width` and `height`. +Gets metadata for an image. This supports common formats like JPEG, PNG, as well as SVG. +Currently, the only supported keys are `width` and `height`. ```jinja2 {% set meta = get_image_metadata(path="...") %}