More robust SVG auto-detection (#5878)

This commit is contained in:
Laurenz 2025-02-17 11:56:00 +01:00
parent e294fe85a5
commit d48708c5d5
7 changed files with 33 additions and 6 deletions

1
Cargo.lock generated
View File

@ -2966,6 +2966,7 @@ dependencies = [
"kamadak-exif", "kamadak-exif",
"kurbo", "kurbo",
"lipsum", "lipsum",
"memchr",
"palette", "palette",
"phf", "phf",
"png", "png",

View File

@ -73,6 +73,7 @@ kamadak-exif = "0.6"
kurbo = "0.11" kurbo = "0.11"
libfuzzer-sys = "0.4" libfuzzer-sys = "0.4"
lipsum = "0.9" lipsum = "0.9"
memchr = "2"
miniz_oxide = "0.8" miniz_oxide = "0.8"
native-tls = "0.2" native-tls = "0.2"
notify = "8" notify = "8"

View File

@ -38,6 +38,7 @@ indexmap = { workspace = true }
kamadak-exif = { workspace = true } kamadak-exif = { workspace = true }
kurbo = { workspace = true } kurbo = { workspace = true }
lipsum = { workspace = true } lipsum = { workspace = true }
memchr = { workspace = true }
palette = { workspace = true } palette = { workspace = true }
phf = { workspace = true } phf = { workspace = true }
png = { workspace = true } png = { workspace = true }

View File

@ -398,8 +398,7 @@ impl ImageFormat {
return Some(Self::Raster(RasterFormat::Exchange(format))); return Some(Self::Raster(RasterFormat::Exchange(format)));
} }
// SVG or compressed SVG. if is_svg(data) {
if data.starts_with(b"<svg") || data.starts_with(&[0x1f, 0x8b]) {
return Some(Self::Vector(VectorFormat::Svg)); 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. /// A vector graphics format.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
pub enum VectorFormat { pub enum VectorFormat {

View File

@ -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 interaction of clipping and outset on [`box`] and [`block`]
- Fixed panic with [`path`] of infinite length - Fixed panic with [`path`] of infinite length
- Fixed non-solid (e.g. tiling) text fills in clipped blocks - 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 - Auto-detection of image formats from a raw buffer now has support for SVGs
SVGs
## Scripting ## Scripting
- Functions that accept [file paths]($syntax/#paths) now also accept raw - Functions that accept [file paths]($syntax/#paths) now also accept raw

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

View File

@ -65,6 +65,17 @@ A #box(image("/assets/images/tiger.jpg", height: 1cm, width: 80%)) B
caption: [Bilingual text] 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-pixmap-rgb8 ---
#image( #image(
bytes(( bytes((
@ -152,8 +163,8 @@ A #box(image("/assets/images/tiger.jpg", height: 1cm, width: 80%)) B
#image("path/does/not/exist") #image("path/does/not/exist")
--- image-bad-format --- --- image-bad-format ---
// Error: 2-22 unknown image format // Error: 2-37 unknown image format
#image("./image.typ") #image("/assets/plugins/hello.wasm")
--- image-bad-svg --- --- image-bad-svg ---
// Error: 2-33 failed to parse SVG (found closing tag 'g' instead of 'style' in line 4) // Error: 2-33 failed to parse SVG (found closing tag 'g' instead of 'style' in line 4)