0.5.0: Parse SVG size
This commit is contained in:
parent
de1284ae43
commit
99ed10ff27
5 changed files with 188 additions and 15 deletions
48
Cargo.lock
generated
48
Cargo.lock
generated
|
@ -2,6 +2,17 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||
dependencies = [
|
||||
"getrandom 0.2.8",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.19"
|
||||
|
@ -566,6 +577,9 @@ name = "hashbrown"
|
|||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
|
@ -792,6 +806,12 @@ version = "1.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.137"
|
||||
|
@ -826,6 +846,25 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lol_html"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b16d7d17063591adf0017a068a8ce2ed29a4f5402b9e6cac01888ff9c0527ff7"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cssparser",
|
||||
"encoding_rs",
|
||||
"hashbrown",
|
||||
"lazy_static",
|
||||
"lazycell",
|
||||
"memchr",
|
||||
"safemem",
|
||||
"selectors",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mac"
|
||||
version = "0.1.1"
|
||||
|
@ -1453,6 +1492,12 @@ version = "1.0.11"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||
|
||||
[[package]]
|
||||
name = "safemem"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.20"
|
||||
|
@ -1638,7 +1683,7 @@ checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
|
|||
|
||||
[[package]]
|
||||
name = "site_icons"
|
||||
version = "0.4.13"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"clap",
|
||||
|
@ -1648,6 +1693,7 @@ dependencies = [
|
|||
"html5ever",
|
||||
"itertools",
|
||||
"log",
|
||||
"lol_html",
|
||||
"mime_4",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "site_icons"
|
||||
version = "0.4.13"
|
||||
version = "0.5.0"
|
||||
authors = ["Sam Denty <sam@samdenty.com>"]
|
||||
edition = "2018"
|
||||
license = "GPL-3.0"
|
||||
|
@ -35,6 +35,7 @@ serde = { version = "1.0", features = ["derive", "rc"] }
|
|||
serde_json = "1.0"
|
||||
futures = "0.3.25"
|
||||
tldextract = "0.6.0"
|
||||
lol_html = "0.3.2"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
reqwest = { package = "reqwest-wasm", version = "0.11.16", features = [
|
||||
|
|
|
@ -11,9 +11,8 @@ use std::{
|
|||
io,
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
enum IconKind {
|
||||
SVG,
|
||||
PNG,
|
||||
JPEG,
|
||||
ICO,
|
||||
|
@ -28,7 +27,7 @@ pub enum IconInfo {
|
|||
JPEG { size: IconSize },
|
||||
ICO { sizes: IconSizes },
|
||||
GIF { size: IconSize },
|
||||
SVG,
|
||||
SVG { size: Option<IconSize> },
|
||||
}
|
||||
|
||||
impl IconInfo {
|
||||
|
@ -40,6 +39,14 @@ impl IconInfo {
|
|||
reader.read_exact(&mut header).await?;
|
||||
|
||||
match (kind, &header) {
|
||||
(Some(IconKind::SVG), bytes) => {
|
||||
let size = get_svg_size(bytes, reader).await?;
|
||||
Ok(IconInfo::SVG { size })
|
||||
}
|
||||
(_, &[0x60, byte_two]) => {
|
||||
let size = get_svg_size(&[0x60, byte_two], reader).await?;
|
||||
Ok(IconInfo::SVG { size })
|
||||
}
|
||||
(Some(IconKind::PNG), _) | (_, b"\x89P") => {
|
||||
let size = get_png_size(reader).await?;
|
||||
Ok(IconInfo::PNG { size })
|
||||
|
@ -135,7 +142,25 @@ impl IconInfo {
|
|||
Some(IconKind::ICO)
|
||||
}
|
||||
|
||||
(mime::IMAGE, mime::SVG) | (mime::TEXT, mime::PLAIN) => return Ok(IconInfo::SVG),
|
||||
(mime::IMAGE, mime::GIF) => {
|
||||
if let Some(sizes) = sizes {
|
||||
return Ok(IconInfo::GIF {
|
||||
size: *sizes.largest(),
|
||||
});
|
||||
}
|
||||
|
||||
Some(IconKind::GIF)
|
||||
}
|
||||
|
||||
(mime::IMAGE, mime::SVG) | (mime::TEXT, mime::PLAIN) => {
|
||||
if let Some(sizes) = sizes {
|
||||
return Ok(IconInfo::SVG {
|
||||
size: Some(*sizes.largest()),
|
||||
});
|
||||
}
|
||||
|
||||
Some(IconKind::SVG)
|
||||
}
|
||||
|
||||
_ => None,
|
||||
};
|
||||
|
@ -147,7 +172,7 @@ impl IconInfo {
|
|||
match self {
|
||||
IconInfo::ICO { sizes } => Some(sizes.largest()),
|
||||
IconInfo::PNG { size } | IconInfo::JPEG { size } | IconInfo::GIF { size } => Some(size),
|
||||
IconInfo::SVG => None,
|
||||
IconInfo::SVG { size } => size.as_ref(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,7 +182,7 @@ impl IconInfo {
|
|||
IconInfo::PNG { size } | IconInfo::JPEG { size } | IconInfo::GIF { size } => {
|
||||
Some((*size).into())
|
||||
}
|
||||
IconInfo::SVG => None,
|
||||
IconInfo::SVG { size } => size.map(|size| size.into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,7 +192,7 @@ impl IconInfo {
|
|||
IconInfo::JPEG { .. } => "image/jpeg",
|
||||
IconInfo::ICO { .. } => "image/x-icon",
|
||||
IconInfo::GIF { .. } => "image/gif",
|
||||
IconInfo::SVG => "image/svg+xml",
|
||||
IconInfo::SVG { .. } => "image/svg+xml",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -179,7 +204,17 @@ impl Display for IconInfo {
|
|||
IconInfo::JPEG { size } => write!(f, "jpeg {}", size),
|
||||
IconInfo::GIF { size } => write!(f, "gif {}", size),
|
||||
IconInfo::ICO { sizes } => write!(f, "ico {}", sizes),
|
||||
IconInfo::SVG => write!(f, "svg"),
|
||||
IconInfo::SVG { size } => {
|
||||
write!(
|
||||
f,
|
||||
"svg{}",
|
||||
if let Some(size) = size {
|
||||
format!(" {}", size)
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -187,15 +222,20 @@ impl Display for IconInfo {
|
|||
impl Ord for IconInfo {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
match (self, other) {
|
||||
(IconInfo::SVG, IconInfo::SVG) => Ordering::Equal,
|
||||
(IconInfo::SVG, _) => Ordering::Less,
|
||||
(_, IconInfo::SVG) => Ordering::Greater,
|
||||
(IconInfo::SVG { size }, IconInfo::SVG { size: other_size }) => match (size, other_size) {
|
||||
(Some(_), None) => Ordering::Less,
|
||||
(None, Some(_)) => Ordering::Greater,
|
||||
(Some(size), Some(other_size)) => size.cmp(other_size),
|
||||
(None, None) => Ordering::Equal,
|
||||
},
|
||||
(IconInfo::SVG { .. }, _) => Ordering::Less,
|
||||
(_, IconInfo::SVG { .. }) => Ordering::Greater,
|
||||
|
||||
_ => {
|
||||
let this_size = self.size().unwrap();
|
||||
let size = self.size().unwrap();
|
||||
let other_size = other.size().unwrap();
|
||||
|
||||
this_size.cmp(other_size).then_with(|| match (self, other) {
|
||||
size.cmp(other_size).then_with(|| match (self, other) {
|
||||
(IconInfo::PNG { .. }, IconInfo::PNG { .. }) => Ordering::Equal,
|
||||
(IconInfo::PNG { .. }, _) => Ordering::Less,
|
||||
(_, IconInfo::PNG { .. }) => Ordering::Greater,
|
||||
|
|
|
@ -3,12 +3,14 @@ mod ico;
|
|||
mod icon_sizes;
|
||||
mod jpeg;
|
||||
mod png;
|
||||
mod svg;
|
||||
|
||||
pub use gif::*;
|
||||
pub use ico::*;
|
||||
pub use icon_sizes::*;
|
||||
pub use jpeg::*;
|
||||
pub use png::*;
|
||||
pub use svg::*;
|
||||
|
||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::{
|
||||
|
|
84
src/icon_size/svg.rs
Normal file
84
src/icon_size/svg.rs
Normal file
|
@ -0,0 +1,84 @@
|
|||
use super::IconSize;
|
||||
use futures::prelude::*;
|
||||
use lol_html::{element, errors::RewritingError, HtmlRewriter, Settings};
|
||||
use std::{
|
||||
error::Error,
|
||||
fmt::{self, Display},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SizeResult(Option<IconSize>);
|
||||
|
||||
impl Display for SizeResult {
|
||||
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for SizeResult {}
|
||||
|
||||
fn parse_size<S: ToString>(size: S) -> Option<u32> {
|
||||
size
|
||||
.to_string()
|
||||
.parse::<f64>()
|
||||
.ok()
|
||||
.map(|size| size.round() as u32)
|
||||
}
|
||||
|
||||
pub async fn get_svg_size<R: AsyncRead + Unpin>(
|
||||
first_bytes: &[u8; 2],
|
||||
reader: &mut R,
|
||||
) -> Result<Option<IconSize>, Box<dyn Error>> {
|
||||
let mut rewriter = HtmlRewriter::new(
|
||||
Settings {
|
||||
element_content_handlers: vec![
|
||||
// Rewrite insecure hyperlinks
|
||||
element!("svg", |el| {
|
||||
let viewbox = el.get_attribute("viewbox");
|
||||
|
||||
let width = el.get_attribute("width").and_then(parse_size);
|
||||
let height = el.get_attribute("height").and_then(parse_size);
|
||||
|
||||
Err(Box::new(SizeResult(
|
||||
if let (Some(width), Some(height)) = (width, height) {
|
||||
Some(IconSize::new(width, height))
|
||||
} else if let Some(viewbox) = viewbox {
|
||||
regex!(r"^\d+\s+\d+\s+(\d+\.?[\d]?)\s+(\d+\.?[\d]?)")
|
||||
.captures(&viewbox)
|
||||
.map(|captures| {
|
||||
let width = parse_size(captures.get(1).unwrap().as_str()).unwrap();
|
||||
let height = parse_size(captures.get(2).unwrap().as_str()).unwrap();
|
||||
IconSize::new(width, height)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
},
|
||||
)))
|
||||
}),
|
||||
],
|
||||
..Settings::default()
|
||||
},
|
||||
|_: &[u8]| {},
|
||||
);
|
||||
|
||||
rewriter.write(first_bytes)?;
|
||||
|
||||
let mut buffer = [0; 100];
|
||||
|
||||
loop {
|
||||
let n = reader.read(&mut buffer).await?;
|
||||
if n == 0 {
|
||||
return Err("invalid svg".into());
|
||||
}
|
||||
|
||||
match rewriter.write(&buffer[..n]) {
|
||||
Err(RewritingError::ContentHandlerError(result)) => {
|
||||
let result = result.downcast::<SizeResult>().unwrap();
|
||||
|
||||
return Ok(result.0);
|
||||
}
|
||||
|
||||
result => result?,
|
||||
}
|
||||
}
|
||||
}
|
Reference in a new issue