From 41c373eff159273e107584e1511badbfbb1d5c50 Mon Sep 17 00:00:00 2001 From: Kamal Ahmad Date: Fri, 14 Mar 2025 18:25:11 +0500 Subject: [PATCH] gpui: Add support for text in SVGs (#26335) Closes #21319 Before: ![image](https://github.com/user-attachments/assets/f75d7d59-75b1-4836-ae3b-6a1f526a5833) After: ![image](https://github.com/user-attachments/assets/5fa28a6d-c417-4777-99f8-2a17edf759a0) Use fontdb to load system fonts and pass it to resvg renderer. This adds a small increase in startup time (around 30ms on my Linux system to traverse fonts on a cold start). In the future once cosmic-text bumps their version of fontdb we could clone the Database from CosmicTextSystem Release Notes: - Added: support for rendering text in SVGs --- Cargo.lock | 113 ++++++++++++++++++++++++++------ crates/gpui/Cargo.toml | 4 +- crates/gpui/src/svg_renderer.rs | 34 +++++++++- 3 files changed, 126 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cba11b8ee2..5b15dec770 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1835,7 +1835,7 @@ dependencies = [ "bitflags 2.8.0", "cexpr", "clang-sys", - "itertools 0.10.5", + "itertools 0.12.1", "lazy_static", "lazycell", "log", @@ -1858,7 +1858,7 @@ dependencies = [ "bitflags 2.8.0", "cexpr", "clang-sys", - "itertools 0.10.5", + "itertools 0.12.1", "log", "prettyplease", "proc-macro2", @@ -3321,6 +3321,15 @@ 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 = "coreaudio-rs" version = "0.11.3" @@ -3358,16 +3367,16 @@ version = "0.11.2" source = "git+https://github.com/pop-os/cosmic-text?rev=542b20c#542b20ca4376a3b5de5fa629db1a4ace44e18e0c" dependencies = [ "bitflags 2.8.0", - "fontdb", + "fontdb 0.18.0", "log", "rangemap", "rayon", "rustc-hash 1.1.0", - "rustybuzz", + "rustybuzz 0.14.1", "self_cell", "swash", "sys-locale", - "ttf-parser", + "ttf-parser 0.21.1", "unicode-bidi", "unicode-linebreak", "unicode-script", @@ -4961,7 +4970,21 @@ dependencies = [ "memmap2", "slotmap", "tinyvec", - "ttf-parser", + "ttf-parser 0.21.1", +] + +[[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 0.25.1", ] [[package]] @@ -7370,7 +7393,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -10565,8 +10588,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" dependencies = [ "bytes 1.10.1", - "heck 0.4.1", - "itertools 0.10.5", + "heck 0.5.0", + "itertools 0.12.1", "log", "multimap 0.10.0", "once_cell", @@ -10599,7 +10622,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.12.1", "proc-macro2", "quote", "syn 2.0.100", @@ -11409,9 +11432,9 @@ dependencies = [ [[package]] name = "resvg" -version = "0.44.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a325d5e8d1cebddd070b13f44cec8071594ab67d1012797c121f27a669b7958" +checksum = "dd43d1c474e9dadf09a8fdf22d713ba668b499b5117b9b9079500224e26b5b29" dependencies = [ "log", "pico-args", @@ -11873,9 +11896,27 @@ dependencies = [ "bytemuck", "libm", "smallvec", - "ttf-parser", - "unicode-bidi-mirroring", - "unicode-ccc", + "ttf-parser 0.21.1", + "unicode-bidi-mirroring 0.2.0", + "unicode-ccc 0.2.0", + "unicode-properties", + "unicode-script", +] + +[[package]] +name = "rustybuzz" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" +dependencies = [ + "bitflags 2.8.0", + "bytemuck", + "core_maths", + "log", + "smallvec", + "ttf-parser 0.25.1", + "unicode-bidi-mirroring 0.4.0", + "unicode-ccc 0.4.0", "unicode-properties", "unicode-script", ] @@ -13323,9 +13364,9 @@ checksum = "ce5d813d71d82c4cbc1742135004e4a79fd870214c155443451c139c9470a0aa" [[package]] name = "svgtypes" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "794de53cc48eaabeed0ab6a3404a65f40b3e38c067e4435883a65d2aa4ca000e" +checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc" dependencies = [ "kurbo", "siphasher 1.0.1", @@ -14657,6 +14698,15 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" +[[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.20.1" @@ -14804,12 +14854,24 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86" +[[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.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" +[[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.14" @@ -14849,6 +14911,12 @@ 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" @@ -14899,23 +14967,28 @@ checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] name = "usvg" -version = "0.44.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7447e703d7223b067607655e625e0dbca80822880248937da65966194c4864e6" +checksum = "2ac8e0e3e4696253dc06167990b3fe9a2668ab66270adf949a464db4088cb354" dependencies = [ "base64 0.22.1", "data-url", "flate2", + "fontdb 0.23.0", "imagesize", "kurbo", "log", "pico-args", "roxmltree", + "rustybuzz 0.20.1", "simplecss", "siphasher 1.0.1", "strict-num", "svgtypes", "tiny-skia-path", + "unicode-bidi", + "unicode-script", + "unicode-vo", "xmlwriter", ] @@ -15976,7 +16049,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 339799102c..b8cb8aa9a1 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -98,8 +98,8 @@ profiling.workspace = true rand = { optional = true, workspace = true } raw-window-handle = "0.6" refineable.workspace = true -resvg = { version = "0.44.0", default-features = false } -usvg = { version = "0.44.0", default-features = false } +resvg = { version = "0.45.0", default-features = false, features = ["text", "system-fonts", "memmap-fonts"] } +usvg = { version = "0.45.0", default-features = false } schemars.workspace = true seahash = "4.1" semantic_version.workspace = true diff --git a/crates/gpui/src/svg_renderer.rs b/crates/gpui/src/svg_renderer.rs index 306f8ed651..f572d07ae7 100644 --- a/crates/gpui/src/svg_renderer.rs +++ b/crates/gpui/src/svg_renderer.rs @@ -1,7 +1,10 @@ use crate::{AssetSource, DevicePixels, IsZero, Result, SharedString, Size}; use anyhow::anyhow; use resvg::tiny_skia::Pixmap; -use std::{hash::Hash, sync::Arc}; +use std::{ + hash::Hash, + sync::{Arc, LazyLock}, +}; /// When rendering SVGs, we render them at twice the size to get a higher-quality result. pub const SMOOTH_SVG_SCALE_FACTOR: f32 = 2.; @@ -15,6 +18,7 @@ pub(crate) struct RenderSvgParams { #[derive(Clone)] pub struct SvgRenderer { asset_source: Arc, + usvg_options: Arc>, } pub enum SvgSize { @@ -24,7 +28,31 @@ pub enum SvgSize { impl SvgRenderer { pub fn new(asset_source: Arc) -> Self { - Self { asset_source } + let font_db = LazyLock::new(|| { + let mut db = usvg::fontdb::Database::new(); + db.load_system_fonts(); + Arc::new(db) + }); + let default_font_resolver = usvg::FontResolver::default_font_selector(); + let font_resolver = Box::new( + move |font: &usvg::Font, db: &mut Arc| { + if db.is_empty() { + *db = font_db.clone(); + } + default_font_resolver(font, db) + }, + ); + let options = usvg::Options { + font_resolver: usvg::FontResolver { + select_font: font_resolver, + select_fallback: usvg::FontResolver::default_fallback_selector(), + }, + ..Default::default() + }; + Self { + asset_source, + usvg_options: Arc::new(options), + } } pub(crate) fn render(&self, params: &RenderSvgParams) -> Result>> { @@ -49,7 +77,7 @@ impl SvgRenderer { } pub fn render_pixmap(&self, bytes: &[u8], size: SvgSize) -> Result { - let tree = usvg::Tree::from_data(bytes, &usvg::Options::default())?; + let tree = usvg::Tree::from_data(bytes, &self.usvg_options)?; let size = match size { SvgSize::Size(size) => size,