diff options
| author | Laurenz <laurmaedje@gmail.com> | 2025-02-17 11:56:00 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2025-02-17 12:49:15 +0100 |
| commit | d48708c5d55a5d56e4cd3a3b0de38425a6fd8380 (patch) | |
| tree | 6719334fc6d5f5ab334f1a3cc46021f852df6cbe | |
| parent | e294fe85a591f01aef1c2466aa0618dda3c58095 (diff) | |
More robust SVG auto-detection (#5878)
| -rw-r--r-- | Cargo.lock | 1 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | crates/typst-library/Cargo.toml | 1 | ||||
| -rw-r--r-- | crates/typst-library/src/visualize/image/mod.rs | 18 | ||||
| -rw-r--r-- | docs/changelog/0.13.0.md | 3 | ||||
| -rw-r--r-- | tests/ref/image-svg-auto-detection.png | bin | 0 -> 129 bytes | |||
| -rw-r--r-- | tests/suite/visualize/image.typ | 15 |
7 files changed, 33 insertions, 6 deletions
@@ -2966,6 +2966,7 @@ dependencies = [ "kamadak-exif", "kurbo", "lipsum", + "memchr", "palette", "phf", "png", @@ -73,6 +73,7 @@ kamadak-exif = "0.6" kurbo = "0.11" libfuzzer-sys = "0.4" lipsum = "0.9" +memchr = "2" miniz_oxide = "0.8" native-tls = "0.2" notify = "8" diff --git a/crates/typst-library/Cargo.toml b/crates/typst-library/Cargo.toml index cc5e2671..fb45ec86 100644 --- a/crates/typst-library/Cargo.toml +++ b/crates/typst-library/Cargo.toml @@ -38,6 +38,7 @@ indexmap = { workspace = true } kamadak-exif = { workspace = true } kurbo = { workspace = true } lipsum = { workspace = true } +memchr = { workspace = true } palette = { workspace = true } phf = { workspace = true } png = { workspace = true } diff --git a/crates/typst-library/src/visualize/image/mod.rs b/crates/typst-library/src/visualize/image/mod.rs index 97189e22..258eb96f 100644 --- a/crates/typst-library/src/visualize/image/mod.rs +++ b/crates/typst-library/src/visualize/image/mod.rs @@ -398,8 +398,7 @@ impl ImageFormat { return Some(Self::Raster(RasterFormat::Exchange(format))); } - // SVG or compressed SVG. - if data.starts_with(b"<svg") || data.starts_with(&[0x1f, 0x8b]) { + if is_svg(data) { return Some(Self::Vector(VectorFormat::Svg)); } @@ -407,6 +406,21 @@ impl ImageFormat { } } +/// Checks whether the data looks like an SVG or a compressed SVG. +fn is_svg(data: &[u8]) -> bool { + // Check for the gzip magic bytes. This check is perhaps a bit too + // permissive as other formats than SVGZ could use gzip. + if data.starts_with(&[0x1f, 0x8b]) { + return true; + } + + // If the first 2048 bytes contain the SVG namespace declaration, we assume + // that it's an SVG. Note that, if the SVG does not contain a namespace + // declaration, usvg will reject it. + let head = &data[..data.len().min(2048)]; + memchr::memmem::find(head, b"http://www.w3.org/2000/svg").is_some() +} + /// A vector graphics format. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)] pub enum VectorFormat { diff --git a/docs/changelog/0.13.0.md b/docs/changelog/0.13.0.md index 5639f95b..e6ae88f0 100644 --- a/docs/changelog/0.13.0.md +++ b/docs/changelog/0.13.0.md @@ -99,8 +99,7 @@ description: Changes slated to appear in Typst 0.13.0 - Fixed interaction of clipping and outset on [`box`] and [`block`] - Fixed panic with [`path`] of infinite length - Fixed non-solid (e.g. tiling) text fills in clipped blocks -- Auto-detection of image formats from a raw buffer now has basic support for - SVGs +- Auto-detection of image formats from a raw buffer now has support for SVGs ## Scripting - Functions that accept [file paths]($syntax/#paths) now also accept raw diff --git a/tests/ref/image-svg-auto-detection.png b/tests/ref/image-svg-auto-detection.png Binary files differnew file mode 100644 index 00000000..0240f8f5 --- /dev/null +++ b/tests/ref/image-svg-auto-detection.png diff --git a/tests/suite/visualize/image.typ b/tests/suite/visualize/image.typ index e37932f2..7ce0c8c0 100644 --- a/tests/suite/visualize/image.typ +++ b/tests/suite/visualize/image.typ @@ -65,6 +65,17 @@ A #box(image("/assets/images/tiger.jpg", height: 1cm, width: 80%)) B caption: [Bilingual text] ) +--- image-svg-auto-detection --- +#image(bytes( + ``` + <?xml version="1.0" encoding="utf-8"?> + <!-- An SVG --> + <svg width="200" height="150" xmlns="http://www.w3.org/2000/svg"> + <rect fill="red" stroke="black" x="25" y="25" width="150" height="100"/> + </svg> + ```.text +)) + --- image-pixmap-rgb8 --- #image( bytes(( @@ -152,8 +163,8 @@ A #box(image("/assets/images/tiger.jpg", height: 1cm, width: 80%)) B #image("path/does/not/exist") --- image-bad-format --- -// Error: 2-22 unknown image format -#image("./image.typ") +// Error: 2-37 unknown image format +#image("/assets/plugins/hello.wasm") --- image-bad-svg --- // Error: 2-33 failed to parse SVG (found closing tag 'g' instead of 'style' in line 4) |
