summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2025-02-17 11:56:00 +0100
committerGitHub <noreply@github.com>2025-02-17 10:56:00 +0000
commit25c86accbb4adc0e7542d2c5957dff6e939cbf48 (patch)
tree8db19e48038ffe1eeee1232dc54ec242ce48e029
parent5fc679f3e7501ee5831f1b4b7789350f43b4c221 (diff)
More robust SVG auto-detection (#5878)
-rw-r--r--Cargo.lock1
-rw-r--r--Cargo.toml1
-rw-r--r--crates/typst-library/Cargo.toml1
-rw-r--r--crates/typst-library/src/visualize/image/mod.rs18
-rw-r--r--docs/changelog/0.13.0.md3
-rw-r--r--tests/ref/image-svg-auto-detection.pngbin0 -> 129 bytes
-rw-r--r--tests/suite/visualize/image.typ15
7 files changed, 33 insertions, 6 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 66a1e3a1..249ee3bc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2966,6 +2966,7 @@ dependencies = [
"kamadak-exif",
"kurbo",
"lipsum",
+ "memchr",
"palette",
"phf",
"png",
diff --git a/Cargo.toml b/Cargo.toml
index ea9f8510..6fb64d3a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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 2caace72..e5315e5b 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
new file mode 100644
index 00000000..0240f8f5
--- /dev/null
+++ b/tests/ref/image-svg-auto-detection.png
Binary files differ
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)