diff --git a/Cargo.lock b/Cargo.lock index 9fda6755a..ca56b5ecc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -103,8 +103,8 @@ dependencies = [ "accesskit_consumer 0.35.0", "hashbrown 0.16.1", "static_assertions", - "windows 0.62.2", - "windows-core 0.62.2", + "windows", + "windows-core", ] [[package]] @@ -245,7 +245,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f2a1bb052857d5dd49572219344a7332b31b76405648eabac5bc68978251bcd" dependencies = [ "android-properties", - "bitflags 2.12.1", + "bitflags 2.11.1", "cc", "jni", "libc", @@ -595,9 +595,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.5.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "av-scenechange" @@ -676,7 +676,7 @@ version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "cexpr", "clang-sys", "itertools 0.13.0", @@ -686,7 +686,7 @@ dependencies = [ "quote", "regex", "rustc-hash 2.1.2", - "shlex 1.3.0", + "shlex", "syn", ] @@ -696,7 +696,7 @@ version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "cexpr", "clang-sys", "itertools 0.13.0", @@ -706,7 +706,7 @@ dependencies = [ "quote", "regex", "rustc-hash 2.1.2", - "shlex 1.3.0", + "shlex", "syn", ] @@ -739,9 +739,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.12.1" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84d7ced0ae9557296835c32bf1b1e02b44c746701f898460fb000d7eaa84f00a" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" [[package]] name = "bitstream-io" @@ -815,15 +815,15 @@ dependencies = [ [[package]] name = "built" -version = "0.8.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c0e531d93d39c34eef561e929e8a7f86d77a5af08aac4f6d6e39976c51858e9" +checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64" [[package]] name = "bumpalo" -version = "3.20.3" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "bytecount" @@ -875,7 +875,7 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cc8d9aa793480744cd9a0524fef1a2e197d9eaa0f739cde19d16aba530dcb95" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "cairo-sys-rs", "glib", "libc", @@ -898,7 +898,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "log", "polling", "rustix 0.38.44", @@ -912,7 +912,7 @@ version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dbf9978365bac10f54d1d4b04f7ce4427e51f71d61f2fe15e3fed5166474df7" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "polling", "rustix 1.1.4", "slab", @@ -951,14 +951,14 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.63" +version = "1.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" dependencies = [ "find-msvc-tools", "jobserver", "libc", - "shlex 2.0.1", + "shlex", ] [[package]] @@ -972,9 +972,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.20.8" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb693542bcafa528e198be0ebd9d3632ca5b7c93dbe7237460e199910835997c" +checksum = "3c6b04e07d8080154ed4ac03546d9a2b303cc2fe1901ba0b35b301516e289368" dependencies = [ "smallvec", "target-lexicon", @@ -1164,12 +1164,6 @@ dependencies = [ "log", ] -[[package]] -name = "color" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7f99105610438d4b3ee7ae8e453c2990e325c806a14d71e8ea937d584c5289" - [[package]] name = "color" version = "0.3.3" @@ -1675,15 +1669,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "objc2 0.6.4", ] [[package]] name = "displaydoc" -version = "0.2.6" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", @@ -1726,7 +1720,7 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80bc8c5c6c2941f70a55c15f8d9f00f9710ebda3ffda98075f996a0e6c92756f" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "bytemuck", "drm-ffi", "drm-fourcc", @@ -1838,7 +1832,7 @@ checksum = "42112be0ae157289312b92b3dfaf20e911b5a3c4c65d4aab0e7c47fbc0ce16e3" dependencies = [ "accesskit", "ahash", - "bitflags 2.12.1", + "bitflags 2.11.1", "emath", "epaint", "log", @@ -1909,9 +1903,9 @@ dependencies = [ [[package]] name = "either" -version = "1.16.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "emath" @@ -1998,7 +1992,7 @@ dependencies = [ "ecolor", "emath", "epaint_default_fonts", - "font-types 0.11.3", + "font-types", "log", "nohash-hasher", "parking_lot", @@ -2456,7 +2450,7 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c7e611d49285d4c4b2e1727b72cf05353558885cc5252f93707b845dfcaf3d3" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "byteorder", "core-foundation 0.9.4", "core-graphics", @@ -2475,15 +2469,6 @@ dependencies = [ "yeslogic-fontconfig-sys", ] -[[package]] -name = "font-types" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa6a5e5a77b5f3f7f9e32879f484aa5b3632ddfbe568a16266c904a6f32cdaf" -dependencies = [ - "bytemuck", -] - [[package]] name = "font-types" version = "0.11.3" @@ -2493,16 +2478,6 @@ dependencies = [ "bytemuck", ] -[[package]] -name = "fontconfig-cache-parser" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7f8afb20c8069fd676d27b214559a337cc619a605d25a87baa90b49a06f3b18" -dependencies = [ - "bytemuck", - "thiserror 1.0.69", -] - [[package]] name = "fontconfig-parser" version = "0.5.8" @@ -2528,31 +2503,31 @@ dependencies = [ [[package]] name = "fontique" -version = "0.3.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b5fcb214137f01bc842c4fd633236255c51f8a24c6d3923eb8361c6d0940737" +checksum = "7c20b425addb8661e97fe1d51c4d8bcec3ec29ed6ad0db983976a7521276b8f7" dependencies = [ - "bytemuck", - "fontconfig-cache-parser", - "hashbrown 0.15.5", - "icu_locid", + "hashbrown 0.17.1", + "linebender_resource_handle", "memmap2", + "objc2 0.6.4", "objc2-core-foundation", "objc2-core-text", "objc2-foundation 0.3.2", - "peniko 0.3.2", - "read-fonts 0.25.3", - "roxmltree 0.20.0", + "parlance", + "read-fonts 0.39.2", + "roxmltree 0.21.1", "smallvec", - "windows 0.58.0", - "windows-core 0.58.0", + "windows", + "windows-core", + "yeslogic-fontconfig-sys", ] [[package]] name = "fontique" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c20b425addb8661e97fe1d51c4d8bcec3ec29ed6ad0db983976a7521276b8f7" +checksum = "274fa4f0f0a926ae182c7c076c078cce8a38471d15e61a102a02cac984be9813" dependencies = [ "hashbrown 0.17.1", "linebender_resource_handle", @@ -2565,8 +2540,8 @@ dependencies = [ "read-fonts 0.39.2", "roxmltree 0.21.1", "smallvec", - "windows 0.62.2", - "windows-core 0.62.2", + "windows", + "windows-core", "yeslogic-fontconfig-sys", ] @@ -2612,7 +2587,7 @@ version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5442dee36ca09604133580dc0553780e867936bb3cbef3275859e889026d2b17" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "freetype-sys", "libc", ] @@ -2995,7 +2970,7 @@ version = "0.22.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c207e04e51605dcf7b2924c41591b3a10e1438eaac5bcf448fb91f325381104a" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "futures-channel", "futures-core", "futures-executor", @@ -3056,7 +3031,7 @@ version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12124de845cacfebedff80e877bb37b5b75c34c5a4c89e47e1cdd67fb6041325" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "cfg_aliases", "cgl", "dispatch2", @@ -3229,7 +3204,7 @@ dependencies = [ "anyhow", "async-channel", "async-trait", - "bitflags 2.12.1", + "bitflags 2.11.1", "bytes", "cairo-rs", "chrono", @@ -3301,13 +3276,14 @@ dependencies = [ "font-kit", "freetype-rs", "gosub_interface", + "gosub_shared", "gtk4", "image", "lazy_static", "log", "pangocairo", "parking_lot", - "parley 0.9.0", + "parley", "pollster", "prettytable", "rand 0.10.1", @@ -3396,10 +3372,11 @@ dependencies = [ "cow-utils", "csscolorparser", "file_type", - "fontique 0.9.0", + "fontique 0.10.0", "gdk4-wayland", "gdk4-x11", "gosub_css3", + "gosub_fontmanager", "gosub_html5", "gosub_interface", "gosub_net", @@ -3408,7 +3385,7 @@ dependencies = [ "image", "log", "parking_lot", - "parley 0.9.0", + "parley", "rand 0.10.1", "regex", "resvg", @@ -3457,11 +3434,12 @@ name = "gosub_renderer_vello" version = "0.1.0" dependencies = [ "anyhow", + "gosub_fontmanager", "gosub_render_pipeline", "gosub_shared", "log", "parking_lot", - "parley 0.3.0", + "parley", "pollster", "resvg", "skia-safe", @@ -3514,7 +3492,7 @@ dependencies = [ "gosub_shared", "log", "parking_lot", - "parley 0.9.0", + "parley", "regex", "taffy", ] @@ -3576,7 +3554,7 @@ dependencies = [ "log", "presser", "thiserror 2.0.18", - "windows 0.62.2", + "windows", ] [[package]] @@ -3585,7 +3563,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "gpu-descriptor-types", "hashbrown 0.15.5", ] @@ -3596,7 +3574,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", ] [[package]] @@ -3763,11 +3741,11 @@ dependencies = [ [[package]] name = "harfrust" -version = "0.6.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551ed25397e4b444e89686602877d5cf3a7f6e3d548dcac37a8357d1e195f4df" +checksum = "a9ce5e848d21ba97a324266e41c70e0eb5e2577a610c1fbd546e15096f2e8e37" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "bytemuck", "core_maths", "read-fonts 0.39.2", @@ -3789,8 +3767,6 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "allocator-api2", - "equivalent", "foldhash 0.1.5", ] @@ -3927,9 +3903,9 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" -version = "1.10.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" dependencies = [ "atomic-waker", "bytes", @@ -3998,7 +3974,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.2", + "windows-core", ] [[package]] @@ -4035,7 +4011,7 @@ dependencies = [ "icu_locale_data", "icu_provider", "potential_utf", - "tinystr 0.8.3", + "tinystr", "zerovec", ] @@ -4046,10 +4022,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "displaydoc", - "litemap 0.8.2", + "litemap", "serde", - "tinystr 0.8.3", - "writeable 0.6.3", + "tinystr", + "writeable", "zerovec", ] @@ -4059,18 +4035,6 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5fdcc9ac77c6d74ff5cf6e65ef3181d6af32003b16fce3a77fb451d2f695993" -[[package]] -name = "icu_locid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" -dependencies = [ - "displaydoc", - "litemap 0.7.5", - "tinystr 0.7.6", - "writeable 0.5.5", -] - [[package]] name = "icu_normalizer" version = "2.2.0" @@ -4121,7 +4085,7 @@ dependencies = [ "icu_locale_core", "serde", "stable_deref_trait", - "writeable 0.6.3", + "writeable", "yoke", "zerofrom", "zerotrie", @@ -4316,9 +4280,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jiff" -version = "0.2.28" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4603d3033e49e2b0e31229fcab20a5d40089c607d975cd9c80551dc69eed9102" +checksum = "f00b5dbd620d61dfdcb6007c9c1f6054ebd75319f163d886a9055cec1155073d" dependencies = [ "jiff-static", "log", @@ -4329,9 +4293,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.28" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "782d32378dddf207193ac91cefb848ad41abb58195c95168e1291227a0832b47" +checksum = "e000de030ff8022ea1da3f466fbb0f3a809f5e51ed31f6dd931c35181ad8e6d7" dependencies = [ "proc-macro2", "quote", @@ -4408,9 +4372,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.99" +version = "0.3.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" +checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" dependencies = [ "cfg-if", "futures-util", @@ -4435,17 +4399,6 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" -[[package]] -name = "kurbo" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62" -dependencies = [ - "arrayvec", - "euclid", - "smallvec", -] - [[package]] name = "kurbo" version = "0.13.1" @@ -4484,9 +4437,9 @@ checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libfuzzer-sys" -version = "0.4.13" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9fd2f41a1cba099f79a0b6b6c35656cf7c03351a7bae8ff0f28f25270f929d2" +checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d" dependencies = [ "arbitrary", "cc", @@ -4510,14 +4463,14 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.17" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "libc", "plain", - "redox_syscall 0.8.1", + "redox_syscall 0.7.5", ] [[package]] @@ -4555,12 +4508,6 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" -[[package]] -name = "litemap" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" - [[package]] name = "litemap" version = "0.8.2" @@ -4584,9 +4531,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.32" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "loop9" @@ -4624,9 +4571,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "memmap2" @@ -4695,9 +4642,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "wasi", @@ -4722,7 +4669,7 @@ checksum = "0dd91265cc2454558f659b3b4b9640f0ddb8cc6521277f166b8a8c181c898079" dependencies = [ "arrayvec", "bit-set", - "bitflags 2.12.1", + "bitflags 2.11.1", "cfg-if", "cfg_aliases", "codespan-reporting", @@ -4746,7 +4693,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "jni-sys 0.3.1", "log", "ndk-sys", @@ -4848,9 +4795,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" [[package]] name = "num-derive" @@ -4965,7 +4912,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "block2 0.5.1", "libc", "objc2 0.5.2", @@ -4981,7 +4928,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "objc2 0.6.4", "objc2-core-foundation", "objc2-core-graphics", @@ -4994,7 +4941,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", @@ -5018,7 +4965,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -5030,7 +4977,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "dispatch2", "objc2 0.6.4", ] @@ -5041,7 +4988,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "dispatch2", "objc2 0.6.4", "objc2-core-foundation", @@ -5078,7 +5025,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "objc2-core-foundation", ] @@ -5094,7 +5041,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "block2 0.5.1", "dispatch", "libc", @@ -5107,7 +5054,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "block2 0.6.2", "objc2 0.6.4", "objc2-core-foundation", @@ -5119,7 +5066,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "objc2 0.6.4", "objc2-core-foundation", ] @@ -5142,7 +5089,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -5154,7 +5101,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0125f776a10d00af4152d74616409f0d4a2053a6f57fa5b7d6aa2854ac04794" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "block2 0.6.2", "objc2 0.6.4", "objc2-foundation 0.3.2", @@ -5166,7 +5113,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -5179,7 +5126,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "objc2 0.6.4", "objc2-core-foundation", "objc2-foundation 0.3.2", @@ -5202,7 +5149,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "block2 0.5.1", "objc2 0.5.2", "objc2-cloud-kit", @@ -5223,7 +5170,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "objc2 0.6.4", "objc2-core-foundation", "objc2-foundation 0.3.2", @@ -5246,7 +5193,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", @@ -5291,9 +5238,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "orbclient" -version = "0.3.55" +version = "0.3.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5df339f526ea9a60e371768d50efc2f2508c7203290731565d1f7a6f71d21747" +checksum = "a570f6bca41d29acb2139229a7c873ec99bc9a313bd10804081d89bfac8ff329" dependencies = [ "libc", "libredox", @@ -5422,19 +5369,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b6937eda350acc1a5d05872c3cbf99fe78619c269096e2be3d4a350058639d5" -[[package]] -name = "parley" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d1c2b33b240c246f06cfceac48dc6c96040cb177d2aa5348899982b298b5577" -dependencies = [ - "fontique 0.3.0", - "hashbrown 0.15.5", - "peniko 0.3.2", - "skrifa 0.26.6", - "swash", -] - [[package]] name = "parley" version = "0.9.0" @@ -5495,37 +5429,13 @@ dependencies = [ [[package]] name = "peniko" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1f594c54ccdc9bd177a726885f066bf28d20e17169e31a8a1456217b1316b4" -dependencies = [ - "color 0.2.4", - "kurbo 0.11.3", - "peniko 0.4.1", - "smallvec", -] - -[[package]] -name = "peniko" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b44f9ddd2f480176b34278eb653ec1c8062f3b143a4e16eeff5ffac3334e288" -dependencies = [ - "color 0.3.3", - "kurbo 0.11.3", - "linebender_resource_handle", - "smallvec", -] - -[[package]] -name = "peniko" -version = "0.6.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "839c8299360d2e998bdb106dc0a6cd71dcc5f4df51df1b620361bf50e283cca6" +checksum = "9a2b6aadb221872732e87d465213e9be5af2849b0e8cc5300a8ba98fffa2e00a" dependencies = [ "bytemuck", - "color 0.3.3", - "kurbo 0.13.1", + "color", + "kurbo", "linebender_resource_handle", "smallvec", ] @@ -5670,7 +5580,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "crc32fast", "fdeflate", "flate2", @@ -5740,7 +5650,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" dependencies = [ "serde_core", - "writeable 0.6.3", + "writeable", "zerovec", ] @@ -5795,7 +5705,7 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.25.12+spec-1.1.0", + "toml_edit", ] [[package]] @@ -6151,16 +6061,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "read-fonts" -version = "0.25.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f9e8a4f503e5c8750e4cd3b32a4e090035c46374b305a15c70bad833dca05f" -dependencies = [ - "bytemuck", - "font-types 0.8.4", -] - [[package]] name = "read-fonts" version = "0.37.0" @@ -6168,7 +6068,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b634fabf032fab15307ffd272149b622260f55974d9fad689292a5d33df02e5" dependencies = [ "bytemuck", - "font-types 0.11.3", + "font-types", ] [[package]] @@ -6178,7 +6078,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4ed38b89c2c77ff968c524145ad65fb010f38af5c7a224b53b81d47ac2daa81" dependencies = [ "bytemuck", - "font-types 0.11.3", + "core_maths", + "font-types", ] [[package]] @@ -6196,16 +6097,16 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", ] [[package]] name = "redox_syscall" -version = "0.8.1" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b44b894f2a6e36457d665d1e08c3866add6ed5e70050c1b4ba8a8ddedb02ce7" +checksum = "4666a1a60d8412eab19d94f6d13dcc9cea0a5ef4fdf6a5db306537413c661b1b" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", ] [[package]] @@ -6267,9 +6168,9 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" [[package]] name = "reqwest" -version = "0.13.4" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" +checksum = "62e0021ea2c22aed41653bc7e1419abb2c97e038ff2c33d0e1309e49a97deec0" dependencies = [ "base64", "bytes", @@ -6368,9 +6269,9 @@ dependencies = [ [[package]] name = "rsqlite-vfs" -version = "0.1.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51c9ae4df8a7fba42103df5c621fa3c37eccf3a3c650879e90fc48b11cc192c" +checksum = "a8a1f2315036ef6b1fbacd1972e8ee7688030b0a2121edfc2a6550febd41574d" dependencies = [ "hashbrown 0.16.1", "thiserror 2.0.18", @@ -6393,7 +6294,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0d2b0146dd9661bf67bb107c0bb2a55064d556eeb3fc314151b957f313bcd4e" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -6429,7 +6330,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "errno", "libc", "linux-raw-sys 0.4.15", @@ -6442,7 +6343,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "errno", "libc", "linux-raw-sys 0.12.1", @@ -6465,9 +6366,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" dependencies = [ "openssl-probe", "rustls-pki-types", @@ -6536,7 +6437,7 @@ version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "bytemuck", "core_maths", "log", @@ -6612,7 +6513,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -6696,15 +6597,6 @@ dependencies = [ "syn", ] -[[package]] -name = "serde_spanned" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" -dependencies = [ - "serde", -] - [[package]] name = "serde_spanned" version = "1.1.1" @@ -6740,12 +6632,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "shlex" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" - [[package]] name = "signal-hook-registry" version = "1.4.8" @@ -6816,43 +6702,31 @@ checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" [[package]] name = "skia-bindings" -version = "0.87.0" +version = "0.97.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704242769235d2ffe66a2a0a3002661262fc4af08d32807c362d7b0160ee703c" +checksum = "e6a38f4d460276ea40588c958396a9de0eec808c679009a5b52afdf6ebf34132" dependencies = [ "bindgen 0.72.1", "cc", "flate2", "heck", - "lazy_static", "pkg-config", "regex", "serde_json", "tar", - "toml 0.8.23", + "toml", ] [[package]] name = "skia-safe" -version = "0.87.0" +version = "0.97.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f7d94f3e7537c71ad4cf132eb26e3be8c8a886ed3649c4525c089041fc312b2" +checksum = "1ecfc54fcd40dc5dea15dd2a84f8422cdb0f76c6a51ebb8835ce0b7ef590a21f" dependencies = [ - "bitflags 2.12.1", - "lazy_static", + "bitflags 2.11.1", "skia-bindings", ] -[[package]] -name = "skrifa" -version = "0.26.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cc1aa86c26dbb1b63875a7180aa0819709b33348eb5b1491e4321fae388179d" -dependencies = [ - "bytemuck", - "read-fonts 0.25.3", -] - [[package]] name = "skrifa" version = "0.40.0" @@ -6900,7 +6774,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "calloop 0.13.0", "calloop-wayland-source 0.3.0", "cursor-icon", @@ -6925,7 +6799,7 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "calloop 0.14.4", "calloop-wayland-source 0.4.1", "cursor-icon", @@ -6968,9 +6842,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.4" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", "windows-sys 0.61.2", @@ -7014,14 +6888,14 @@ version = "0.4.0+sdk-1.4.341.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9571ea910ebd84c86af4b3ed27f9dbdc6ad06f17c5f96146b2b671e2976744f" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", ] [[package]] name = "sqlite-wasm-rs" -version = "0.5.5" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc3efc0da82635d7e1ced0053bbbfa8c7ab9645d0bf36ceb4f7127bb85315d75" +checksum = "1b2c760607300407ddeaee518acf28c795661b7108c75421303dbefb237d3a36" dependencies = [ "cc", "js-sys", @@ -7074,7 +6948,7 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "695b5790b3131dafa99b3bbfd25a216edb3d216dad9ca208d4657bfb8f2abc3d" dependencies = [ - "kurbo 0.13.1", + "kurbo", "siphasher", ] @@ -7126,7 +7000,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -7150,7 +7024,7 @@ dependencies = [ "cfg-expr", "heck", "pkg-config", - "toml 1.1.2+spec-1.1.0", + "toml", "version-compare", ] @@ -7168,9 +7042,9 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.46" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" +checksum = "22692a6476a21fa75fdfc11d452fda482af402c008cdbaf3476414e122040973" dependencies = [ "filetime", "libc", @@ -7179,9 +7053,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.13.5" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" +checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" [[package]] name = "tempfile" @@ -7418,15 +7292,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "tinystr" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" -dependencies = [ - "displaydoc", -] - [[package]] name = "tinystr" version = "0.8.3" @@ -7527,18 +7392,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" -dependencies = [ - "serde", - "serde_spanned 0.6.9", - "toml_datetime 0.6.11", - "toml_edit 0.22.27", -] - [[package]] name = "toml" version = "1.1.2+spec-1.1.0" @@ -7547,20 +7400,11 @@ checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" dependencies = [ "indexmap", "serde_core", - "serde_spanned 1.1.1", - "toml_datetime 1.1.1+spec-1.1.0", + "serde_spanned", + "toml_datetime", "toml_parser", "toml_writer", - "winnow 1.0.3", -] - -[[package]] -name = "toml_datetime" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" -dependencies = [ - "serde", + "winnow", ] [[package]] @@ -7574,28 +7418,14 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.27" +version = "0.25.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" dependencies = [ "indexmap", - "serde", - "serde_spanned 0.6.9", - "toml_datetime 0.6.11", - "toml_write", - "winnow 0.7.15", -] - -[[package]] -name = "toml_edit" -version = "0.25.12+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" -dependencies = [ - "indexmap", - "toml_datetime 1.1.1+spec-1.1.0", + "toml_datetime", "toml_parser", - "winnow 1.0.3", + "winnow", ] [[package]] @@ -7604,15 +7434,9 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.3", + "winnow", ] -[[package]] -name = "toml_write" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" - [[package]] name = "toml_writer" version = "1.1.1+spec-1.1.0" @@ -7636,12 +7460,12 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.11" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +checksum = "68d6fdd9f81c2819c9a8b0e0cd91660e7746a8e6ea2ba7c6b2b057985f6bcb51" dependencies = [ "async-compression", - "bitflags 2.12.1", + "bitflags 2.11.1", "bytes", "futures-core", "futures-util", @@ -7770,9 +7594,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.20.1" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" [[package]] name = "uds_windows" @@ -7832,9 +7656,9 @@ checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" [[package]] name = "unicode-segmentation" -version = "1.13.3" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" [[package]] name = "unicode-vo" @@ -7905,7 +7729,7 @@ dependencies = [ "flate2", "fontdb", "imagesize", - "kurbo 0.13.1", + "kurbo", "log", "pico-args", "roxmltree 0.21.1", @@ -7936,9 +7760,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.2" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d258b83ceec21034727ecee8c382cfa6c3e133699b0742c64571814fb420c9f7" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" dependencies = [ "getrandom 0.4.2", "js-sys", @@ -7954,7 +7778,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "278d906d3513fce0be40e1b28eb4c482f44e9d3bf7c1be880441e706bebf5e43" dependencies = [ "bindgen 0.71.1", - "bitflags 2.12.1", + "bitflags 2.11.1", "fslock", "gzip-header", "home", @@ -7994,7 +7818,7 @@ dependencies = [ "bytemuck", "futures-intrusive", "log", - "peniko 0.6.1", + "peniko", "png", "skrifa 0.42.1", "static_assertions", @@ -8014,7 +7838,7 @@ dependencies = [ "fearless_simd", "hashbrown 0.16.1", "log", - "peniko 0.6.1", + "peniko", "skrifa 0.40.0", "smallvec", ] @@ -8037,7 +7861,7 @@ source = "git+https://github.com/linebender/vello?rev=1b24e77ba4e01d12f5db86a9b1 dependencies = [ "bytemuck", "guillotiere", - "peniko 0.6.1", + "peniko", "skrifa 0.42.1", "smallvec", ] @@ -8111,9 +7935,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.122" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" +checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" dependencies = [ "cfg-if", "once_cell", @@ -8124,9 +7948,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.72" +version = "0.4.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" +checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8" dependencies = [ "js-sys", "wasm-bindgen", @@ -8134,9 +7958,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.122" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" +checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -8144,9 +7968,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.122" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" +checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" dependencies = [ "bumpalo", "proc-macro2", @@ -8157,18 +7981,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.122" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" +checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" dependencies = [ "unicode-ident", ] [[package]] name = "wasm-bindgen-test" -version = "0.3.72" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74fde991ccdc895cb7fbaa14b137d62af74d9011be67b71c694bfc40edd3119c" +checksum = "af5ec93229ad9ccd0a545a516dec76dc276613f278f6a91aa6b463d5b33d42d0" dependencies = [ "async-trait", "cast", @@ -8188,9 +8012,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.72" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e925354648d2a4d1bf205412e36d520a800280622eef4719678d268e5d40e978" +checksum = "3c81b9fef827e575e0e54431736d1baa0d700315d8c62cfef1f61fa3aad0cbeb" dependencies = [ "proc-macro2", "quote", @@ -8199,9 +8023,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-shared" -version = "0.2.122" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684365b586a9a6256c1cc3544eee8680de48d6041142f581776ec7b139622ae9" +checksum = "4f4d8ae7ad5440360e9799dfd42857d126454a88441ddf72d288ef83fa47f527" [[package]] name = "wasm-encoder" @@ -8244,7 +8068,7 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "hashbrown 0.15.5", "indexmap", "semver", @@ -8270,7 +8094,7 @@ version = "0.31.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "rustix 1.1.4", "wayland-backend", "wayland-scanner", @@ -8282,7 +8106,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "cursor-icon", "wayland-backend", ] @@ -8304,7 +8128,7 @@ version = "0.32.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "wayland-backend", "wayland-client", "wayland-scanner", @@ -8316,7 +8140,7 @@ version = "20250721.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -8329,7 +8153,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -8342,7 +8166,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b6d8cf1eb2c1c31ed1f5643c88a6e53538129d4af80030c8cabd1f9fa884d91" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -8355,7 +8179,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -8387,9 +8211,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.99" +version = "0.3.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" +checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa" dependencies = [ "js-sys", "wasm-bindgen", @@ -8443,7 +8267,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb3feacc458f7bee8bc1737149b42b6c731aa461039a4264a67bb6681646b250" dependencies = [ "arrayvec", - "bitflags 2.12.1", + "bitflags 2.11.1", "bytemuck", "cfg-if", "cfg_aliases", @@ -8475,7 +8299,7 @@ dependencies = [ "arrayvec", "bit-set", "bit-vec", - "bitflags 2.12.1", + "bitflags 2.11.1", "bytemuck", "cfg_aliases", "document-features", @@ -8546,7 +8370,7 @@ dependencies = [ "arrayvec", "ash", "bit-set", - "bitflags 2.12.1", + "bitflags 2.11.1", "block2 0.6.2", "bytemuck", "cfg-if", @@ -8585,9 +8409,9 @@ dependencies = [ "web-sys", "wgpu-naga-bridge", "wgpu-types", - "windows 0.62.2", - "windows-core 0.62.2", - "windows-result 0.4.1", + "windows", + "windows-core", + "windows-result", ] [[package]] @@ -8606,7 +8430,7 @@ version = "29.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9bcc31518a0e9735aefebedb5f7a9ef3ed1c42549c9f4c882fa9060ceaac639" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "bytemuck", "js-sys", "log", @@ -8663,16 +8487,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" -dependencies = [ - "windows-core 0.58.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows" version = "0.62.2" @@ -8680,7 +8494,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" dependencies = [ "windows-collections", - "windows-core 0.62.2", + "windows-core", "windows-future", "windows-numerics", ] @@ -8691,20 +8505,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" dependencies = [ - "windows-core 0.62.2", -] - -[[package]] -name = "windows-core" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" -dependencies = [ - "windows-implement 0.58.0", - "windows-interface 0.58.0", - "windows-result 0.2.0", - "windows-strings 0.1.0", - "windows-targets 0.52.6", + "windows-core", ] [[package]] @@ -8713,11 +8514,11 @@ version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-implement 0.60.2", - "windows-interface 0.59.3", + "windows-implement", + "windows-interface", "windows-link", - "windows-result 0.4.1", - "windows-strings 0.5.1", + "windows-result", + "windows-strings", ] [[package]] @@ -8726,22 +8527,11 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" dependencies = [ - "windows-core 0.62.2", + "windows-core", "windows-link", "windows-threading", ] -[[package]] -name = "windows-implement" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-implement" version = "0.60.2" @@ -8753,17 +8543,6 @@ dependencies = [ "syn", ] -[[package]] -name = "windows-interface" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-interface" version = "0.59.3" @@ -8787,7 +8566,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" dependencies = [ - "windows-core 0.62.2", + "windows-core", "windows-link", ] @@ -8798,17 +8577,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" dependencies = [ "windows-link", - "windows-result 0.4.1", - "windows-strings 0.5.1", -] - -[[package]] -name = "windows-result" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" -dependencies = [ - "windows-targets 0.52.6", + "windows-result", + "windows-strings", ] [[package]] @@ -8820,16 +8590,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-strings" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" -dependencies = [ - "windows-result 0.2.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows-strings" version = "0.5.1" @@ -9022,7 +8782,7 @@ dependencies = [ "ahash", "android-activity", "atomic-waker", - "bitflags 2.12.1", + "bitflags 2.11.1", "block2 0.5.1", "bytemuck", "calloop 0.13.0", @@ -9067,18 +8827,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" -dependencies = [ - "memchr", -] - -[[package]] -name = "winnow" -version = "1.0.3" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" dependencies = [ "memchr", ] @@ -9162,7 +8913,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.12.1", + "bitflags 2.11.1", "indexmap", "log", "serde", @@ -9192,12 +8943,6 @@ dependencies = [ "wasmparser", ] -[[package]] -name = "writeable" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" - [[package]] name = "writeable" version = "0.6.3" @@ -9258,7 +9003,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.11.1", "dlib", "log", "once_cell", @@ -9308,9 +9053,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.3" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -9331,9 +9076,9 @@ dependencies = [ [[package]] name = "zbus" -version = "5.16.0" +version = "5.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee682d202a77e4a9f3b2c2bdf48a7b28af5c08c34ddf66f98c93e5e39464285" +checksum = "c3bcbf15c8708d7fc1be0c993622e0a5cbd5e8b52bfa40afa4c3e0cd8d724ac1" dependencies = [ "async-broadcast", "async-executor", @@ -9358,7 +9103,7 @@ dependencies = [ "uds_windows", "uuid", "windows-sys 0.61.2", - "winnow 1.0.3", + "winnow", "zbus_macros", "zbus_names", "zvariant", @@ -9390,9 +9135,9 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.16.0" +version = "5.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adf1bd45a81a103745b1757754762a26e8cd01e4532e4d6c8ec431624b80d1d6" +checksum = "51fa5406ad9175a8c825a931f8cf347116b531b3634fcb0b627c290f1f2516ff" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -9410,7 +9155,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d" dependencies = [ "serde", - "winnow 1.0.3", + "winnow", "zvariant", ] @@ -9434,18 +9179,18 @@ checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" [[package]] name = "zerocopy" -version = "0.8.50" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.50" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", @@ -9546,23 +9291,23 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.12.0" +version = "5.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a192a0bde63360d77a7523c833d4b4ce6070a927e2c53246e4c540b1a3e27be0" +checksum = "1c1567a6ec68df868cbbfde844cfc6d81649fe5109a62b116b19fabd53e618ee" dependencies = [ "endi", "enumflags2", "serde", - "winnow 1.0.3", + "winnow", "zvariant_derive", "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.12.0" +version = "5.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc6cde9c01c511074be97f7ccb6c19d0da89e3f8662e812e999dcfd4638737" +checksum = "c7d5b780599bbde114e39d9a0799577fad1ced5105d38515745f7b3099d8ceda" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -9573,13 +9318,13 @@ dependencies = [ [[package]] name = "zvariant_utils" -version = "3.4.0" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e8535915cfa75547e559d8c68e8139909a4aeee076831e4ef7fc59d8172c4d6" +checksum = "6d464f5733ffa07a3164d656f18533caace9d0638596721355d73256a410d691" dependencies = [ "proc-macro2", "quote", "serde", "syn", - "winnow 1.0.3", + "winnow", ] diff --git a/crates/gosub_engine/Cargo.toml b/crates/gosub_engine/Cargo.toml index e032702fb..0715879c1 100644 --- a/crates/gosub_engine/Cargo.toml +++ b/crates/gosub_engine/Cargo.toml @@ -47,7 +47,7 @@ r2d2_sqlite = { version = "0.33", optional = true, features = ["bundled"] } gtk4 = { version = "0.11", optional = true } cairo-rs = { version = "0.22", optional = true } -skia-safe = { version = "0.87.0", optional = true } +skia-safe = { version = "0.97.2", optional = true } vello = { version = "0.8.0", optional = true } wgpu = { version = "29.0.0", optional = true } diff --git a/crates/gosub_engine/src/lib.rs b/crates/gosub_engine/src/lib.rs index c3a2b73f4..45dc2bcf7 100644 --- a/crates/gosub_engine/src/lib.rs +++ b/crates/gosub_engine/src/lib.rs @@ -163,6 +163,6 @@ pub fn init_gtk_resources() { #[cfg(feature = "backend_cairo_pango")] { gtk4::init().expect("GTK init failed — on headless systems set GDK_BACKEND=offscreen"); - gosub_renderer_cairo::font::pango::init_system_ui_font(); + gosub_renderer_cairo::font::pango::init(); } } diff --git a/crates/gosub_fontmanager/Cargo.toml b/crates/gosub_fontmanager/Cargo.toml index f3b6a8704..92ed18206 100644 --- a/crates/gosub_fontmanager/Cargo.toml +++ b/crates/gosub_fontmanager/Cargo.toml @@ -30,6 +30,7 @@ path = "src/bin/vello-test.rs" [dependencies] gosub_interface = { version = "0.1.1", registry = "gosub", path = "../gosub_interface", features = [] } +gosub_shared = { version = "0.1.1", registry = "gosub", path = "../gosub_shared" } parking_lot = "0.12" colog = "1.4.0" log = "0.4.29" diff --git a/crates/gosub_fontmanager/src/lib.rs b/crates/gosub_fontmanager/src/lib.rs index c7cd2b898..78b75e3c2 100644 --- a/crates/gosub_fontmanager/src/lib.rs +++ b/crates/gosub_fontmanager/src/lib.rs @@ -1,5 +1,7 @@ pub mod flatland; pub mod font_manager; +pub mod parley_system; pub use font_manager::font_info::FontInfo; pub use font_manager::manager::FontManager; +pub use parley_system::ParleyFontSystem; diff --git a/crates/gosub_fontmanager/src/parley_system.rs b/crates/gosub_fontmanager/src/parley_system.rs new file mode 100644 index 000000000..32d0801be --- /dev/null +++ b/crates/gosub_fontmanager/src/parley_system.rs @@ -0,0 +1,271 @@ +use cow_utils::CowUtils; +use gosub_interface::font::{FontBlob, FontError, FontStyle}; +use gosub_interface::font_system::{ + FontQuery, FontStretch, FontSystem, FontWeight, ResolvedFont, ShapedGlyph, ShapedRun, ShapedText, +}; +use parley::fontique::{Attributes, FontWidth, GenericFamily, QueryFamily, QueryStatus, SourceCache}; +use parley::style::{FontStyle as ParleyStyle, FontWeight as ParleyWeight}; +use parley::{Alignment, AlignmentOptions, FontContext, LayoutContext, PositionedLayoutItem}; +use std::any::Any; + +/// A [`FontSystem`] implementation backed by Parley + Fontique. +/// +/// Holds a single `FontContext` (fontique collection) and `LayoutContext` so that +/// all callers — the layout engine and every renderer — share the same font data and +/// produce consistent glyph metrics. +/// +/// Construct once at application start, wrap in `Arc>`, and +/// pass the same `Arc` into both the Taffy layouter and the rendering backend. +pub struct ParleyFontSystem { + font_cx: FontContext, + layout_cx: LayoutContext<()>, + source_cache: SourceCache, +} + +impl std::fmt::Debug for ParleyFontSystem { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ParleyFontSystem").finish_non_exhaustive() + } +} + +impl Default for ParleyFontSystem { + fn default() -> Self { + Self::new() + } +} + +impl ParleyFontSystem { + /// Create a new font system with system fonts loaded and Roboto registered as + /// the built-in fallback. + pub fn new() -> Self { + let mut font_cx = FontContext::new(); + + // Register Roboto as a bundled fallback so there is always something to + // render with even on systems that have no fonts installed. + font_cx + .collection + .register_fonts(gosub_shared::ROBOTO_FONT.to_vec().into(), None); + + Self { + font_cx, + layout_cx: LayoutContext::new(), + source_cache: SourceCache::new_shared(), + } + } +} + +impl ParleyFontSystem { + /// Grants direct access to the underlying Parley font collection. + /// + /// Used by `TaffyLayouter` so that the same font collection is shared between + /// the layout engine and rendering, ensuring consistent shaping. + pub fn font_cx_mut(&mut self) -> &mut FontContext { + &mut self.font_cx + } +} + +impl FontSystem for ParleyFontSystem { + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + fn register_font(&mut self, data: Vec, _family_override: Option<&str>) -> Result<(), FontError> { + // fontique derives the family name from the font's own `name` table; + // custom name overrides are not yet supported here. + self.font_cx.collection.register_fonts(data.into(), None); + Ok(()) + } + + fn resolve(&mut self, query: &FontQuery<'_>) -> Result { + let families: Vec = query.families.iter().map(|&name| css_family_to_query(name)).collect(); + + let attrs = Attributes::new( + stretch_to_width(query.stretch), + style_to_fontique(query.style), + weight_to_fontique(query.weight), + ); + + let mut col_clone = self.font_cx.collection.clone(); + let mut q = self.font_cx.collection.query(&mut self.source_cache); + q.set_families(families); + q.set_attributes(attrs); + + let mut found: Option = None; + q.matches_with(|cand| { + // Extract the inner Arc from fontique's Blob without copying bytes. + let (data_arc, _) = cand.blob.clone().into_raw_parts(); + let blob = FontBlob::new(data_arc, cand.index); + + let (fam_id, _) = cand.family; + let family = col_clone + .family(fam_id) + .map(|f| f.name().to_string()) + .unwrap_or_else(|| query.families.first().copied().unwrap_or("sans-serif").to_string()); + + found = Some(ResolvedFont { + family, + style: query.style, + weight: query.weight, + stretch: query.stretch, + blob, + }); + + QueryStatus::Stop + }); + + found.ok_or_else(|| FontError::FontNotFound(query.families.join(", "))) + } + + fn shape(&mut self, text: &str, font: &ResolvedFont, size: f32, max_width: Option) -> ShapedText { + if text.is_empty() { + return ShapedText::empty(); + } + + let mut builder = self.layout_cx.ranged_builder(&mut self.font_cx, text, 1.0, false); + builder.push_default(parley::StyleProperty::FontSize(size)); + builder.push_default(parley::StyleProperty::FontFamily(parley::FontFamily::Source( + font.family.as_str().into(), + ))); + builder.push_default(parley::StyleProperty::FontWeight(ParleyWeight::new( + font.weight.0 as f32, + ))); + builder.push_default(parley::StyleProperty::FontStyle(style_to_parley(font.style))); + builder.push_default(parley::StyleProperty::Brush(())); + + let mut layout = builder.build(text); + layout.break_all_lines(Some(max_width.unwrap_or(f32::INFINITY))); + layout.align(Alignment::Start, AlignmentOptions::default()); + + let mut runs: Vec = Vec::new(); + let mut pen_y = 0.0f32; + let mut total_width = 0.0f32; + let mut first_ascent = 0.0f32; + let mut last_line_height = 0.0f32; + let mut first_line = true; + + for line in layout.lines() { + let lm = line.metrics(); + if first_line { + first_ascent = lm.ascent; + first_line = false; + } + last_line_height = lm.line_height; + let baseline = lm.ascent; + + for item in line.items() { + if let PositionedLayoutItem::GlyphRun(run) = item { + total_width = total_width.max(run.offset() + run.advance()); + + let run_x = run.offset(); + let mut pen_x = 0.0f32; + + let glyphs: Vec = run + .glyphs() + .map(|g| { + let x = run_x + pen_x + g.x; + let y = pen_y + baseline + g.y; + pen_x += g.advance; + ShapedGlyph { id: g.id, x, y } + }) + .collect(); + + if !glyphs.is_empty() { + runs.push(ShapedRun { + font: font.clone(), + font_size: size, + glyphs, + }); + } + } + } + + pen_y += lm.line_height; + } + + ShapedText { + runs, + width: total_width, + height: pen_y, + line_height: last_line_height, + ascent: first_ascent, + } + } + + fn measure(&mut self, text: &str, font: &ResolvedFont, size: f32, max_width: Option) -> (f32, f32) { + if text.is_empty() { + return (0.0, 0.0); + } + + let mut builder = self.layout_cx.ranged_builder(&mut self.font_cx, text, 1.0, false); + builder.push_default(parley::StyleProperty::FontSize(size)); + builder.push_default(parley::StyleProperty::FontFamily(parley::FontFamily::Source( + font.family.as_str().into(), + ))); + builder.push_default(parley::StyleProperty::FontWeight(ParleyWeight::new( + font.weight.0 as f32, + ))); + builder.push_default(parley::StyleProperty::FontStyle(style_to_parley(font.style))); + builder.push_default(parley::StyleProperty::Brush(())); + + let mut layout = builder.build(text); + layout.break_all_lines(Some(max_width.unwrap_or(f32::INFINITY))); + + let mut width = 0.0f32; + let mut height = 0.0f32; + for line in layout.lines() { + let lm = line.metrics(); + for item in line.items() { + if let PositionedLayoutItem::GlyphRun(run) = item { + width = width.max(run.offset() + run.advance()); + } + } + height += lm.line_height; + } + + (width, height) + } +} + +// --------------------------------------------------------------------------- +// Conversion helpers +// --------------------------------------------------------------------------- + +fn css_family_to_query(name: &str) -> QueryFamily<'_> { + match name.cow_to_lowercase().as_ref() { + "sans-serif" => GenericFamily::SansSerif.into(), + "serif" => GenericFamily::Serif.into(), + "monospace" | "monospaced" => GenericFamily::Monospace.into(), + "cursive" => GenericFamily::Cursive.into(), + "fantasy" => GenericFamily::Fantasy.into(), + "system-ui" => GenericFamily::SystemUi.into(), + "ui-sans-serif" => GenericFamily::UiSansSerif.into(), + "ui-serif" => GenericFamily::UiSerif.into(), + "ui-monospace" => GenericFamily::UiMonospace.into(), + "ui-rounded" => GenericFamily::UiRounded.into(), + _ => QueryFamily::Named(name), + } +} + +fn weight_to_fontique(w: FontWeight) -> parley::fontique::FontWeight { + parley::fontique::FontWeight::new(w.0 as f32) +} + +fn stretch_to_width(s: FontStretch) -> FontWidth { + FontWidth::from_ratio(s.0) +} + +fn style_to_fontique(s: FontStyle) -> parley::fontique::FontStyle { + match s { + FontStyle::Normal => parley::fontique::FontStyle::Normal, + FontStyle::Italic => parley::fontique::FontStyle::Italic, + FontStyle::Oblique => parley::fontique::FontStyle::Oblique(None), + } +} + +fn style_to_parley(s: FontStyle) -> ParleyStyle { + match s { + FontStyle::Normal => ParleyStyle::Normal, + FontStyle::Italic => ParleyStyle::Italic, + FontStyle::Oblique => ParleyStyle::Oblique(None), + } +} diff --git a/crates/gosub_interface/src/font_system.rs b/crates/gosub_interface/src/font_system.rs new file mode 100644 index 000000000..ec8d89041 --- /dev/null +++ b/crates/gosub_interface/src/font_system.rs @@ -0,0 +1,219 @@ +use parking_lot::Mutex; +use std::any::Any; +use std::sync::Arc; + +use crate::font::{FontBlob, FontError, FontStyle}; + +// --------------------------------------------------------------------------- +// Value types +// --------------------------------------------------------------------------- + +/// CSS font-weight (100–900). Common constants provided for convenience. +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] +pub struct FontWeight(pub u16); + +impl FontWeight { + pub const THIN: Self = Self(100); + pub const EXTRA_LIGHT: Self = Self(200); + pub const LIGHT: Self = Self(300); + pub const NORMAL: Self = Self(400); + pub const MEDIUM: Self = Self(500); + pub const SEMI_BOLD: Self = Self(600); + pub const BOLD: Self = Self(700); + pub const EXTRA_BOLD: Self = Self(800); + pub const BLACK: Self = Self(900); +} + +impl Default for FontWeight { + fn default() -> Self { + Self::NORMAL + } +} + +/// CSS font-stretch as a multiplier (1.0 = normal, 0.5 = condensed, 2.0 = expanded). +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] +pub struct FontStretch(pub f32); + +impl FontStretch { + pub const ULTRA_CONDENSED: Self = Self(0.5); + pub const CONDENSED: Self = Self(0.75); + pub const NORMAL: Self = Self(1.0); + pub const EXPANDED: Self = Self(1.25); + pub const ULTRA_EXPANDED: Self = Self(2.0); +} + +impl Default for FontStretch { + fn default() -> Self { + Self::NORMAL + } +} + +/// A CSS font-family query with full property set. +/// +/// `families` is a priority-ordered slice of family names, exactly as they appear +/// in the CSS `font-family` property (e.g. `["Helvetica Neue", "Arial", "sans-serif"]`). +#[derive(Debug, Clone)] +pub struct FontQuery<'a> { + pub families: &'a [&'a str], + pub style: FontStyle, + pub weight: FontWeight, + pub stretch: FontStretch, +} + +impl<'a> FontQuery<'a> { + pub fn new(families: &'a [&'a str]) -> Self { + Self { + families, + style: FontStyle::Normal, + weight: FontWeight::NORMAL, + stretch: FontStretch::NORMAL, + } + } +} + +/// A concrete font that has been resolved from a `FontQuery`. +/// +/// Carries the raw font bytes so both the layout engine and the renderer can +/// use the same data without going back to the font system. +#[derive(Debug, Clone)] +pub struct ResolvedFont { + /// The family name that was actually selected (may differ from what was requested + /// if a fallback was used). + pub family: String, + pub style: FontStyle, + pub weight: FontWeight, + pub stretch: FontStretch, + /// Raw font bytes + collection index. + pub blob: FontBlob, +} + +// --------------------------------------------------------------------------- +// Shaping output +// --------------------------------------------------------------------------- + +/// A single positioned glyph. +/// +/// `x` and `y` are in pixels, with `y` already including the baseline and any +/// line offsets — (0, 0) is the top-left of the shaped block, not the baseline. +#[derive(Debug, Clone, Copy)] +pub struct ShapedGlyph { + pub id: u32, + pub x: f32, + pub y: f32, +} + +/// A contiguous run of glyphs rendered with the same font and size. +/// +/// A single call to `FontSystem::shape` may return multiple runs when font +/// fallback kicks in mid-string (e.g. an emoji in a Latin text run). +#[derive(Debug, Clone)] +pub struct ShapedRun { + pub font: ResolvedFont, + pub font_size: f32, + pub glyphs: Vec, +} + +/// The complete result of shaping a string: positioned glyph runs plus metrics. +#[derive(Debug, Clone)] +pub struct ShapedText { + /// One entry per font run. May span multiple lines. + pub runs: Vec, + /// Bounding box of all runs. + pub width: f32, + pub height: f32, + /// Dominant line height (useful for cursor positioning and decoration). + pub line_height: f32, + /// Baseline of the first line, measured from the top of the bounding box. + pub ascent: f32, +} + +impl ShapedText { + pub fn empty() -> Self { + Self { + runs: Vec::new(), + width: 0.0, + height: 0.0, + line_height: 0.0, + ascent: 0.0, + } + } + + pub fn is_empty(&self) -> bool { + self.runs.is_empty() + } +} + +// --------------------------------------------------------------------------- +// Core trait +// --------------------------------------------------------------------------- + +/// A swappable font back-end. +/// +/// Implementations exist (or will exist) for: +/// - `ParleyFontSystem` — uses parley + fontique (default, supports Vello/Skia) +/// - `PangoFontSystem` — wraps GTK/Pango (used by the Cairo renderer on Linux) +/// +/// The system is intended to be shared between the layout engine (Taffy) and the +/// renderer so that shaping is done once and the resulting `ShapedText` is passed +/// through the pipeline rather than re-shaped at draw time. +/// +/// # Threading +/// `FontSystem` requires `Send + Sync` so it can be wrapped in `Arc>`. +/// Callers must hold the mutex across a full resolve+shape pair if they need +/// the results to be consistent. +pub trait FontSystem: Send + Sync + 'static { + /// Required for downcasting to a concrete font system implementation. + fn as_any_mut(&mut self) -> &mut dyn Any; + + /// Register a font from raw bytes. + /// + /// Used for `@font-face` web fonts and for bundled fallback fonts (e.g. Roboto). + /// `family_override` lets callers assign a logical name that CSS can reference; + /// if `None` the name is taken from the font's own `name` table. + fn register_font(&mut self, data: Vec, family_override: Option<&str>) -> Result<(), FontError>; + + /// Resolve a CSS font query to a concrete `ResolvedFont`. + /// + /// Walks `query.families` in order and applies weight/style/stretch matching. + /// Returns `Err(FontError::FontNotFound)` only if no family in the list (including + /// generic families like `sans-serif`) could be resolved. + fn resolve(&mut self, query: &FontQuery<'_>) -> Result; + + /// Shape `text` with `font` at `size` pixels. + /// + /// `max_width`: if `Some(w)`, soft-wrap lines at `w` pixels. `None` = single line. + /// + /// Returns fully positioned glyphs. The returned `ShapedText` is cheaply cloneable + /// (glyphs are usually stored in an `Arc<[ShapedGlyph]>` or `Vec`) so callers can + /// cache it at the layout node level. + fn shape(&mut self, text: &str, font: &ResolvedFont, size: f32, max_width: Option) -> ShapedText; + + /// Return only the bounding box of `text` without glyph positions. + /// + /// The default implementation delegates to `shape()`. Override this when the + /// back-end has a cheaper measurement path (e.g. `pango::Layout::pixel_size()`). + fn measure(&mut self, text: &str, font: &ResolvedFont, size: f32, max_width: Option) -> (f32, f32) { + let shaped = self.shape(text, font, size, max_width); + (shaped.width, shaped.height) + } +} + +// --------------------------------------------------------------------------- +// Config integration +// --------------------------------------------------------------------------- + +/// Marker trait: a config type `C` that carries a `FontSystem`. +/// +/// Implement this on your top-level `Config` struct, then pass +/// `Arc>` into both the layout engine and the renderer. +/// +/// ```ignore +/// impl HasFontSystem for MyConfig { +/// fn font_system(&self) -> Arc> { +/// Arc::clone(&self.font_system) +/// } +/// } +/// ``` +pub trait HasFontSystem { + fn font_system(&self) -> Arc>; +} diff --git a/crates/gosub_interface/src/lib.rs b/crates/gosub_interface/src/lib.rs index 315f9e0aa..d13d17fe6 100644 --- a/crates/gosub_interface/src/lib.rs +++ b/crates/gosub_interface/src/lib.rs @@ -2,6 +2,7 @@ pub mod config; pub mod css3; pub mod document; pub mod font; +pub mod font_system; pub mod html5; pub mod input; pub mod layout; diff --git a/crates/gosub_render_pipeline/Cargo.toml b/crates/gosub_render_pipeline/Cargo.toml index bfc3612f7..e8804c4f1 100644 --- a/crates/gosub_render_pipeline/Cargo.toml +++ b/crates/gosub_render_pipeline/Cargo.toml @@ -26,6 +26,7 @@ parking_lot = "0.12" parley = "0.9.0" gosub_shared = { version = "0.1.1", path = "../gosub_shared", registry = "gosub" } gosub_interface = { version = "0.1.2", path = "../gosub_interface", registry = "gosub" } +gosub_fontmanager = { version = "0.1.0", path = "../gosub_fontmanager", registry = "gosub" } # backend_cairo optional deps cairo-rs = { version = "0.22", optional = true } @@ -33,12 +34,12 @@ cairo-rs = { version = "0.22", optional = true } # backend_vello optional deps vello = { version = "0.8.0", optional = true } wgpu = { version = "29.0.0", optional = true } -fontique = { version = "0.9.0", optional = true } +fontique = { version = "0.10.0", optional = true } hashbrown = { version = "0.17.1", optional = true } skrifa = { version = "0.42.1", optional = true } # backend_skia / backend_skia_gl optional deps -skia-safe = { version = "0.87.0", optional = true } +skia-safe = { version = "0.97.2", optional = true } [target.'cfg(target_os = "linux")'.dependencies] gdk4-wayland = { version = "0.11", optional = true, features = ["wayland_crate"] } diff --git a/crates/gosub_render_pipeline/src/common/font/parley.rs b/crates/gosub_render_pipeline/src/common/font/parley.rs index e69393bc9..9b9db0cda 100644 --- a/crates/gosub_render_pipeline/src/common/font/parley.rs +++ b/crates/gosub_render_pipeline/src/common/font/parley.rs @@ -1,21 +1,11 @@ use crate::common::font::FontAlignment; -use parking_lot::Mutex; -use parley::Layout; -use std::sync::OnceLock; - -static FONT_CTX: OnceLock> = OnceLock::new(); -static LAYOUT_CTX: OnceLock>> = OnceLock::new(); - -pub fn get_font_context() -> parking_lot::MutexGuard<'static, parley::FontContext> { - FONT_CTX.get_or_init(|| Mutex::new(parley::FontContext::new())).lock() -} - -fn get_layout_context() -> parking_lot::MutexGuard<'static, parley::LayoutContext<[u8; 4]>> { - LAYOUT_CTX - .get_or_init(|| Mutex::new(parley::LayoutContext::new())) - .lock() -} +use parley::{Alignment, AlignmentOptions, FontContext, FontFamily, Layout, LayoutContext, LineHeight, StyleProperty}; +/// Build a Parley layout for `text` using the shared `font_cx`. +/// +/// A local `LayoutContext` is created per call — it is pure scratch space +/// (the expensive state lives in `font_cx`) so allocation cost is negligible. +#[allow(clippy::too_many_arguments)] pub fn get_parley_layout( text: &str, font_family: &str, @@ -24,35 +14,30 @@ pub fn get_parley_layout( font_weight: i32, max_width: f64, alignment: FontAlignment, + font_cx: &mut FontContext, ) -> Layout<[u8; 4]> { + let mut layout_cx: LayoutContext<[u8; 4]> = LayoutContext::new(); + let display_scale = 1.0_f32; let max_advance = (max_width * display_scale as f64) as f32; - let mut font_ctx = get_font_context(); - let mut layout_ctx = get_layout_context(); + let mut builder = layout_cx.ranged_builder(font_cx, text, display_scale, false); - let mut builder = layout_ctx.ranged_builder(&mut font_ctx, text, display_scale, false); - builder.push_default(parley::style::StyleProperty::FontFamily( - parley::style::FontFamily::Source(font_family.into()), - )); - builder.push_default(parley::style::StyleProperty::LineHeight( - parley::style::LineHeight::Absolute(line_height as f32), - )); - builder.push_default(parley::style::StyleProperty::FontSize(font_size as f32)); - builder.push_default(parley::style::StyleProperty::FontWeight( - parley::style::FontWeight::new(font_weight as f32), - )); + builder.push_default(StyleProperty::FontFamily(FontFamily::Source(font_family.into()))); + builder.push_default(StyleProperty::LineHeight(LineHeight::Absolute(line_height as f32))); + builder.push_default(StyleProperty::FontSize(font_size as f32)); + builder.push_default(StyleProperty::FontWeight(parley::FontWeight::new(font_weight as f32))); let align = match alignment { - FontAlignment::Start => parley::Alignment::Start, - FontAlignment::Center => parley::Alignment::Center, - FontAlignment::End => parley::Alignment::End, - FontAlignment::Justify => parley::Alignment::Justify, + FontAlignment::Start => Alignment::Start, + FontAlignment::Center => Alignment::Center, + FontAlignment::End => Alignment::End, + FontAlignment::Justify => Alignment::Justify, }; let mut layout: Layout<[u8; 4]> = builder.build(text); layout.break_all_lines(Some(max_advance)); - layout.align(align, parley::AlignmentOptions::default()); + layout.align(align, AlignmentOptions::default()); layout } diff --git a/crates/gosub_render_pipeline/src/common/media/media_store.rs b/crates/gosub_render_pipeline/src/common/media/media_store.rs index 0bc9577bc..dc1fe870a 100644 --- a/crates/gosub_render_pipeline/src/common/media/media_store.rs +++ b/crates/gosub_render_pipeline/src/common/media/media_store.rs @@ -7,9 +7,26 @@ use parking_lot::RwLock; use resvg::usvg; use std::collections::HashMap; use std::sync::atomic::{AtomicU64, Ordering}; -use std::sync::Arc; +use std::sync::{Arc, OnceLock}; use url::Url; +/// Return `usvg::Options` backed by a shared fontdb that has system fonts loaded. +/// +/// The database is built once the first time this is called and then reused, +/// so system font discovery only happens once per process. +fn svg_options() -> usvg::Options<'static> { + static FONTDB: OnceLock> = OnceLock::new(); + let fontdb = Arc::clone(FONTDB.get_or_init(|| { + let mut db = usvg::fontdb::Database::new(); + db.load_system_fonts(); + Arc::new(db) + })); + usvg::Options { + fontdb, + ..Default::default() + } +} + const DEFAULT_SVG_ID: MediaId = MediaId::new(0); const DEFAULT_IMAGE_ID: MediaId = MediaId::new(1); const FIRST_FREE_IMAGE_ID: u64 = 100; @@ -47,7 +64,7 @@ impl MediaStore { // Add "default svg" to the store. let default_svg_tree = - usvg::Tree::from_data(DEFAULT_SVG_DATA, &usvg::Options::default()).expect("Failed to load default svg"); + usvg::Tree::from_data(DEFAULT_SVG_DATA, &svg_options()).expect("Failed to load default svg"); let mut entries = store.entries.write(); let media = Media::svg("gosub://default/svg", Svg::new(default_svg_tree)); entries.insert(DEFAULT_SVG_ID, Arc::new(media)); @@ -99,7 +116,7 @@ impl MediaStore { let media_id = match media_type { MediaType::Svg => { - let svg_tree = match usvg::Tree::from_data(data, &usvg::Options::default()) { + let svg_tree = match usvg::Tree::from_data(data, &svg_options()) { Ok(tree) => tree, Err(_) => { return Err(anyhow::anyhow!("Failed to parse SVG data")); @@ -151,7 +168,7 @@ impl MediaStore { let media = match media_type { MediaType::Svg => { - let svg_tree = match usvg::Tree::from_data(&raw_data, &usvg::Options::default()) { + let svg_tree = match usvg::Tree::from_data(&raw_data, &svg_options()) { Ok(tree) => tree, Err(_) => { return Err(anyhow::anyhow!("Failed to parse SVG data")); diff --git a/crates/gosub_render_pipeline/src/layouter/taffy.rs b/crates/gosub_render_pipeline/src/layouter/taffy.rs index 83f9f6217..5f41c7aaa 100644 --- a/crates/gosub_render_pipeline/src/layouter/taffy.rs +++ b/crates/gosub_render_pipeline/src/layouter/taffy.rs @@ -13,7 +13,8 @@ use crate::layouter::{ LayoutElementNode, LayoutTree, }; use crate::rendertree_builder::{RenderNodeId, RenderTree}; -use parking_lot::RwLock; +use gosub_fontmanager::ParleyFontSystem; +use parking_lot::{Mutex, RwLock}; use std::borrow::Borrow; use std::collections::HashMap; use std::sync::Arc; @@ -39,6 +40,10 @@ pub struct TaffyLayouter { anon_container_map: HashMap, /// Media store for loading images/SVGs during layout media_store: MediaStore, + /// Shared font system used for text measurement. Locking once per measurement + /// call avoids keeping the guard alive across the full layout pass, which lets + /// other threads (e.g. the rasterizer) access the font collection between calls. + font_system: Arc>, } /// Context structures to pass to taffy measure functions so we can calculate the size of the text or images. @@ -92,16 +97,31 @@ impl Default for TaffyLayouter { } impl TaffyLayouter { + /// Create a layouter with its own font system. + /// + /// To share the font collection with other components (e.g. a `VelloRasterizer`) + /// use [`TaffyLayouter::with_font_system`] and pass the same `Arc` to both. pub fn new() -> Self { + Self::with_font_system(Arc::new(Mutex::new(ParleyFontSystem::new()))) + } + + /// Create a layouter that shares an existing font system. + pub fn with_font_system(font_system: Arc>) -> Self { Self { tree: TaffyTree::new(), root_id: TaffyNodeId::new(0), layout_taffy_mapping: HashMap::new(), anon_container_map: HashMap::new(), media_store: MediaStore::new(), + font_system, } } + /// Expose the font system so callers can share it with other components. + pub fn font_system(&self) -> Arc> { + Arc::clone(&self.font_system) + } + pub fn print_tree(&mut self) { self.tree.print_tree(self.root_id); } @@ -136,6 +156,10 @@ impl CanLayout for TaffyLayouter { None => Size::MAX_CONTENT, }; + // Clone the Arc so the closure can lock the font system without holding a + // borrow of `self` while `self.tree` is also mutably borrowed. + let font_system = Arc::clone(&self.font_system); + // Compute the layout with a measure function if let Err(e) = self .tree @@ -153,9 +177,20 @@ impl CanLayout for TaffyLayouter { AvailableSpace::MinContent => 0.0, } }; - // Calculate the text layout dimensions and return it to taffy - let text_layout = - get_text_layout(text_ctx.text.as_str(), &text_ctx.font_info, max_width, dpi_scale_factor); + // Acquire the font context for this measurement. The lock is + // released immediately after the call so other callers + // (e.g. the rasterizer) can interleave without contention. + let text_layout = { + let mut fs = font_system.lock(); + let font_cx = fs.font_cx_mut(); + get_text_layout( + text_ctx.text.as_str(), + &text_ctx.font_info, + max_width, + dpi_scale_factor, + font_cx, + ) + }; match text_layout { Ok(text_layout) => { // Ceil width to the nearest CSS pixel. Parley returns a fractional diff --git a/crates/gosub_render_pipeline/src/layouter/text/parley.rs b/crates/gosub_render_pipeline/src/layouter/text/parley.rs index 4a91843d7..2bd579883 100644 --- a/crates/gosub_render_pipeline/src/layouter/text/parley.rs +++ b/crates/gosub_render_pipeline/src/layouter/text/parley.rs @@ -1,12 +1,14 @@ use crate::common::font::parley::get_parley_layout; use crate::common::font::FontInfo; use crate::common::geo::Dimension; +use parley::FontContext; pub fn get_text_layout( text: &str, font_info: &FontInfo, max_width: f64, _dpi_scale_factor: f32, + font_cx: &mut FontContext, ) -> Result { let layout = get_parley_layout( text, @@ -16,6 +18,7 @@ pub fn get_text_layout( font_info.weight, max_width, font_info.alignment.clone(), + font_cx, ); Ok(Dimension { diff --git a/crates/gosub_render_pipeline/src/render/backends/vello.rs b/crates/gosub_render_pipeline/src/render/backends/vello.rs index 47d1bc79d..e4652113c 100644 --- a/crates/gosub_render_pipeline/src/render/backends/vello.rs +++ b/crates/gosub_render_pipeline/src/render/backends/vello.rs @@ -6,7 +6,9 @@ use crate::render::backends::vello::text_renderer::{TextKey, TextRenderer}; use crate::render::render_context::RenderContext; use crate::render::render_list::DisplayItem; use anyhow::{anyhow, Result}; +use gosub_fontmanager::ParleyFontSystem; use parking_lot::Mutex; +use parley::FontContext; use std::any::Any; use std::sync::Arc; use vello::kurbo::{Affine, Vec2}; @@ -41,6 +43,9 @@ pub struct VelloBackend { text_renderer: Mutex, font_manager: Mutex, font_cache: Mutex, + /// Shared font system. Holds the Parley font collection so that all text + /// shaping in this backend uses a single, consistent font discovery context. + font_system: Arc>, } impl VelloBackend { @@ -58,9 +63,16 @@ impl VelloBackend { text_renderer: Mutex::new(TextRenderer::new()), font_manager: Mutex::new(FontManager::new()), font_cache: Mutex::new(FontCache::new()), + font_system: Arc::new(Mutex::new(ParleyFontSystem::new())), }) } + /// Expose the font system so callers can share it with `TaffyLayouter` or + /// `VelloRasterizer` to ensure consistent font discovery across layout and render. + pub fn font_system(&self) -> Arc> { + Arc::clone(&self.font_system) + } + /// Returns the shared wgpu resources (device, queue, renderer) for use by the tile rasterizer. pub fn wgpu_resources(&self) -> Arc { Arc::clone(&self.resources) @@ -93,6 +105,7 @@ impl VelloBackend { text_renderer: &mut TextRenderer, font_manager: &mut FontManager, font_cache: &mut FontCache, + font_cx: &mut FontContext, ctx: &mut dyn RenderContext, ) -> Result { let vp = ctx.viewport(); @@ -143,7 +156,16 @@ impl VelloBackend { align: 0, }; - text_renderer.draw(font_manager, font_cache, &mut scene, &key, x, y, (*color).into()); + text_renderer.draw( + font_manager, + font_cache, + font_cx, + &mut scene, + &key, + x, + y, + (*color).into(), + ); } DisplayItem::Blit { x, y, w, h, data } => { // Cairo emits premultiplied ARgb32; in-memory bytes are [B, G, R, A] on @@ -201,7 +223,9 @@ impl RenderBackend for VelloBackend { let mut tr = self.text_renderer.lock(); let mut fm = self.font_manager.lock(); let mut fc = self.font_cache.lock(); - self.build_scene(&mut tr, &mut fm, &mut fc, ctx)? + let mut fs = self.font_system.lock(); + let font_cx = fs.font_cx_mut(); + self.build_scene(&mut tr, &mut fm, &mut fc, font_cx, ctx)? }; self.render_to_surface(s, &scene)?; diff --git a/crates/gosub_render_pipeline/src/render/backends/vello/text_renderer.rs b/crates/gosub_render_pipeline/src/render/backends/vello/text_renderer.rs index e907eb42f..12bf6efb7 100644 --- a/crates/gosub_render_pipeline/src/render/backends/vello/text_renderer.rs +++ b/crates/gosub_render_pipeline/src/render/backends/vello/text_renderer.rs @@ -14,7 +14,10 @@ use crate::render::backends::vello::font_manager::FontManager; #[cfg(not(feature = "parley_layout"))] use parley::FontData as Font; #[cfg(feature = "parley_layout")] -use parley::{FontContext, FontData as Font, LayoutContext}; +use parley::{FontData as Font, LayoutContext}; +// FontContext is always in the draw/shape signatures so that the caller +// (VelloBackend) never needs cfg guards at the call site. +use parley::FontContext; #[cfg(not(feature = "parley_layout"))] use skrifa::MetadataProvider; use std::collections::HashMap; @@ -91,24 +94,16 @@ pub struct CachedRun { /// accounts for line height and baseline. /// - `draw()` looks up/creates cached runs and submits them to the [`Scene`] /// with a single affine translation for the target (x, y). +/// +/// The `FontContext` (Parley's font collection) is injected by the caller rather +/// than owned here, so all rendering components share the same font collection. pub struct TextRenderer { - #[cfg(feature = "parley_layout")] - font_cx: FontContext, - #[cfg(feature = "parley_layout")] - layout_cx: LayoutContext<[u8; 4]>, cache: HashMap>, } impl TextRenderer { - /// Create a fresh renderer with empty cache and shaping contexts. pub fn new() -> Self { - Self { - #[cfg(feature = "parley_layout")] - font_cx: FontContext::new(), - #[cfg(feature = "parley_layout")] - layout_cx: LayoutContext::new(), - cache: HashMap::new(), - } + Self { cache: HashMap::new() } } #[allow(unused)] @@ -127,11 +122,16 @@ impl TextRenderer { /// Performance: /// - Multiple calls with the same `key` reuse shaping work. /// - If you animate only the position/color, reuse the same `key`. + /// + /// `font_cx` is the shared Parley font collection. It is used by the `parley_layout` + /// shaping path; the skrifa fallback path ignores it but the parameter is kept + /// unconditional so the call site never needs `#[cfg]` guards. #[allow(clippy::too_many_arguments)] pub fn draw( &mut self, fm: &mut FontManager, fc: &mut FontCache, + font_cx: &mut FontContext, scene: &mut Scene, key: &TextKey, x: f32, @@ -141,7 +141,7 @@ impl TextRenderer { let runs = if let Some(r) = self.cache.get(key) { r.clone() } else { - let shaped = self.shape(fm, fc, key); + let shaped = self.shape(fm, fc, font_cx, key); self.cache.insert(key.clone(), shaped.clone()); shaped }; @@ -176,8 +176,18 @@ impl TextRenderer { /// /// Alignment: /// - `align` is recorded in the key but currently not applied to advance/x positioning. - /// When adding alignment, adjust each line’s glyph x by the rag width delta. - fn shape(&mut self, fm: &mut FontManager, fc: &mut FontCache, key: &TextKey) -> Arc<[CachedRun]> { + /// When adding alignment, adjust each line's glyph x by the rag width delta. + /// + /// The `parley_layout` feature selects the Parley shaping path (uses `font_cx`). + /// Without it the skrifa path is used (maps codepoints directly, ignores `font_cx`). + #[cfg_attr(not(feature = "parley_layout"), allow(unused_variables))] + fn shape( + &mut self, + fm: &mut FontManager, + fc: &mut FontCache, + font_cx: &mut FontContext, + key: &TextKey, + ) -> Arc<[CachedRun]> { // Resolve font let (vello_font, _resolved_name) = match fc.fetch(&key.font_name) { Some(f) => (f.0.clone(), f.1), @@ -236,26 +246,22 @@ impl TextRenderer { #[cfg(feature = "parley_layout")] { - // Build layout - let mut builder = self - .layout_cx - .ranged_builder(&mut self.font_cx, key.text.as_ref(), 1.0, true); + // A fresh LayoutContext is cheap (it is pure scratch space). + // FontContext is the expensive shared state — it is injected by the caller. + let mut layout_cx: LayoutContext<[u8; 4]> = LayoutContext::new(); + + let mut builder = layout_cx.ranged_builder(font_cx, key.text.as_ref(), 1.0, true); builder.push_default(parley::style::StyleProperty::FontSize(key.font_size as f32)); builder.push_default(parley::style::StyleProperty::FontFamily( parley::style::FontFamily::Source(_resolved_name.into()), )); let mut layout = builder.build(key.text.as_ref()); - match key.wrap { - Some(w) if w > 0 => { - let max_width = w as f32; - layout.break_all_lines(Some(max_width)); - } - _ => { - let max_width = f32::INFINITY; - layout.break_all_lines(Some(max_width)); - } - } + let max_width = match key.wrap { + Some(w) if w > 0 => w as f32, + _ => f32::INFINITY, + }; + layout.break_all_lines(Some(max_width)); let mut pen_y = 0.0f32; let mut out: Vec = Vec::new(); diff --git a/crates/gosub_renderer_cairo/src/font/pango.rs b/crates/gosub_renderer_cairo/src/font/pango.rs index 8b94fd93f..145a586cd 100644 --- a/crates/gosub_renderer_cairo/src/font/pango.rs +++ b/crates/gosub_renderer_cairo/src/font/pango.rs @@ -2,46 +2,148 @@ use cow_utils::CowUtils; use gtk4::pango; use gtk4::pango::Weight; use gtk4::prelude::FontFamilyExt; -use std::sync::OnceLock; +use std::sync::{Arc, OnceLock}; const DEFAULT_FONT_FAMILY: &str = "sans"; -/// Cached system-ui font family. Must be populated from the GTK main thread before any -/// background rendering begins — call `init_system_ui_font()` once at startup. -static SYSTEM_UI_FONT: OnceLock> = OnceLock::new(); +// --------------------------------------------------------------------------- +// PangoFontSystem +// --------------------------------------------------------------------------- -/// Resolve and cache the system-ui font. Call once from the GTK main thread at startup. -pub fn init_system_ui_font() { - SYSTEM_UI_FONT.get_or_init(get_system_ui_font_from_gsettings); +/// Font-system state for the Cairo/Pango backend. +/// +/// Holds the cached `system-ui` family name, which must be resolved from the +/// GTK main thread before any background rendering. After [`init_from_gtk_thread`] +/// is called the struct is read-only and can be shared freely behind an [`Arc`]. +/// +/// Obtain a shared instance via [`get`] (which returns the process-wide singleton +/// initialised by [`init`]) or construct an independent instance with [`new`] and +/// call [`PangoFontSystem::init_from_gtk_thread`] yourself. +pub struct PangoFontSystem { + system_ui_font: Option, } -pub fn find_available_font(families: &str, ctx: &pango::Context) -> String { - let available_fonts: Vec = ctx - .list_families() - .iter() - .map(|f| f.name().cow_to_ascii_lowercase().into_owned()) - .collect(); +impl std::fmt::Debug for PangoFontSystem { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PangoFontSystem") + .field("system_ui_font", &self.system_ui_font) + .finish() + } +} - for font in families.split(',') { - let font_name = font.trim().trim_matches(|c| c == '"' || c == '\'').to_string(); +impl PangoFontSystem { + pub fn new() -> Self { + Self { system_ui_font: None } + } - if font_name.eq_ignore_ascii_case("system-ui") { - if let Some(Some(system_font)) = SYSTEM_UI_FONT.get() { - return system_font.clone(); - } - continue; + /// Resolve and cache the system-ui font via GSettings. + /// + /// **Must** be called from the GTK main thread before any background rendering + /// begins. Calling it a second time is a no-op. + pub fn init_from_gtk_thread(&mut self) { + if self.system_ui_font.is_none() { + self.system_ui_font = get_system_ui_font_from_gsettings(); } + } + + /// Walk `families` (comma-separated CSS `font-family` value) and return the + /// first family name that Pango knows about, falling back to `"sans"`. + pub fn find_available_font(&self, families: &str, ctx: &pango::Context) -> String { + let available_fonts: Vec = ctx + .list_families() + .iter() + .map(|f| f.name().cow_to_ascii_lowercase().into_owned()) + .collect(); - let normalized = font_name.cow_to_ascii_lowercase(); - if available_fonts.contains(&normalized.into_owned()) { - return font_name; + for font in families.split(',') { + let font_name = font.trim().trim_matches(|c| c == '"' || c == '\'').to_string(); + + if font_name.eq_ignore_ascii_case("system-ui") { + if let Some(ref system_font) = self.system_ui_font { + return system_font.clone(); + } + continue; + } + + let normalized = font_name.cow_to_ascii_lowercase(); + if available_fonts.contains(&normalized.into_owned()) { + return font_name; + } } + + DEFAULT_FONT_FAMILY.to_string() + } +} + +impl Default for PangoFontSystem { + fn default() -> Self { + Self::new() + } +} + +// --------------------------------------------------------------------------- +// Process-wide singleton (required because GTK init must happen on main thread) +// --------------------------------------------------------------------------- + +/// Process-wide `PangoFontSystem` singleton. +/// +/// Set by [`init`]; read by [`get`]. The `OnceLock` is intentional — GTK's +/// font resolution is tied to the main thread and the result is immutable once +/// resolved, so a static `Arc` is the correct primitive here. +static PANGO_FONT_SYSTEM: OnceLock> = OnceLock::new(); + +/// Initialise the singleton from the GTK main thread. +/// +/// Called once at startup (e.g. from `gosub_engine::init_gtk_resources()`). +/// Subsequent calls are silently ignored. +pub fn init() { + PANGO_FONT_SYSTEM.get_or_init(|| { + let mut fs = PangoFontSystem::new(); + fs.init_from_gtk_thread(); + Arc::new(fs) + }); +} + +/// Return the process-wide font system, initialising it without GTK-thread +/// resolution if it hasn't been set yet (fallback path for headless tests). +pub fn get() -> Arc { + Arc::clone(PANGO_FONT_SYSTEM.get_or_init(|| Arc::new(PangoFontSystem::new()))) +} + +// --------------------------------------------------------------------------- +// Weight mapping +// --------------------------------------------------------------------------- + +pub fn to_pango_weight(weight: usize) -> Weight { + match weight { + 0..=149 => Weight::Thin, // 100 + 150..=249 => Weight::Ultralight, // 200 + 250..=324 => Weight::Light, // 300 + 325..=374 => Weight::Semilight, // 350 + 375..=449 => Weight::Normal, // 400 + 450..=549 => Weight::Medium, // 500 + 550..=649 => Weight::Semibold, // 600 + 650..=749 => Weight::Bold, // 700 + 750..=849 => Weight::Ultrabold, // 800 + _ => Weight::Heavy, // 900 } +} + +// --------------------------------------------------------------------------- +// Backward-compat shim used by gosub_engine::init_gtk_resources +// --------------------------------------------------------------------------- - DEFAULT_FONT_FAMILY.to_string() +/// Deprecated entry point kept for ABI compatibility. +/// Prefer calling [`init`] directly. +#[deprecated(since = "0.1.0", note = "Use `gosub_renderer_cairo::font::pango::init()` instead")] +pub fn init_system_ui_font() { + init(); } -/// Read the GNOME system font from GSettings. Must be called from the GTK main thread. +// --------------------------------------------------------------------------- +// Internal helpers +// --------------------------------------------------------------------------- + fn get_system_ui_font_from_gsettings() -> Option { use gtk4::gio::{Settings, SettingsSchemaSource}; use gtk4::prelude::SettingsExt; @@ -63,18 +165,3 @@ fn get_system_ui_font_from_gsettings() -> Option { None } - -pub fn to_pango_weight(weight: usize) -> Weight { - match weight { - 0..=149 => Weight::Thin, // 100 - 150..=249 => Weight::Ultralight, // 200 - 250..=324 => Weight::Light, // 300 - 325..=374 => Weight::Semilight, // 350 - 375..=449 => Weight::Normal, // 400 — CSS "normal" weight lands here - 450..=549 => Weight::Medium, // 500 - 550..=649 => Weight::Semibold, // 600 - 650..=749 => Weight::Bold, // 700 - 750..=849 => Weight::Ultrabold, // 800 - _ => Weight::Heavy, // 900 - } -} diff --git a/crates/gosub_renderer_cairo/src/rasterizer.rs b/crates/gosub_renderer_cairo/src/rasterizer.rs index 26db5b948..d57f3dd74 100644 --- a/crates/gosub_renderer_cairo/src/rasterizer.rs +++ b/crates/gosub_renderer_cairo/src/rasterizer.rs @@ -5,6 +5,10 @@ use gosub_render_pipeline::common::TextureStore; use gosub_render_pipeline::painter::commands::PaintCommand; use gosub_render_pipeline::rasterizer::Rasterable; use gosub_render_pipeline::tiler::Tile; +use std::sync::Arc; + +#[cfg(feature = "text_pango")] +use crate::font::pango::{get as get_font_system, PangoFontSystem}; mod brush; mod rectangle; @@ -13,12 +17,45 @@ mod text; pub use gosub_render_pipeline::render::backends::cairo::DEVICE_PIXEL_RATIO; -#[derive(Default)] -pub struct CairoRasterizer {} +pub struct CairoRasterizer { + #[cfg(feature = "text_pango")] + font_system: Arc, +} + +impl Default for CairoRasterizer { + fn default() -> Self { + Self::new() + } +} impl CairoRasterizer { - pub fn new() -> CairoRasterizer { - CairoRasterizer {} + /// Create a rasterizer using the process-wide font system singleton. + /// + /// The singleton is populated by [`gosub_engine::init_gtk_resources`] (or + /// `gosub_renderer_cairo::font::pango::init()`) from the GTK main thread. + /// If it has not been initialised yet the singleton is created without + /// system-ui font resolution; `"sans"` is used as the fallback in that case. + pub fn new() -> Self { + Self { + #[cfg(feature = "text_pango")] + font_system: get_font_system(), + } + } + + /// Create a rasterizer with an explicitly provided font system. + /// + /// Use this when you want to share a pre-initialised `PangoFontSystem` + /// between multiple rasterizer instances, or in tests where you control + /// font resolution yourself. + #[cfg(feature = "text_pango")] + pub fn with_font_system(font_system: Arc) -> Self { + Self { font_system } + } + + /// Expose the font system so callers can share it with other components. + #[cfg(feature = "text_pango")] + pub fn font_system(&self) -> Arc { + Arc::clone(&self.font_system) } } @@ -54,7 +91,14 @@ impl Rasterable for CairoRasterizer { } PaintCommand::Text(command) => { #[cfg(feature = "text_pango")] - match text::pango::do_paint_text(&cr.clone(), tile, command, media_store, dpr) { + match text::pango::do_paint_text( + &cr.clone(), + tile, + command, + media_store, + dpr, + &self.font_system, + ) { Ok(_) => {} Err(e) => { log::warn!("Failed to paint text: {:?}", e); diff --git a/crates/gosub_renderer_cairo/src/rasterizer/text/pango.rs b/crates/gosub_renderer_cairo/src/rasterizer/text/pango.rs index 7ebd17e31..c814667d2 100644 --- a/crates/gosub_renderer_cairo/src/rasterizer/text/pango.rs +++ b/crates/gosub_renderer_cairo/src/rasterizer/text/pango.rs @@ -1,4 +1,4 @@ -use crate::font::pango::{find_available_font, to_pango_weight}; +use crate::font::pango::{to_pango_weight, PangoFontSystem}; use crate::rasterizer::brush::set_brush; use cairo::{Antialias, Context, Error, Filter, FontOptions, Format, HintMetrics, HintStyle, ImageSurface}; use gosub_render_pipeline::common::media::MediaStore; @@ -14,8 +14,9 @@ pub(crate) fn do_paint_text( cmd: &Text, media_store: &MediaStore, dpr: i32, + font_system: &PangoFontSystem, ) -> Result<(), Error> { - let surface = create_text_layout(cmd, media_store, dpr)?; + let surface = create_text_layout(cmd, media_store, dpr, font_system)?; cr.save()?; @@ -34,7 +35,12 @@ pub(crate) fn do_paint_text( Ok(()) } -fn create_text_layout(cmd: &Text, media_store: &MediaStore, dpr: i32) -> Result { +fn create_text_layout( + cmd: &Text, + media_store: &MediaStore, + dpr: i32, + font_system: &PangoFontSystem, +) -> Result { // Physical pixel width = CSS width × DPR. let taffy_width = (cmd.rect.width.ceil() as i32 * dpr).max(1); @@ -44,7 +50,7 @@ fn create_text_layout(cmd: &Text, media_store: &MediaStore, dpr: i32) -> Result< // text that taffy already wrapped is not forced back onto one line. let probe_surface = ImageSurface::create(Format::ARgb32, 1, 1)?; let probe_cr = Context::new(&probe_surface)?; - let natural_layout = build_pango_layout_unconstrained(&probe_cr, cmd, dpr); + let natural_layout = build_pango_layout_unconstrained(&probe_cr, cmd, dpr, font_system); let (_, natural_rect) = natural_layout.pixel_extents(); let pango_natural_width = natural_rect.width().max(1); let metric_slack = 20 * dpr; @@ -57,7 +63,7 @@ fn create_text_layout(cmd: &Text, media_store: &MediaStore, dpr: i32) -> Result< // Measure actual pango height at the resolved width. let measure_surface = ImageSurface::create(Format::ARgb32, width, 1)?; let measure_cr = Context::new(&measure_surface)?; - let measure_layout = build_pango_layout(&measure_cr, cmd, width, dpr); + let measure_layout = build_pango_layout(&measure_cr, cmd, width, dpr, font_system); let (_, logical_rect) = measure_layout.pixel_extents(); let pango_height = logical_rect.height().max(1); @@ -66,7 +72,7 @@ fn create_text_layout(cmd: &Text, media_store: &MediaStore, dpr: i32) -> Result< let surface = ImageSurface::create(Format::ARgb32, width, height)?; let cr = Context::new(&surface)?; - let layout = build_pango_layout(&cr, cmd, width, dpr); + let layout = build_pango_layout(&cr, cmd, width, dpr, font_system); set_brush(&cr, &cmd.brush, cmd.rect, media_store); cr.move_to(0.0, 0.0); @@ -79,19 +85,35 @@ fn create_text_layout(cmd: &Text, media_store: &MediaStore, dpr: i32) -> Result< Ok(surface) } -fn build_pango_layout_unconstrained(cr: &Context, cmd: &Text, dpr: i32) -> pangocairo::pango::Layout { - let layout = build_pango_layout_inner(cr, cmd, dpr); +fn build_pango_layout_unconstrained( + cr: &Context, + cmd: &Text, + dpr: i32, + font_system: &PangoFontSystem, +) -> pangocairo::pango::Layout { + let layout = build_pango_layout_inner(cr, cmd, dpr, font_system); layout.set_width(-1); layout } -fn build_pango_layout(cr: &Context, cmd: &Text, width: i32, dpr: i32) -> pangocairo::pango::Layout { - let layout = build_pango_layout_inner(cr, cmd, dpr); +fn build_pango_layout( + cr: &Context, + cmd: &Text, + width: i32, + dpr: i32, + font_system: &PangoFontSystem, +) -> pangocairo::pango::Layout { + let layout = build_pango_layout_inner(cr, cmd, dpr, font_system); layout.set_width(width * SCALE); layout } -fn build_pango_layout_inner(cr: &Context, cmd: &Text, dpr: i32) -> pangocairo::pango::Layout { +fn build_pango_layout_inner( + cr: &Context, + cmd: &Text, + dpr: i32, + font_system: &PangoFontSystem, +) -> pangocairo::pango::Layout { if let Ok(mut font_opts) = FontOptions::new() { font_opts.set_antialias(Antialias::Gray); font_opts.set_hint_style(HintStyle::Full); @@ -102,7 +124,7 @@ fn build_pango_layout_inner(cr: &Context, cmd: &Text, dpr: i32) -> pangocairo::p let layout = create_layout(cr); context_set_resolution(&layout.context(), 72.0); - let selected_family = find_available_font(cmd.font_info.family.as_str(), &layout.context()); + let selected_family = font_system.find_available_font(cmd.font_info.family.as_str(), &layout.context()); let mut font_desc = FontDescription::new(); font_desc.set_family(&selected_family); // Font size in physical pixels = CSS size × DPR. diff --git a/crates/gosub_renderer_skia/Cargo.toml b/crates/gosub_renderer_skia/Cargo.toml index f4ce6a4bc..66ef9a854 100644 --- a/crates/gosub_renderer_skia/Cargo.toml +++ b/crates/gosub_renderer_skia/Cargo.toml @@ -12,7 +12,7 @@ log = "0.4.29" anyhow = "1.0" parking_lot = "0.12" -skia-safe = { version = "0.87.0", features = ["textlayout"] } +skia-safe = { version = "0.97.2", features = ["textlayout"] } [lints] workspace = true diff --git a/crates/gosub_renderer_vello/Cargo.toml b/crates/gosub_renderer_vello/Cargo.toml index 735a1832b..1724a25e1 100644 --- a/crates/gosub_renderer_vello/Cargo.toml +++ b/crates/gosub_renderer_vello/Cargo.toml @@ -12,6 +12,7 @@ required-features = ["text_parley"] [dependencies] gosub_render_pipeline = { version = "0.1.0", path = "../gosub_render_pipeline", features = ["backend_vello"] } +gosub_fontmanager = { version = "0.1.0", path = "../gosub_fontmanager", registry = "gosub" } gosub_shared = { version = "0.1.1", path = "../gosub_shared", registry = "gosub" } resvg = "0.47.0" log = "0.4.29" @@ -22,8 +23,8 @@ vello = "0.8.0" pollster = "0.4.0" winit = "0.30.12" -parley = { version = "0.3.0", optional = true } -skia-safe = { version = "0.87.0", features = ["textlayout"], optional = true } +parley = { version = "0.9.0", optional = true, default-features = false, features = ["std"] } +skia-safe = { version = "0.97.2", features = ["textlayout"], optional = true } [features] default = ["text_parley"] diff --git a/crates/gosub_renderer_vello/src/font/parley.rs b/crates/gosub_renderer_vello/src/font/parley.rs index ce1b437fb..c9307fc43 100644 --- a/crates/gosub_renderer_vello/src/font/parley.rs +++ b/crates/gosub_renderer_vello/src/font/parley.rs @@ -1,53 +1,48 @@ use gosub_render_pipeline::common::font::{FontAlignment, FontInfo}; -use parking_lot::Mutex; -use parley::{AlignmentOptions, Layout}; -use std::sync::OnceLock; - -static FONT_CTX: OnceLock> = OnceLock::new(); -static LAYOUT_CTX: OnceLock> = OnceLock::new(); - -pub fn get_font_context() -> parking_lot::MutexGuard<'static, parley::FontContext> { - FONT_CTX.get_or_init(|| Mutex::new(parley::FontContext::new())).lock() -} - -fn get_layout_context() -> parking_lot::MutexGuard<'static, parley::LayoutContext> { - LAYOUT_CTX - .get_or_init(|| Mutex::new(parley::LayoutContext::new())) - .lock() -} - -pub fn get_parley_layout(text: &str, font_info: &FontInfo, max_width: f64) -> Layout<[u8; 4]> { - let font_stack = parley::FontStack::from(font_info.family.as_str()); - - let display_scale = 1.0; - let max_advance = (max_width * display_scale) as f32; - - let mut font_ctx = get_font_context(); - let mut layout_ctx = get_layout_context(); - - let mut builder = layout_ctx.ranged_builder(&mut font_ctx, text, display_scale as f32); - builder.push_default(font_stack); - builder.push_default(parley::StyleProperty::LineHeight( - font_info.line_height as f32 / font_info.size as f32, - )); - builder.push_default(parley::StyleProperty::FontSize(font_info.size as f32)); - builder.push_default(parley::StyleProperty::FontWeight(parley::FontWeight::new( +use parley::{Alignment, AlignmentOptions, FontContext, FontFamily, Layout, LayoutContext, StyleProperty}; + +/// Build a parley layout for `text` using the shared `font_cx`. +/// +/// A fresh `LayoutContext` is created per call because its brush type `[u8; 4]` +/// is render-pipeline-specific and it holds no expensive state — the expensive +/// state (font collection) lives in `font_cx` which is shared. +pub fn get_parley_layout( + text: &str, + font_info: &FontInfo, + max_width: f64, + font_cx: &mut FontContext, +) -> Layout<[u8; 4]> { + let mut layout_cx: LayoutContext<[u8; 4]> = LayoutContext::new(); + + let display_scale = 1.0_f32; + let max_advance = (max_width * display_scale as f64) as f32; + + let mut builder = layout_cx.ranged_builder(font_cx, text, display_scale, false); + + builder.push_default(StyleProperty::FontFamily(FontFamily::Source( + font_info.family.as_str().into(), + ))); + builder.push_default(StyleProperty::FontSize(font_info.size as f32)); + builder.push_default(StyleProperty::LineHeight(parley::LineHeight::Absolute( + font_info.line_height as f32, + ))); + builder.push_default(StyleProperty::FontWeight(parley::FontWeight::new( font_info.weight as f32, ))); if font_info.slant != 0 { - builder.push_default(parley::StyleProperty::FontStyle(parley::FontStyle::Italic)); + builder.push_default(StyleProperty::FontStyle(parley::FontStyle::Italic)); } let align = match font_info.alignment { - FontAlignment::Start => parley::layout::Alignment::Start, - FontAlignment::Center => parley::layout::Alignment::Middle, - FontAlignment::End => parley::layout::Alignment::End, - FontAlignment::Justify => parley::layout::Alignment::Justified, + FontAlignment::Start => Alignment::Start, + FontAlignment::Center => Alignment::Center, + FontAlignment::End => Alignment::End, + FontAlignment::Justify => Alignment::Justify, }; let mut layout: Layout<[u8; 4]> = builder.build(text); layout.break_all_lines(Some(max_advance * 1.01)); - layout.align(Some(max_advance), align, AlignmentOptions::default()); + layout.align(align, AlignmentOptions::default()); layout } diff --git a/crates/gosub_renderer_vello/src/rasterizer.rs b/crates/gosub_renderer_vello/src/rasterizer.rs index b13d3394d..bf3b2c873 100644 --- a/crates/gosub_renderer_vello/src/rasterizer.rs +++ b/crates/gosub_renderer_vello/src/rasterizer.rs @@ -1,3 +1,4 @@ +use gosub_fontmanager::ParleyFontSystem; use gosub_render_pipeline::common::geo::Dimension; use gosub_render_pipeline::common::media::MediaStore; use gosub_render_pipeline::common::texture::TextureId; @@ -6,6 +7,7 @@ use gosub_render_pipeline::painter::commands::PaintCommand; use gosub_render_pipeline::rasterizer::Rasterable; use gosub_render_pipeline::render::backends::vello::WgpuResources; use gosub_render_pipeline::tiler::{Tile, TileId}; +use parking_lot::Mutex; use std::sync::Arc; use vello::kurbo::{Affine, Rect, Vec2}; use vello::peniko::{Color, Fill}; @@ -19,11 +21,30 @@ mod text; pub struct VelloRasterizer { resources: Arc, + font_system: Arc>, } impl VelloRasterizer { + /// Create a rasterizer with its own font system. + /// + /// To share the font collection with `TaffyLayouter` (so layout and rendering + /// use identical font data), use [`VelloRasterizer::with_font_system`] instead + /// and pass the same `Arc` to both. pub fn new(resources: Arc) -> Self { - Self { resources } + Self { + resources, + font_system: Arc::new(Mutex::new(ParleyFontSystem::new())), + } + } + + /// Create a rasterizer that shares an existing font system. + pub fn with_font_system(resources: Arc, font_system: Arc>) -> Self { + Self { resources, font_system } + } + + /// Expose the font system so callers can share it with the layout engine. + pub fn font_system(&self) -> Arc> { + Arc::clone(&self.font_system) } } @@ -38,6 +59,11 @@ impl Rasterable for VelloRasterizer { let affine = Affine::translate(Vec2::new(-tile.rect.x, -tile.rect.y)); + // Lock the font system once per tile. All text commands in this tile share + // the same FontContext, so font data is loaded at most once per family. + let mut font_guard = self.font_system.lock(); + let font_cx = font_guard.font_cx_mut(); + for element in &tile.elements { for command in &element.paint_commands { match command { @@ -48,7 +74,7 @@ impl Rasterable for VelloRasterizer { rectangle::do_paint_rectangle(&mut scene, command, affine, media_store); } PaintCommand::Text(command) => { - match text::do_paint_text(&mut scene, command, tile_size, affine, media_store) { + match text::do_paint_text(&mut scene, command, tile_size, affine, media_store, font_cx) { Ok(_) => {} Err(e) => { log::warn!("Failed to paint text: {:?}", e); @@ -58,6 +84,7 @@ impl Rasterable for VelloRasterizer { } } } + drop(font_guard); scene.pop_layer(); diff --git a/crates/gosub_renderer_vello/src/rasterizer/text/parley.rs b/crates/gosub_renderer_vello/src/rasterizer/text/parley.rs index 3fb3a2d66..401250d96 100644 --- a/crates/gosub_renderer_vello/src/rasterizer/text/parley.rs +++ b/crates/gosub_renderer_vello/src/rasterizer/text/parley.rs @@ -5,6 +5,7 @@ use gosub_render_pipeline::common::media::MediaStore; use gosub_render_pipeline::painter::commands::brush::Brush; use gosub_render_pipeline::painter::commands::text::Text; use parley::layout::{GlyphRun, PositionedLayoutItem}; +use parley::FontContext; use vello::kurbo::Affine; use vello::peniko::Fill; use vello::Scene; @@ -15,8 +16,9 @@ pub fn do_paint_text( _tile_size: Dimension, affine: Affine, media_store: &MediaStore, + font_cx: &mut FontContext, ) -> Result<(), anyhow::Error> { - let layout = get_parley_layout(cmd.text.as_str(), &cmd.font_info, cmd.rect.width); + let layout = get_parley_layout(cmd.text.as_str(), &cmd.font_info, cmd.rect.width, font_cx); for line in layout.lines() { for item in line.items() { diff --git a/crates/gosub_svg/src/lib.rs b/crates/gosub_svg/src/lib.rs index e074b0227..67f72a38c 100644 --- a/crates/gosub_svg/src/lib.rs +++ b/crates/gosub_svg/src/lib.rs @@ -1,9 +1,23 @@ use ::resvg::usvg; use gosub_interface::config::HasDocument; use gosub_interface::document::Document; - use gosub_shared::node::NodeId; use gosub_shared::types::Result; +use std::sync::{Arc, OnceLock}; + +/// Return `usvg::Options` backed by a shared fontdb that has system fonts loaded. +fn svg_options() -> usvg::Options<'static> { + static FONTDB: OnceLock> = OnceLock::new(); + let fontdb = Arc::clone(FONTDB.get_or_init(|| { + let mut db = usvg::fontdb::Database::new(); + db.load_system_fonts(); + Arc::new(db) + })); + usvg::Options { + fontdb, + ..Default::default() + } +} pub struct SVGDocument { pub tree: usvg::Tree, @@ -12,7 +26,7 @@ pub struct SVGDocument { impl SVGDocument { #[allow(clippy::should_implement_trait)] pub fn from_str(svg: &str) -> Result { - let opts = usvg::Options { ..Default::default() }; + let opts = svg_options(); let tree = usvg::Tree::from_str(svg, &opts)?; Ok(Self { tree }) diff --git a/crates/gosub_taffy/src/compute/inline.rs b/crates/gosub_taffy/src/compute/inline.rs index 96d02bbb3..cc3aa53a6 100644 --- a/crates/gosub_taffy/src/compute/inline.rs +++ b/crates/gosub_taffy/src/compute/inline.rs @@ -1,7 +1,4 @@ -use parking_lot::Mutex; -use parley::fontique::{FallbackKey, FontWeight, Script}; -use parley::{AlignmentOptions, FontContext}; -use std::sync::LazyLock; +use parley::AlignmentOptions; use taffy::{ AvailableSpace, CollapsibleMarginSet, Layout, LayoutInput, LayoutOutput, LayoutPartialTree, NodeId, Point, Rect, RunMode, Size, @@ -9,28 +6,15 @@ use taffy::{ use gosub_interface::config::HasLayouter; use gosub_interface::css3::{CssProperty, CssValue}; -use gosub_interface::font::{FontBlob, FontInfo, FontManager, FontStyle, HasFontManager}; +use gosub_interface::font::{FontBlob, FontStyle}; use gosub_interface::layout::{Decoration, DecorationStyle, HasTextLayout, LayoutNode, LayoutTree}; use gosub_shared::font::Glyph; +use gosub_shared::geo; use gosub_shared::geo::FP; -use gosub_shared::{geo, ROBOTO_FONT}; use crate::text::TextLayout; use crate::{Display, LayoutDocument, TaffyLayouter}; -static FONT_CX: LazyLock> = LazyLock::new(|| { - let mut ctx = FontContext::default(); - - let fonts = ctx.collection.register_fonts(ROBOTO_FONT.to_vec().into(), None); - - ctx.collection.append_fallbacks( - FallbackKey::new(Script::from_str_unchecked("Latn"), None), - fonts.iter().map(|f| f.0), - ); - - Mutex::new(ctx) -}); - /// Computes the layout for inline elements. pub fn compute_inline_layout>( tree: &mut LayoutDocument, @@ -42,7 +26,7 @@ pub fn compute_inline_layout>( // layout_input.sizing_mode = SizingMode::ContentSize; // If there are no children, the node is hidden - let Some(children) = tree.0.children(node_id) else { + let Some(children) = tree.tree.children(node_id) else { return LayoutOutput::HIDDEN; }; @@ -53,7 +37,7 @@ pub fn compute_inline_layout>( let mut str_buf = String::new(); // Text node data holds the information about the text nodes. There can be multiple text node data elements for // a single text, for instance, if there are different font sizes or weights inside the text. - let mut text_node_data: Vec> = Vec::new(); + let mut text_node_data: Vec = Vec::new(); // List of any inline boxes that are inside the node let mut inline_boxes = Vec::new(); @@ -65,7 +49,7 @@ pub fn compute_inline_layout>( let child_node_id = NodeId::from((*child).into()); // If the child is not a node, we skip it - let Some(node) = tree.0.get_node_mut(*child) else { + let Some(node) = tree.tree.get_node_mut(*child) else { continue; }; node.clear_text_layout(); @@ -109,10 +93,11 @@ pub fn compute_inline_layout>( .get_property("letter-spacing") .map(gosub_interface::css3::CssProperty::unit_to_px); - let font_info = ::FontInfo::new(&font_family) - .unwrap() - .with_weight(font_weight.value() as i32) - .with_style(font_style); + let font_info = FontDescriptor { + family: font_family, + weight: font_weight, + style: font_style, + }; let mut underline = false; let mut overline = false; @@ -126,8 +111,8 @@ pub fn compute_inline_layout>( .and_then(gosub_interface::css3::CssProperty::parse_color); // Generate decoration styles - if let Some(actual_parent) = tree.0.parent_id(node_id) { - if let Some(node) = tree.0.get_node_mut(actual_parent) { + if let Some(actual_parent) = tree.tree.parent_id(node_id) { + if let Some(node) = tree.tree.get_node_mut(actual_parent) { let decoration_line = node.get_property("text-decoration-line"); if let Some(decoration_line) = decoration_line { @@ -213,7 +198,7 @@ pub fn compute_inline_layout>( tree.update_style(*child); - let size = if let Some(cache) = tree.0.get_cache(*child) { + let size = if let Some(cache) = tree.tree.get_cache(*child) { if cache.display == Display::Inline { //TODO: handle margins here out.content_size @@ -244,21 +229,23 @@ pub fn compute_inline_layout>( str_buf.push(0 as char); } - // We use the parley layout engine to generate the text layout + // We use the parley layout engine to generate the text layout. + // FontContext is sourced from the shared ParleyFontSystem so that all callers + // (layout, render) use the same font collection. let mut layout_cx: parley::LayoutContext = parley::LayoutContext::new(); - // let mut scale_cx = ScaleContext::new(); - let mut font_context = FONT_CX.lock(); + let mut font_system = tree.font_system.lock(); + let font_context = font_system.font_cx_mut(); - let mut builder = layout_cx.ranged_builder(&mut font_context, &str_buf, 1.0, false); + let mut builder = layout_cx.ranged_builder(font_context, &str_buf, 1.0, false); let mut align = parley::Alignment::default(); // The first text node is the default style for the text. This is why this is treated separately. if let Some(default) = text_node_data.first() { - let info: &<::FontManager as FontManager>::FontInfo = default.font_info(); + let info = &default.font_info; builder.push_default(parley::StyleProperty::FontFamily(parley::FontFamily::Source( - info.family().into(), + info.family.as_str().into(), ))); builder.push_default(parley::StyleProperty::FontSize(default.font_size)); if let Some(line_height) = default.line_height { @@ -272,8 +259,8 @@ pub fn compute_inline_layout>( if let Some(letter_spacing) = default.letter_spacing { builder.push_default(parley::StyleProperty::LetterSpacing(letter_spacing)); } - builder.push_default(parley::StyleProperty::FontWeight(FontWeight::new(info.weight() as f32))); - builder.push_default(parley::StyleProperty::FontStyle(match info.style() { + builder.push_default(parley::StyleProperty::FontWeight(info.weight)); + builder.push_default(parley::StyleProperty::FontStyle(match info.style { FontStyle::Normal => parley::FontStyle::Normal, FontStyle::Italic => parley::FontStyle::Italic, FontStyle::Oblique => parley::FontStyle::Oblique(None), @@ -311,10 +298,10 @@ pub fn compute_inline_layout>( let mut from = default.to; for (idx, text_node) in text_node_data.get(1..).unwrap_or_default().iter().enumerate() { - let info: &<::FontManager as FontManager>::FontInfo = text_node.font_info(); + let info = &text_node.font_info; builder.push( - parley::StyleProperty::FontFamily(parley::FontFamily::Source(info.family().into())), + parley::StyleProperty::FontFamily(parley::FontFamily::Source(info.family.as_str().into())), from..text_node.to, ); builder.push(parley::StyleProperty::FontSize(text_node.font_size), from..text_node.to); @@ -330,12 +317,9 @@ pub fn compute_inline_layout>( if let Some(letter_spacing) = text_node.letter_spacing { builder.push(parley::StyleProperty::LetterSpacing(letter_spacing), from..text_node.to); } + builder.push(parley::StyleProperty::FontWeight(info.weight), from..text_node.to); builder.push( - parley::StyleProperty::FontWeight(FontWeight::new(info.weight() as f32)), - from..text_node.to, - ); - builder.push( - parley::StyleProperty::FontStyle(match info.style() { + parley::StyleProperty::FontStyle(match info.style { FontStyle::Normal => parley::FontStyle::Normal, FontStyle::Italic => parley::FontStyle::Italic, FontStyle::Oblique => parley::FontStyle::Oblique(None), @@ -398,7 +382,7 @@ pub fn compute_inline_layout>( let mut layout = builder.build(&str_buf); - drop(font_context); + drop(font_system); let max_width = match layout_input.available_space.width { AvailableSpace::Definite(width) => Some(width), @@ -525,7 +509,7 @@ pub fn compute_inline_layout>( ids.push(node_id); - let Some(node) = tree.0.get_node_mut(node_id) else { + let Some(node) = tree.tree.get_node_mut(node_id) else { continue; }; @@ -561,7 +545,9 @@ pub fn compute_inline_layout>( } for id in ids { - let Some(node) = tree.0.get_node_mut(id) else { continue }; + let Some(node) = tree.tree.get_node_mut(id) else { + continue; + }; let Some(layouts) = node.get_text_layouts_mut() else { continue; @@ -621,18 +607,27 @@ pub fn compute_inline_layout>( } } +/// CSS font properties for a single text run. Replaces the old `FontInfo` +/// associated-type machinery — this is a plain data struct with no generics. +#[derive(Debug, Clone)] +struct FontDescriptor { + family: String, + weight: parley::FontWeight, + style: FontStyle, +} + /// Structure that holds information for a (partial) text that consists of a single font size, weight, etc. /// If a string consists of multiple font sizes, weights, etc., there will be multiple `TextNodeData` elements. /// For instance: "This is a bold text". In this example there will be three text nodes: "This is a ", /// "bold" and " text" with different font weights. #[derive(Debug)] -struct TextNodeData { +struct TextNodeData { /// Start index of the text node in the complete string (`str_buf`) to: usize, /// Node identifier that holds the text id: NodeId, - /// Actual font for rendering and layouting - font_info: <::FontManager as FontManager>::FontInfo, + /// CSS font properties for this run + font_info: FontDescriptor, /// Font size font_size: f32, /// Line height in case of multiple lines @@ -643,19 +638,12 @@ struct TextNodeData { letter_spacing: Option, /// Alignment of the text alignment: parley::Alignment, - /// Unknown + /// Font variation axes var_axes: Vec, /// Decoration of the font (strikethrough, underline etc) decoration: Decoration, } -impl TextNodeData { - /// Returns the font info of the text node - pub fn font_info(&self) -> &<::FontManager as FontManager>::FontInfo { - &self.font_info - } -} - fn parse_alignment(node: &mut impl LayoutNode) -> parley::Alignment { let Some(prop) = node.get_property("text-align") else { return parley::Alignment::Start; diff --git a/crates/gosub_taffy/src/lib.rs b/crates/gosub_taffy/src/lib.rs index faff65628..cdc1fc5cb 100644 --- a/crates/gosub_taffy/src/lib.rs +++ b/crates/gosub_taffy/src/lib.rs @@ -1,4 +1,6 @@ +use parking_lot::Mutex; use std::ops::{Deref, DerefMut}; +use std::sync::Arc; use std::vec::IntoIter; use taffy::{ compute_block_layout, compute_cached_layout, compute_flexbox_layout, compute_grid_layout, compute_hidden_layout, @@ -7,6 +9,7 @@ use taffy::{ LayoutOutput, LayoutPartialTree, NodeId as TaffyId, SizingMode, Style, TraversePartialTree, }; +use gosub_fontmanager::ParleyFontSystem; use gosub_interface::config::HasLayouter; use gosub_interface::font::HasFontManager; use gosub_interface::layout::{Layout as TLayout, LayoutCache, LayoutNode, LayoutTree, Layouter}; @@ -93,8 +96,20 @@ impl TLayout for Layout { } /// Our implementation of the Taffy layouter. -#[derive(Clone, Copy, Debug)] -pub struct TaffyLayouter; +/// +/// Carries the shared font system so all inline layout passes use the same +/// font collection, avoiding the divergence that arises from per-call static +/// `FontContext` instances. +#[derive(Clone, Debug)] +pub struct TaffyLayouter { + font_system: Arc>, +} + +impl TaffyLayouter { + pub fn new(font_system: Arc>) -> Self { + Self { font_system } + } +} #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] #[allow(unused)] @@ -160,16 +175,13 @@ impl + HasFontManager> Layouter for height: AvailableSpace::Definite(space.height as f32), }; - // We need to convert our tree into a LayoutDocument. This document can be used by Taffy to layout the tree - // throughout the LayoutPartialTree trait that our LayoutDocument implements. - let mut tree: LayoutDocument = LayoutDocument(tree); - - // Precompute the styles for all nodes in the layout tree. This will convert all the CSS properties we need - // for layouting into Taffy properties that are stored in a cache. - Self::precompute_style(&mut tree, root); + let mut doc: LayoutDocument = LayoutDocument { + tree, + font_system: Arc::clone(&self.font_system), + }; - // Now let taffy compute the layout of the tree. - compute_root_layout(&mut tree, TaffyId::from(root.into()), size); + Self::precompute_style(&mut doc, root); + compute_root_layout(&mut doc, TaffyId::from(root.into()), size); Ok(()) } @@ -180,22 +192,22 @@ impl TaffyLayouter { tree: &mut LayoutDocument, root: >::NodeId, ) { - // Convert our CSS properties into Taffy properties and store them in a cache. tree.update_style(root); - let Some(children) = tree.0.children(root) else { + let Some(children) = tree.tree.children(root) else { return; }; - // Recursively precompute the style for all children of the current node. for child in children { Self::precompute_style(tree, >::NodeId::from(child.into())); } } } -#[repr(transparent)] -pub struct LayoutDocument<'a, C: HasLayouter>(&'a mut C::LayoutTree); +pub struct LayoutDocument<'a, C: HasLayouter> { + pub(crate) tree: &'a mut C::LayoutTree, + pub(crate) font_system: Arc>, +} impl> TraversePartialTree for LayoutDocument<'_, C> { type ChildIter<'a> @@ -206,10 +218,10 @@ impl> TraversePartialTree for LayoutDoc fn child_ids(&self, parent: TaffyId) -> Self::ChildIter<'_> { let parent = >::NodeId::from(parent.into()); - if let Some(children) = self.0.children(parent) { + if let Some(children) = self.tree.children(parent) { children .iter() - .filter(|id| self.0.contains(id)) //FIXME: This is a hack, we should not have to filter out non-existing nodes + .filter(|id| self.tree.contains(id)) //FIXME: This is a hack, we should not have to filter out non-existing nodes .map(|id| TaffyId::from(Into::into(*id))) .collect::>() .into_iter() @@ -221,16 +233,16 @@ impl> TraversePartialTree for LayoutDoc fn child_count(&self, parent: TaffyId) -> usize { let parent = >::NodeId::from(parent.into()); - self.0.child_count(parent) + self.tree.child_count(parent) } fn get_child_id(&self, parent: TaffyId, index: usize) -> TaffyId { let parent = >::NodeId::from(parent.into()); - if let Some(node) = self.0.children(parent) { + if let Some(node) = self.tree.children(parent) { TaffyId::from( node.into_iter() - .filter(|id| self.0.contains(id)) //FIXME: This is a hack, we should not have to filter out non-existing nodes + .filter(|id| self.tree.contains(id)) //FIXME: This is a hack, we should not have to filter out non-existing nodes .nth(index) .map(Into::into) .unwrap_or_default(), @@ -244,13 +256,13 @@ impl> TraversePartialTree for LayoutDoc impl> LayoutDocument<'_, C> { /// Get the CSS properties for the given node, and store it inside the cache fn update_style(&mut self, node_id: >::NodeId) { - let Some(node) = self.0.get_node_mut(node_id) else { + let Some(node) = self.tree.get_node_mut(node_id) else { return; }; let (style, display, calc_storage) = get_style_from_node(node); - if let Some(cache) = self.0.get_cache_mut(node_id) { + if let Some(cache) = self.tree.get_cache_mut(node_id) { cache.style = style; cache.display = display; // Replace any previous calc expressions atomically with the new style so the raw @@ -261,13 +273,13 @@ impl> LayoutDocument<'_, C> { /// Get the taffy style properties for a given node. If the style is dirty, we will update the style first. fn get_taffy_style(&mut self, node_id: >::NodeId) -> &Style { - let dirty_style = self.0.style_dirty(node_id); + let dirty_style = self.tree.style_dirty(node_id); if dirty_style { self.update_style(node_id); } let cache = self - .0 + .tree .get_cache(node_id) .expect("Cache not found, why again does taffy don't use optionals?"); @@ -276,7 +288,7 @@ impl> LayoutDocument<'_, C> { /// Force the taffy style from the cache. Do not care about dirty styles fn get_taffy_style_no_update(&self, node_id: >::NodeId) -> &Style { - if let Some(cache) = self.0.get_cache(node_id) { + if let Some(cache) = self.tree.get_cache(node_id) { return &cache.style; } panic!( @@ -290,7 +302,7 @@ impl> CacheTree for LayoutDocument<'_, fn cache_get(&self, node_id: TaffyId, input: &LayoutInput) -> Option { let node_id = >::NodeId::from(node_id.into()); let cache = &self - .0 + .tree .get_cache(node_id) .expect("Cache not found, why again does taffy don't use optionals?") .taffy; @@ -301,7 +313,7 @@ impl> CacheTree for LayoutDocument<'_, fn cache_store(&mut self, node_id: TaffyId, input: &LayoutInput, layout_output: LayoutOutput) { let node_id = >::NodeId::from(node_id.into()); let cache = &mut self - .0 + .tree .get_cache_mut(node_id) .expect("Cache not found, why again does taffy don't use optionals?") .taffy; @@ -312,7 +324,7 @@ impl> CacheTree for LayoutDocument<'_, fn cache_clear(&mut self, node_id: TaffyId) { let node_id = >::NodeId::from(node_id.into()); let cache = &mut self - .0 + .tree .get_cache_mut(node_id) .expect("Cache not found, why again does taffy don't use optionals?") .taffy; @@ -342,7 +354,7 @@ impl> LayoutPartialTree for LayoutDocum let node_id = >::NodeId::from(node_id.into()); - self.0.set_layout(node_id, layout); + self.tree.set_layout(node_id, layout); } fn compute_child_layout(&mut self, node_id: TaffyId, mut inputs: LayoutInput) -> LayoutOutput { @@ -351,7 +363,7 @@ impl> LayoutPartialTree for LayoutDocum compute_cached_layout(self, node_id, inputs, |tree, node_id_taffy, inputs| { let node_id = >::NodeId::from(node_id_taffy.into()); - if let Some(node) = tree.0.get_node_mut(node_id) { + if let Some(node) = tree.tree.get_node_mut(node_id) { // If we are an inline parent, we should compute the inline layout if node.is_anon_inline_parent() { println!("Node: {node_id:?} is inline parent"); diff --git a/examples/gtk4-skia-gpu/Cargo.toml b/examples/gtk4-skia-gpu/Cargo.toml index 8c72d5ae7..e3d538c7b 100644 --- a/examples/gtk4-skia-gpu/Cargo.toml +++ b/examples/gtk4-skia-gpu/Cargo.toml @@ -11,7 +11,7 @@ path = "main.rs" gosub_engine = { path = "../../crates/gosub_engine", features = ["gtk4", "backend_skia", "sqlite_cookie_store"] } gosub_render_pipeline = { path = "../../crates/gosub_render_pipeline", features = ["backend_skia_gl"] } gtk4 = "0.11.1" -skia-safe = { version = "0.87.0", features = ["gl", "egl"] } +skia-safe = { version = "0.97.2", features = ["gl", "egl"] } once_cell = "1.21" parking_lot = "0.12" tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "time"] } diff --git a/examples/winit-skia-gpu/Cargo.toml b/examples/winit-skia-gpu/Cargo.toml index 452b440b9..f746a0f7c 100644 --- a/examples/winit-skia-gpu/Cargo.toml +++ b/examples/winit-skia-gpu/Cargo.toml @@ -12,7 +12,7 @@ gosub_engine = { path = "../../crates/gosub_engine", features = ["winit", "backe gosub_render_pipeline = { path = "../../crates/gosub_render_pipeline", features = ["backend_skia_gl"] } winit = "0.30.12" softbuffer = "0.4" -skia-safe = { version = "0.87.0", features = ["gl", "egl"] } +skia-safe = { version = "0.97.2", features = ["gl", "egl"] } glutin = "0.32" glutin-winit = "0.5" raw-window-handle = "0.6" diff --git a/examples/winit-skia/Cargo.toml b/examples/winit-skia/Cargo.toml index 1cbf992ca..92626445c 100644 --- a/examples/winit-skia/Cargo.toml +++ b/examples/winit-skia/Cargo.toml @@ -12,7 +12,7 @@ gosub_engine = { path = "../../crates/gosub_engine", features = ["winit", "backe gosub_render_pipeline = { path = "../../crates/gosub_render_pipeline", features = ["backend_skia"] } winit = "0.30.12" softbuffer = "0.4" -skia-safe = "0.87.0" +skia-safe = "0.97.2" once_cell = "1.21" parking_lot = "0.12" tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "time"] }