From c89bbf640f17be10b6401ab7572e2504361c2355 Mon Sep 17 00:00:00 2001 From: "Calum H. (IMB11)" Date: Mon, 27 Jul 2026 18:30:49 +0100 Subject: [PATCH] fix: yeet svg --- Cargo.lock | 311 +-------------------- Cargo.toml | 1 - apps/labrinth/src/queue/email/templates.rs | 16 +- packages/app-lib/Cargo.toml | 1 - packages/app-lib/src/api/instance/icon.rs | 167 +++++------ 5 files changed, 100 insertions(+), 396 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aeff746ba9..b3eefef689 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -503,12 +503,6 @@ dependencies = [ "uuid 1.23.3", ] -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - [[package]] name = "arrayvec" version = "0.7.6" @@ -2445,15 +2439,6 @@ dependencies = [ "libc", ] -[[package]] -name = "core_maths" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30" -dependencies = [ - "libm", -] - [[package]] name = "cpufeatures" version = "0.2.17" @@ -3477,15 +3462,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "euclid" -version = "0.22.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06" -dependencies = [ - "num-traits", -] - [[package]] name = "event-listener" version = "2.5.3" @@ -3654,12 +3630,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "float-cmp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" - [[package]] name = "flume" version = "0.11.1" @@ -3689,29 +3659,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" -[[package]] -name = "fontconfig-parser" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" -dependencies = [ - "roxmltree 0.20.0", -] - -[[package]] -name = "fontdb" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" -dependencies = [ - "fontconfig-parser", - "log", - "memmap2", - "slotmap", - "tinyvec", - "ttf-parser", -] - [[package]] name = "foreign-types" version = "0.3.2" @@ -4136,16 +4083,6 @@ dependencies = [ "weezl", ] -[[package]] -name = "gif" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" -dependencies = [ - "color_quant", - "weezl", -] - [[package]] name = "gimli" version = "0.32.3" @@ -5055,7 +4992,7 @@ dependencies = [ "byteorder-lite", "color_quant", "exr", - "gif 0.13.3", + "gif", "image-webp", "moxcms", "num-traits", @@ -5065,8 +5002,8 @@ dependencies = [ "rayon", "rgb", "tiff", - "zune-core 0.4.12", - "zune-jpeg 0.4.21", + "zune-core", + "zune-jpeg", ] [[package]] @@ -5079,12 +5016,6 @@ dependencies = [ "quick-error", ] -[[package]] -name = "imagesize" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09e54e57b4c48b40f7aec75635392b12b3421fa26fe8b4332e63138ed278459c" - [[package]] name = "imgref" version = "1.12.0" @@ -5510,18 +5441,6 @@ dependencies = [ "selectors", ] -[[package]] -name = "kurbo" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b60dfc32f652b926df6192e55525b16d186c69d47876c3ead4da5cc9f8450e2" -dependencies = [ - "arrayvec", - "euclid", - "polycool", - "smallvec", -] - [[package]] name = "labrinth" version = "2.7.0" @@ -6084,15 +6003,6 @@ version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" -[[package]] -name = "memmap2" -version = "0.9.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0" -dependencies = [ - "libc", -] - [[package]] name = "memoffset" version = "0.9.1" @@ -7522,12 +7432,6 @@ dependencies = [ "siphasher 1.0.1", ] -[[package]] -name = "pico-args" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" - [[package]] name = "pin-project" version = "1.1.10" @@ -7651,15 +7555,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "polycool" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50596ddc09eb5ad5f75cacd40209568e66df71baf86e1499a0e99c4cff12a5a6" -dependencies = [ - "arrayvec", -] - [[package]] name = "portable-atomic" version = "1.11.1" @@ -8685,23 +8580,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "194d8e591e405d1eecf28819740abed6d719d1a2db87fc0bcdedee9a26d55560" -[[package]] -name = "resvg" -version = "0.47.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be183ad6a216aa96f33e4c8033b0988b8b3ea6fd2359d19af5bac4643fd8e81" -dependencies = [ - "gif 0.14.2", - "image-webp", - "log", - "pico-args", - "rgb", - "svgtypes", - "tiny-skia", - "usvg", - "zune-jpeg 0.5.15", -] - [[package]] name = "rfc6979" version = "0.4.0" @@ -8789,21 +8667,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "roxmltree" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" - -[[package]] -name = "roxmltree" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1964b10c76125c36f8afe190065a4bf9a87bf324842c05701330bba9f1cacbb" -dependencies = [ - "memchr", -] - [[package]] name = "rsa" version = "0.9.8" @@ -9143,24 +9006,6 @@ dependencies = [ "rust_decimal_macros", ] -[[package]] -name = "rustybuzz" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" -dependencies = [ - "bitflags 2.9.4", - "bytemuck", - "core_maths", - "log", - "smallvec", - "ttf-parser", - "unicode-bidi-mirroring", - "unicode-ccc", - "unicode-properties", - "unicode-script", -] - [[package]] name = "rxml" version = "0.11.1" @@ -9921,15 +9766,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" -[[package]] -name = "simplecss" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c" -dependencies = [ - "log", -] - [[package]] name = "siphasher" version = "0.3.11" @@ -9948,15 +9784,6 @@ version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" -[[package]] -name = "slotmap" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" -dependencies = [ - "version_check", -] - [[package]] name = "smallvec" version = "1.15.1" @@ -10349,15 +10176,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29fdc163db75f7b5ffa3daf0c5a7136fb0d4b2f35523cd1769da05e034159feb" -[[package]] -name = "strict-num" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" -dependencies = [ - "float-cmp", -] - [[package]] name = "string_cache" version = "0.8.9" @@ -10480,16 +10298,6 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" -[[package]] -name = "svgtypes" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "695b5790b3131dafa99b3bbfd25a216edb3d216dad9ca208d4657bfb8f2abc3d" -dependencies = [ - "kurbo", - "siphasher 1.0.1", -] - [[package]] name = "swift-rs" version = "1.0.7" @@ -11229,7 +11037,6 @@ dependencies = [ "rand 0.8.5", "regex", "reqwest 0.12.24", - "resvg", "rgb", "serde", "serde_ini", @@ -11373,7 +11180,7 @@ dependencies = [ "half 2.7.0", "quick-error", "weezl", - "zune-jpeg 0.4.21", + "zune-jpeg", ] [[package]] @@ -11447,32 +11254,6 @@ dependencies = [ "crunchy", ] -[[package]] -name = "tiny-skia" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47ffee5eaaf5527f630fb0e356b90ebdec84d5d18d937c5e440350f88c5a91ea" -dependencies = [ - "arrayref", - "arrayvec", - "bytemuck", - "cfg-if", - "log", - "png 0.18.0", - "tiny-skia-path", -] - -[[package]] -name = "tiny-skia-path" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca365c3faccca67d06593c5980fa6c57687de727a03131735bb85f01fdeeb9" -dependencies = [ - "arrayref", - "bytemuck", - "strict-num", -] - [[package]] name = "tinystr" version = "0.8.1" @@ -11956,15 +11737,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "ttf-parser" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" -dependencies = [ - "core_maths", -] - [[package]] name = "tungstenite" version = "0.27.0" @@ -12085,18 +11857,6 @@ version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" -[[package]] -name = "unicode-bidi-mirroring" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfa6e8c60bb66d49db113e0125ee8711b7647b5579dc7f5f19c42357ed039fe" - -[[package]] -name = "unicode-ccc" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e" - [[package]] name = "unicode-ident" version = "1.0.19" @@ -12118,24 +11878,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" -[[package]] -name = "unicode-script" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" - [[package]] name = "unicode-segmentation" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" -[[package]] -name = "unicode-vo" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" - [[package]] name = "unicode-width" version = "0.1.14" @@ -12225,34 +11973,6 @@ dependencies = [ "url", ] -[[package]] -name = "usvg" -version = "0.47.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d46cf96c5f498d36b7a9693bc6a7075c0bb9303189d61b2249b0dc3d309c07de" -dependencies = [ - "base64 0.22.1", - "data-url", - "flate2", - "fontdb", - "imagesize", - "kurbo", - "log", - "pico-args", - "roxmltree 0.21.1", - "rustybuzz", - "simplecss", - "siphasher 1.0.1", - "strict-num", - "svgtypes", - "tiny-skia-path", - "ttf-parser", - "unicode-bidi", - "unicode-script", - "unicode-vo", - "xmlwriter", -] - [[package]] name = "utf-8" version = "0.7.6" @@ -13715,12 +13435,6 @@ version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" -[[package]] -name = "xmlwriter" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" - [[package]] name = "xredis" version = "0.1.0" @@ -14036,12 +13750,6 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" -[[package]] -name = "zune-core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" - [[package]] name = "zune-inflate" version = "0.2.54" @@ -14057,16 +13765,7 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" dependencies = [ - "zune-core 0.4.12", -] - -[[package]] -name = "zune-jpeg" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" -dependencies = [ - "zune-core 0.5.1", + "zune-core", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 2c2c57050a..1529ce8910 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -159,7 +159,6 @@ rdkafka = { version = "0.36.2", features = ["cmake-build"] } redis = "1.4.1" regex = "1.12.2" reqwest = { version = "0.12.24", default-features = false } -resvg = "0.47.0" rgb = "0.8.52" rust_decimal = { version = "1.39.0", features = [ "serde-with-float", diff --git a/apps/labrinth/src/queue/email/templates.rs b/apps/labrinth/src/queue/email/templates.rs index 8daf236bf3..1ef37e77fe 100644 --- a/apps/labrinth/src/queue/email/templates.rs +++ b/apps/labrinth/src/queue/email/templates.rs @@ -329,15 +329,19 @@ async fn resolve_report_title( return Ok(title); } - let Some(shared_instance_id) = DBReport::get(report_id, &mut *exec) - .await? - .and_then(|report| report.shared_instance_id) - else { + let Some(report) = DBReport::get(report_id, &mut *exec).await? else { + return Ok(title); + }; + let Some(shared_instance_id) = report.shared_instance_id else { return Ok(title); }; let instance_id = to_base62(shared_instance_id.0 as u64); - let fallback = format!("shared instance {instance_id}"); + let with_version = |name: String| match report.shared_instance_version_id { + Some(version) => format!("{name} (version {version})"), + None => name, + }; + let fallback = with_version(format!("shared instance {instance_id}")); let response = HTTP_CLIENT .get(format!( "{}/v1/instances/{instance_id}", @@ -362,7 +366,7 @@ async fn resolve_report_title( }; match response.json::().await { - Ok(instance) => Ok(instance.name), + Ok(instance) => Ok(with_version(instance.name)), Err(error) => { warn!( %error, diff --git a/packages/app-lib/Cargo.toml b/packages/app-lib/Cargo.toml index a299298858..b3d05f8885 100644 --- a/packages/app-lib/Cargo.toml +++ b/packages/app-lib/Cargo.toml @@ -75,7 +75,6 @@ reqwest = { workspace = true, features = [ "rustls-tls-webpki-roots", "stream", ] } -resvg = { workspace = true } rgb = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_ini = { workspace = true } diff --git a/packages/app-lib/src/api/instance/icon.rs b/packages/app-lib/src/api/instance/icon.rs index fc06f1c8f3..1e79dbd49b 100644 --- a/packages/app-lib/src/api/instance/icon.rs +++ b/packages/app-lib/src/api/instance/icon.rs @@ -13,7 +13,12 @@ const INSTANCE_ICON_MAX_BYTES: usize = 4 * 1024 * 1024; const INSTANCE_ICON_MAX_DIMENSION: u32 = 512; const INSTANCE_ICON_MAX_SOURCE_DIMENSION: u32 = 8_192; const INSTANCE_ICON_MAX_DECODE_BYTES: u64 = 64 * 1024 * 1024; -const INSTANCE_ICON_MAX_SVG_SOURCE_BYTES: u64 = 16 * 1024 * 1024; + +enum LegacyIconAction { + Keep, + Normalize, + Remove, +} pub async fn edit_icon( instance_id: &str, @@ -40,10 +45,10 @@ pub(crate) async fn cache_icon( ) -> crate::Result { let bytes = tokio::task::spawn_blocking(move || { if looks_like_svg(&bytes) { - normalize_svg(&bytes, None) - } else { - normalize_raster(Cursor::new(bytes)) + return Err(svg_not_supported_error()); } + + normalize_raster(Cursor::new(bytes)) }) .await??; @@ -56,17 +61,27 @@ pub(crate) async fn cache_icon_from_path( ) -> crate::Result { let icon_path = icon_path.to_path_buf(); let bytes = tokio::task::spawn_blocking(move || { - if has_svg_extension(&icon_path) { - normalize_svg_from_path(&icon_path) - } else { - let file = StdFile::open(&icon_path).map_err(|error| { + let file = StdFile::open(&icon_path).map_err(|error| { + crate::ErrorKind::InputError(format!( + "Could not open instance icon {}: {error}", + icon_path.display() + )) + })?; + let mut reader = BufReader::new(file); + let looks_like_svg = { + let bytes = reader.fill_buf().map_err(|error| { crate::ErrorKind::InputError(format!( - "Could not open instance icon {}: {error}", + "Could not inspect instance icon {}: {error}", icon_path.display() )) })?; - normalize_raster(BufReader::new(file)) + looks_like_svg(bytes) + }; + if has_svg_extension(&icon_path) || looks_like_svg { + return Err(svg_not_supported_error()); } + + normalize_raster(reader) }) .await??; @@ -81,8 +96,8 @@ pub(crate) async fn migrate_legacy_icons() -> crate::Result<()> { let Some(icon_path) = instance.icon_path.as_deref() else { continue; }; - let metadata = match io::metadata(icon_path).await { - Ok(metadata) => metadata, + let action = match inspect_legacy_icon(Path::new(icon_path)) { + Ok(action) => action, Err(error) => { tracing::warn!( instance_id = instance.id, @@ -93,19 +108,33 @@ pub(crate) async fn migrate_legacy_icons() -> crate::Result<()> { continue; } }; - if metadata.len() <= INSTANCE_ICON_MAX_BYTES as u64 { - continue; - } - if let Err(error) = - edit_icon(&instance.id, Some(Path::new(icon_path))).await - { - tracing::warn!( - instance_id = instance.id, - icon_path, - error = %error, - "Failed to normalize oversized legacy instance icon" - ); + match action { + LegacyIconAction::Keep => {} + LegacyIconAction::Normalize => { + if let Err(error) = + edit_icon(&instance.id, Some(Path::new(icon_path))).await + { + tracing::warn!( + instance_id = instance.id, + icon_path, + error = %error, + "Failed to normalize legacy instance icon" + ); + } + } + LegacyIconAction::Remove => { + if let Err(error) = + apply_instance_icon(&instance.id, None, &state).await + { + tracing::warn!( + instance_id = instance.id, + icon_path, + error = %error, + "Failed to remove legacy SVG instance icon" + ); + } + } } } @@ -156,7 +185,7 @@ async fn write_cached_icon( bytes: Bytes, state: &State, ) -> crate::Result { - if bytes.len() > INSTANCE_ICON_MAX_BYTES { + if bytes.len() >= INSTANCE_ICON_MAX_BYTES { return Err(icon_too_large_error()); } @@ -216,79 +245,42 @@ where validate_normalized_icon(normalized.into_inner()) } -fn normalize_svg_from_path(icon_path: &Path) -> crate::Result { +fn inspect_legacy_icon(icon_path: &Path) -> crate::Result { let metadata = std::fs::metadata(icon_path).map_err(|error| { crate::ErrorKind::InputError(format!( "Could not inspect instance icon {}: {error}", icon_path.display() )) })?; - if metadata.len() > INSTANCE_ICON_MAX_SVG_SOURCE_BYTES { - return Err(crate::ErrorKind::InputError(format!( - "SVG instance icons cannot exceed {INSTANCE_ICON_MAX_SVG_SOURCE_BYTES} bytes before normalization" - )) - .into()); - } - - let bytes = std::fs::read(icon_path).map_err(|error| { + let file = StdFile::open(icon_path).map_err(|error| { crate::ErrorKind::InputError(format!( - "Could not read instance icon {}: {error}", + "Could not open instance icon {}: {error}", icon_path.display() )) })?; - normalize_svg(&bytes, icon_path.parent()) -} - -fn normalize_svg( - bytes: &[u8], - resources_dir: Option<&Path>, -) -> crate::Result { - if bytes.len() as u64 > INSTANCE_ICON_MAX_SVG_SOURCE_BYTES { - return Err(crate::ErrorKind::InputError(format!( - "SVG instance icons cannot exceed {INSTANCE_ICON_MAX_SVG_SOURCE_BYTES} bytes before normalization" - )) - .into()); - } - - let mut options = resvg::usvg::Options { - resources_dir: resources_dir.map(Path::to_path_buf), - ..Default::default() - }; - options.fontdb_mut().load_system_fonts(); - let tree = - resvg::usvg::Tree::from_data(bytes, &options).map_err(|error| { - crate::ErrorKind::InputError(format!( - "Could not decode SVG instance icon: {error}" - )) - })?; - let size = tree.size(); - let scale = (INSTANCE_ICON_MAX_DIMENSION as f32 - / size.width().max(size.height())) - .min(1.0); - let width = (size.width() * scale).ceil().max(1.0) as u32; - let height = (size.height() * scale).ceil().max(1.0) as u32; - let mut pixmap = - resvg::tiny_skia::Pixmap::new(width, height).ok_or_else(|| { - crate::ErrorKind::InputError( - "Could not allocate SVG instance icon output".to_string(), - ) - })?; - resvg::render( - &tree, - resvg::tiny_skia::Transform::from_scale(scale, scale), - &mut pixmap.as_mut(), - ); - let normalized = pixmap.encode_png().map_err(|error| { + let mut reader = BufReader::new(file); + let bytes = reader.fill_buf().map_err(|error| { crate::ErrorKind::InputError(format!( - "Could not encode SVG instance icon as PNG: {error}" + "Could not inspect instance icon {}: {error}", + icon_path.display() )) })?; - validate_normalized_icon(normalized) + if has_svg_extension(icon_path) || looks_like_svg(bytes) { + return Ok(LegacyIconAction::Remove); + } + + if metadata.len() < INSTANCE_ICON_MAX_BYTES as u64 + && image::guess_format(bytes).ok() == Some(image::ImageFormat::Png) + { + return Ok(LegacyIconAction::Keep); + } + + Ok(LegacyIconAction::Normalize) } fn validate_normalized_icon(normalized: Vec) -> crate::Result { - if normalized.len() > INSTANCE_ICON_MAX_BYTES { + if normalized.len() >= INSTANCE_ICON_MAX_BYTES { return Err(icon_too_large_error()); } @@ -296,6 +288,10 @@ fn validate_normalized_icon(normalized: Vec) -> crate::Result { } fn looks_like_svg(bytes: &[u8]) -> bool { + if image::guess_format(bytes).is_ok() { + return false; + } + bytes[..bytes.len().min(1_024)] .windows(4) .any(|window| window.eq_ignore_ascii_case(b" bool { fn icon_too_large_error() -> crate::Error { crate::ErrorKind::InputError(format!( - "Instance icons cannot exceed {INSTANCE_ICON_MAX_BYTES} bytes" + "Instance icons must be smaller than {INSTANCE_ICON_MAX_BYTES} bytes" )) .into() } + +fn svg_not_supported_error() -> crate::Error { + crate::ErrorKind::InputError( + "SVG instance icons are not supported".to_string(), + ) + .into() +}