diff --git a/Cargo.lock b/Cargo.lock index 10da1e42..4f5a0d40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" dependencies = [ "getrandom", "once_cell", @@ -30,9 +30,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] @@ -88,9 +88,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.5.0" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" dependencies = [ "anstyle", "anstyle-parse", @@ -102,15 +102,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" dependencies = [ "utf8parse", ] @@ -126,9 +126,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "2.1.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", "windows-sys 0.48.0", @@ -146,6 +146,15 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb8f874ecf419dd8165d0279746de966cb8966636d028845e3bd65d519812a" +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + [[package]] name = "arrayref" version = "0.3.7" @@ -180,18 +189,18 @@ checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] name = "async-trait" -version = "0.1.73" +version = "0.1.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -223,9 +232,9 @@ dependencies = [ [[package]] name = "base64" -version = "0.21.4" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "base64-serde" @@ -239,11 +248,10 @@ dependencies = [ [[package]] name = "biblatex" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc9fd60378277e44cd400ec5f35e768ce0d5a63d8d18ac7b1a9231196251dae5" +checksum = "2e41df82f0d1c4919d946bb0c7c3d179b6071246243d308a1bdee6cfecee3bc7" dependencies = [ - "chrono", "numerals", "paste", "strum", @@ -283,9 +291,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" dependencies = [ "serde", ] @@ -313,9 +321,9 @@ dependencies = [ [[package]] name = "brotli" -version = "3.3.4" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -324,9 +332,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "2.3.4" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" +checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -368,9 +376,9 @@ checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" @@ -452,11 +460,21 @@ dependencies = [ "half", ] +[[package]] +name = "citationberg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c857faf24e89710f105b623c174508070a9e11e056a749f251ca4c56f59ad88" +dependencies = [ + "quick-xml 0.28.2", + "serde", +] + [[package]] name = "clap" -version = "4.4.5" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824956d0dca8334758a5b7f7e50518d66ea319330cbceedcf76905c2f6ab30e3" +checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" dependencies = [ "clap_builder", "clap_derive", @@ -464,9 +482,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.5" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122ec64120a49b4563ccaedcbea7818d069ed8e9aa6d829b82d8a4128936b2ab" +checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" dependencies = [ "anstream", "anstyle", @@ -479,18 +497,18 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.4.2" +version = "4.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8baeccdb91cd69189985f87f3c7e453a3a451ab5746cf3be6acc92120bd16d24" +checksum = "bffe91f06a11b4b9420f62103854e90867812cd5d01557f853c5ee8e791b12ae" dependencies = [ "clap", ] [[package]] name = "clap_complete_fig" -version = "4.4.1" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29bdbe21a263b628f83fcbeac86a4416a1d588c7669dd41473bc4149e4e7d2f1" +checksum = "87e571d70e22ec91d34e1c5317c8308035a2280d925167646bf094fc5de1737c" dependencies = [ "clap", "clap_complete", @@ -498,21 +516,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.2" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] name = "clap_lex" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "clipboard-win" @@ -527,22 +545,24 @@ dependencies = [ [[package]] name = "cmd_lib" -version = "1.3.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ba0f413777386d37f85afa5242f277a7b461905254c1af3c339d4af06800f62" +checksum = "5d9f37d2014085bc0596bfbf0f230e43843869a28f9e478563af001750625739" dependencies = [ "cmd_lib_macros", + "env_logger", "faccess", "lazy_static", "log", + "main_error", "os_pipe", ] [[package]] name = "cmd_lib_macros" -version = "1.3.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e66605092ff6c6e37e0246601ae6c3f62dc1880e0599359b5f303497c112dc0" +checksum = "2e99fef68541947629e413080394504cf61142a179eb243fead7beb39b8a94ea" dependencies = [ "proc-macro-error", "proc-macro2", @@ -648,9 +668,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" dependencies = [ "libc", ] @@ -719,9 +739,9 @@ dependencies = [ [[package]] name = "csv" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" dependencies = [ "csv-core", "itoa", @@ -731,9 +751,9 @@ dependencies = [ [[package]] name = "csv-core" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" dependencies = [ "memchr", ] @@ -759,7 +779,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -770,7 +790,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -779,12 +799,6 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" -[[package]] -name = "data-url" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5" - [[package]] name = "data-url" version = "0.3.0" @@ -793,10 +807,11 @@ checksum = "41b319d1b62ffbd002e057f36bebd1f42b9f97927c9577461d855f3513c4289f" [[package]] name = "deranged" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" dependencies = [ + "powerfmt", "serde", ] @@ -855,7 +870,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -872,9 +887,9 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "ecow" -version = "0.1.2" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1990d053cf6edf3f030682dba3b0eb65ef01fabb2686072765d8a17d6728e8" +checksum = "e6ea5e3f9cda726431da9d1a8d5a29785d544b31e98e1ca7a210906244002e02" dependencies = [ "serde", ] @@ -923,15 +938,15 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" [[package]] name = "enum-ordinalize" -version = "3.1.13" +version = "3.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4f76552f53cefc9a7f64987c3701b99d982f7690606fd67de1d09712fbf52f1" +checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" dependencies = [ "num-bigint", "num-traits", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -955,25 +970,14 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ - "errno-dragonfly", "libc", "windows-sys 0.48.0", ] -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "error-code" version = "2.3.1" @@ -1005,6 +1009,12 @@ dependencies = [ "regex", ] +[[package]] +name = "fast-srgb8" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" + [[package]] name = "fastrand" version = "2.0.1" @@ -1045,9 +1055,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", "miniz_oxide", @@ -1067,14 +1077,14 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fontdb" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af8d8cbea8f21307d7e84bca254772981296f058a1d36b461bf4d83a7499fc9e" +checksum = "020e203f177c0fb250fb19455a252e838d2bbbce1f80f25ecc42402aafa8cd38" dependencies = [ "log", "slotmap", "tinyvec", - "ttf-parser 0.19.2", + "ttf-parser", ] [[package]] @@ -1124,9 +1134,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" dependencies = [ "futures-channel", "futures-core", @@ -1139,9 +1149,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", "futures-sink", @@ -1149,15 +1159,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" dependencies = [ "futures-core", "futures-task", @@ -1166,38 +1176,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ "futures-channel", "futures-core", @@ -1263,7 +1273,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf97ba92db08df386e10c8ede66a2a0369bd277090afd8710e19e38de9ec0cd" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "libc", "libgit2-sys", "log", @@ -1306,29 +1316,29 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" [[package]] name = "hayagriva" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065e90e53aa502be868a307f58ca6b46e31143641e809047c689de75619d8cea" +version = "0.4.0" +source = "git+https://github.com/Myriad-Dreamin/hayagriva.git?branch=typst.ts-v0.9.0#567dcc691ddaf0f30644c53db3f50072250fbd93" dependencies = [ "biblatex", - "chrono", - "isolang", - "lazy_static", - "linked-hash-map", + "ciborium", + "citationberg", + "indexmap 2.0.2", + "numerals", "paste", - "regex", - "strum", + "rkyv", + "serde", + "serde_yaml", "thiserror", "unic-langid", "unicode-segmentation", + "unscanny", "url", - "yaml-rust", ] [[package]] @@ -1418,9 +1428,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "human-panic" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2df2fb4e13fa697d21d93061ebcbbd876f5ef643b48ff59cfab57a726ef140" +checksum = "b82da652938b83f94cfdaaf9ae7aaadb8430d84b0dfda226998416318727eac2" dependencies = [ "anstream", "anstyle", @@ -1455,7 +1465,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.9", + "socket2 0.4.10", "tokio", "tower-service", "tracing", @@ -1464,9 +1474,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http", @@ -1491,22 +1501,22 @@ dependencies = [ [[package]] name = "hypher" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "723e315d77ea8aa1aedf53ad979ff0e763cfa2a1b3403248e427ae052f403cad" +checksum = "94bf16dd62ea2bec617a6f8a3e1ba03107311783069a647787ac689d1f35321e" [[package]] name = "iana-time-zone" -version = "0.1.57" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows-core", ] [[package]] @@ -1520,9 +1530,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.3.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b222d891e7bd8c3fb8122cbf255c5e7763ee4824f3620d54a009077c30539fe1" +checksum = "3907b2246e8dd5a29ead8a965e7c0c8a90e9b928e614a4279257d45c5e553e91" dependencies = [ "displaydoc", "serde", @@ -1533,9 +1543,9 @@ dependencies = [ [[package]] name = "icu_locid" -version = "1.3.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56b72c6de0121c00da9828eb3e2603041d563788289bb15feba7c3331de71b5f" +checksum = "f284eb342dc49d3e9d9f3b188489d76b5d22dfb1d1a5e0d1941811253bac625c" dependencies = [ "displaydoc", "litemap", @@ -1546,9 +1556,9 @@ dependencies = [ [[package]] name = "icu_locid_transform" -version = "1.3.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49464337ec1e96a409e34be1d7215bdc20159193df2eb2a89fb94403996092ac" +checksum = "6551daf80882d8e68eee186cc19e132d8bde1b1f059a79b93384a5ca0e8fc5e7" dependencies = [ "displaydoc", "icu_locid", @@ -1560,15 +1570,15 @@ dependencies = [ [[package]] name = "icu_locid_transform_data" -version = "1.3.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce5d8151979828a4645be945302e05c903cbb5c4a86a936965f7605bd5142e06" +checksum = "2a741eba5431f75eb2f1f9022d3cffabcadda6771e54fb4e77c8ba8653e4da44" [[package]] name = "icu_properties" -version = "1.3.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00a2167b0f6a44eebc1f8153e742ce07c6218d5be61eac92b9744b086b802cfc" +checksum = "3477ae70f8ca8dc08ff7574b5398ed0a2f2e4e6b66bdff2558a92ed67e262be1" dependencies = [ "displaydoc", "icu_collections", @@ -1582,15 +1592,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "1.3.0" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7f4134189d15b08d2abf068c444ba408fdc125d00d4dbd9176765a190023c2" +checksum = "98507b488098f45eb95ef495612a2012e4d8ad6095dda86cb2f1728aa2204a60" [[package]] name = "icu_provider" -version = "1.3.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d3810a06fce5c900f8ace41b72abf8f6308f77c9e7647211aa5f121c0c9f43" +checksum = "68acdef80034b5e35d8524e9817479d389a4f9774f3f0cbe1bf3884d80fd5934" dependencies = [ "displaydoc", "icu_locid", @@ -1607,9 +1617,9 @@ dependencies = [ [[package]] name = "icu_provider_adapters" -version = "1.3.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb43949fa871c2c79828575058971af57b0ac5e58c9e4fdb69f1f4a2f3f4e17" +checksum = "36b380ef2d3d93b015cd0563d7e0d005cc07f82a5503716dbc191798d0079e1d" dependencies = [ "icu_locid", "icu_locid_transform", @@ -1620,9 +1630,9 @@ dependencies = [ [[package]] name = "icu_provider_blob" -version = "1.3.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cd69321b38b334c1257a53182273e3cfbcde4a5975c6193b43b6dfd3829d8e5" +checksum = "c31326d28c7f95a964a4f0ee86c24002da5f6db907e3bcb079949b4ff103b6a9" dependencies = [ "icu_provider", "postcard", @@ -1633,20 +1643,20 @@ dependencies = [ [[package]] name = "icu_provider_macros" -version = "1.3.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca9be8af0b117ccf1516251daab4c9137c012646a211c2a02d2f568ea3cd0df4" +checksum = "2060258edfcfe32ca7058849bf0f146cb5c59aadbedf480333c0d0002f97bc99" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] name = "icu_segmenter" -version = "1.3.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9221a9db12a8026cd94f3a171a6514763daafadd64bc0f127c05a5f09836ded" +checksum = "bcb3c1981ce2187a745f391a741cb14e77453325acb3b2e014b05da51c0a39f2" dependencies = [ "core_maths", "displaydoc", @@ -1661,9 +1671,9 @@ dependencies = [ [[package]] name = "icu_segmenter_data" -version = "1.3.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23ca0059266f591bfb7cac9ee3fce5f9861beaa4532ef3629653653acba0a94c" +checksum = "9703f6713044d1c0a1335a6d78ffece4c9380582416ace6feeb608e84d279fc7" [[package]] name = "ident_case" @@ -1735,12 +1745,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad227c3af19d4914570ad36d30409928b75967c298feb9ea1969db3a610bb14e" +checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.2", "serde", ] @@ -1772,9 +1782,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.33.0" +version = "1.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aa511b2e298cd49b1856746f6bb73e17036bcd66b25f5e92cdcdbec9bd75686" +checksum = "5d64600be34b2fcfc267740a243fa7744441bb4947a619ac4e5bb6507f35fbfc" dependencies = [ "console", "lazy_static", @@ -1798,9 +1808,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" @@ -1813,15 +1823,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "isolang" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f80f221db1bc708b71128757b9396727c04de86968081e18e89b0575e03be071" -dependencies = [ - "phf", -] - [[package]] name = "itoa" version = "1.0.9" @@ -1830,9 +1831,9 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" dependencies = [ "libc", ] @@ -1889,9 +1890,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.148" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libgit2-sys" @@ -1907,9 +1908,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libz-sys" @@ -1940,9 +1941,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.7" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "lipsum" @@ -1962,9 +1963,9 @@ checksum = "77a1a2647d5b7134127971a6de0d533c49de2159167e7f259c427195f87168a1" [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -1976,17 +1977,23 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "main_error" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "155db5e86c6e45ee456bf32fad5a290ee1f7151c2faca27ea27097568da67d1a" + [[package]] name = "memchr" -version = "2.6.3" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memmap2" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49388d20533534cd19360ad3d6a7dadc885944aa802ba3995040c5ec11288c6" +checksum = "deaba38d7abf1d4cca21cc89e932e542ba2b9258664d2a9ef0e61512039c9375" dependencies = [ "libc", ] @@ -2038,9 +2045,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", "log", @@ -2062,7 +2069,7 @@ dependencies = [ "log", "memchr", "mime", - "spin 0.9.8", + "spin", "version_check", ] @@ -2116,7 +2123,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "crossbeam-channel", "filetime", "fsevent-sys", @@ -2182,9 +2189,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] @@ -2223,15 +2230,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "oklab" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "467e40ada50d13bab19019e3707862b5076ca15841f31ee1474c40397c1b9f11" -dependencies = [ - "rgb", -] - [[package]] name = "once_cell" version = "1.18.0" @@ -2244,7 +2242,7 @@ version = "0.10.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "cfg-if", "foreign-types", "libc", @@ -2261,7 +2259,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -2301,12 +2299,12 @@ dependencies = [ [[package]] name = "os_pipe" -version = "0.9.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb233f06c2307e1f5ce2ecad9f8121cffbbee2c95428f44ea85222e460d0d213" +checksum = "0ae859aa07428ca9a929b936690f8b12dc5f11dd8c6992a18ca93919f28bc177" dependencies = [ "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -2315,6 +2313,29 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "palette" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2e2f34147767aa758aa649415b50a69eeb46a67f9dc7db8011eeb3d84b351dc" +dependencies = [ + "approx", + "fast-srgb8", + "libm", + "palette_derive", +] + +[[package]] +name = "palette_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7db010ec5ff3d4385e4f133916faacd9dad0f6a09394c92d825b3aed310fa0a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -2327,13 +2348,13 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", + "redox_syscall 0.4.1", "smallvec", "windows-targets 0.48.5", ] @@ -2358,12 +2379,13 @@ checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "pdf-writer" -version = "0.8.1" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d77bc47c8968aa63f86a7e6693e270a6cbd1e3b784c364f1711a0ddecc71447" +checksum = "690874e8cf95d36ddffbdbdaad6ef8714c88bf8085996b673559389a04e38a02" dependencies = [ "bitflags 1.3.2", "itoa", + "memchr", "ryu", ] @@ -2373,24 +2395,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" -[[package]] -name = "phf" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" -dependencies = [ - "phf_shared", -] - -[[package]] -name = "phf_shared" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" -dependencies = [ - "siphasher", -] - [[package]] name = "pico-args" version = "0.5.0" @@ -2414,7 +2418,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -2435,7 +2439,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f67591f21f6668e63c1cd85adab066ac8a92bc7b962668dd8042197a6e4b8f8f" dependencies = [ - "ttf-parser 0.19.2", + "ttf-parser", ] [[package]] @@ -2446,14 +2450,14 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "plist" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdc0001cfea3db57a2e24bc0d818e9e20e554b5f97fabb9bc231dc240269ae06" +checksum = "9a4a0cfc5fb21a09dc6af4bf834cf10d4a32fccd9e2ea468c4b1751a097487aa" dependencies = [ "base64", "indexmap 1.9.3", "line-wrap", - "quick-xml", + "quick-xml 0.30.0", "serde", "time", ] @@ -2488,6 +2492,12 @@ dependencies = [ "serde", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -2529,9 +2539,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.67" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -2567,9 +2577,19 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.29.0" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b9228215d82c7b61490fec1de287136b5de6f5700f6e58ea9ad61a7964ca51" +checksum = "0ce5e73202a820a31f8a0ee32ada5e21029c81fd9e3ebf668a40832e4219d9d1" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "quick-xml" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" dependencies = [ "memchr", ] @@ -2682,6 +2702,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -2695,25 +2724,25 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.5" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", "regex-automata", - "regex-syntax", + "regex-syntax 0.8.2", ] [[package]] name = "regex-automata" -version = "0.3.8" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.2", ] [[package]] @@ -2722,6 +2751,12 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + [[package]] name = "rend" version = "0.4.1" @@ -2733,9 +2768,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.20" +version = "0.11.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ "base64", "bytes", @@ -2762,6 +2797,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "system-configuration", "tokio", "tokio-native-tls", "tokio-rustls", @@ -2776,9 +2812,9 @@ dependencies = [ [[package]] name = "resvg" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6554f47c38eca56827eea7f285c2a3018b4e12e0e195cc105833c008be338f1" +checksum = "cc7980f653f9a7db31acff916a262c3b78c562919263edea29bf41a056e20497" dependencies = [ "gif", "jpeg-decoder", @@ -2793,26 +2829,25 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.36" +version = "0.8.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20ec2d3e3fc7a92ced357df9cebd5a10b6fb2aa1ee797bf7e9ce2f17dffc8f59" +checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8" dependencies = [ "bytemuck", ] [[package]] name = "ring" -version = "0.16.20" +version = "0.17.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" dependencies = [ "cc", + "getrandom", "libc", - "once_cell", - "spin 0.5.2", + "spin", "untrusted", - "web-sys", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -2867,9 +2902,9 @@ dependencies = [ [[package]] name = "roxmltree" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8f595a457b6b8c6cda66a48503e92ee8d19342f905948f29c383200ec9eb1d8" +checksum = "862340e351ce1b271a378ec53f304a5558f7db87f3769dc655a8f6ecbb68b302" dependencies = [ "xmlparser", ] @@ -2921,11 +2956,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.14" +version = "0.38.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" +checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", @@ -2934,9 +2969,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.7" +version = "0.21.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" +checksum = "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c" dependencies = [ "log", "ring", @@ -2955,9 +2990,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.101.6" +version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ "ring", "untrusted", @@ -2971,17 +3006,17 @@ checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "rustybuzz" -version = "0.7.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162bdf42e261bee271b3957691018634488084ef577dddeb6420a9684cab2a6a" +checksum = "71cd15fef9112a1f94ac64b58d1e4628192631ad6af4dc69997f995459c874e7" dependencies = [ "bitflags 1.3.2", "bytemuck", "smallvec", - "ttf-parser 0.18.1", + "ttf-parser", "unicode-bidi-mirroring", "unicode-ccc", - "unicode-general-category", + "unicode-properties", "unicode-script", ] @@ -2991,7 +3026,7 @@ version = "12.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "994eca4bca05c87e86e15d90fc7a91d1be64b4482b38cb2d27474568fe7c9db9" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "cfg-if", "clipboard-win", "fd-lock", @@ -3017,7 +3052,7 @@ checksum = "5a32af5427251d2e4be14fc151eabe18abb4a7aad5efee7044da9f096c906a43" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -3064,9 +3099,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ "ring", "untrusted", @@ -3103,9 +3138,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "send_wrapper" @@ -3115,18 +3150,18 @@ checksum = "930c0acf610d3fdb5e2ab6213019aaa04e227ebe9547b0649ba599b16d788bd7" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" dependencies = [ "serde_derive", ] [[package]] name = "serde-wasm-bindgen" -version = "0.5.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" +checksum = "17ba92964781421b6cef36bf0d7da26d201e96d84e1b10e7ae6ed416e516906d" dependencies = [ "js-sys", "serde", @@ -3135,20 +3170,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", @@ -3157,9 +3192,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" dependencies = [ "serde", ] @@ -3178,15 +3213,15 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca3b16a3d82c4088f343b7480a93550b3eabe1a358569c2dfe38bbcead07237" +checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" dependencies = [ "base64", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.0.1", + "indexmap 2.0.2", "serde", "serde_json", "serde_with_macros", @@ -3195,23 +3230,23 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e6be15c453eb305019bfa438b1593c731f36a289a7853f7707ee29e870b3b3c" +checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] name = "serde_yaml" -version = "0.9.25" +version = "0.9.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574" +checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" dependencies = [ - "indexmap 2.0.1", + "indexmap 2.0.2", "itoa", "ryu", "serde", @@ -3242,9 +3277,9 @@ dependencies = [ [[package]] name = "sharded-slab" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b21f559e07218024e7e9f90f96f601825397de0e25420135f7f952453fed0b" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] @@ -3272,9 +3307,9 @@ checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" [[package]] name = "similar" -version = "2.2.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf" +checksum = "2aeaf503862c419d66959f5d7ca015337d864e9c49485d771b732e2a20453597" [[package]] name = "simplecss" @@ -3317,9 +3352,9 @@ checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "socket2" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi", @@ -3327,20 +3362,14 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", "windows-sys 0.48.0", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spin" version = "0.9.8" @@ -3423,9 +3452,9 @@ checksum = "09eab8a83bff89ba2200bd4c59be45c7c787f988431b936099a5a266c957f2f9" [[package]] name = "svg2pdf" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2adc7de163bd53f323850e65269280b2a66ffceee291cb8eca34f2eabc3acad" +checksum = "363c5346967da04bf3ebb3d8bafa7f52c53c810167047904df1960eac3fc08b7" dependencies = [ "image", "miniz_oxide", @@ -3435,9 +3464,9 @@ dependencies = [ [[package]] name = "svgtypes" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed4b0611e7f3277f68c0fa18e385d9e2d26923691379690039548f867cef02a7" +checksum = "d71499ff2d42f59d26edb21369a308ede691421f79ebc0f001e2b1fd3a7c9e52" dependencies = [ "kurbo", "siphasher", @@ -3456,9 +3485,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.37" +version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", @@ -3473,7 +3502,7 @@ checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", "unicode-xid", ] @@ -3490,7 +3519,7 @@ dependencies = [ "fnv", "once_cell", "plist", - "regex-syntax", + "regex-syntax 0.7.5", "serde", "serde_json", "thiserror", @@ -3508,6 +3537,27 @@ dependencies = [ "libc", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tap" version = "1.0.1" @@ -3527,13 +3577,13 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.8.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.3.5", + "redox_syscall 0.4.1", "rustix", "windows-sys 0.48.0", ] @@ -3559,22 +3609,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -3595,14 +3645,15 @@ dependencies = [ [[package]] name = "time" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ "deranged", "itoa", "libc", "num_threads", + "powerfmt", "serde", "time-core", "time-macros", @@ -3625,9 +3676,9 @@ dependencies = [ [[package]] name = "tiny-skia" -version = "0.10.0" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7db11798945fa5c3e5490c794ccca7c6de86d3afdd54b4eb324109939c6f37bc" +checksum = "3b72a92a05db376db09fe6d50b7948d106011761c05a6a45e23e17ee9b556222" dependencies = [ "arrayref", "arrayvec", @@ -3635,25 +3686,14 @@ dependencies = [ "cfg-if", "log", "png", - "tiny-skia-path 0.10.0", + "tiny-skia-path", ] [[package]] name = "tiny-skia-path" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f60aa35c89ac2687ace1a2556eaaea68e8c0d47408a2e3e7f5c98a489e7281c" -dependencies = [ - "arrayref", - "bytemuck", - "strict-num", -] - -[[package]] -name = "tiny-skia-path" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93a323d1de20dad9bc8b32daf57702c585ce76e80792d8151de1fc9dfc8d1ca7" +checksum = "6ac3865b9708fc7e1961a65c3a4fa55e984272f33092d3c859929f887fceb647" dependencies = [ "arrayref", "bytemuck", @@ -3662,9 +3702,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b07bb54ef1f8ff27564b08b861144d3b8d40263efe07684f64987f4c0d044e3e" +checksum = "d5d0e245e80bdc9b4e5356fc45a72184abbc3861992603f515270e9340f5a219" dependencies = [ "displaydoc", "serde", @@ -3688,9 +3728,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.32.0" +version = "1.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" dependencies = [ "backtrace", "bytes", @@ -3700,7 +3740,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.4", + "socket2 0.5.5", "tokio-macros", "windows-sys 0.48.0", ] @@ -3713,7 +3753,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -3761,9 +3801,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", @@ -3787,21 +3827,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.1" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc1433177506450fe920e46a4f9812d0c211f5dd556da10e731a0a3dfa151f0" +checksum = "8ff9e3abce27ee2c9a37f9ad37238c1bdd4e789c84ba37df76aa4d528f5072cc" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.20.1", + "toml_edit 0.20.7", ] [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] @@ -3812,7 +3852,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.0.1", + "indexmap 2.0.2", "serde", "serde_spanned", "toml_datetime", @@ -3821,11 +3861,11 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.20.1" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca676d9ba1a322c1b64eb8045a5ec5c0cfb0c9d08e15e9ff622589ad5221c8fe" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ - "indexmap 2.0.1", + "indexmap 2.0.2", "serde", "serde_spanned", "toml_datetime", @@ -3840,11 +3880,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -3853,20 +3892,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", @@ -3884,12 +3923,12 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" dependencies = [ - "lazy_static", "log", + "once_cell", "tracing-core", ] @@ -3923,12 +3962,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" -[[package]] -name = "ttf-parser" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0609f771ad9c6155384897e1df4d948e692667cc0588548b68eb44d052b27633" - [[package]] name = "ttf-parser" version = "0.19.2" @@ -3968,23 +4001,23 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "typst" -version = "0.8.0" -source = "git+https://github.com/Myriad-Dreamin/typst.git?branch=typst.ts-v0.8.0#c7e91e216310d5c8dca3eee8a23a64b3ede99e15" +version = "0.9.0" +source = "git+https://github.com/Myriad-Dreamin/typst.git?branch=typst.ts-v0.9.0#1e987a9ebad28c4f425d7fb02ca1c98aeea28ee2" dependencies = [ "base64", - "bitflags 2.4.0", + "bitflags 2.4.1", "bytemuck", "comemo", "ecow", "flate2", "fontdb", - "if_chain", "image", - "indexmap 2.0.1", + "indexmap 2.0.2", + "kurbo", "log", "miniz_oxide", - "oklab", "once_cell", + "palette", "pdf-writer", "pixglyph", "regex", @@ -3998,9 +4031,9 @@ dependencies = [ "svg2pdf", "time", "tiny-skia", - "toml 0.8.1", + "toml 0.8.6", "tracing", - "ttf-parser 0.19.2", + "ttf-parser", "typst-macros", "typst-syntax", "unicode-ident", @@ -4015,10 +4048,24 @@ dependencies = [ "xmp-writer", ] +[[package]] +name = "typst-ide" +version = "0.9.0" +source = "git+https://github.com/Myriad-Dreamin/typst.git?branch=typst.ts-v0.9.0#1e987a9ebad28c4f425d7fb02ca1c98aeea28ee2" +dependencies = [ + "comemo", + "ecow", + "if_chain", + "log", + "serde", + "typst", + "unscanny", +] + [[package]] name = "typst-library" -version = "0.8.0" -source = "git+https://github.com/Myriad-Dreamin/typst.git?branch=typst.ts-v0.8.0#c7e91e216310d5c8dca3eee8a23a64b3ede99e15" +version = "0.9.0" +source = "git+https://github.com/Myriad-Dreamin/typst.git?branch=typst.ts-v0.9.0#1e987a9ebad28c4f425d7fb02ca1c98aeea28ee2" dependencies = [ "az", "chinese-number", @@ -4033,6 +4080,7 @@ dependencies = [ "icu_provider_adapters", "icu_provider_blob", "icu_segmenter", + "indexmap 2.0.2", "kurbo", "lipsum", "log", @@ -4044,9 +4092,9 @@ dependencies = [ "smallvec", "syntect", "time", - "toml 0.8.1", + "toml 0.8.6", "tracing", - "ttf-parser 0.19.2", + "ttf-parser", "typed-arena", "typst", "unicode-bidi", @@ -4057,19 +4105,19 @@ dependencies = [ [[package]] name = "typst-macros" -version = "0.8.0" -source = "git+https://github.com/Myriad-Dreamin/typst.git?branch=typst.ts-v0.8.0#c7e91e216310d5c8dca3eee8a23a64b3ede99e15" +version = "0.9.0" +source = "git+https://github.com/Myriad-Dreamin/typst.git?branch=typst.ts-v0.9.0#1e987a9ebad28c4f425d7fb02ca1c98aeea28ee2" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] name = "typst-syntax" -version = "0.8.0" -source = "git+https://github.com/Myriad-Dreamin/typst.git?branch=typst.ts-v0.8.0#c7e91e216310d5c8dca3eee8a23a64b3ede99e15" +version = "0.9.0" +source = "git+https://github.com/Myriad-Dreamin/typst.git?branch=typst.ts-v0.9.0#1e987a9ebad28c4f425d7fb02ca1c98aeea28ee2" dependencies = [ "comemo", "ecow", @@ -4099,14 +4147,14 @@ dependencies = [ "async-trait", "base64", "comemo", - "data-url 0.3.0", + "data-url", "elsa", "flate2", "js-sys", "log", "once_cell", "tiny-skia", - "ttf-parser 0.19.2", + "ttf-parser", "typst", "typst-ts-core", "wasm-bindgen", @@ -4133,11 +4181,12 @@ dependencies = [ "serde", "serde_json", "tokio", - "toml 0.8.1", + "toml 0.8.6", "tracing", "tracing-error", "tracing-subscriber", "typst", + "typst-ide", "typst-library", "typst-ts-ast-exporter", "typst-ts-compiler", @@ -4160,10 +4209,11 @@ dependencies = [ "comemo", "dirs", "dissimilar", + "ecow", "flate2", "fst", "hex", - "indexmap 2.0.1", + "indexmap 2.0.2", "instant", "js-sys", "log", @@ -4176,7 +4226,6 @@ dependencies = [ "pollster", "reqwest", "rustc-hash", - "same-file", "serde", "serde-wasm-bindgen", "serde_json", @@ -4218,8 +4267,8 @@ dependencies = [ "siphasher", "svgtypes", "tiny-skia", - "tiny-skia-path 0.11.1", - "ttf-parser 0.19.2", + "tiny-skia-path", + "ttf-parser", "typst", "wasm-bindgen", "web-sys", @@ -4338,7 +4387,7 @@ dependencies = [ "resvg", "roxmltree", "tiny-skia", - "ttf-parser 0.19.2", + "ttf-parser", "typst", "usvg", ] @@ -4432,6 +4481,7 @@ dependencies = [ "base64", "comemo", "log", + "siphasher", "tiny-skia", "typst", "typst-ts-core", @@ -4492,6 +4542,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e35bfd2f2b8796545b55d7d3fd3e89a0613f68a0d1c8bc28cb7ff96b411a35ff" dependencies = [ + "serde", "tinystr", ] @@ -4522,12 +4573,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1" -[[package]] -name = "unicode-general-category" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2281c8c1d221438e373249e065ca4989c4c36952c211ff21a0ee91c44a3869e7" - [[package]] name = "unicode-ident" version = "1.0.12" @@ -4599,9 +4644,9 @@ checksum = "e9df2af067a7953e9c3831320f35c1cc0600c30d44d9f7a12b01db1cd88d6b47" [[package]] name = "untrusted" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" @@ -4612,13 +4657,14 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] name = "usvg" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14d09ddfb0d93bf84824c09336d32e42f80961a9d1680832eb24fdf249ce11e6" +checksum = "c51daa774fe9ee5efcf7b4fec13019b8119cda764d9a8b5b06df02bb1445c656" dependencies = [ "base64", "log", @@ -4631,11 +4677,11 @@ dependencies = [ [[package]] name = "usvg-parser" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19bf93d230813599927d88557014e0908ecc3531666d47c634c6838bc8db408" +checksum = "45c88a5ffaa338f0e978ecf3d4e00d8f9f493e29bed0752e1a808a1db16afc40" dependencies = [ - "data-url 0.2.0", + "data-url", "flate2", "imagesize", "kurbo", @@ -4649,9 +4695,9 @@ dependencies = [ [[package]] name = "usvg-text-layout" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "035044604e89652c0a2959b8b356946997a52649ba6cade45928c2842376feb4" +checksum = "4d2374378cb7a3fb8f33894e0fdb8625e1bbc4f25312db8d91f862130b541593" dependencies = [ "fontdb", "kurbo", @@ -4665,14 +4711,14 @@ dependencies = [ [[package]] name = "usvg-tree" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7939a7e4ed21cadb5d311d6339730681c3e24c3e81d60065be80e485d3fc8b92" +checksum = "6cacb0c5edeaf3e80e5afcf5b0d4004cc1d36318befc9a7c6606507e5d0f4062" dependencies = [ "rctree", "strict-num", "svgtypes", - "tiny-skia-path 0.10.0", + "tiny-skia-path", ] [[package]] @@ -4695,9 +4741,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" dependencies = [ "getrandom", ] @@ -4811,7 +4857,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", "wasm-bindgen-shared", ] @@ -4845,7 +4891,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4887,7 +4933,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f341edb80021141d4ae6468cbeefc50798716a347d4085c3811900049ea8945" dependencies = [ "smallvec", - "spin 0.9.8", + "spin", "wasmi_arena", "wasmi_core", "wasmparser-nostd", @@ -4974,10 +5020,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.48.0" +name = "windows-core" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ "windows-targets 0.48.5", ] @@ -5116,9 +5162,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.5.15" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +checksum = "176b6138793677221d420fd2f0aeeced263f197688b36484660da767bca2fa32" dependencies = [ "memchr", ] @@ -5159,9 +5205,9 @@ dependencies = [ [[package]] name = "xmlparser" -version = "0.13.5" +version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" [[package]] name = "xmlwriter" @@ -5171,9 +5217,9 @@ checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" [[package]] name = "xmp-writer" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fd742bbbb930fc972b28bf66b7546dfbc7bb9a4c7924299df0ae6a5641fcadf" +checksum = "4543ba138f64a94b19e1e9c66c165bca7e03d470e1c066cb76ea279d9d0e1989" [[package]] name = "yaml-rust" @@ -5204,7 +5250,7 @@ checksum = "d5e19fb6ed40002bab5403ffa37e53e0e56f914a4450c8765f533018db1db35f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", "synstructure", ] @@ -5225,7 +5271,7 @@ checksum = "e6a647510471d372f2e6c2e6b7219e44d8c574d24fdc11c610a61455782f18c3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", "synstructure", ] @@ -5249,5 +5295,5 @@ checksum = "acabf549809064225ff8878baedc4ce3732ac3b07e7c7ce6e5c2ccdbc485c324" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] diff --git a/Cargo.toml b/Cargo.toml index 2b254fab..d49d2950 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,9 +60,10 @@ strip = true # typesetting pixglyph = "0.2" -typst = "0.8.0" -typst-library = "0.8.0" -typst-syntax = "0.8.0" +typst = "0.9.0" +typst-ide = "0.9.0" +typst-library = "0.9.0" +typst-syntax = "0.9.0" ttf-parser = "0.19.2" # general @@ -86,7 +87,7 @@ bytemuck = "1" comemo = "0.3" dissimilar = "1.0" elsa = "1.8.1" -ecow = "0.1" +ecow = "0.2" fst = "0.4.7" indexmap = "2" @@ -103,11 +104,12 @@ tokio-tungstenite = "0.20.0" # system dirs = "5" -memmap2 = "0.7" +memmap2 = "0.9" notify = "6" path-clean = "1.0.1" pathdiff = "0.2" walkdir = "2" +instant = { version = "0.1", features = ["wasm-bindgen"] } # web js-sys = "^0.3" @@ -127,17 +129,17 @@ image = { version = "0.24", default-features = false, features = [ "jpeg", "gif", ] } -resvg = { version = "0.35", default-features = false, features = [ +resvg = { version = "0.36", default-features = false, features = [ "raster-images", ] } -svgtypes = "0.11" -tiny-skia = "0.10.0" +svgtypes = "0.12" +tiny-skia = "0.11" tiny-skia-path = "0.11" -usvg = { version = "0.35", default-features = false, features = ["text"] } +usvg = { version = "0.36", default-features = false, features = ["text"] } # cryptography and processing ansi-to-html = "0.1.3" -base64 = "0.21.0" +base64 = "0.21.2" base64-serde = "0.7.0" byteorder = "1.4.3" data-url = "0.3.0" @@ -152,7 +154,7 @@ rustc-hash = "1.1.0" serde = { version = "1.0.188" } serde_json = "1.0.106" serde_with = { version = "3.3", features = ["base64"] } -serde-wasm-bindgen = "^0.5" +serde-wasm-bindgen = "^0.6" sha2 = "0.10.6" # todo: typst prevent it, we should notice and bump it later siphasher = "0.3.10" @@ -206,9 +208,14 @@ typst-ts-raster-exporter = { path = "exporter/raster" } typst-ts-serde-exporter = { path = "exporter/serde" } typst-ts-svg-exporter = { path = "exporter/svg" } -typst = { git = "https://github.com/Myriad-Dreamin/typst.git", branch = "typst.ts-v0.8.0" } -typst-library = { git = "https://github.com/Myriad-Dreamin/typst.git", branch = "typst.ts-v0.8.0" } -typst-syntax = { git = "https://github.com/Myriad-Dreamin/typst.git", branch = "typst.ts-v0.8.0" } +typst = { git = "https://github.com/Myriad-Dreamin/typst.git", branch = "typst.ts-v0.9.0" } +typst-library = { git = "https://github.com/Myriad-Dreamin/typst.git", branch = "typst.ts-v0.9.0" } +typst-syntax = { git = "https://github.com/Myriad-Dreamin/typst.git", branch = "typst.ts-v0.9.0" } +typst-ide = { git = "https://github.com/Myriad-Dreamin/typst.git", branch = "typst.ts-v0.9.0" } +hayagriva = { git = "https://github.com/Myriad-Dreamin/hayagriva.git", branch = "typst.ts-v0.9.0" } # typst = { path = "../typst/crates/typst" } # typst-library = { path = "../typst/crates/typst-library" } +# typst-syntax = { path = "../typst/crates/typst-syntax" } +# typst-ide = { path = "../typst/crates/typst-ide" } +# hayagriva = { path = "../hayagriva" } diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 8e7f94d2..17c1dfec 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -11,6 +11,7 @@ repository.workspace = true [dependencies] typst.workspace = true typst-library.workspace = true +typst-ide.workspace = true clap.workspace = true clap_complete.workspace = true diff --git a/cli/src/main.rs b/cli/src/main.rs index a0af6d09..279c586b 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -47,7 +47,8 @@ fn main() { if !matches!(&opts.sub, Some(Subcommands::Compile(CompileArgs { trace: _trace @ Some(_), .. }))) { builder .filter_module("typst::", log::LevelFilter::Warn) - .filter_module("typst_library::", log::LevelFilter::Warn); + .filter_module("typst_library::", log::LevelFilter::Warn) + .filter_module("tracing::", log::LevelFilter::Off); } builder.init(); } diff --git a/cli/src/query_repl.rs b/cli/src/query_repl.rs index 20021280..2835697b 100644 --- a/cli/src/query_repl.rs +++ b/cli/src/query_repl.rs @@ -15,8 +15,8 @@ use typst_ts_core::TakeAs; use crate::query::serialize; use crate::CompileOnceArgs; -use typst::ide::autocomplete; use typst::World; +use typst_ide::autocomplete; #[derive(Helper, Validator)] struct ReplContext { @@ -87,7 +87,7 @@ impl Highlighter for ReplContext { } } -fn to_repl_completion_pair(item: typst::ide::Completion) -> Pair { +fn to_repl_completion_pair(item: typst_ide::Completion) -> Pair { // we does not support code snippet // let rep = item.apply.as_ref().unwrap_or(&item.label).into(); let rep = item.label.clone().into(); diff --git a/compiler/Cargo.toml b/compiler/Cargo.toml index 17bb65ee..155e3423 100644 --- a/compiler/Cargo.toml +++ b/compiler/Cargo.toml @@ -21,14 +21,13 @@ parking_lot.workspace = true hex.workspace = true sha2.workspace = true flate2.workspace = true -instant = { version = "0.1", features = ["wasm-bindgen"] } +ecow.workspace = true +instant.workspace = true serde.workspace = true serde_json.workspace = true serde-wasm-bindgen = { workspace = true, optional = true } -same-file = { version = "1", optional = true } - memmap2 = { workspace = true, optional = true } dirs = { workspace = true, optional = true } walkdir = { workspace = true, optional = true } @@ -92,7 +91,7 @@ system-compile = [ "dep:notify", "dep:log", ] -system-watch = ["dep:notify", "dep:tokio", "dep:same-file"] +system-watch = ["dep:notify", "dep:tokio"] system = ["system-compile", "system-watch"] dynamic-layout = ["dep:typst-ts-svg-exporter"] __web = [ diff --git a/compiler/src/lib.rs b/compiler/src/lib.rs index 31780234..017699dc 100644 --- a/compiler/src/lib.rs +++ b/compiler/src/lib.rs @@ -99,7 +99,7 @@ pub trait ShadowApi { f: impl FnOnce(&mut Self) -> SourceResult, ) -> SourceResult { self.map_shadow(file_path, content).at(Span::detached())?; - let res: Result>> = f(self); + let res: Result> = f(self); self.unmap_shadow(file_path).at(Span::detached())?; res } diff --git a/compiler/src/service/diag.rs b/compiler/src/service/diag.rs index af037e2b..09ff02cd 100644 --- a/compiler/src/service/diag.rs +++ b/compiler/src/service/diag.rs @@ -31,7 +31,7 @@ fn color_stream() -> StandardStream { /// Print diagnostic messages to the terminal. pub fn print_diagnostics<'files, W: World + Files<'files, FileId = TypstFileId>>( world: &'files W, - errors: Vec, + errors: ecow::EcoVec, ) -> Result<(), codespan_reporting::files::Error> { let mut w = color_stream(); let config = term::Config { diff --git a/compiler/src/service/mod.rs b/compiler/src/service/mod.rs index 532ac1c5..70829721 100644 --- a/compiler/src/service/mod.rs +++ b/compiler/src/service/mod.rs @@ -298,7 +298,7 @@ pub trait DiagObserver { /// Print diagnostic messages to the terminal. fn print_diagnostics( &self, - errors: Vec, + errors: ecow::EcoVec, ) -> Result<(), codespan_reporting::files::Error>; /// Print status message to the terminal. @@ -329,7 +329,7 @@ where /// Print diagnostic messages to the terminal. fn print_diagnostics( &self, - errors: Vec, + errors: ecow::EcoVec, ) -> Result<(), codespan_reporting::files::Error> { diag::print_diagnostics(self.world(), errors) } @@ -367,7 +367,7 @@ where } Err(errs) => { self.print_status::(DiagStatus::Error(start.elapsed())); - let _err = self.print_diagnostics(*errs); + let _err = self.print_diagnostics(errs); // todo: log in browser compiler #[cfg(feature = "system-compile")] if _err.is_err() { diff --git a/compiler/src/vfs/notify.rs b/compiler/src/vfs/notify.rs index a196adb9..d7fe3738 100644 --- a/compiler/src/vfs/notify.rs +++ b/compiler/src/vfs/notify.rs @@ -1,3 +1,4 @@ +use core::fmt; use std::{collections::HashMap, path::Path}; use typst::diag::{FileError, FileResult}; @@ -15,9 +16,27 @@ struct NotifyFileRepr { /// A file snapshot that is notified by some external source /// /// Note: The error is boxed to avoid large stack size -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct FileSnapshot(Result>); +impl fmt::Debug for FileSnapshot { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0.as_ref() { + Ok(v) => f + .debug_struct("FileSnapshot") + .field("mtime", &v.mtime) + .field( + "content", + &FileContent { + len: v.content.len(), + }, + ) + .finish(), + Err(e) => f.debug_struct("FileSnapshot").field("error", &e).finish(), + } + } +} + impl FileSnapshot { /// Access the internal data of the file snapshot #[inline] @@ -245,3 +264,9 @@ impl AccessModel for NotifyAccessModel { self.inner.content(src) } } + +#[derive(Debug)] +#[allow(dead_code)] +struct FileContent { + len: usize, +} diff --git a/core/src/exporter.rs b/core/src/exporter.rs index 11251583..272f5035 100644 --- a/core/src/exporter.rs +++ b/core/src/exporter.rs @@ -51,6 +51,7 @@ pub mod builtins { use crate::{exporter_utils::map_err, AsOwnedBytes, AsOwnedString, AsWritable, Transformer}; use super::{utils, DynExporter, Exporter}; + use ecow::EcoVec; use typst::{diag::SourceResult, World}; pub struct GroupExporter { @@ -73,7 +74,7 @@ pub mod builtins { impl Exporter for GroupExporter { fn export(&self, world: &dyn World, output: Arc) -> SourceResult<()> { - let mut errors = Vec::new(); + let mut errors = EcoVec::new(); for f in &self.exporters { utils::collect_err(&mut errors, f.export(world, output.clone())) @@ -82,7 +83,7 @@ pub mod builtins { if errors.is_empty() { Ok(()) } else { - Err(Box::new(errors)) + Err(errors) } } } @@ -237,21 +238,21 @@ pub mod builtins { pub mod utils { use core::fmt::Display; + use ecow::{eco_vec, EcoVec}; use typst::diag::{SourceDiagnostic, SourceResult}; - pub fn collect_err(errors: &mut Vec, res: SourceResult<()>) { + pub fn collect_err(errors: &mut EcoVec, res: SourceResult<()>) { if let Err(errs) = res { - let mut errs = *errs; - errors.append(&mut errs); + errors.extend(errs); } } /// Convert the given error to a vector of source errors. // todo: report the component position - pub fn map_err(e: E) -> Box> { - Box::new(vec![SourceDiagnostic::error( + pub fn map_err(e: E) -> EcoVec { + eco_vec![SourceDiagnostic::error( typst::syntax::Span::detached(), e.to_string(), - )]) + )] } } diff --git a/core/src/font/glyph.rs b/core/src/font/glyph.rs index 1f960f20..484513c8 100644 --- a/core/src/font/glyph.rs +++ b/core/src/font/glyph.rs @@ -4,7 +4,7 @@ use std::{ops::Deref, sync::Arc}; pub use ttf_parser::GlyphId; use typst::font::Font; -use typst::geom::Axes; +// use typst::geom::Axes; use typst::image::{Image as TypstImage, RasterFormat}; use crate::hash::item_hash128; @@ -96,10 +96,11 @@ impl IGlyphProvider for FontGlyphProvider { } // convert to typst's image format - let glyph_image = TypstImage::new_raw( + // todo: verify result + let glyph_image = TypstImage::new( raster.data.into(), RasterFormat::Png.into(), - Axes::new(raster.width as u32, raster.height as u32), + // Axes::new(raster.width as u32, raster.height as u32), None, ) .ok()?; diff --git a/core/src/hash.rs b/core/src/hash.rs index 26ad1730..b219e16c 100644 --- a/core/src/hash.rs +++ b/core/src/hash.rs @@ -5,6 +5,7 @@ use siphasher::sip128::{Hasher128, SipHasher13}; #[cfg(feature = "rkyv")] use rkyv::{Archive, Deserialize as rDeser, Serialize as rSer}; +use typst::diag::StrResult; /// See /// The fingerprint conflicts should be very rare and should be handled by the @@ -35,26 +36,39 @@ impl Fingerprint { ((self.1 as u128) << 64) | self.0 as u128 } + pub fn try_from_str(s: &str) -> StrResult { + let bytes = base64::engine::general_purpose::STANDARD_NO_PAD + .decode(&s.as_bytes()[..11]) + .expect("invalid base64 string"); + let lo = u64::from_le_bytes(bytes.try_into().unwrap()); + let mut bytes = base64::engine::general_purpose::STANDARD_NO_PAD + .decode(&s.as_bytes()[11..]) + .expect("invalid base64 string"); + bytes.resize(8, 0); + let hi = u64::from_le_bytes(bytes.try_into().unwrap()); + Ok(Self::from_pair(lo, hi)) + } + /// Create a xml id from the given prefix and the fingerprint of this /// reference. Note that the entire html document shares namespace for /// ids. #[comemo::memoize] pub fn as_svg_id(self, prefix: &'static str) -> String { - let fingerprint_hi = + let fingerprint_lo = base64::engine::general_purpose::STANDARD_NO_PAD.encode(self.0.to_le_bytes()); if self.1 == 0 { - return [prefix, &fingerprint_hi].join(""); + return [prefix, &fingerprint_lo].join(""); } // possible the id in the lower 64 bits. - let fingerprint_lo = { + let fingerprint_hi = { let id = self.1.to_le_bytes(); // truncate zero let rev_zero = id.iter().rev().skip_while(|&&b| b == 0).count(); let id = &id[..rev_zero]; base64::engine::general_purpose::STANDARD_NO_PAD.encode(id) }; - [prefix, &fingerprint_hi, &fingerprint_lo].join("") + [prefix, &fingerprint_lo, &fingerprint_hi].join("") } } @@ -66,18 +80,21 @@ pub trait FingerprintHasher: std::hash::Hasher { } /// A fingerprint hasher that uses the [`SipHasher13`] algorithm. -struct FingerprintSipHasher { +#[derive(Default)] +pub struct FingerprintSipHasher { /// The underlying data passed to the hasher. data: Vec, } +pub type FingerprintSipHasherBase = SipHasher13; + impl std::hash::Hasher for FingerprintSipHasher { fn write(&mut self, bytes: &[u8]) { self.data.extend_from_slice(bytes); } fn finish(&self) -> u64 { - let mut inner = SipHasher13::new(); + let mut inner = FingerprintSipHasherBase::new(); self.data.hash(&mut inner); inner.finish() } @@ -86,7 +103,7 @@ impl std::hash::Hasher for FingerprintSipHasher { impl FingerprintHasher for FingerprintSipHasher { fn finish_fingerprint(&self) -> (Fingerprint, Vec) { let buffer = self.data.clone(); - let mut inner = SipHasher13::new(); + let mut inner = FingerprintSipHasherBase::new(); buffer.hash(&mut inner); let hash = inner.finish128(); (Fingerprint(hash.h1, hash.h2), buffer) @@ -157,3 +174,18 @@ pub fn item_hash128(item: &T) -> u128 { pub fn hash128(t: &T) -> u128 { typst::util::hash128(t) } + +#[test] +fn test_fingerprint() { + let t = Fingerprint::from_pair(0, 1); + assert_eq!(Fingerprint::try_from_str(&t.as_svg_id("")).unwrap(), t); + + let t = Fingerprint::from_pair(1, 1); + assert_eq!(Fingerprint::try_from_str(&t.as_svg_id("")).unwrap(), t); + + let t = Fingerprint::from_pair(1, 0); + assert_eq!(Fingerprint::try_from_str(&t.as_svg_id("")).unwrap(), t); + + let t = Fingerprint::from_pair(0, 0); + assert_eq!(Fingerprint::try_from_str(&t.as_svg_id("")).unwrap(), t); +} diff --git a/core/src/vector/bbox.rs b/core/src/vector/bbox.rs index 52e87b56..14be00b2 100644 --- a/core/src/vector/bbox.rs +++ b/core/src/vector/bbox.rs @@ -4,7 +4,8 @@ use std::{collections::HashMap, ops::Deref}; use comemo::Prehashed; use typst::font::Font; -use super::ir::{FontIndice, FontRef, GlyphPackBuilder, GlyphRef}; +use super::ir::{FontIndice, FontRef, GlyphIndice, GlyphPackBuilder, GlyphRef}; +use super::vm::RenderState; use super::{ flat_ir::{self, Module}, flat_vm::{FlatGroupContext, FlatIncrGroupContext, FlatIncrRenderVm, FlatRenderVm}, @@ -15,10 +16,6 @@ use super::{ use crate::font::GlyphProvider; use crate::hash::Fingerprint; -pub trait GlyphIndice<'m> { - fn get_glyph(&self, value: &GlyphRef) -> Option<&'m ir::GlyphItem>; -} - pub trait BBoxIndice { fn get_bbox(&self, value: &Fingerprint) -> Option; } @@ -205,8 +202,14 @@ impl TransformContext for BBoxBuilder { /// See [`GroupContext`]. impl> GroupContext for BBoxBuilder { - fn render_item_at(&mut self, ctx: &mut C, pos: ir::Point, item: &ir::SvgItem) { - let bbox = ctx.render_item(item); + fn render_item_at( + &mut self, + state: RenderState, + ctx: &mut C, + pos: ir::Point, + item: &ir::SvgItem, + ) { + let bbox = ctx.render_item(state, item); self.inner.push((pos, bbox)); } @@ -215,7 +218,13 @@ impl> GroupContext for BBoxBuilder self.render_glyph_ref_inner(pos, &glyph_ref, glyph) } - fn render_path(&mut self, _ctx: &mut C, path: &ir::PathItem) { + fn render_path( + &mut self, + _state: RenderState, + _ctx: &mut C, + path: &ir::PathItem, + _abs_ref: &Fingerprint, + ) { let path = PathRepr::from_item(path).unwrap(); self.inner.push(( ir::Point::default(), @@ -238,8 +247,14 @@ impl> GroupContext for BBoxBuilder impl<'m, C: FlatRenderVm<'m, Resultant = BBox> + GlyphIndice<'m>> FlatGroupContext for BBoxBuilder { - fn render_item_ref_at(&mut self, ctx: &mut C, pos: ir::Point, item: &Fingerprint) { - let bbox = ctx.render_flat_item(item); + fn render_item_ref_at( + &mut self, + state: RenderState, + ctx: &mut C, + pos: ir::Point, + item: &Fingerprint, + ) { + let bbox = ctx.render_flat_item(state, item); self.inner.push((pos, bbox)); } @@ -256,6 +271,7 @@ impl<'m, C: FlatIncrRenderVm<'m, Resultant = BBox, Group = BBoxBuilder> + BBoxIn { fn render_diff_item_ref_at( &mut self, + state: RenderState, ctx: &mut C, pos: ir::Point, item: &Fingerprint, @@ -264,7 +280,7 @@ impl<'m, C: FlatIncrRenderVm<'m, Resultant = BBox, Group = BBoxBuilder> + BBoxIn let bbox = (prev_item == item) .then(|| ctx.get_bbox(prev_item)) .flatten(); - let bbox = bbox.unwrap_or_else(|| ctx.render_diff_item(item, prev_item)); + let bbox = bbox.unwrap_or_else(|| ctx.render_diff_item(state, item, prev_item)); self.inner.push((pos, bbox)); } } @@ -315,12 +331,12 @@ impl<'m, 't> FlatRenderVm<'m> for BBoxTask<'m, 't> { self.start_group() } - fn render_flat_item(&mut self, abs_ref: &Fingerprint) -> Self::Resultant { + fn render_flat_item(&mut self, state: RenderState, abs_ref: &Fingerprint) -> Self::Resultant { if let Some(bbox) = self.bbox_cache.get(abs_ref) { return bbox.clone(); } - let bbox = self._render_flat_item(abs_ref); + let bbox = self._render_flat_item(state, abs_ref); self.bbox_cache.insert(*abs_ref, bbox.clone()); bbox } @@ -329,10 +345,11 @@ impl<'m, 't> FlatRenderVm<'m> for BBoxTask<'m, 't> { impl<'m, 't> FlatIncrRenderVm<'m> for BBoxTask<'m, 't> { fn render_diff_item( &mut self, + state: RenderState, next_abs_ref: &Fingerprint, prev_abs_ref: &Fingerprint, ) -> Self::Resultant { - let bbox = self._render_diff_item(next_abs_ref, prev_abs_ref); + let bbox = self._render_diff_item(state, next_abs_ref, prev_abs_ref); self.bbox_cache.insert(*next_abs_ref, bbox.clone()); bbox } @@ -407,7 +424,7 @@ fn convert_path(path_data: &str) -> Option { #[cfg(test)] mod tests { - use tests::ir::PathItem; + use tests::ir::{PathItem, Size}; use crate::vector::path2d::SvgPath2DBuilder; @@ -438,6 +455,7 @@ mod tests { let d = d.0.into(); let path = PathItem { d, + size: None, styles: Default::default(), }; @@ -450,7 +468,10 @@ mod tests { let mut task = t.get(); let rect = get_rect_item(1., 2., 10., 20.); - let bbox = task.render_item(&rect); + let bbox = task.render_item( + RenderState::new_size(Size::new(Scalar(10.), Scalar(20.))), + &rect, + ); println!("{:?}", bbox.realize(Transform::identity())); } @@ -461,7 +482,10 @@ mod tests { let mut task = t.get(); let rect = get_rect_item(1., 2., 10., 20.); - let bbox = task.render_item(&rect); + let bbox = task.render_item( + RenderState::new_size(Size::new(Scalar(10.), Scalar(20.))), + &rect, + ); let ts = sk::Transform::from_translate(10., 20.); println!("{:?}", bbox.realize(ts.into())); diff --git a/core/src/vector/flat_ir/mod.rs b/core/src/vector/flat_ir/mod.rs index 7dcda903..2c05b2c6 100644 --- a/core/src/vector/flat_ir/mod.rs +++ b/core/src/vector/flat_ir/mod.rs @@ -39,8 +39,8 @@ use crate::{ use super::{ geom::{Abs, Point, Size}, ir::{ - DefId, FontItem, FontRef, GlyphItem, GlyphRef, ImageGlyphItem, ImageItem, ImmutStr, - LinkItem, OutlineGlyphItem, PathItem, SpanId, TextShape, TransformItem, + DefId, FontItem, FontRef, GlyphItem, GlyphRef, GradientItem, ImageGlyphItem, ImageItem, + ImmutStr, LinkItem, OutlineGlyphItem, PathItem, SpanId, TextShape, TransformItem, }, }; @@ -67,7 +67,8 @@ pub enum FlatSvgItem { Path(PathItem), Text(FlatTextItem), Item(TransformedRef), - Group(GroupRef), + Group(GroupRef, Option), + Gradient(GradientItem), } /// Flatten text item. @@ -80,6 +81,13 @@ pub struct FlatTextItem { pub shape: Arc, } +impl FlatTextItem { + pub fn render_glyphs(&self, upem: Abs, consume_glyph: impl FnMut(Abs, &GlyphRef)) -> Abs { + self.shape + .render_glyphs(upem, self.content.glyphs.iter(), consume_glyph) + } +} + /// The content metadata of a [`FlatTextItem`]. #[derive(Debug, Clone, Hash, PartialEq, Eq)] #[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))] diff --git a/core/src/vector/flat_ir/module.rs b/core/src/vector/flat_ir/module.rs index a2778834..5f640fed 100644 --- a/core/src/vector/flat_ir/module.rs +++ b/core/src/vector/flat_ir/module.rs @@ -255,6 +255,7 @@ impl ModuleBuilderImpl { FlatSvgItem::Path(path) } + SvgItem::Gradient(g) => FlatSvgItem::Gradient(g), SvgItem::Link(link) => FlatSvgItem::Link(link), SvgItem::Text(text) => { let font = self.build_font(&text.font); @@ -288,7 +289,7 @@ impl ModuleBuilderImpl { FlatSvgItem::Item(TransformedRef(transform, item_id)) } - SvgItem::Group(group) => { + SvgItem::Group(group, size) => { let t = if self.should_attach_debug_info { Some(self.source_mapping_buffer.len()) } else { @@ -312,7 +313,8 @@ impl ModuleBuilderImpl { .push(SourceMappingNode::Group(sm_range.collect())); self.source_mapping_buffer.push(sm_id); } - FlatSvgItem::Group(GroupRef(items.into())) + + FlatSvgItem::Group(GroupRef(items.into()), size) } }; diff --git a/core/src/vector/flat_vm.rs b/core/src/vector/flat_vm.rs index 71b5735f..959066c4 100644 --- a/core/src/vector/flat_vm.rs +++ b/core/src/vector/flat_vm.rs @@ -4,7 +4,8 @@ use std::collections::{BTreeMap, HashSet}; use crate::hash::Fingerprint; use super::flat_ir as ir; -use super::ir::{FontIndice, GlyphRef}; +use super::ir::{FontIndice, GlyphRef, Size, Transform}; +use super::vm::RenderState; use super::{ ir::{Point, Scalar}, vm::{GroupContext, TransformContext}, @@ -12,9 +13,15 @@ use super::{ /// A RAII trait for rendering flatten SVG items into underlying context. pub trait FlatGroupContext: Sized { - fn render_item_ref_at(&mut self, ctx: &mut C, pos: Point, item: &Fingerprint); - fn render_item_ref(&mut self, ctx: &mut C, item: &Fingerprint) { - self.render_item_ref_at(ctx, Point::default(), item); + fn render_item_ref_at( + &mut self, + state: RenderState, + ctx: &mut C, + pos: Point, + item: &Fingerprint, + ); + fn render_item_ref(&mut self, state: RenderState, ctx: &mut C, item: &Fingerprint) { + self.render_item_ref_at(state, ctx, Point::default(), item); } fn render_glyph_ref(&mut self, _ctx: &mut C, _pos: Scalar, _item: &GlyphRef) {} @@ -30,7 +37,13 @@ pub trait FlatGroupContext: Sized { fn with_frame(self, _ctx: &mut C, _group: &ir::GroupRef) -> Self { self } - fn with_text(self, _ctx: &mut C, _text: &ir::FlatTextItem) -> Self { + fn with_text( + self, + _ctx: &mut C, + _text: &ir::FlatTextItem, + _fill_key: &Fingerprint, + _state: RenderState, + ) -> Self { self } fn with_reuse(self, _ctx: &mut C, _v: &Fingerprint) -> Self { @@ -56,21 +69,33 @@ pub trait FlatRenderVm<'m>: Sized + FontIndice<'m> { self.start_flat_group(value) } - fn start_flat_text(&mut self, value: &Fingerprint, _text: &ir::FlatTextItem) -> Self::Group { + fn start_flat_text( + &mut self, + _state: RenderState, + value: &Fingerprint, + _text: &ir::FlatTextItem, + ) -> Self::Group { self.start_flat_group(value) } #[doc(hidden)] /// Default implemenetion to render an item into the a `` element. - fn _render_flat_item(&mut self, abs_ref: &Fingerprint) -> Self::Resultant { + fn _render_flat_item(&mut self, state: RenderState, abs_ref: &Fingerprint) -> Self::Resultant { let item: &'m ir::FlatSvgItem = self.get_item(abs_ref).unwrap(); match &item { - ir::FlatSvgItem::Group(group) => self.render_group_ref(abs_ref, group), - ir::FlatSvgItem::Item(transformed) => self.render_transformed_ref(abs_ref, transformed), - ir::FlatSvgItem::Text(text) => self.render_flat_text(abs_ref, text), + ir::FlatSvgItem::Group(group, sz) => self.render_group_ref(state, abs_ref, group, sz), + ir::FlatSvgItem::Item(transformed) => { + self.render_transformed_ref(state, abs_ref, transformed) + } + ir::FlatSvgItem::Text(text) => { + let mut g = self.start_flat_text(state, abs_ref, text); + g = self.render_flat_text(state, g, abs_ref, text); + + g.into() + } ir::FlatSvgItem::Path(path) => { let mut g = self.start_flat_group(abs_ref); - g.render_path(self, path); + g.render_path(state, self, path, abs_ref); g.into() } ir::FlatSvgItem::Link(link) => { @@ -83,24 +108,34 @@ pub trait FlatRenderVm<'m>: Sized + FontIndice<'m> { g.render_image(self, image); g.into() } - ir::FlatSvgItem::None => { + ir::FlatSvgItem::Gradient(..) | ir::FlatSvgItem::None => { panic!("FlatRenderVm.RenderFrame.UnknownItem {:?}", item) } } } /// Render an item into the a `` element. - fn render_flat_item(&mut self, abs_ref: &Fingerprint) -> Self::Resultant { - self._render_flat_item(abs_ref) + fn render_flat_item(&mut self, state: RenderState, abs_ref: &Fingerprint) -> Self::Resultant { + self._render_flat_item(state, abs_ref) } /// Render a frame group into underlying context. - fn render_group_ref(&mut self, abs_ref: &Fingerprint, group: &ir::GroupRef) -> Self::Resultant { + fn render_group_ref( + &mut self, + mut state: RenderState, + abs_ref: &Fingerprint, + group: &ir::GroupRef, + sz: &Option, + ) -> Self::Resultant { let mut group_ctx = self.start_flat_frame(abs_ref, group); + if let Some(sz) = sz { + state = state.with_transform(Transform::identity()).with_size(*sz); + } + for (pos, item_ref) in group.0.iter() { // let item = self.get_item(&item_ref).unwrap(); - group_ctx.render_item_ref_at(self, *pos, item_ref); + group_ctx.render_item_ref_at(state.pre_translate(*pos), self, *pos, item_ref); } group_ctx.into() @@ -109,6 +144,7 @@ pub trait FlatRenderVm<'m>: Sized + FontIndice<'m> { /// Render a transformed frame into underlying context. fn render_transformed_ref( &mut self, + state: RenderState, abs_ref: &Fingerprint, transformed: &ir::TransformedRef, ) -> Self::Resultant { @@ -118,41 +154,28 @@ pub trait FlatRenderVm<'m>: Sized + FontIndice<'m> { let item_ref = &transformed.1; // let item = self.get_item(&item_ref).unwrap(); - ts.render_item_ref(self, item_ref); + ts.render_item_ref(state.pre_apply(&transformed.0), self, item_ref); ts.into() } /// Render a text into the underlying context. fn render_flat_text( &mut self, - abs_ref: &Fingerprint, + _state: RenderState, + mut group_ctx: Self::Group, + _abs_ref: &Fingerprint, text: &ir::FlatTextItem, - ) -> Self::Resultant { - let group_ctx = self.start_flat_text(abs_ref, text); - - let font = self.get_font(&text.font).unwrap(); - + ) -> Self::Group { // upem is the unit per em defined in the font. - // ppem is calcuated by the font size. - // > ppem = text_size / upem - let upem = font.unit_per_em.0; - let ppem = Scalar(text.shape.size.0 / upem); - let inv_ppem = upem / text.shape.size.0; - - let mut group_ctx = group_ctx.transform_scale(self, ppem, -ppem); - - let mut x = 0f32; - for (offset, advance, glyph) in text.content.glyphs.iter() { - let offset = x + offset.0; - let ts = offset * inv_ppem; - - group_ctx.render_glyph_ref(self, Scalar(ts), glyph); - - x += advance.0; - } + let font = self.get_font(&text.font).unwrap(); + let upem = Scalar(font.unit_per_em.0); - group_ctx.render_flat_text_semantics(self, text, Scalar(x)); - group_ctx.into() + // Rescale the font size and put glyphs into the group. + group_ctx = text.shape.add_transform(self, group_ctx, upem); + text.render_glyphs(upem, |x, g| { + group_ctx.render_glyph_ref(self, x, g); + }); + group_ctx } } @@ -160,13 +183,21 @@ pub trait FlatRenderVm<'m>: Sized + FontIndice<'m> { pub trait FlatIncrGroupContext: Sized { fn render_diff_item_ref_at( &mut self, + state: RenderState, ctx: &mut C, + pos: Point, item: &Fingerprint, prev_item: &Fingerprint, ); - fn render_diff_item_ref(&mut self, ctx: &mut C, item: &Fingerprint, prev_item: &Fingerprint) { - self.render_diff_item_ref_at(ctx, Point::default(), item, prev_item); + fn render_diff_item_ref( + &mut self, + state: RenderState, + ctx: &mut C, + item: &Fingerprint, + prev_item: &Fingerprint, + ) { + self.render_diff_item_ref_at(state, ctx, Point::default(), item, prev_item); } } @@ -181,6 +212,7 @@ where /// Default implemenetion to Render an item into the a `` element. fn _render_diff_item( &mut self, + state: RenderState, next_abs_ref: &Fingerprint, prev_abs_ref: &Fingerprint, ) -> Self::Resultant { @@ -190,26 +222,26 @@ where let mut group_ctx = self.start_flat_group(next_abs_ref); match &next_item { - ir::FlatSvgItem::Group(group) => { + ir::FlatSvgItem::Group(group, sz) => { let mut group_ctx = group_ctx .with_reuse(self, prev_abs_ref) .with_frame(self, group); - self.render_diff_group_ref(&mut group_ctx, prev_item, group); + self.render_diff_group_ref(state, &mut group_ctx, prev_item, group, sz); group_ctx } ir::FlatSvgItem::Item(transformed) => { let mut group_ctx = group_ctx .with_reuse(self, prev_abs_ref) .transform(self, &transformed.0); - self.render_diff_transformed_ref(&mut group_ctx, prev_item, transformed); + self.render_diff_transformed_ref(state, &mut group_ctx, prev_item, transformed); group_ctx } ir::FlatSvgItem::Text(text) => { - let group_ctx = group_ctx.with_text(self, text); - self.render_diff_flat_text(group_ctx, text) + let group_ctx = group_ctx.with_text(self, text, next_abs_ref, state); + self.render_diff_flat_text(state, group_ctx, next_abs_ref, prev_abs_ref, text) } ir::FlatSvgItem::Path(path) => { - group_ctx.render_path(self, path); + group_ctx.render_path(state, self, path, next_abs_ref); group_ctx } ir::FlatSvgItem::Link(link) => { @@ -220,7 +252,7 @@ where group_ctx.render_image(self, image); group_ctx } - ir::FlatSvgItem::None => { + ir::FlatSvgItem::Gradient(..) | ir::FlatSvgItem::None => { panic!("FlatRenderVm.RenderFrame.UnknownItem {:?}", next_item) } } @@ -230,20 +262,27 @@ where /// Render an item into the a `` element. fn render_diff_item( &mut self, + state: RenderState, next_abs_ref: &Fingerprint, prev_abs_ref: &Fingerprint, ) -> Self::Resultant { - self._render_diff_item(next_abs_ref, prev_abs_ref) + self._render_diff_item(state, next_abs_ref, prev_abs_ref) } /// Render a frame group into underlying context. fn render_diff_group_ref( &mut self, + mut state: RenderState, group_ctx: &mut Self::Group, prev_item_: Option<&ir::FlatSvgItem>, next: &ir::GroupRef, + sz: &Option, ) { - if let Some(ir::FlatSvgItem::Group(prev_group)) = prev_item_ { + if let Some(sz) = sz { + state = state.with_size(*sz); + } + + if let Some(ir::FlatSvgItem::Group(prev_group, _)) = prev_item_ { let mut unused_prev: BTreeMap = prev_group.0.iter().map(|v| v.1).enumerate().collect(); let reusable: HashSet = @@ -260,17 +299,18 @@ where } for (pos, item_ref) in next.0.iter() { + let state = state.pre_translate(*pos); if reusable.contains(item_ref) { - group_ctx.render_diff_item_ref_at(self, *pos, item_ref, item_ref); + group_ctx.render_diff_item_ref_at(state, self, *pos, item_ref, item_ref); } else if let Some((_, prev_item_re_)) = &unused_prev.pop_first() { - group_ctx.render_diff_item_ref_at(self, *pos, item_ref, prev_item_re_) + group_ctx.render_diff_item_ref_at(state, self, *pos, item_ref, prev_item_re_) } else { - group_ctx.render_item_ref_at(self, *pos, item_ref) + group_ctx.render_item_ref_at(state, self, *pos, item_ref) } } } else { for (pos, item_ref) in next.0.iter() { - group_ctx.render_item_ref_at(self, *pos, item_ref); + group_ctx.render_item_ref_at(state.pre_translate(*pos), self, *pos, item_ref); } } } @@ -278,50 +318,33 @@ where /// Render a transformed frame into underlying context. fn render_diff_transformed_ref( &mut self, + state: RenderState, ts: &mut Self::Group, prev_item_: Option<&ir::FlatSvgItem>, transformed: &ir::TransformedRef, ) { let child_ref = &transformed.1; + let state = state.pre_apply(&transformed.0); if matches!(prev_item_, Some(ir::FlatSvgItem::Item(ir::TransformedRef(_item, prev_ref))) if prev_ref == child_ref) { // assert!(item != &transformed.0); - ts.render_diff_item_ref_at(self, Point::default(), child_ref, child_ref); + ts.render_diff_item_ref_at(state, self, Point::default(), child_ref, child_ref); return; } // failed to reuse - ts.render_item_ref(self, child_ref); + ts.render_item_ref(state, self, child_ref); } - /// Render a text into the underlying context. + /// Render a diff text into the underlying context. fn render_diff_flat_text( &mut self, + state: RenderState, group_ctx: Self::Group, + next_abs_ref: &Fingerprint, + _prev_abs_ref: &Fingerprint, text: &ir::FlatTextItem, ) -> Self::Group { - let font = self.get_font(&text.font).unwrap(); - - // upem is the unit per em defined in the font. - // ppem is calcuated by the font size. - // > ppem = text_size / upem - let upem = font.unit_per_em.0; - let ppem = Scalar(text.shape.size.0 / upem); - let inv_ppem = upem / text.shape.size.0; - - let mut group_ctx = group_ctx.transform_scale(self, ppem, -ppem); - - let mut x = 0f32; - for (offset, advance, glyph) in text.content.glyphs.iter() { - let offset = x + offset.0; - let ts = offset * inv_ppem; - - group_ctx.render_glyph_ref(self, Scalar(ts), glyph); - - x += advance.0; - } - - group_ctx.render_flat_text_semantics(self, text, Scalar(x)); - group_ctx + self.render_flat_text(state, group_ctx, next_abs_ref, text) } } diff --git a/core/src/vector/geom.rs b/core/src/vector/geom.rs index 9ed96d2f..5c214b37 100644 --- a/core/src/vector/geom.rs +++ b/core/src/vector/geom.rs @@ -5,8 +5,8 @@ use std::{ }; use typst::geom::{ - Abs as TypstAbs, Axes as TypstAxes, Point as TypstPoint, Ratio as TypstRatio, - Scalar as TypstScalar, Transform as TypstTransform, + Abs as TypstAbs, Angle as TypstAngle, Axes as TypstAxes, Point as TypstPoint, + Ratio as TypstRatio, Scalar as TypstScalar, Transform as TypstTransform, }; #[cfg(feature = "rkyv")] @@ -19,6 +19,12 @@ use rkyv::{Archive, Deserialize as rDeser, Serialize as rSer}; #[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] pub struct Scalar(pub f32); +impl Scalar { + fn is_zero(&self) -> bool { + self.0 == 0.0 + } +} + impl From for Scalar { fn from(float: f32) -> Self { Self(float) @@ -33,13 +39,13 @@ impl From for f32 { impl From for Scalar { fn from(scalar: TypstScalar) -> Self { - Self(scalar.0 as f32) + Self(scalar.get() as f32) } } impl From for TypstScalar { fn from(scalar: Scalar) -> Self { - Self(scalar.0 as f64) + Self::from(scalar.0 as f64) } } @@ -55,6 +61,12 @@ impl From for Scalar { } } +impl From for Scalar { + fn from(scalar: TypstAngle) -> Self { + Self(scalar.to_rad() as f32) + } +} + impl fmt::Debug for Scalar { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.fmt(f) @@ -128,6 +140,8 @@ pub type Size = Axes; pub type Point = Axes; /// Ratio within range [0, 1] pub type Ratio = Scalar; +/// Angle in radians +pub type Angle = Scalar; /// A container with a horizontal and vertical component. #[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Hash)] @@ -257,12 +271,65 @@ impl Transform { ts.into() } + #[inline] + pub fn post_concat(self, other: Self) -> Self { + other.pre_concat(self) + } + #[inline] pub fn pre_translate(self, tx: f32, ty: f32) -> Self { let ts: tiny_skia::Transform = self.into(); let ts = ts.pre_translate(tx, ty); ts.into() } + + /// Whether this is the identity transformation. + pub fn is_identity(self) -> bool { + self == Self::identity() + } + + /// Inverts the transformation. + /// + /// Returns `None` if the determinant of the matrix is zero. + pub fn invert(self) -> Option { + // Allow the trivial case to be inlined. + if self.is_identity() { + return Some(self); + } + + // Fast path for scale-translate-only transforms. + if self.kx.is_zero() && self.ky.is_zero() { + if self.sx.is_zero() || self.sy.is_zero() { + return Some(Self::from_translate(-self.tx, -self.ty)); + } + + let inv_x = 1.0 / self.sx.0; + let inv_y = 1.0 / self.sy.0; + return Some(Self { + sx: Scalar(inv_x), + ky: Scalar(0.), + kx: Scalar(0.), + sy: Scalar(inv_y), + tx: Scalar(-self.tx.0 * inv_x), + ty: Scalar(-self.ty.0 * inv_y), + }); + } + + let det = self.sx.0 * self.sy.0 - self.kx.0 * self.ky.0; + if det.abs() < 1e-12 { + return None; + } + + let inv_det = 1.0 / det; + Some(Self { + sx: Scalar(self.sy.0 * inv_det), + ky: Scalar(-self.ky.0 * inv_det), + kx: Scalar(-self.kx.0 * inv_det), + sy: Scalar(self.sx.0 * inv_det), + tx: Scalar((self.kx.0 * self.ty.0 - self.sy.0 * self.tx.0) * inv_det), + ty: Scalar((self.ky.0 * self.tx.0 - self.sx.0 * self.ty.0) * inv_det), + }) + } } impl From for Transform { @@ -304,32 +371,6 @@ impl From for tiny_skia::Transform { } } -impl From for Transform { - fn from(skia_transform: tiny_skia_path::Transform) -> Self { - Self { - sx: skia_transform.sx.into(), - ky: skia_transform.ky.into(), - kx: skia_transform.kx.into(), - sy: skia_transform.sy.into(), - tx: skia_transform.tx.into(), - ty: skia_transform.ty.into(), - } - } -} - -impl From for tiny_skia_path::Transform { - fn from(ir_transform: Transform) -> Self { - Self { - sx: ir_transform.sx.into(), - ky: ir_transform.ky.into(), - kx: ir_transform.kx.into(), - sy: ir_transform.sy.into(), - tx: ir_transform.tx.into(), - ty: ir_transform.ty.into(), - } - } -} - #[derive(Debug, Clone, Copy, PartialEq, Hash, Default)] pub struct Rect { pub lo: Point, diff --git a/core/src/vector/incr.rs b/core/src/vector/incr.rs index db9f8741..93a68f20 100644 --- a/core/src/vector/incr.rs +++ b/core/src/vector/incr.rs @@ -70,6 +70,10 @@ impl IncrDocServer { } }) .collect::>(); + + for ext in lower_builder.extra_items.into_values() { + builder.build(ext); + } let delta = builder.finalize_delta(); // max, min lifetime current, gc_items diff --git a/core/src/vector/ir.rs b/core/src/vector/ir.rs index 396b7771..5dd2489e 100644 --- a/core/src/vector/ir.rs +++ b/core/src/vector/ir.rs @@ -21,6 +21,7 @@ use crate::{ pub use crate::ImmutStr; pub use super::geom::*; +use super::vm::{GroupContext, TransformContext}; /// Create a xml id from the given prefix and the def id of this reference. /// Note that the def id may not be stable across compilation. @@ -138,7 +139,9 @@ pub enum SvgItem { Path((PathItem, SpanId)), Text(TextItem), Transformed(TransformedItem), - Group(GroupItem), + Group(GroupItem, Option), + // todo: big size 64 + Gradient(GradientItem), } /// Data of an `` element. @@ -178,7 +181,7 @@ impl From for Image { Image { data: image.data().to_vec(), format: format.into(), - size: image.size().into(), + size: Axes::new(image.width(), image.height()), alt: image.alt().map(|s| s.into()), hash: Fingerprint::from_u128(hash), } @@ -232,6 +235,112 @@ pub struct LinkItem { pub size: Size, } +/// Item representing an `` element. +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))] +#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] +pub struct ColorItem { + pub r: u8, + pub g: u8, + pub b: u8, + pub a: u8, +} + +impl ColorItem { + // todo: to_css + pub fn to_hex(self) -> String { + let Self { r, g, b, a } = self; + if a != 255 { + format!("#{:02x}{:02x}{:02x}{:02x}", r, g, b, a) + } else { + format!("#{:02x}{:02x}{:02x}", r, g, b) + } + } + + pub fn typst(&self) -> typst::geom::Color { + typst::geom::Color::from_u8(self.r, self.g, self.b, self.a) + } +} + +/// A color space for mixing. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))] +#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] +pub enum ColorSpace { + /// A perceptual color space. + Oklab, + + /// The standard RGB color space. + Srgb, + + /// The D65-gray color space. + D65Gray, + + /// The linear RGB color space. + LinearRgb, + + /// The HSL color space. + Hsl, + + /// The HSV color space. + Hsv, + + /// The CMYK color space. + Cmyk, +} + +impl From for ColorSpace { + fn from(value: typst::geom::ColorSpace) -> Self { + use typst::geom::ColorSpace::*; + match value { + Oklab => Self::Oklab, + Srgb => Self::Srgb, + D65Gray => Self::D65Gray, + LinearRgb => Self::LinearRgb, + Hsl => Self::Hsl, + Hsv => Self::Hsv, + Cmyk => Self::Cmyk, + } + } +} + +impl From for typst::geom::ColorSpace { + fn from(value: ColorSpace) -> Self { + use typst::geom::ColorSpace::*; + match value { + ColorSpace::Oklab => Oklab, + ColorSpace::Srgb => Srgb, + ColorSpace::D65Gray => D65Gray, + ColorSpace::LinearRgb => LinearRgb, + ColorSpace::Hsl => Hsl, + ColorSpace::Hsv => Hsv, + ColorSpace::Cmyk => Cmyk, + } + } +} + +/// Item representing an `` element. +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))] +#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] +pub struct GradientItem { + /// The path instruction. + pub stops: Vec<(ColorItem, Scalar)>, + /// Whether the gradient is relative to itself (its own bounding box). + /// Otherwise, the gradient is relative to the parent bounding box. + pub relative_to_self: Option, + /// Whether to anti-alias the gradient (used for sharp gradients). + pub anti_alias: bool, + /// A color space for mixing. + pub space: ColorSpace, + /// The gradient kind. + /// See [`GradientKind`] for more information. + pub kind: GradientKind, + /// Additional gradient styles. + /// See [`GradientStyle`] for more information. + pub styles: Vec, +} + /// Item representing an `` element. #[derive(Debug, Clone, Hash, PartialEq, Eq)] #[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))] @@ -239,11 +348,39 @@ pub struct LinkItem { pub struct PathItem { /// The path instruction. pub d: ImmutStr, + /// bbox of the path. + pub size: Option, /// The path style. /// See [`PathStyle`] for more information. pub styles: Vec, } +/// Kind of graidents for [`GradientItem`]. +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))] +#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] +pub enum GradientKind { + /// Angle of a linear gradient. + Linear(Scalar), + /// Radius of a radial gradient. + Radial(Scalar), + /// Angle of a conic gradient. + Conic(Scalar), +} + +/// Attributes that is applicable to the [`GradientItem`]. +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))] +#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] +pub enum GradientStyle { + /// Center of a radial or conic gradient. + Center(Point), + /// Focal center of a radial gradient. + FocalCenter(Point), + /// Focal radius of a radial gradient. + FocalRadius(Scalar), +} + /// Attributes that is applicable to the [`PathItem`]. #[derive(Debug, Clone, Hash, PartialEq, Eq)] #[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))] @@ -294,6 +431,13 @@ pub struct TextItem { pub shape: Arc, } +impl TextItem { + pub fn render_glyphs(&self, upem: Abs, consume_glyph: impl FnMut(Abs, &GlyphItem)) -> Abs { + self.shape + .render_glyphs(upem, self.content.glyphs.iter(), consume_glyph) + } +} + /// The content metadata of a [`TextItem`]. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct TextItemContent { @@ -406,6 +550,50 @@ pub struct TextShape { pub fill: ImmutStr, } +impl TextShape { + /// ppem is calcuated by the font size. + fn ppem(&self, upem: f32) -> Scalar { + Scalar(self.size.0 / upem) + } + + /// inv_ppem is calcuated by the font size. + fn inv_ppem(&self, upem: f32) -> Scalar { + Scalar(upem / self.size.0) + } + + pub fn add_transform + TransformContext>( + &self, + ctx: &mut C, + group_ctx: T, + upem: Scalar, + ) -> T { + let ppem = self.ppem(upem.0); + group_ctx.transform_scale(ctx, ppem, -ppem) + } + + #[inline] + pub(super) fn render_glyphs<'a, Glyph: 'a>( + &self, + upem: Abs, + glyph_iter: impl Iterator, + mut consume_glyph: impl FnMut(Abs, &'a Glyph), + ) -> Abs { + let inv_ppem = self.inv_ppem(upem.0).0; + + let mut x = 0f32; + for (offset, advance, glyph) in glyph_iter { + let offset = x + offset.0; + let ts = offset * inv_ppem; + + consume_glyph(Scalar(ts), glyph); + + x += advance.0; + } + + Scalar(x) + } +} + /// Item representing an `` element applied with a [`TransformItem`]. #[derive(Debug, Clone)] pub struct TransformedItem(pub TransformItem, pub Box); @@ -577,6 +765,10 @@ pub trait FontIndice<'m> { fn get_font(&self, value: &FontRef) -> Option<&'m FontItem>; } +pub trait GlyphIndice<'m> { + fn get_glyph(&self, value: &GlyphRef) -> Option<&'m GlyphItem>; +} + pub trait BuildGlyph { fn build_font(&mut self, font: &Font) -> FontRef; fn build_glyph(&mut self, glyph: &GlyphItem) -> GlyphRef; diff --git a/core/src/vector/lowering.rs b/core/src/vector/lowering.rs index 787954c2..6f1a7fe0 100644 --- a/core/src/vector/lowering.rs +++ b/core/src/vector/lowering.rs @@ -1,12 +1,20 @@ //! Lowering Typst Document into SvgItem. +use std::any::Any; +use std::collections::HashMap; +use std::hash::Hash; use std::io::Read; use std::sync::Arc; use once_cell::sync::OnceCell; -use typst::doc::{Destination, Document, Frame, FrameItem, GroupItem, Meta, Position, TextItem}; +use typst::doc::{ + Destination, Document, Frame, FrameItem, FrameKind, GroupItem, Meta, Position, TextItem, +}; use typst::font::Font; -use typst::geom::{Dir, FixedStroke, Geometry, LineCap, LineJoin, Paint, PathItem, Shape, Size}; +use typst::geom::{ + Abs as TypstAbs, Dir, FixedStroke, Geometry, Gradient, LineCap, LineJoin, Paint, PathItem, + Ratio as TypstRatio, Relative, Shape, Size, Smart, +}; use typst::image::Image; use ttf_parser::OutlineBuilder; @@ -23,19 +31,34 @@ use super::{ utils::{AbsExt, ToCssExt}, }; use crate::font::GlyphProvider; +use crate::hash::{Fingerprint, FingerprintHasher, FingerprintSipHasher}; +use crate::vector::flat_ir::FlatSvgItem; +use crate::vector::ir::{ColorItem, GradientItem, GradientKind}; +use crate::ImmutStr; use ttf_parser::GlyphId; static WARN_VIEW_BOX: OnceCell<()> = OnceCell::new(); +#[derive(Clone)] +struct ShapeInfo { + path: ir::PathItem, + fill_gradient: Option<(Fingerprint, GradientItem)>, + stroke_gradient: Option<(Fingerprint, GradientItem)>, +} + /// Lower a frame item into svg item. pub struct LowerBuilder { introspector: Introspector, + /// Extra items that used by the document but not directly rendered. + /// For example, gradients. + pub extra_items: HashMap, } impl LowerBuilder { pub fn new(output: &Document) -> Self { Self { introspector: Introspector::new(&output.pages), + extra_items: HashMap::new(), } } @@ -51,9 +74,17 @@ impl LowerBuilder { for (pos, item) in frame.items() { let item = match item { FrameItem::Group(group) => self.lower_group(group), - FrameItem::Text(text) => Self::lower_text(text), + FrameItem::Text(text) => self.lower_text(text), FrameItem::Shape(shape, span_id) => { - SvgItem::Path((Self::lower_shape(shape), span_id_to_u64(span_id))) + let s = Self::lower_shape(shape); + if let Some((f, gradient)) = s.fill_gradient { + self.extra_items.insert(f, ir::SvgItem::Gradient(gradient)); + } + if let Some((f, gradient)) = s.stroke_gradient { + self.extra_items.insert(f, ir::SvgItem::Gradient(gradient)); + } + + SvgItem::Path((s.path, span_id_to_u64(span_id))) } FrameItem::Image(image, size, span_id) => { SvgItem::Image((lower_image(image, *size), span_id_to_u64(span_id))) @@ -96,29 +127,51 @@ impl LowerBuilder { std::cmp::Ordering::Equal }); - SvgItem::Group(ir::GroupItem(items)) + match frame.kind() { + FrameKind::Hard => SvgItem::Group(ir::GroupItem(items), Some(frame.size().into())), + FrameKind::Soft => SvgItem::Group(ir::GroupItem(items), None), + } } /// Lower a group frame with optional transform and clipping into svg item. fn lower_group(&mut self, group: &GroupItem) -> SvgItem { let mut inner = self.lower_frame(&group.frame); - if group.clips { - let mask_box = { - let mut builder = SvgPath2DBuilder::default(); - - // build a rectangle path - let size = group.frame.size(); - let w = size.x.to_f32(); - let h = size.y.to_f32(); - builder.rect(0., 0., w, h); - - builder.0 - }; + if let Some(p) = group.clip_path.as_ref() { + // todo: merge + let mut builder = SvgPath2DBuilder(String::new()); + + // to ensure that our shape focus on the original point + builder.move_to(0., 0.); + for elem in &p.0 { + match elem { + PathItem::MoveTo(p) => { + builder.move_to(p.x.to_f32(), p.y.to_f32()); + } + PathItem::LineTo(p) => { + builder.line_to(p.x.to_f32(), p.y.to_f32()); + } + PathItem::CubicTo(p1, p2, p3) => { + builder.curve_to( + p1.x.to_f32(), + p1.y.to_f32(), + p2.x.to_f32(), + p2.y.to_f32(), + p3.x.to_f32(), + p3.y.to_f32(), + ); + } + PathItem::ClosePath => { + builder.close(); + } + }; + } + let d = builder.0.into(); inner = SvgItem::Transformed(ir::TransformedItem( TransformItem::Clip(Arc::new(ir::PathItem { - d: mask_box.into(), + d, + size: None, styles: vec![], })), Box::new(inner), @@ -158,7 +211,7 @@ impl LowerBuilder { /// Lower a text into svg item. // #[comemo::memoize] - pub(super) fn lower_text(text: &TextItem) -> SvgItem { + pub(super) fn lower_text(&mut self, text: &TextItem) -> SvgItem { let mut glyphs = Vec::with_capacity(text.glyphs.len()); for glyph in &text.glyphs { let id = GlyphId(glyph.id); @@ -171,8 +224,12 @@ impl LowerBuilder { let glyph_chars: String = text.text.to_string(); - let Paint::Solid(fill) = text.fill; - let fill = fill.to_css().into(); + // let fill = text.fill.clone().to_css().into(); + let mut fill_gradient = None; + let fill = Self::lower_paint(text.fill.clone(), &mut fill_gradient); + if let Some((f, gradient)) = fill_gradient { + self.extra_items.insert(f, ir::SvgItem::Gradient(gradient)); + } let span_id = text .glyphs @@ -206,7 +263,7 @@ impl LowerBuilder { /// Lower a geometrical shape into svg item. #[comemo::memoize] - pub(super) fn lower_shape(shape: &Shape) -> ir::PathItem { + fn lower_shape(shape: &Shape) -> ShapeInfo { let mut builder = SvgPath2DBuilder(String::new()); // to ensure that our shape focus on the original point @@ -254,13 +311,15 @@ impl LowerBuilder { let mut styles = Vec::new(); + let mut fill_gradient = None; if let Some(paint_fill) = &shape.fill { - let Paint::Solid(color) = paint_fill; - let c = color.to_css(); - - styles.push(ir::PathStyle::Fill(c.into())); + styles.push(ir::PathStyle::Fill(Self::lower_paint( + paint_fill.clone(), + &mut fill_gradient, + ))); } + let mut stroke_gradient = None; // todo: default miter_limit, thickness if let Some(FixedStroke { paint, @@ -291,11 +350,110 @@ impl LowerBuilder { LineJoin::Round => styles.push(ir::PathStyle::StrokeLineJoin("round".into())), } - let Paint::Solid(color) = paint; - styles.push(ir::PathStyle::Stroke(color.to_css().into())); + styles.push(ir::PathStyle::Stroke(Self::lower_paint( + paint.clone(), + &mut stroke_gradient, + ))); + } + + let mut shape_size = shape.geometry.bbox_size(); + // Edge cases for strokes. + if shape_size.x.to_pt() == 0.0 { + shape_size.x = TypstAbs::pt(1.0); + } + + if shape_size.y.to_pt() == 0.0 { + shape_size.y = TypstAbs::pt(1.0); + } + + ShapeInfo { + path: ir::PathItem { + d, + size: Some(shape_size.into()), + styles, + }, + fill_gradient, + stroke_gradient, + } + } + + #[inline] + pub(super) fn lower_paint( + g: Paint, + cell: &mut Option<(Fingerprint, GradientItem)>, + ) -> ImmutStr { + match g { + Paint::Solid(c) => c.to_css().into(), + Paint::Gradient(g) => { + let (g, fingerprint) = Self::lower_graident(g); + *cell = Some((fingerprint, g)); + format!("@{}", fingerprint.as_svg_id("g")).into() + } + } + } + + #[comemo::memoize] + pub(super) fn lower_graident(g: Gradient) -> (GradientItem, Fingerprint) { + let mut stops = Vec::with_capacity(g.stops_ref().len()); + for (c, step) in g.stops_ref() { + let [r, g, b, a] = c.to_vec4_u8(); + stops.push((ColorItem { r, g, b, a }, (*step).into())) } - ir::PathItem { d, styles } + let relative_to_self = match g.relative() { + Smart::Auto => None, + Smart::Custom(t) => Some(t == Relative::Self_), + }; + + let anti_alias = g.anti_alias(); + let space = g.space().into(); + + let mut styles = Vec::new(); + let kind = match g { + Gradient::Linear(l) => GradientKind::Linear(l.angle.into()), + Gradient::Radial(l) => { + if l.center.x != TypstRatio::new(0.5) || l.center.y != TypstRatio::new(0.5) { + styles.push(ir::GradientStyle::Center(l.center.into())); + } + + if l.focal_center.x != TypstRatio::new(0.5) + || l.focal_center.y != TypstRatio::new(0.5) + { + styles.push(ir::GradientStyle::FocalCenter(l.focal_center.into())); + } + + if l.focal_radius != TypstRatio::zero() { + styles.push(ir::GradientStyle::FocalRadius(l.focal_radius.into())); + } + + GradientKind::Radial(l.radius.into()) + } + Gradient::Conic(l) => { + if l.center.x != TypstRatio::new(0.5) || l.center.y != TypstRatio::new(0.5) { + styles.push(ir::GradientStyle::Center(l.center.into())); + } + + GradientKind::Conic(l.angle.into()) + } + }; + + let g = GradientItem { + stops, + relative_to_self, + anti_alias, + space, + kind, + styles, + }; + + // todo: don't leak the fingerprint primitive + let flat_item = &FlatSvgItem::Gradient(g.clone()); + let mut f = FingerprintSipHasher::default(); + flat_item.type_id().hash(&mut f); + flat_item.hash(&mut f); + let (f, _) = f.finish_fingerprint(); + + (g, f) } } @@ -326,17 +484,7 @@ impl<'a> GlyphLowerBuilder<'a> { /// Lower an SVG glyph into svg item. /// More information: https://learn.microsoft.com/zh-cn/typography/opentype/spec/svg fn lower_svg_glyph(&self, font: &Font, id: GlyphId) -> Option> { - let glyph_image = extract_svg_glyph(self.gp, font, id)?; - - let sz = Size::new( - typst::geom::Abs::raw(glyph_image.width() as f64), - typst::geom::Abs::raw(glyph_image.height() as f64), - ); - - let image = ir::ImageItem { - image: Arc::new(glyph_image.into()), - size: sz.into(), - }; + let image = extract_svg_glyph(self.gp, font, id)?; // position our image let ascender = font @@ -409,7 +557,7 @@ impl<'a> GlyphLowerBuilder<'a> { } } -fn extract_svg_glyph(g: &GlyphProvider, font: &Font, id: GlyphId) -> Option { +fn extract_svg_glyph(g: &GlyphProvider, font: &Font, id: GlyphId) -> Option { let data = g.svg_glyph(font, id)?; let mut data = data.as_ref(); @@ -476,15 +624,23 @@ fn extract_svg_glyph(g: &GlyphProvider, font: &Font, id: GlyphId) -> Option() == 4); const _: () = assert!(core::mem::size_of::() == 16); const _: () = assert!(core::mem::align_of::() == 4); - const _: () = assert!(core::mem::size_of::() == 16); + const _: () = assert!(core::mem::size_of::() == 28); const _: () = assert!(core::mem::align_of::() == 4); const _: () = assert!(core::mem::size_of::() == 36); const _: () = assert!(core::mem::align_of::() == 4); @@ -102,4 +102,6 @@ fn rkyv_assertions() { const _: () = assert!(core::mem::align_of::() == 4); const _: () = assert!(core::mem::size_of::() == 16); const _: () = assert!(core::mem::align_of::() == 4); + const _: () = assert!(core::mem::size_of::() == 4); + const _: () = assert!(core::mem::align_of::() == 1); } diff --git a/core/src/vector/path2d.rs b/core/src/vector/path2d.rs index 0b99aa05..e3541a4b 100644 --- a/core/src/vector/path2d.rs +++ b/core/src/vector/path2d.rs @@ -5,6 +5,7 @@ pub struct SvgPath2DBuilder(pub String); /// See: https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths impl SvgPath2DBuilder { + #[allow(dead_code)] pub fn rect(&mut self, x: f32, y: f32, w: f32, h: f32) { write!( &mut self.0, diff --git a/core/src/vector/utils.rs b/core/src/vector/utils.rs index f0c3d446..b1d4d60c 100644 --- a/core/src/vector/utils.rs +++ b/core/src/vector/utils.rs @@ -1,5 +1,5 @@ use tiny_skia::Transform; -use typst::geom::{Abs, Color}; +use typst::geom::{Abs, Color, Paint}; use super::ir; @@ -22,9 +22,9 @@ pub trait ToCssExt { impl ToCssExt for Color { fn to_css(self) -> String { - let color = self.to_rgba(); - if color.a == 255 { - let shorter = format!("#{:02x}{:02x}{:02x}", color.r, color.g, color.b); + let [r, g, b, a] = self.to_vec4_u8(); + if a == 255 { + let shorter = format!("#{:02x}{:02x}{:02x}", r, g, b); if shorter.chars().nth(1) == shorter.chars().nth(2) && shorter.chars().nth(3) == shorter.chars().nth(4) && shorter.chars().nth(5) == shorter.chars().nth(6) @@ -39,10 +39,18 @@ impl ToCssExt for Color { return shorter; } - format!( - "#{:02x}{:02x}{:02x}{:02x}", - color.r, color.g, color.b, color.a - ) + format!("#{:02x}{:02x}{:02x}{:02x}", r, g, b, a) + } +} + +impl ToCssExt for Paint { + fn to_css(self) -> String { + let fill = match self { + Paint::Solid(color) => color, + // todo: gradient + Paint::Gradient(..) => Color::BLACK, + }; + fill.to_css() } } diff --git a/core/src/vector/vm.rs b/core/src/vector/vm.rs index 75d78946..fe5f7e05 100644 --- a/core/src/vector/vm.rs +++ b/core/src/vector/vm.rs @@ -1,4 +1,6 @@ -use super::ir::{self, Abs, Axes, Point, Ratio, Scalar, SvgItem}; +use crate::hash::{item_hash128, Fingerprint}; + +use super::ir::{self, Abs, Axes, Point, Ratio, Scalar, Size, SvgItem, Transform}; /// A build pattern for applying transforms to the group of items. /// See [`ir::Transform`]. @@ -30,23 +32,35 @@ pub trait TransformContext: Sized { /// A RAII trait for rendering SVG items into underlying context. pub trait GroupContext: Sized { /// attach shape of the text to the node using css rules. - fn with_text_shape(&mut self, _ctx: &mut C, _shape: &ir::TextShape) {} + fn with_text_shape( + &mut self, + _ctx: &mut C, + _upem: Scalar, + _shape: &ir::TextShape, + _fill_key: &Fingerprint, + _state: RenderState, + ) { + } /// Render an item at point into underlying context. - fn render_item_at(&mut self, ctx: &mut C, pos: Point, item: &SvgItem); + fn render_item_at(&mut self, state: RenderState, ctx: &mut C, pos: Point, item: &SvgItem); /// Render an item into underlying context. - fn render_item(&mut self, ctx: &mut C, item: &SvgItem) { - self.render_item_at(ctx, Point::default(), item); + fn render_item(&mut self, state: RenderState, ctx: &mut C, item: &SvgItem) { + self.render_item_at(state, ctx, Point::default(), item); } - /// Render a semantic text into underlying context. - fn render_semantic_text(&mut self, _ctx: &mut C, _text: &ir::TextItem, _width: Scalar) {} - /// Render a glyph into underlying context. fn render_glyph(&mut self, _ctx: &mut C, _pos: Scalar, _item: &ir::GlyphItem) {} /// Render a geometrical shape into underlying context. - fn render_path(&mut self, _ctx: &mut C, _path: &ir::PathItem) {} + fn render_path( + &mut self, + _state: RenderState, + _ctx: &mut C, + _path: &ir::PathItem, + _abs_ref: &Fingerprint, + ) { + } /// Render a semantic link into underlying context. fn render_link(&mut self, _ctx: &mut C, _link: &ir::LinkItem) {} @@ -57,6 +71,86 @@ pub trait GroupContext: Sized { fn attach_debug_info(&mut self, _ctx: &mut C, _span_id: u64) {} } +/// Contextual information for rendering. +#[derive(Clone, Copy, Hash)] +pub struct RenderState { + /// The transform of the current item. + pub transform: Transform, + /// The size of the first hard frame in the hierarchy. + pub size: Size, +} + +impl Default for RenderState { + fn default() -> Self { + Self { + transform: Transform::identity(), + size: Size::default(), + } + } +} + +impl RenderState { + pub fn new_size(size: Size) -> Self { + Self { + transform: Transform::identity(), + size, + } + } + + /// Pre translate the current item's transform. + pub fn pre_translate(self, pos: Point) -> Self { + self.pre_concat(Transform::from_translate(pos.x, pos.y)) + } + + /// Pre concat the current item's transform. + pub fn pre_concat(self, transform: Transform) -> Self { + Self { + transform: self.transform.pre_concat(transform), + ..self + } + } + + /// Sets the size of the first hard frame in the hierarchy. + pub fn with_size(self, size: Size) -> Self { + Self { size, ..self } + } + + /// Sets the current item's transform. + pub fn with_transform(self, transform: Transform) -> Self { + Self { transform, ..self } + } + + pub fn pre_apply(self, transform: &ir::TransformItem) -> RenderState { + match transform { + ir::TransformItem::Matrix(transform) => self.pre_concat(**transform), + ir::TransformItem::Translate(transform) => { + self.pre_concat(Transform::from_translate(transform.x, transform.y)) + } + ir::TransformItem::Scale(transform) => { + self.pre_concat(Transform::from_scale(transform.0, transform.1)) + } + ir::TransformItem::Rotate(_transform) => { + todo!() + } + ir::TransformItem::Skew(transform) => { + self.pre_concat(Transform::from_skew(transform.0, transform.1)) + } + ir::TransformItem::Clip(_transform) => self, + } + } + + pub fn inv_transform(&self) -> Transform { + Transform::from_scale(self.size.x, self.size.y) + .post_concat(self.transform.invert().unwrap()) + } + + pub fn at(&self, pos: &Fingerprint) -> Fingerprint { + // todo: performance + let item = (*self, *pos); + Fingerprint::from_u128(item_hash128(&item)) + } +} + /// A trait for rendering SVG items into underlying context. pub trait RenderVm: Sized { type Resultant; @@ -71,19 +165,24 @@ pub trait RenderVm: Sized { } /// Start a new `` like object for text. - fn start_text(&mut self, _text: &ir::TextItem) -> Self::Group { + fn start_text(&mut self, _state: RenderState, _text: &ir::TextItem) -> Self::Group { self.start_group() } /// Render an item into underlying context. - fn render_item(&mut self, item: &SvgItem) -> Self::Resultant { + fn render_item(&mut self, state: RenderState, item: &SvgItem) -> Self::Resultant { match &item { - ir::SvgItem::Group(group) => self.render_group(group), - ir::SvgItem::Transformed(transformed) => self.render_transformed(transformed), - ir::SvgItem::Text(text) => self.render_text(text), + ir::SvgItem::Group(group, sz) => self.render_group(state, group, sz), + ir::SvgItem::Transformed(transformed) => self.render_transformed(state, transformed), + ir::SvgItem::Text(text) => self.render_text(state, text), ir::SvgItem::Path((path, ..)) => { let mut g = self.start_group(); - g.render_path(self, path); + g.render_path( + state, + self, + path, + &Fingerprint::from_u128(item_hash128(path)), + ); g.into() } ir::SvgItem::Link(link) => { @@ -96,52 +195,55 @@ pub trait RenderVm: Sized { g.render_image(self, image); g.into() } + ir::SvgItem::Gradient(..) => { + panic!("RenderVm.RenderFrame.UnknownItem {:?}", item) + } } } /// Render a frame group into underlying context. - fn render_group(&mut self, group: &ir::GroupItem) -> Self::Resultant { + fn render_group( + &mut self, + mut state: RenderState, + group: &ir::GroupItem, + sz: &Option, + ) -> Self::Resultant { let mut group_ctx = self.start_frame(group); + if let Some(sz) = sz { + state = state.with_transform(Transform::identity()).with_size(*sz); + } + for (pos, item_ref) in group.0.iter() { - group_ctx.render_item_at(self, *pos, item_ref); + group_ctx.render_item_at(state.pre_translate(*pos), self, *pos, item_ref); } group_ctx.into() } /// Render a transformed frame into underlying context. - fn render_transformed(&mut self, transformed: &ir::TransformedItem) -> Self::Resultant { + fn render_transformed( + &mut self, + state: RenderState, + transformed: &ir::TransformedItem, + ) -> Self::Resultant { let mut ts = self.start_group().transform(self, &transformed.0); - ts.render_item(self, &transformed.1); + ts.render_item(state.pre_apply(&transformed.0), self, &transformed.1); ts.into() } /// Render a text into the underlying context. - // todo: combine with flat item one - fn render_text(&mut self, text: &ir::TextItem) -> Self::Resultant { - let group_ctx = self.start_text(text); - + fn render_text(&mut self, state: RenderState, text: &ir::TextItem) -> Self::Resultant { // upem is the unit per em defined in the font. - // ppem is calcuated by the font size. - // > ppem = text_size / upem - let upem = text.font.units_per_em() as f32; - let ppem = Scalar(text.shape.size.0 / upem); - let inv_ppem = upem / text.shape.size.0; - - let mut group_ctx = group_ctx.transform_scale(self, ppem, -ppem); + let upem = Scalar(text.font.units_per_em() as f32); - let mut x = 0f32; - for (offset, advance, glyph) in text.content.glyphs.iter() { - let offset = x + offset.0; - let ts = offset * inv_ppem; + let group_ctx = self.start_text(state, text); + let mut group_ctx = text.shape.add_transform(self, group_ctx, upem); - group_ctx.render_glyph(self, Scalar(ts), glyph); - - x += advance.0; - } + text.render_glyphs(upem, |x, g| { + group_ctx.render_glyph(self, x, g); + }); - group_ctx.render_semantic_text(self, text, Scalar(x)); group_ctx.into() } } diff --git a/exporter/ast/src/ast.rs b/exporter/ast/src/ast.rs index 7eb20748..e18cb57b 100644 --- a/exporter/ast/src/ast.rs +++ b/exporter/ast/src/ast.rs @@ -2,8 +2,7 @@ use std::fmt::Display; use std::io::{self, Write}; use std::sync::Arc; -use typst::ide::Tag; -use typst::syntax::{LinkedNode, Source, SyntaxKind}; +use typst::syntax::{LinkedNode, Source, SyntaxKind, Tag}; use typst_ts_core::exporter_utils::map_err; use typst_ts_core::Transformer; @@ -42,7 +41,7 @@ impl<'a, W: io::Write> AstWriter<'a, W> { fn write_repr(&mut self, ast: &LinkedNode) { let k = ast.kind(); - if let Some(hl) = typst::ide::highlight(ast) { + if let Some(hl) = typst::syntax::highlight(ast) { match hl { Tag::Comment => { self.painted(COMMENT, format!("Ct::{:?}", k)); diff --git a/exporter/canvas/src/annotation.rs b/exporter/canvas/src/annotation.rs index ba9ea314..31adbd5f 100644 --- a/exporter/canvas/src/annotation.rs +++ b/exporter/canvas/src/annotation.rs @@ -40,7 +40,7 @@ impl<'m, 't> AnnotationListTask<'m, 't> { }), &t.1, ), - SvgItem::Group(group) => self.process_group(ts, group), + SvgItem::Group(group, _) => self.process_group(ts, group), SvgItem::Link(link) => self.process_link(ts, link), _ => {} } @@ -64,7 +64,7 @@ impl<'m, 't> AnnotationListTask<'m, 't> { }), &t.1, ), - FlatSvgItem::Group(group) => self.process_flat_group(ts, group), + FlatSvgItem::Group(group, _) => self.process_flat_group(ts, group), FlatSvgItem::Link(link) => self.process_link(ts, link), _ => {} } diff --git a/exporter/canvas/src/content.rs b/exporter/canvas/src/content.rs index ceede74a..d5721ec6 100644 --- a/exporter/canvas/src/content.rs +++ b/exporter/canvas/src/content.rs @@ -11,7 +11,7 @@ use typst_ts_core::{ flat_ir::{self, FlatSvgItem, FlatTextItem, GroupRef, Module}, flat_vm::{FlatGroupContext, FlatRenderVm}, ir::{self, Abs, Axes, FontIndice, FontRef, Ratio, Scalar, SvgItem}, - vm::{GroupContext, RenderVm, TransformContext}, + vm::{GroupContext, RenderState, RenderVm, TransformContext}, }, TextContent, }; @@ -69,9 +69,15 @@ trait TranslateCtx { /// See [`GroupContext`]. impl> GroupContext for TextContentBuilder { - fn render_item_at(&mut self, ctx: &mut C, pos: ir::Point, item: &ir::SvgItem) { + fn render_item_at( + &mut self, + state: RenderState, + ctx: &mut C, + pos: ir::Point, + item: &ir::SvgItem, + ) { ctx.translate(pos.x, pos.y); - ctx.render_item(item); + ctx.render_item(state, item); ctx.translate(-pos.x, -pos.y); } } @@ -80,9 +86,15 @@ impl> GroupContext for TextContent impl<'m, C: TranslateCtx + FlatRenderVm<'m, Resultant = ()>> FlatGroupContext for TextContentBuilder { - fn render_item_ref_at(&mut self, ctx: &mut C, pos: ir::Point, item: &Fingerprint) { + fn render_item_ref_at( + &mut self, + state: RenderState, + ctx: &mut C, + pos: ir::Point, + item: &Fingerprint, + ) { ctx.translate(pos.x, pos.y); - ctx.render_flat_item(item); + ctx.render_flat_item(state, item); ctx.translate(-pos.x, -pos.y); } } @@ -122,7 +134,7 @@ impl<'m, 't> TextContentTask<'m, 't> { }), &t.1, ), - SvgItem::Group(group) => self.process_group(ts, group), + SvgItem::Group(group, _) => self.process_group(ts, group), SvgItem::Text(text) => self.process_text(ts, text), _ => {} } @@ -142,7 +154,7 @@ impl<'m, 't> TextContentTask<'m, 't> { }), &t.1, ), - SvgItem::Group(group) => { + SvgItem::Group(group, _) => { self.process_group(ts, group); } SvgItem::Text(text) => { @@ -186,7 +198,7 @@ impl<'m, 't> TextContentTask<'m, 't> { }), &t.1, ), - FlatSvgItem::Group(group) => self.process_flat_group(ts, group), + FlatSvgItem::Group(group, _) => self.process_flat_group(ts, group), FlatSvgItem::Text(text) => self.process_flat_text(ts, text), _ => {} } @@ -207,7 +219,7 @@ impl<'m, 't> TextContentTask<'m, 't> { }), &t.1, ), - FlatSvgItem::Group(group) => { + FlatSvgItem::Group(group, _) => { self.process_flat_group(ts, group); } FlatSvgItem::Text(text) => { diff --git a/exporter/canvas/src/lib.rs b/exporter/canvas/src/lib.rs index e582764c..7e5214d4 100644 --- a/exporter/canvas/src/lib.rs +++ b/exporter/canvas/src/lib.rs @@ -10,15 +10,15 @@ use typst_ts_core::{ font::GlyphProvider, hash::Fingerprint, vector::{ - bbox::GlyphIndice, flat_ir::{self, LayoutRegionNode, Module, ModuleBuilder, Page}, flat_vm::{FlatGroupContext, FlatRenderVm}, incr::IncrDocClient, ir::{ - self, Abs, Axes, BuildGlyph, FontIndice, FontRef, GlyphItem, GlyphPackBuilder, - GlyphRef, Image, ImageItem, ImmutStr, PathStyle, Ratio, Rect, Scalar, Size, SvgItem, + self, Abs, Axes, BuildGlyph, FontIndice, FontRef, GlyphIndice, GlyphItem, + GlyphPackBuilder, GlyphRef, Image, ImageItem, ImmutStr, PathStyle, Ratio, Rect, Scalar, + Size, SvgItem, }, - vm::{GroupContext, RenderVm, TransformContext}, + vm::{GroupContext, RenderState, RenderVm, TransformContext}, }, }; @@ -177,11 +177,20 @@ impl CanvasElem for CanvasPathElem { } if fill { + // todo: canvas gradient + if fill_color.starts_with("url") { + fill_color = "black".into() + } canvas.set_fill_style(&fill_color.as_ref().into()); canvas.fill_with_path_2d(&Path2d::new_with_path_string(&self.path_data.d).unwrap()); } if stroke && stroke_width.abs() > 1e-5 { + // todo: canvas gradient + if stroke_color.starts_with("url") { + stroke_color = "black".into() + } + canvas.set_stroke_style(&stroke_color.as_ref().into()); canvas.stroke_with_path(&Path2d::new_with_path_string(&self.path_data.d).unwrap()); } @@ -458,8 +467,14 @@ impl TransformContext for CanvasStack { impl<'m, C: BuildGlyph + RenderVm + GlyphIndice<'m>> GroupContext for CanvasStack { - fn render_item_at(&mut self, ctx: &mut C, pos: ir::Point, item: &ir::SvgItem) { - self.inner.push((pos, ctx.render_item(item))); + fn render_item_at( + &mut self, + state: RenderState, + ctx: &mut C, + pos: ir::Point, + item: &ir::SvgItem, + ) { + self.inner.push((pos, ctx.render_item(state, item))); } fn render_glyph(&mut self, ctx: &mut C, pos: Scalar, glyph: &ir::GlyphItem) { @@ -469,7 +484,13 @@ impl<'m, C: BuildGlyph + RenderVm + GlyphIndice<'m>> Gro } } - fn render_path(&mut self, _ctx: &mut C, path: &ir::PathItem) { + fn render_path( + &mut self, + _state: RenderState, + _ctx: &mut C, + path: &ir::PathItem, + _abs_ref: &Fingerprint, + ) { self.inner.push(( ir::Point::default(), Arc::new(Box::new(CanvasPathElem { @@ -492,8 +513,14 @@ impl<'m, C: BuildGlyph + RenderVm + GlyphIndice<'m>> Gro impl<'m, C: FlatRenderVm<'m, Resultant = CanvasNode> + GlyphIndice<'m>> FlatGroupContext for CanvasStack { - fn render_item_ref_at(&mut self, ctx: &mut C, pos: crate::ir::Point, item: &Fingerprint) { - self.inner.push((pos, ctx.render_flat_item(item))); + fn render_item_ref_at( + &mut self, + state: RenderState, + ctx: &mut C, + pos: crate::ir::Point, + item: &Fingerprint, + ) { + self.inner.push((pos, ctx.render_flat_item(state, item))); } fn render_glyph_ref(&mut self, ctx: &mut C, pos: Scalar, glyph: &GlyphRef) { @@ -560,6 +587,7 @@ impl<'m, 't, Feat: ExportFeature> FlatRenderVm<'m> for CanvasRenderTask<'m, 't, fn start_flat_text( &mut self, + _state: RenderState, value: &Fingerprint, text: &flat_ir::FlatTextItem, ) -> Self::Group { @@ -655,9 +683,10 @@ impl IncrementalCanvasExporter { } // (ct.render_flat_item(content), *size, *content) + let state = RenderState::new_size(*size); CanvasPage { content: *content, - elem: ct.render_flat_item(content), + elem: ct.render_flat_item(state, content), size: *size, } }) @@ -726,7 +755,7 @@ impl IncrCanvasDocClient { // prepare an empty page for the pages that are not rendered // todo: better solution? - let empty_page = self.mb.build(SvgItem::Group(Default::default())); + let empty_page = self.mb.build(SvgItem::Group(Default::default(), None)); kern.doc .module .items diff --git a/exporter/pdf/src/lib.rs b/exporter/pdf/src/lib.rs index c5f6aa91..a40743cd 100644 --- a/exporter/pdf/src/lib.rs +++ b/exporter/pdf/src/lib.rs @@ -14,6 +14,7 @@ impl Exporter> for PdfDocExporter { _world: &dyn World, output: Arc, ) -> SourceResult> { - Ok(typst::export::pdf(output.as_ref())) + // todo: ident and timestamp option + Ok(typst::export::pdf(output.as_ref(), None, None)) } } diff --git a/exporter/raster/src/lib.rs b/exporter/raster/src/lib.rs index 854da04f..54a3a730 100644 --- a/exporter/raster/src/lib.rs +++ b/exporter/raster/src/lib.rs @@ -6,15 +6,18 @@ use std::sync::Arc; use image::imageops::FilterType; use image::{GenericImageView, Rgba}; use pixglyph::Bitmap; -use sk::IntRect; +use resvg::tiny_skia::IntRect; use tiny_skia as sk; use ttf_parser::{GlyphId, OutlineBuilder}; use usvg::{NodeExt, TreeParsing}; -use typst::doc::{Frame, FrameItem, GroupItem, Meta, TextItem}; +use typst::doc::{Frame, FrameItem, FrameKind, GroupItem, Meta, TextItem}; use typst::font::Font; -use typst::geom::{self, Abs, Color, FixedStroke, Geometry, Paint, PathItem, Shape, Size}; -use typst::image::{DecodedImage, Image, RasterFormat}; +use typst::geom::{ + self, Abs, Axes, Color, FixedStroke, Geometry, Gradient, Paint, PathItem, Point, Ratio, + Relative, Shape, Size, Transform, +}; +use typst::image::{Image, ImageKind, RasterFormat}; pub mod pixmap; @@ -24,35 +27,93 @@ pub mod pixmap; /// the resulting `tiny-skia` pixel buffer. pub fn render(canvas: &mut sk::PixmapMut, frame: &Frame, pixel_per_pt: f32, fill: Color) { canvas.fill(fill.into()); + let size = frame.size(); let ts = sk::Transform::from_scale(pixel_per_pt, pixel_per_pt); - render_frame(canvas, ts, None, frame); + render_frame(canvas, State::new(size, ts, pixel_per_pt), frame); +} + +/// Additional metadata carried through the rendering process. +#[derive(Clone, Copy, Default)] +struct State<'a> { + /// The transform of the current item. + transform: sk::Transform, + /// The transform of the first hard frame in the hierarchy. + container_transform: sk::Transform, + /// The mask of the current item. + mask: Option<&'a sk::Mask>, + /// The pixel per point ratio. + pixel_per_pt: f32, + /// The size of the first hard frame in the hierarchy. + size: Size, +} + +impl<'a> State<'a> { + fn new(size: Size, transform: sk::Transform, pixel_per_pt: f32) -> Self { + Self { + size, + transform, + container_transform: transform, + pixel_per_pt, + ..Default::default() + } + } + + /// Pre translate the current item's transform. + fn pre_translate(self, pos: Point) -> Self { + Self { + transform: self.transform.pre_translate(pos.x.to_f32(), pos.y.to_f32()), + ..self + } + } + + /// Pre concat the current item's transform. + fn pre_concat(self, transform: sk::Transform) -> Self { + Self { + transform: self.transform.pre_concat(transform), + ..self + } + } + + /// Sets the current mask. + fn with_mask(self, mask: Option<&sk::Mask>) -> State<'_> { + // Ensure that we're using the parent's mask if we don't have one. + if mask.is_some() { + State { mask, ..self } + } else { + State { mask: None, ..self } + } + } + + /// Sets the size of the first hard frame in the hierarchy. + fn with_size(self, size: Size) -> Self { + Self { size, ..self } + } + + /// Pre concat the container's transform. + fn pre_concat_container(self, transform: sk::Transform) -> Self { + Self { + container_transform: self.container_transform.pre_concat(transform), + ..self + } + } } /// Render a frame into the canvas. -fn render_frame( - canvas: &mut sk::PixmapMut, - ts: sk::Transform, - mask: Option<&sk::Mask>, - frame: &Frame, -) { +fn render_frame(canvas: &mut sk::PixmapMut, state: State, frame: &Frame) { for (pos, item) in frame.items() { - let x = pos.x.to_f32(); - let y = pos.y.to_f32(); - let ts = ts.pre_translate(x, y); - match item { FrameItem::Group(group) => { - render_group(canvas, ts, mask, group); + render_group(canvas, state, *pos, group); } FrameItem::Text(text) => { - render_text(canvas, ts, mask, text); + render_text(canvas, state.pre_translate(*pos), text); } FrameItem::Shape(shape, _) => { - render_shape(canvas, ts, mask, shape); + render_shape(canvas, state.pre_translate(*pos), shape); } FrameItem::Image(image, size, _) => { - render_image(canvas, ts, mask, image, *size); + render_image(canvas, state.pre_translate(*pos), image, *size); } FrameItem::Meta(meta, _) => match meta { Meta::Link(_) => {} @@ -66,23 +127,26 @@ fn render_frame( } /// Render a group frame with optional transform and clipping into the canvas. -fn render_group( - canvas: &mut sk::PixmapMut, - ts: sk::Transform, - mask: Option<&sk::Mask>, - group: &GroupItem, -) { - let ts = ts.pre_concat(group.transform.into()); +fn render_group(canvas: &mut sk::PixmapMut, state: State, pos: Point, group: &GroupItem) { + let state = match group.frame.kind() { + FrameKind::Soft => state.pre_translate(pos).pre_concat(group.transform.into()), + FrameKind::Hard => state + .pre_translate(pos) + .pre_concat(group.transform.into()) + .pre_concat_container( + state + .transform + .post_concat(state.container_transform.invert().unwrap()), + ) + .pre_concat_container(Transform::translate(pos.x, pos.y).into()) + .pre_concat_container(group.transform.into()) + .with_size(group.frame.size()), + }; - let mut mask = mask; + let mut mask = state.mask; let storage; - if group.clips { - let size = group.frame.size(); - let w = size.x.to_f32(); - let h = size.y.to_f32(); - if let Some(path) = sk::Rect::from_xywh(0.0, 0.0, w, h) - .map(sk::PathBuilder::from_rect) - .and_then(|path| path.transform(ts)) + if let Some(clip_path) = group.clip_path.as_ref() { + if let Some(path) = convert_path(clip_path).and_then(|path| path.transform(state.transform)) { if let Some(mask) = mask { let mut mask = mask.clone(); @@ -115,25 +179,20 @@ fn render_group( } } - render_frame(canvas, ts, mask, &group.frame); + render_frame(canvas, state.with_mask(mask), &group.frame); } /// Render a text run into the canvas. -fn render_text( - canvas: &mut sk::PixmapMut, - ts: sk::Transform, - mask: Option<&sk::Mask>, - text: &TextItem, -) { +fn render_text(canvas: &mut sk::PixmapMut, state: State, text: &TextItem) { let mut x = 0.0; for glyph in &text.glyphs { let id = GlyphId(glyph.id); let offset = x + glyph.x_offset.at(text.size).to_f32(); - let ts = ts.pre_translate(offset, 0.0); + let state = state.pre_translate(Point::new(Abs::raw(offset as _), Abs::raw(0.0))); - render_svg_glyph(canvas, ts, mask, text, id) - .or_else(|| render_bitmap_glyph(canvas, ts, mask, text, id)) - .or_else(|| render_outline_glyph(canvas, ts, mask, text, id)); + render_svg_glyph(canvas, state, text, id) + .or_else(|| render_bitmap_glyph(canvas, state, text, id)) + .or_else(|| render_outline_glyph(canvas, state, text, id)); x += glyph.x_advance.at(text.size).to_f32(); } @@ -142,11 +201,11 @@ fn render_text( /// Render an SVG glyph into the canvas. fn render_svg_glyph( canvas: &mut sk::PixmapMut, - ts: sk::Transform, - mask: Option<&sk::Mask>, + state: State, text: &TextItem, id: GlyphId, ) -> Option<()> { + let ts = &state.transform; let mut data = text.font.ttf().glyph_svg_image(id)?; // Decompress SVGZ. @@ -220,7 +279,7 @@ fn render_svg_glyph( pixmap.as_ref(), &sk::PixmapPaint::default(), sk::Transform::identity(), - mask, + state.mask, ); Some(()) @@ -229,11 +288,11 @@ fn render_svg_glyph( /// Render a bitmap glyph into the canvas. fn render_bitmap_glyph( canvas: &mut sk::PixmapMut, - ts: sk::Transform, - mask: Option<&sk::Mask>, + state: State, text: &TextItem, id: GlyphId, ) -> Option<()> { + let ts = state.transform; let size = text.size.to_f32(); let ppem = size * ts.sy; let raster = text.font.ttf().glyph_raster_image(id, ppem as u16)?; @@ -249,18 +308,22 @@ fn render_bitmap_glyph( let w = (image.width() as f64 / image.height() as f64) * h; let dx = (raster.x as f32) / (image.width() as f32) * size; let dy = (raster.y as f32) / (image.height() as f32) * size; - let ts = ts.pre_translate(dx, -size - dy); - render_image(canvas, ts, mask, &image, Size::new(w, h)) + render_image( + canvas, + state.pre_translate(Point::new(Abs::raw(dx as _), Abs::raw((-size - dy) as _))), + &image, + Size::new(w, h), + ) } /// Render an outline glyph into the canvas. This is the "normal" case. fn render_outline_glyph( canvas: &mut sk::PixmapMut, - ts: sk::Transform, - mask: Option<&sk::Mask>, + state: State, text: &TextItem, id: GlyphId, ) -> Option<()> { + let ts = &state.transform; let ppem = text.size.to_f32() * ts.sy; // Render a glyph directly as a path. This only happens when the fast glyph @@ -273,14 +336,25 @@ fn render_outline_glyph( builder.0.finish()? }; - let paint = (&text.fill).into(); + let scale = text.size.to_f32() / text.font.units_per_em() as f32; + + let mut pixmap = None; + let paint = to_sk_paint( + &text.fill, + state.pre_concat(sk::Transform::from_scale(scale, -scale)), + Size::zero(), + true, + None, + &mut pixmap, + None, + ); + let rule = sk::FillRule::default(); // Flip vertically because font design coordinate // system is Y-up. - let scale = text.size.to_f32() / text.font.units_per_em() as f32; let ts = ts.pre_scale(scale, -scale); - canvas.fill_path(&path, &paint, rule, ts, mask); + canvas.fill_path(&path, &paint, rule, ts, state.mask); return Some(()); } @@ -304,24 +378,42 @@ fn render_outline_glyph( ts.ty.to_bits(), ppem.to_bits(), )?; + match &text.fill { + Paint::Gradient(gradient) => { + let sampler = GradientSampler::new(gradient, &state, Size::zero(), true); + write_bitmap(canvas, &bitmap, &state, sampler)?; + } + Paint::Solid(color) => { + write_bitmap(canvas, &bitmap, &state, *color)?; + } + } + + Some(()) +} +fn write_bitmap( + canvas: &mut sk::PixmapMut, + bitmap: &Bitmap, + state: &State, + sampler: S, +) -> Option<()> { // If we have a clip mask we first render to a pixmap that we then blend // with our canvas - if mask.is_some() { + if state.mask.is_some() { let mw = bitmap.width; let mh = bitmap.height; - let Paint::Solid(color) = text.fill; - let c = color.to_rgba(); - // Pad the pixmap with 1 pixel in each dimension so that we do // not get any problem with floating point errors along their border let mut pixmap = sk::Pixmap::new(mw + 2, mh + 2)?; for x in 0..mw { for y in 0..mh { let alpha = bitmap.coverage[(y * mw + x) as usize]; - let color = sk::ColorU8::from_rgba(c.r, c.g, c.b, alpha).premultiply(); - pixmap.pixels_mut()[((y + 1) * (mw + 2) + (x + 1)) as usize] = color; + let color: sk::ColorU8 = sampler.sample((x, y)).into(); + + pixmap.pixels_mut()[((y + 1) * (mw + 2) + (x + 1)) as usize] = + sk::ColorU8::from_rgba(color.red(), color.green(), color.blue(), alpha) + .premultiply(); } } @@ -334,7 +426,7 @@ fn render_outline_glyph( pixmap.as_ref(), &sk::PixmapPaint::default(), sk::Transform::identity(), - mask, + state.mask, ); } else { let cw = canvas.width() as i32; @@ -348,11 +440,6 @@ fn render_outline_glyph( let top = bitmap.top; let bottom = top + mh; - // Premultiply the text color. - let Paint::Solid(color) = text.fill; - let c = color.to_rgba(); - let color = bytemuck::cast(sk::ColorU8::from_rgba(c.r, c.g, c.b, 255).premultiply()); - // Blend the glyph bitmap with the existing pixels on the canvas. let pixels = bytemuck::cast_slice_mut::(canvas.data_mut()); for x in left.clamp(0, cw)..right.clamp(0, cw) { @@ -363,6 +450,8 @@ fn render_outline_glyph( continue; } + let color: sk::ColorU8 = sampler.sample((x as _, y as _)).into(); + let color = bytemuck::cast(color.premultiply()); let pi = (y * cw + x) as usize; if cov == 255 { pixels[pi] = color; @@ -379,12 +468,8 @@ fn render_outline_glyph( } /// Render a geometrical shape into the canvas. -fn render_shape( - canvas: &mut sk::PixmapMut, - ts: sk::Transform, - mask: Option<&sk::Mask>, - shape: &Shape, -) -> Option<()> { +fn render_shape(canvas: &mut sk::PixmapMut, state: State, shape: &Shape) -> Option<()> { + let ts = state.transform; let path = match shape.geometry { Geometry::Line(target) => { let mut builder = sk::PathBuilder::new(); @@ -401,13 +486,23 @@ fn render_shape( }; if let Some(fill) = &shape.fill { - let mut paint: sk::Paint = fill.into(); + let mut pixmap = None; + let mut paint: sk::Paint = to_sk_paint( + fill, + state, + shape.geometry.bbox_size(), + false, + None, + &mut pixmap, + None, + ); + if matches!(shape.geometry, Geometry::Rect(_)) { paint.anti_alias = false; } let rule = sk::FillRule::default(); - canvas.fill_path(&path, &paint, rule, ts, mask); + canvas.fill_path(&path, &paint, rule, ts, state.mask); } if let Some(FixedStroke { @@ -442,15 +537,46 @@ fn render_shape( sk::StrokeDash::new(dash_array, pattern.phase.to_f32()) }); - let paint = paint.into(); + + let bbox = shape.geometry.bbox_size(); + let offset_bbox = (!matches!(shape.geometry, Geometry::Line(..))) + .then(|| offset_bounding_box(bbox, *thickness)) + .unwrap_or(bbox); + + let fill_transform = (!matches!(shape.geometry, Geometry::Line(..))) + .then(|| sk::Transform::from_translate(-thickness.to_f32(), -thickness.to_f32())); + + let gradient_map = (!matches!(shape.geometry, Geometry::Line(..))).then(|| { + ( + Point::new( + -*thickness * state.pixel_per_pt as f64, + -*thickness * state.pixel_per_pt as f64, + ), + Axes::new( + Ratio::new(offset_bbox.x / bbox.x), + Ratio::new(offset_bbox.y / bbox.y), + ), + ) + }); + + let mut pixmap = None; + let paint = to_sk_paint( + paint, + state, + offset_bbox, + false, + fill_transform, + &mut pixmap, + gradient_map, + ); let stroke = sk::Stroke { width, line_cap: line_cap.into(), line_join: line_join.into(), dash, - miter_limit: miter_limit.0 as f32, + miter_limit: miter_limit.get() as f32, }; - canvas.stroke_path(&path, &paint, &stroke, ts, mask); + canvas.stroke_path(&path, &paint, &stroke, ts, state.mask); } } @@ -487,13 +613,8 @@ fn convert_path(path: &geom::Path) -> Option { } /// Render a raster or SVG image into the canvas. -fn render_image( - canvas: &mut sk::PixmapMut, - ts: sk::Transform, - mask: Option<&sk::Mask>, - image: &Image, - size: Size, -) -> Option<()> { +fn render_image(canvas: &mut sk::PixmapMut, state: State, image: &Image, size: Size) -> Option<()> { + let ts = state.transform; let view_width = size.x.to_f32(); let view_height = size.y.to_f32(); @@ -531,7 +652,7 @@ fn render_image( }; let rect = sk::Rect::from_xywh(0.0, 0.0, view_width, view_height)?; - canvas.fill_rect(rect, &paint, ts, mask); + canvas.fill_rect(rect, &paint, ts, state.mask); Some(()) } @@ -540,30 +661,190 @@ fn render_image( #[comemo::memoize] fn scaled_texture(image: &Image, w: u32, h: u32) -> Option> { let mut pixmap = sk::Pixmap::new(w, h)?; - match image.decoded().as_ref() { - DecodedImage::Raster(dynamic, _, _) => { + match image.kind() { + ImageKind::Raster(raster) => { let downscale = w < image.width(); let filter = if downscale { FilterType::Lanczos3 } else { FilterType::CatmullRom }; - let buf = dynamic.resize(w, h, filter); + let buf = raster.dynamic().resize(w, h, filter); for ((_, _, src), dest) in buf.pixels().zip(pixmap.pixels_mut()) { let Rgba([r, g, b, a]) = src; *dest = sk::ColorU8::from_rgba(r, g, b, a).premultiply(); } } - DecodedImage::Svg(tree) => { - let tree = resvg::Tree::from_usvg(tree); - let ts = tiny_skia::Transform::from_scale( - w as f32 / tree.size.width(), - h as f32 / tree.size.height(), + // Safety: We do not keep any references to tree nodes beyond the scope + // of `with`. + ImageKind::Svg(svg) => unsafe { + svg.with(|tree| { + let tree = resvg::Tree::from_usvg(tree); + let ts = tiny_skia::Transform::from_scale( + w as f32 / tree.size.width(), + h as f32 / tree.size.height(), + ); + tree.render(ts, &mut pixmap.as_mut()) + }); + }, + } + Some(Arc::new(pixmap)) +} + +/// Trait for sampling of a paint, used as a generic +/// abstraction over solid colors and gradients. +trait PaintSampler: Copy { + /// Sample the color at the `pos` in the pixmap. + fn sample(self, pos: (u32, u32)) -> Color; +} + +impl PaintSampler for Color { + fn sample(self, _: (u32, u32)) -> Color { + self + } +} + +/// State used when sampling colors for text. +/// +/// It caches the inverse transform to the parent, so that we can +/// reuse it instead of recomputing it for each pixel. +#[derive(Clone, Copy)] +struct GradientSampler<'a> { + gradient: &'a Gradient, + container_size: Size, + transform_to_parent: sk::Transform, +} + +impl<'a> GradientSampler<'a> { + fn new(gradient: &'a Gradient, state: &State, item_size: Size, on_text: bool) -> Self { + let relative = gradient.unwrap_relative(on_text); + let container_size = match relative { + Relative::Self_ => item_size, + Relative::Parent => state.size, + }; + + let fill_transform = match relative { + Relative::Self_ => sk::Transform::identity(), + Relative::Parent => state.container_transform.invert().unwrap(), + }; + + Self { + gradient, + container_size, + transform_to_parent: fill_transform, + } + } +} + +impl PaintSampler for GradientSampler<'_> { + /// Samples a single point in a glyph. + fn sample(self, (x, y): (u32, u32)) -> Color { + // Compute the point in the gradient's coordinate space. + let mut point = sk::Point { + x: x as f32, + y: y as f32, + }; + self.transform_to_parent.map_point(&mut point); + + // Sample the gradient + self.gradient.sample_at( + (point.x, point.y), + ( + self.container_size.x.to_f32(), + self.container_size.y.to_f32(), + ), + ) + } +} + +/// Transforms a [`Paint`] into a [`sk::Paint`]. +/// Applying the necessary transform, if the paint is a gradient. +/// +/// `gradient_map` is used to scale and move the gradient being sampled, +/// this is used to line up the stroke and the fill of a shape. +fn to_sk_paint<'a>( + paint: &Paint, + state: State, + item_size: Size, + on_text: bool, + fill_transform: Option, + pixmap: &'a mut Option>, + gradient_map: Option<(Point, Axes)>, +) -> sk::Paint<'a> { + /// Actual sampling of the gradient, cached for performance. + #[comemo::memoize] + fn cached( + gradient: &Gradient, + width: u32, + height: u32, + gradient_map: Option<(Point, Axes)>, + ) -> Arc { + let (offset, scale) = + gradient_map.unwrap_or_else(|| (Point::zero(), Axes::splat(Ratio::one()))); + let mut pixmap = sk::Pixmap::new(width.max(1), height.max(1)).unwrap(); + for x in 0..width { + for y in 0..height { + let color: sk::Color = gradient + .sample_at( + ( + (x as f32 + offset.x.to_f32()) * scale.x.get() as f32, + (y as f32 + offset.y.to_f32()) * scale.y.get() as f32, + ), + (width as f32, height as f32), + ) + .into(); + + pixmap.pixels_mut()[(y * width + x) as usize] = color.premultiply().to_color_u8(); + } + } + + Arc::new(pixmap) + } + + let mut sk_paint: sk::Paint<'_> = sk::Paint::default(); + match paint { + Paint::Solid(color) => { + sk_paint.set_color((*color).into()); + sk_paint.anti_alias = true; + } + Paint::Gradient(gradient) => { + let relative = gradient.unwrap_relative(on_text); + let container_size = match relative { + Relative::Self_ => item_size, + Relative::Parent => state.size, + }; + + let fill_transform = match relative { + Relative::Self_ => fill_transform.unwrap_or_default(), + Relative::Parent => state + .container_transform + .post_concat(state.transform.invert().unwrap()), + }; + let width = (container_size.x.to_f32() * state.pixel_per_pt).ceil() as u32; + let height = (container_size.y.to_f32() * state.pixel_per_pt).ceil() as u32; + + *pixmap = Some(cached( + gradient, + width.max(state.pixel_per_pt.ceil() as u32), + height.max(state.pixel_per_pt.ceil() as u32), + gradient_map, + )); + + // We can use FilterQuality::Nearest here because we're + // rendering to a pixmap that is already at native resolution. + sk_paint.shader = sk::Pattern::new( + pixmap.as_ref().unwrap().as_ref().as_ref(), + sk::SpreadMode::Pad, + sk::FilterQuality::Nearest, + 1.0, + fill_transform.pre_scale(1.0 / state.pixel_per_pt, 1.0 / state.pixel_per_pt), ); - tree.render(ts, &mut pixmap.as_mut()) + + sk_paint.anti_alias = gradient.anti_alias(); } } - Some(Arc::new(pixmap)) + + sk_paint } /// Allows to build tiny-skia paths from glyph outlines. @@ -619,3 +900,7 @@ fn alpha_mul(color: u32, scale: u32) -> u32 { let ag = ((color >> 8) & mask) * scale; (rb & mask) | (ag & !mask) } + +fn offset_bounding_box(bbox: Size, stroke_width: Abs) -> Size { + Size::new(bbox.x + stroke_width * 2.0, bbox.y + stroke_width * 2.0) +} diff --git a/exporter/svg/Cargo.toml b/exporter/svg/Cargo.toml index ed2d8619..bcd1e9a2 100644 --- a/exporter/svg/Cargo.toml +++ b/exporter/svg/Cargo.toml @@ -17,7 +17,7 @@ base64.workspace = true typst-ts-core.workspace = true log.workspace = true - +siphasher.workspace = true [features] rkyv = ["typst-ts-core/rkyv"] diff --git a/exporter/svg/src/backend/mod.rs b/exporter/svg/src/backend/mod.rs index adecf9b9..7b23233f 100644 --- a/exporter/svg/src/backend/mod.rs +++ b/exporter/svg/src/backend/mod.rs @@ -9,10 +9,10 @@ use typst_ts_core::{ flat_ir, flat_vm::{FlatGroupContext, FlatIncrGroupContext, FlatIncrRenderVm, FlatRenderVm}, ir::{ - self, Abs, Axes, BuildGlyph, FontIndice, GlyphHashStablizer, GlyphRef, ImmutStr, - PathStyle, Ratio, Scalar, Size, + self, Abs, Axes, BuildGlyph, FontIndice, GlyphHashStablizer, GlyphIndice, GlyphRef, + ImmutStr, PathStyle, Ratio, Scalar, Size, Transform, }, - vm::{GroupContext, RenderVm, TransformContext}, + vm::{GroupContext, RenderState, RenderVm, TransformContext}, GlyphLowerBuilder, }, }; @@ -30,6 +30,10 @@ pub trait BuildFillStyleClass { fn build_fill_style_class(&mut self, fill: ImmutStr) -> String; } +pub trait NotifyPaint { + fn notify_paint(&mut self, url_ref: ImmutStr) -> (u8, Fingerprint, Option); +} + pub trait DynExportFeature { fn should_render_text_element(&self) -> bool; @@ -124,14 +128,19 @@ pub struct SvgGlyphBuilder { } impl SvgGlyphBuilder { + pub fn new(glyph_provider: GlyphProvider) -> Self { + Self { glyph_provider } + } + + // todo: merge is_image_glyph and render_glyph pub fn render_glyph(&mut self, glyph_id: &str, glyph_item: &ir::GlyphItem) -> Option { let gp = &self.glyph_provider; Self::render_glyph_inner(gp, glyph_id, glyph_item) } - #[comemo::memoize] - pub fn render_glyph_pure(glyph_id: &str, glyph_item: ir::GlyphItem) -> Option { - Self::render_glyph_pure_inner(glyph_id, &glyph_item) + pub fn is_image_glyph(&mut self, glyph_item: &ir::GlyphItem) -> Option { + let gp: &GlyphProvider = &self.glyph_provider; + Self::is_image_glyph_inner(gp, glyph_item) } #[comemo::memoize] @@ -150,6 +159,17 @@ impl SvgGlyphBuilder { Self::render_glyph_pure_inner(glyph_id, glyph_item) } + #[comemo::memoize] + fn is_image_glyph_inner(gp: &GlyphProvider, glyph_item: &ir::GlyphItem) -> Option { + if matches!(glyph_item, ir::GlyphItem::Raw(..)) { + return Self::is_image_glyph_pure_inner( + &GlyphLowerBuilder::new(gp).lower_glyph(glyph_item)?, + ); + } + + Self::is_image_glyph_pure_inner(glyph_item) + } + fn render_glyph_pure_inner(glyph_id: &str, glyph_item: &ir::GlyphItem) -> Option { match glyph_item { ir::GlyphItem::Image(image_glyph) => Self::render_image_glyph(glyph_id, image_glyph), @@ -161,6 +181,15 @@ impl SvgGlyphBuilder { } } + fn is_image_glyph_pure_inner(glyph_item: &ir::GlyphItem) -> Option { + match glyph_item { + ir::GlyphItem::Image(..) => Some(true), + ir::GlyphItem::Outline(..) => Some(false), + ir::GlyphItem::Raw(..) => unreachable!(), + ir::GlyphItem::None => None, + } + } + /// Render an image glyph into the svg text. fn render_image_glyph(glyph_id: &str, ig: &ir::ImageGlyphItem) -> Option { let transform_style = format!(r#" style="transform: {}""#, ig.ts.to_css()); @@ -180,7 +209,7 @@ impl SvgGlyphBuilder { outline_glyph: &ir::OutlineGlyphItem, ) -> Option { let symbol_def = format!( - r#""#, + r#""#, glyph_id, outline_glyph.d ); Some(symbol_def) @@ -192,6 +221,7 @@ impl SvgGlyphBuilder { pub struct SvgTextBuilder { pub attributes: Vec<(&'static str, String)>, pub content: Vec, + pub text_fill: Option, } impl From for Arc { @@ -323,25 +353,65 @@ impl TransformContext for SvgTextBuilder { /// See [`GroupContext`]. impl< + 'm, C: RenderVm> + BuildGlyph + GlyphHashStablizer + + GlyphIndice<'m> + BuildFillStyleClass + + NotifyPaint + DynExportFeature, > GroupContext for SvgTextBuilder { - fn with_text_shape(&mut self, ctx: &mut C, shape: &ir::TextShape) { + fn with_text_shape( + &mut self, + ctx: &mut C, + upem: Scalar, + shape: &ir::TextShape, + fill_key: &Fingerprint, + state: RenderState, + ) { + let color = &shape.fill; // shorten black fill - let fill_id = ctx.build_fill_style_class(shape.fill.clone()); + let fill_id = if shape.fill.starts_with('@') { + let (kind, cano_ref, relative_to_self) = ctx.notify_paint(shape.fill.clone()); + + let text_scale = upem.0 / shape.size.0; + let text_scale = Transform::from_scale(Scalar(text_scale), Scalar(-text_scale)); + + let mat = if relative_to_self.unwrap_or(false) { + text_scale + } else { + state.inv_transform().post_concat(text_scale) + }; + let mat = mat.to_css(); + + // abs_ref + let paint_id = fill_key.as_svg_id("tf"); + self.text_fill = Some(*fill_key); + + let decl = transform_gradient(kind, cano_ref, &paint_id, &mat); + self.content.push(decl); + + "".to_owned() + } else { + ctx.build_fill_style_class(color.clone()) + }; self.attributes .push(("class", format!("typst-text {}", fill_id))); } - fn render_item_at(&mut self, ctx: &mut C, pos: ir::Point, item: &ir::SvgItem) { + fn render_item_at( + &mut self, + state: RenderState, + ctx: &mut C, + pos: ir::Point, + item: &ir::SvgItem, + ) { let translate_attr = format!("translate({:.3},{:.3})", pos.x.0, pos.y.0); - let sub_content = ctx.render_item(item); + let sub_content = ctx.render_item(state, item); self.content.push(SvgText::Content(Arc::new(SvgTextNode { attributes: vec![("transform", translate_attr)], @@ -375,29 +445,52 @@ impl< ))) } - fn render_path(&mut self, _ctx: &mut C, path: &ir::PathItem) { - self.content.push(render_path(path)) + fn render_path( + &mut self, + state: RenderState, + ctx: &mut C, + path: &ir::PathItem, + abs_ref: &Fingerprint, + ) { + for s in &path.styles { + match s { + PathStyle::Fill(color) | PathStyle::Stroke(color) => { + let is_fill = matches!(s, PathStyle::Fill(..)); + if color.starts_with('@') { + // todo + let (kind, cano_ref, relative_to_self) = ctx.notify_paint(color.clone()); + + let relative_to_self = relative_to_self.unwrap_or(true); + + let transform_matrix = if relative_to_self { + let self_bbox = path.size.unwrap(); + Transform::from_scale(self_bbox.x, self_bbox.y) + } else { + // println!("state: {:?}", state.inv_transform()); + state.inv_transform() + }; + let transform_matrix = transform_matrix.to_css(); + + let paint_id = + state + .at(abs_ref) + .as_svg_id(if is_fill { "pf" } else { "ps" }); + + let decl = transform_gradient(kind, cano_ref, &paint_id, &transform_matrix); + + self.content.push(decl); + } + } + _ => {} + } + } + self.content.push(render_path(path, state, abs_ref)) } fn render_image(&mut self, _ctx: &mut C, image_item: &ir::ImageItem) { self.content.push(render_image_item(image_item)) } - fn render_semantic_text(&mut self, ctx: &mut C, text: &ir::TextItem, width: Scalar) { - if !ctx.should_render_text_element() { - return; - } - - self.render_text_semantics_inner( - &text.shape, - &text.content.content, - width, - Scalar::from(text.font.metrics().ascender.get() as f32), - Scalar::from(text.font.units_per_em() as f32), - ctx.should_aware_html_entity(), - ) - } - #[inline] fn attach_debug_info(&mut self, ctx: &mut C, span_id: u64) { if ctx.should_attach_debug_info() { @@ -413,16 +506,24 @@ impl< C: RenderVm> + FlatRenderVm<'m, Resultant = Arc> + BuildGlyph + + GlyphIndice<'m> + FontIndice<'m> + GlyphHashStablizer + BuildFillStyleClass + + NotifyPaint + DynExportFeature, > FlatGroupContext for SvgTextBuilder { - fn render_item_ref_at(&mut self, ctx: &mut C, pos: crate::ir::Point, item: &Fingerprint) { + fn render_item_ref_at( + &mut self, + state: RenderState, + ctx: &mut C, + pos: crate::ir::Point, + item: &Fingerprint, + ) { let translate_attr = format!("translate({:.3},{:.3})", pos.x.0, pos.y.0); - let sub_content = ctx.render_flat_item(item); + let sub_content = ctx.render_flat_item(state, item); self.content.push(SvgText::Content(Arc::new(SvgTextNode { attributes: vec![ @@ -464,8 +565,17 @@ impl< self } - fn with_text(mut self, ctx: &mut C, text: &flat_ir::FlatTextItem) -> Self { - self.with_text_shape(ctx, &text.shape); + fn with_text( + mut self, + ctx: &mut C, + text: &flat_ir::FlatTextItem, + fill_key: &Fingerprint, + state: RenderState, + ) -> Self { + let font = ctx.get_font(&text.font).unwrap(); + let upem = font.unit_per_em; + + self.with_text_shape(ctx, upem, &text.shape, &state.at(fill_key), state); self } @@ -481,15 +591,17 @@ impl<'m, C: FlatIncrRenderVm<'m, Resultant = Arc, Group = SvgTextBu { fn render_diff_item_ref_at( &mut self, + state: RenderState, ctx: &mut C, pos: crate::ir::Point, item: &Fingerprint, prev_item: &Fingerprint, ) { let content = if item == prev_item { + // todo: update transform vec![] } else { - let sub_content = ctx.render_diff_item(item, prev_item); + let sub_content = ctx.render_diff_item(state, item, prev_item); vec![SvgText::Content(sub_content)] }; @@ -510,17 +622,30 @@ impl<'m, C: FlatIncrRenderVm<'m, Resultant = Arc, Group = SvgTextBu /// Render a [`ir::PathItem`] into svg text. #[comemo::memoize] -fn render_path(path: &ir::PathItem) -> SvgText { +fn render_path(path: &ir::PathItem, state: RenderState, abs_ref: &Fingerprint) -> SvgText { let mut p = vec![r#" { - fill_color = color; + fill_color = if color.starts_with("url") { + ft = format!(r#"url(#{})"#, state.at(abs_ref).as_svg_id("pf")); + &ft + } else { + color + }; } PathStyle::Stroke(color) => { - p.push(format!(r#"stroke="{}" "#, color)); + // compress the stroke color + p.push(if color.starts_with("url") { + let ps = state.at(abs_ref).as_svg_id("ps"); + format!(r##"stroke="url(#{})" "##, &ps) + } else { + format!(r##"stroke="{}" "##, color) + }); } PathStyle::StrokeWidth(width) => { p.push(format!(r#"stroke-width="{}" "#, width.0)); @@ -605,3 +730,28 @@ pub fn generate_text(text_list: Vec) -> String { } string_io } + +fn transform_gradient(kind: u8, f: Fingerprint, paint_id: &str, transform_matrix: &str) -> SvgText { + let tag = match kind { + b'l' => "linearGradient", + b'r' => "radialGradient", + b'p' => "pattern", + _ => unreachable!(), + }; + + let transform = match kind { + b'p' => "patternTransform", + _ => "gradientTransform", + }; + + SvgText::Plain(format!( + r##"<{} id="{}" {}="{}" href="#{}" xlink:href="#{}">"##, + tag, + paint_id, + transform, + transform_matrix, + f.as_svg_id("g"), + f.as_svg_id("g"), + tag + )) +} diff --git a/exporter/svg/src/frontend/context.rs b/exporter/svg/src/frontend/context.rs index 69266e79..6e8abed6 100644 --- a/exporter/svg/src/frontend/context.rs +++ b/exporter/svg/src/frontend/context.rs @@ -1,22 +1,27 @@ -use std::{collections::HashMap, sync::Arc}; +use siphasher::sip128::Hasher128; +use std::{collections::HashMap, hash::Hash, sync::Arc}; use typst_ts_core::{ - hash::{item_hash128, Fingerprint, FingerprintBuilder}, + hash::{item_hash128, Fingerprint, FingerprintBuilder, FingerprintSipHasherBase}, vector::{ flat_ir::{FlatSvgItem, FlatTextItem, GroupRef, Module}, - flat_vm::{FlatIncrRenderVm, FlatRenderVm}, + flat_vm::{FlatGroupContext, FlatIncrRenderVm, FlatRenderVm}, ir::{ - self, BuildGlyph, FontIndice, FontRef, GlyphHashStablizer, GlyphPackBuilder, GlyphRef, - ImmutStr, PathItem, StyleNs, + self, BuildGlyph, FontIndice, FontRef, GlyphHashStablizer, GlyphIndice, GlyphItem, + GlyphPackBuilder, GlyphRef, ImmutStr, PathItem, Scalar, StyleNs, }, vm::GroupContext, - vm::RenderVm, + vm::{RenderState, RenderVm}, }, + TypstAbs, }; use crate::{ - backend::{BuildClipPath, BuildFillStyleClass, DynExportFeature, SvgTextBuilder, SvgTextNode}, - ExportFeature, GlyphProvider, + backend::{ + BuildClipPath, BuildFillStyleClass, DynExportFeature, NotifyPaint, SvgText, SvgTextBuilder, + SvgTextNode, + }, + ExportFeature, GlyphProvider, SvgGlyphBuilder, }; /// Maps the style name to the style definition. @@ -24,6 +29,8 @@ use crate::{ pub(crate) type StyleDefMap = HashMap<(StyleNs, ImmutStr), String>; /// Maps the clip path's data to the clip path id. pub(crate) type ClipPathMap = HashMap; +/// Maps the clip path's data to the clip path id. +pub(crate) type GradientMap = HashMap)>; /// The task context for rendering svg items /// The 'm lifetime is the lifetime of the module which stores the frame data. @@ -47,6 +54,8 @@ pub struct RenderContext<'m, 't, Feat: ExportFeature> { pub(crate) style_defs: &'t mut StyleDefMap, /// Stores the clip paths used in the document. pub(crate) clip_paths: &'t mut ClipPathMap, + /// Stores the clip paths used in the document. + pub(crate) gradients: &'t mut GradientMap, /// See [`ExportFeature`]. pub should_render_text_element: bool, @@ -88,6 +97,12 @@ impl<'m, 't, Feat: ExportFeature> FontIndice<'m> for RenderContext<'m, 't, Feat> } } +impl<'m, 't, Feat: ExportFeature> GlyphIndice<'m> for RenderContext<'m, 't, Feat> { + fn get_glyph(&self, g: &GlyphRef) -> Option<&'m ir::GlyphItem> { + self.module.glyphs.get(g.glyph_idx as usize).map(|v| &v.1) + } +} + impl<'m, 't, Feat: ExportFeature> BuildGlyph for RenderContext<'m, 't, Feat> { fn build_font(&mut self, font: &typst::font::Font) -> FontRef { self.glyph_defs.build_font(font) @@ -131,6 +146,39 @@ impl<'m, 't, Feat: ExportFeature> BuildClipPath for RenderContext<'m, 't, Feat> } } +impl<'m, 't, Feat: ExportFeature> NotifyPaint for RenderContext<'m, 't, Feat> { + fn notify_paint(&mut self, url_ref: ImmutStr) -> (u8, Fingerprint, Option) { + if let Some(f) = self.gradients.get(&url_ref) { + return *f; + } + + // url(#ghash) + if !url_ref.starts_with("@g") { + panic!("Invalid url reference: {}", url_ref); + } + + let id = url_ref.trim_start_matches("@g"); + let id = Fingerprint::try_from_str(id).unwrap(); + + let (kind, relative_to_self) = match self.get_item(&id) { + Some(FlatSvgItem::Gradient(g)) => (&g.kind, g.relative_to_self), + _ => { + // #[cfg(debug_assertions)] + panic!("Invalid gradient reference: {}", id.as_svg_id("g")); + } + }; + + let kind = match kind { + ir::GradientKind::Linear(..) => b'l', + ir::GradientKind::Radial(..) => b'r', + ir::GradientKind::Conic(..) => b'p', + }; + + self.gradients.insert(url_ref, (kind, id, relative_to_self)); + (kind, id, relative_to_self) + } +} + /// Example of how to implement a RenderVm. impl<'m, 't, Feat: ExportFeature> RenderVm for RenderContext<'m, 't, Feat> { // type Resultant = String; @@ -140,6 +188,7 @@ impl<'m, 't, Feat: ExportFeature> RenderVm for RenderContext<'m, 't, Feat> { fn start_group(&mut self) -> Self::Group { Self::Group { attributes: vec![], + text_fill: None, content: Vec::with_capacity(1), } } @@ -150,13 +199,98 @@ impl<'m, 't, Feat: ExportFeature> RenderVm for RenderContext<'m, 't, Feat> { g } - fn start_text(&mut self, text: &ir::TextItem) -> Self::Group { + fn start_text(&mut self, state: RenderState, text: &ir::TextItem) -> Self::Group { let mut g = self.start_group(); - g.with_text_shape(self, &text.shape); + + let mut k = FingerprintSipHasherBase::new(); + text.font.hash(&mut k); + text.content.glyphs.hash(&mut k); + text.shape.hash(&mut k); + let k = k.finish128().as_u128(); + + let upem = Scalar(text.font.units_per_em() as f32); + + g.with_text_shape( + self, + upem, + &text.shape, + &state.at(&Fingerprint::from_u128(k)), + state, + ); g.attach_debug_info(self, text.content.span_id); g } + + /// Render a text into the underlying context. + // todo: combine with flat item one + fn render_text(&mut self, state: RenderState, text: &ir::TextItem) -> Self::Resultant { + let group_ctx = self.start_text(state, text); + + // upem is the unit per em defined in the font. + let upem = Scalar(text.font.units_per_em() as f32); + + let mut group_ctx = text.shape.add_transform(self, group_ctx, upem); + + if let Some(fill) = &group_ctx.text_fill { + // clip path rect + let clip_id = fill.as_svg_id("tc"); + group_ctx.content.push(SvgText::Plain(format!( + r#""#, + clip_id + ))); + } + let width = text.render_glyphs(upem, |x, g| { + group_ctx.render_glyph(self, x, g); + }); + if let Some(fill) = &group_ctx.text_fill { + let fill_id = fill.as_svg_id("tf"); + let clip_id = fill.as_svg_id("tc"); + + // because the text is already scaled by the font size, + // we need to scale it back to the original size. + // todo: infinite multiplication + let descender = text + .font + .metrics() + .descender + .at(TypstAbs::raw(upem.0 as f64)) + .to_pt() as f32; + let width = width.0 * upem.0 / text.shape.size.0; + + group_ctx + .content + .push(SvgText::Plain(r#""#.to_owned())); + + // clip path rect + group_ctx.content.push(SvgText::Plain(format!( + r##""##, + fill_id, width, upem.0, descender, clip_id + ))); + + // image glyphs + text.render_glyphs(upem, |x, g| { + let built = SvgGlyphBuilder::new(self.glyph_provider.clone()).is_image_glyph(g); + if matches!(built, Some(false) | None) { + return; + } + group_ctx.render_glyph(self, x, g); + }); + } + + if self.should_render_text_element() { + group_ctx.render_text_semantics_inner( + &text.shape, + &text.content.content, + width, + Scalar::from(text.font.metrics().ascender.get() as f32), + upem, + self.should_aware_html_entity(), + ) + } + + group_ctx.into() + } } impl<'m, 't, Feat: ExportFeature> FlatRenderVm<'m> for RenderContext<'m, 't, Feat> { @@ -171,6 +305,7 @@ impl<'m, 't, Feat: ExportFeature> FlatRenderVm<'m> for RenderContext<'m, 't, Fea fn start_flat_group(&mut self, v: &Fingerprint) -> Self::Group { Self::Group { attributes: vec![("data-tid", v.as_svg_id("g"))], + text_fill: None, content: Vec::with_capacity(1), } } @@ -181,11 +316,93 @@ impl<'m, 't, Feat: ExportFeature> FlatRenderVm<'m> for RenderContext<'m, 't, Fea g } - fn start_flat_text(&mut self, value: &Fingerprint, text: &FlatTextItem) -> Self::Group { + fn start_flat_text( + &mut self, + state: RenderState, + value: &Fingerprint, + text: &FlatTextItem, + ) -> Self::Group { let mut g = self.start_flat_group(value); - g.with_text_shape(self, &text.shape); + + let font = self.get_font(&text.font).unwrap(); + let upem = font.unit_per_em; + + g.with_text_shape(self, upem, &text.shape, &state.at(value), state); g } + + /// Render a text into the underlying context. + fn render_flat_text( + &mut self, + _state: RenderState, + mut group_ctx: Self::Group, + _abs_ref: &Fingerprint, + text: &FlatTextItem, + ) -> Self::Group { + let font = self.get_font(&text.font).unwrap(); + + // upem is the unit per em defined in the font. + let upem = font.unit_per_em; + + group_ctx = text.shape.add_transform(self, group_ctx, upem); + + if let Some(fill) = &group_ctx.text_fill { + // clip path rect + let clip_id = fill.as_svg_id("tc"); + group_ctx.content.push(SvgText::Plain(format!( + r#""#, + clip_id + ))); + } + let width = text.render_glyphs(upem, |x, g| { + group_ctx.render_glyph_ref(self, x, g); + }); + if let Some(fill) = &group_ctx.text_fill { + let fill_id = fill.as_svg_id("tf"); + let clip_id = fill.as_svg_id("tc"); + + // because the text is already scaled by the font size, + // we need to scale it back to the original size. + // todo: infinite multiplication + let descender = font.descender.0 * upem.0; + let width = width.0 * upem.0 / text.shape.size.0; + + group_ctx + .content + .push(SvgText::Plain(r#""#.to_owned())); + + // clip path rect + group_ctx.content.push(SvgText::Plain(format!( + r##""##, + fill_id, width, upem.0, descender, clip_id + ))); + + // image glyphs + text.render_glyphs(upem, |x, g| { + let built = self.get_glyph(g); + if matches!( + built, + Some(GlyphItem::Outline(..) | GlyphItem::Raw(..)) | None + ) { + return; + } + group_ctx.render_glyph_ref(self, x, g); + }); + } + + if self.should_render_text_element() { + group_ctx.render_text_semantics_inner( + &text.shape, + &text.content.content, + width, + font.ascender, + upem, + self.should_aware_html_entity(), + ) + } + + group_ctx + } } impl<'m, 't, Feat: ExportFeature> FlatIncrRenderVm<'m> for RenderContext<'m, 't, Feat> {} diff --git a/exporter/svg/src/frontend/dynamic_layout.rs b/exporter/svg/src/frontend/dynamic_layout.rs index 15f37963..2ec1958a 100644 --- a/exporter/svg/src/frontend/dynamic_layout.rs +++ b/exporter/svg/src/frontend/dynamic_layout.rs @@ -35,6 +35,10 @@ impl DynamicLayoutSvgExporter { }) .collect::>(); + for ext in t.extra_items.into_values() { + self.builder.build(ext); + } + self.layouts .push((layout_width.into(), LayoutRegionNode::new_pages(pages))); // log::trace!("svg dynamic layout render time: {:?}", diff --git a/exporter/svg/src/frontend/flat.rs b/exporter/svg/src/frontend/flat.rs index be3975c3..40db8ec7 100644 --- a/exporter/svg/src/frontend/flat.rs +++ b/exporter/svg/src/frontend/flat.rs @@ -3,10 +3,11 @@ use std::sync::Arc; use typst::{diag::SourceResult, doc::Document}; use typst_ts_core::vector::{ flat_ir::{ - flatten_glyphs, FlatModule, ItemPack, LayoutRegion, LayoutRegionNode, LayoutRegionRepr, - Module, ModuleBuilder, ModuleMetadata, Page, SvgDocument, + flatten_glyphs, FlatModule, FlatSvgItem, ItemPack, LayoutRegion, LayoutRegionNode, + LayoutRegionRepr, Module, ModuleBuilder, ModuleMetadata, Page, SvgDocument, }, flat_vm::FlatRenderVm, + vm::RenderState, LowerBuilder, }; @@ -25,6 +26,7 @@ impl SvgTask { let entry = &page.content; let size = Self::page_size(page.size); + let state = RenderState::new_size(page.size); svg_body.push(SvgText::Content(Arc::new(SvgTextNode { attributes: vec![ ("transform", format!("translate(0, {})", acc_height)), @@ -32,7 +34,7 @@ impl SvgTask { ("data-page-width", size.x.to_string()), ("data-page-height", size.y.to_string()), ], - content: vec![SvgText::Content(render_task.render_flat_item(entry))], + content: vec![SvgText::Content(render_task.render_flat_item(state, entry))], }))); acc_height += size.y; } @@ -82,7 +84,22 @@ impl SvgExporter { true, ); - generate_text(Self::render_svg_template(t, header, svg_body, glyphs)) + let gradients = std::mem::take(&mut t.gradients); + let gradients = gradients + .values() + .filter_map(|(_, id, _)| match module.get_item(id) { + Some(FlatSvgItem::Gradient(g)) => Some((id, g)), + _ => { + // #[cfg(debug_assertions)] + panic!("Invalid gradient reference: {}", id.as_svg_id("g")); + #[allow(unreachable_code)] + None + } + }); + + generate_text(Self::render_svg_template( + t, header, svg_body, glyphs, gradients, + )) } } @@ -101,6 +118,10 @@ pub fn export_module(output: &Document) -> SourceResult> { }); } + for ext in t.extra_items.into_values() { + builder.build(ext); + } + let repr: Module = builder.finalize(); let glyphs = flatten_glyphs(repr.glyphs).into(); diff --git a/exporter/svg/src/frontend/incremental.rs b/exporter/svg/src/frontend/incremental.rs index 5fa9afa6..e161213d 100644 --- a/exporter/svg/src/frontend/incremental.rs +++ b/exporter/svg/src/frontend/incremental.rs @@ -7,10 +7,11 @@ use std::{ use typst_ts_core::{ hash::Fingerprint, vector::{ - flat_ir::{LayoutRegionNode, Module, ModuleBuilder, Page}, + flat_ir::{FlatSvgItem, LayoutRegionNode, Module, ModuleBuilder, Page}, flat_vm::{FlatIncrRenderVm, FlatRenderVm}, incr::{IncrDocClient, IncrDocServer}, ir::{Rect, SvgItem}, + vm::RenderState, }, }; @@ -69,10 +70,10 @@ impl SvgTask { for Page { content: entry, - size, + size: size_f32, } in ctx.next.iter() { - let size = Self::page_size(*size); + let size = Self::page_size(*size_f32); if reusable.contains(entry) { // println!("reuse page: {} {:?}", idx, entry); svg_body.push(SvgText::Content(Arc::new(SvgTextNode { @@ -100,15 +101,16 @@ impl SvgTask { ]; // todo: evaluate simlarity + let state = RenderState::new_size(*size_f32); let item = if let Some(prev_entry) = unused_prev.pop_first().map(|(_, v)| v) { // println!("diff page: {} {:?} {:?}", idx, entry, prev_entry); attributes.push(("data-reuse-from", prev_entry.as_svg_id("p"))); - render_task.render_diff_item(entry, &prev_entry) + render_task.render_diff_item(state, entry, &prev_entry) } else { // todo: find a box // println!("rebuild page: {} {:?}", idx, entry); - render_task.render_flat_item(entry) + render_task.render_flat_item(state, entry) }; svg_body.push(SvgText::Content(Arc::new(SvgTextNode { @@ -151,7 +153,7 @@ impl IncrSvgDocClient { // prepare an empty page for the pages that are not rendered // todo: better solution? - let empty_page = self.mb.build(SvgItem::Group(Default::default())); + let empty_page = self.mb.build(SvgItem::Group(Default::default(), None)); kern.module_mut() .items .extend(self.mb.items.iter().map(|(f, (_, v))| (*f, v.clone()))); @@ -205,9 +207,23 @@ impl IncrSvgDocClient { &mut svg_body, ); + let module_ref = kern.module_mut(); + let gradients = std::mem::take(&mut t.gradients); + let gradients = gradients + .values() + .filter_map(|(_, id, _)| match module_ref.get_item(id) { + Some(FlatSvgItem::Gradient(g)) => Some((id, g)), + _ => { + // #[cfg(debug_assertions)] + panic!("Invalid gradient reference: {}", id.as_svg_id("g")); + #[allow(unreachable_code)] + None + } + }); + // render the glyphs svg.push(r#""#.into()); - let glyphs = kern.module_mut().glyphs.iter(); + let glyphs = module_ref.glyphs.iter(); // skip the glyphs that are already rendered let new_glyphs = glyphs.skip(self.glyph_window); let glyph_defs = t.render_glyphs(new_glyphs.enumerate().map(|(x, (_, y))| (x, y)), true); @@ -219,6 +235,7 @@ impl IncrSvgDocClient { svg.push(r#""#.into()); IncrExporter::clip_paths(t.clip_paths, &mut svg); + IncrExporter::gradients(gradients, &mut svg); svg.push("".into()); IncrExporter::style_defs(t.style_defs, &mut svg); diff --git a/exporter/svg/src/frontend/mod.rs b/exporter/svg/src/frontend/mod.rs index 2585dafd..3763f6b1 100644 --- a/exporter/svg/src/frontend/mod.rs +++ b/exporter/svg/src/frontend/mod.rs @@ -1,13 +1,19 @@ -use std::sync::Arc; +use std::{collections::HashSet, f32::consts::TAU, fmt::Write, sync::Arc}; -use typst::doc::Document; +use typst::{ + doc::Document, + geom::{Angle, Color, ColorSpace, Hsl, Hsv, Quadrant, WeightedColor}, +}; use typst_ts_core::{ font::GlyphProvider, - hash::FingerprintBuilder, + hash::{item_hash128, Fingerprint, FingerprintBuilder}, vector::{ - flat_ir::{self, Module}, - ir::{Axes, DefId, GlyphItem, GlyphPackBuilder, Size, SvgItem}, - vm::RenderVm, + flat_ir::{self, ModuleBuilder}, + ir::{ + Axes, DefId, GlyphItem, GlyphPackBuilder, GradientItem, GradientKind, GradientStyle, + Scalar, Size, SvgItem, + }, + vm::{RenderState, RenderVm}, LowerBuilder, }, }; @@ -29,6 +35,8 @@ use crate::{ }; pub use incremental::{IncrSvgDocClient, IncrSvgDocServer, IncrementalRenderContext}; +use self::context::GradientMap; + pub struct SvgExporter { pub _feat_phantom: std::marker::PhantomData, } @@ -104,12 +112,223 @@ impl SvgExporter { } } + /// Render the gradients for SVG + /// .. + /// ^^^^^^^^^^^ + /// See [`GradientMap`]. + fn gradients<'a>( + gradients: impl Iterator, + svg: &mut Vec, + ) { + let mut sub_gradients = HashSet::<(Fingerprint, SVGSubGradient)>::default(); + + // todo: aspect ratio + for (id, gradient) in gradients { + match &gradient.kind { + GradientKind::Linear(angle) => { + // todo: use native angle + let angle = typst::geom::Angle::rad(angle.0 as f64); + + // todo: correct aspect ratio + // let angle = Gradient::correct_aspect_ratio(linear.angle, *ratio); + let (sin, cos) = (angle.sin(), angle.cos()); + let length = sin.abs() + cos.abs(); + let (x1, y1, x2, y2) = match angle.quadrant() { + Quadrant::First => (0.0, 0.0, cos * length, sin * length), + Quadrant::Second => (1.0, 0.0, cos * length + 1.0, sin * length), + Quadrant::Third => (1.0, 1.0, cos * length + 1.0, sin * length + 1.0), + Quadrant::Fourth => (0.0, 1.0, cos * length, sin * length + 1.0), + }; + + svg.push(SvgText::Plain( + format!( + r##""##, + id.as_svg_id("g"), + x1, y1, x2, y2, + ) + )) + } + GradientKind::Radial(radius) => { + let mut center = &Axes::new(Scalar(0.5), Scalar(0.5)); + let mut focal_center = &Axes::new(Scalar(0.5), Scalar(0.5)); + let mut focal_radius = &Scalar(0.); + for s in &gradient.styles { + match s { + GradientStyle::Center(c) => { + center = c; + } + GradientStyle::FocalCenter(c) => { + focal_center = c; + } + GradientStyle::FocalRadius(r) => { + focal_radius = r; + } + } + } + + svg.push(SvgText::Plain( + format!( + r##""##, + id.as_svg_id("g"), + center.x.0, center.y.0, radius.0, focal_center.x.0, focal_center.y.0, focal_radius.0, + ) + )); + } + GradientKind::Conic(angle) => { + svg.push(SvgText::Plain( + format!( + r##""##, + id.as_svg_id("g"), + ) + )); + + // The rotation angle, negated to match rotation in PNG. + // todo: use native angle + // let angle = Gradient::correct_aspect_ratio(angle, *ratio); + // let angle = typst::geom::Angle::rad(angle.0 as f64); + let angle: f32 = -(angle.0).rem_euclid(TAU); + let mut center = &Axes::new(Scalar(0.5), Scalar(0.5)); + for s in &gradient.styles { + if let GradientStyle::Center(c) = s { + center = c; + } + } + + // We build an arg segment for each segment of a circle. + let dtheta = TAU / CONIC_SEGMENT as f32; + for i in 0..CONIC_SEGMENT { + let theta1 = dtheta * i as f32; + let theta2 = dtheta * (i + 1) as f32; + + // Create the path for the segment. + let mut builder = SvgPath2DBuilder::default(); + builder.move_to( + correct_pattern_pos(center.x.0), + correct_pattern_pos(center.y.0), + ); + builder.line_to( + correct_pattern_pos(-2.0 * (theta1 + angle).cos() + center.x.0), + correct_pattern_pos(2.0 * (theta1 + angle).sin() + center.y.0), + ); + builder.arc( + (2.0, 2.0), + 0.0, + 0, + 1, + ( + correct_pattern_pos(-2.0 * (theta2 + angle).cos() + center.x.0), + correct_pattern_pos(2.0 * (theta2 + angle).sin() + center.y.0), + ), + ); + builder.close(); + + let t1 = (i as f32) / CONIC_SEGMENT as f32; + let t2 = (i + 1) as f32 / CONIC_SEGMENT as f32; + let subgradient = SVGSubGradient { + center: *center, + t0: Angle::rad((theta1 + angle) as f64), + t1: Angle::rad((theta2 + angle) as f64), + c0: sample_color_stops(gradient, t1), + c1: sample_color_stops(gradient, t2), + }; + let f = Fingerprint::from_u128(item_hash128(&subgradient)); + sub_gradients.insert((f, subgradient)); + + svg.push(SvgText::Plain(format!( + r##""##, + builder.0, + f.as_svg_id("g"), + f.as_svg_id("g"), + ))); + } + + svg.push(SvgText::Plain("".to_owned())); + + // We skip the default stop generation code. + continue; + } + } + + for window in gradient.stops.windows(2) { + let (start_c, start_t) = &window[0]; + let (end_c, end_t) = &window[1]; + + svg.push(SvgText::Plain(format!( + r##""##, + RatioRepr(start_t.0), + start_c.clone().to_hex(), + ))); + + // Generate (256 / len) stops between the two stops. + // This is a workaround for a bug in many readers: + // They tend to just ignore the color space of the gradient. + // The goal is to have smooth gradients but not to balloon the file size + // too much if there are already a lot of stops as in most presets. + let len = if gradient.anti_alias { + (256 / gradient.stops.len() as u32).max(2) + } else { + 2 + }; + + for i in 1..(len - 1) { + let t0 = i as f32 / (len - 1) as f32; + let t = start_t.0 + (end_t.0 - start_t.0) * t0; + let c = sample_color_stops(gradient, t); + + svg.push(SvgText::Plain(format!( + r##""##, + RatioRepr(t), + c.to_hex(), + ))); + } + + svg.push(SvgText::Plain(format!( + r##""##, + RatioRepr(end_t.0), + end_c.clone().to_hex(), + ))); + } + + svg.push(SvgText::Plain(match gradient.kind { + GradientKind::Linear(..) => "".to_owned(), + GradientKind::Radial(..) => "".to_owned(), + GradientKind::Conic(..) => "".to_owned(), + })); + } + + for (id, gradient) in sub_gradients { + let x1 = 2.0 - gradient.t0.cos() as f32 + gradient.center.x.0; + let y1 = gradient.t0.sin() as f32 + gradient.center.y.0; + let x2 = 2.0 - gradient.t1.cos() as f32 + gradient.center.x.0; + let y2 = gradient.t1.sin() as f32 + gradient.center.y.0; + + svg.push(SvgText::Plain(format!( + r##""##, + id.as_svg_id("g"), + x1, y1, x2, y2, + ))); + + svg.push(SvgText::Plain(format!( + r##""##, + gradient.c0.to_hex(), + ))); + + svg.push(SvgText::Plain(format!( + r##""##, + gradient.c1.to_hex(), + ))); + + svg.push(SvgText::Plain("".to_owned())); + } + } + /// Template SVG. - fn render_svg_template( + fn render_svg_template<'a>( t: SvgTask, header: String, mut body: Vec, glyphs: impl IntoIterator, + gradients: impl Iterator, ) -> Vec { let mut svg = vec![ SvgText::Plain(header), @@ -128,6 +347,7 @@ impl SvgExporter { svg.push("".into()); svg.push(r#""#.into()); Self::clip_paths(t.clip_paths, &mut svg); + Self::gradients(gradients, &mut svg); svg.push("".into()); Self::style_defs(t.style_defs, &mut svg); @@ -163,17 +383,32 @@ impl SvgExporter { .iter() .map(|p| lower_builder.lower(p)) .collect::>(); + let mut module = ModuleBuilder::default(); + + for ext in lower_builder.extra_items.clone().into_values() { + module.build(ext); + } + + let module = module.finalize(); // render SVG body let mut svg_body = vec![]; - t.render_pages_transient(output, pages, &mut svg_body); + t.render_pages_transient(module, output, pages, &mut svg_body); // render the glyphs collected from the pages let (_, glyphs) = std::mem::take(&mut t.glyph_defs).finalize(); let glyphs = t.render_glyphs(glyphs.iter().enumerate().map(|(x, (_, y))| (x, y)), false); + let gradients = lower_builder + .extra_items + .iter() + .filter_map(|(f, item)| match item { + SvgItem::Gradient(item) => Some((f, item)), + _ => None, + }); + // template SVG - Self::render_svg_template(t, header, svg_body, glyphs) + Self::render_svg_template(t, header, svg_body, glyphs, gradients) } /// Render SVG wrapped with HTML for [`Document`]. @@ -217,6 +452,8 @@ pub struct SvgTask { pub(crate) style_defs: StyleDefMap, /// Stores the clip paths used in the document. pub(crate) clip_paths: ClipPathMap, + /// Stores the gradients used in the document. + pub(crate) gradients: GradientMap, _feat_phantom: std::marker::PhantomData, } @@ -232,6 +469,7 @@ impl Default for SvgTask { glyph_defs: GlyphPackBuilder::default(), style_defs: StyleDefMap::default(), clip_paths: ClipPathMap::default(), + gradients: GradientMap::default(), _feat_phantom: std::marker::PhantomData, } @@ -272,6 +510,7 @@ impl SvgTask { glyph_defs: &mut self.glyph_defs, style_defs: &mut self.style_defs, clip_paths: &mut self.clip_paths, + gradients: &mut self.gradients, should_attach_debug_info: Feat::SHOULD_ATTACH_DEBUG_INFO, should_render_text_element: true, @@ -333,28 +572,20 @@ impl SvgTask { /// Render pages into the svg_body. pub fn render_pages_transient( &mut self, + module: flat_ir::Module, output: &Document, pages: Vec, svg_body: &mut Vec, ) { - #[cfg(feature = "flat-vector")] - let module = Module::default(); - let mut render_task = { - #[cfg(feature = "flat-vector")] - let render_task = self.get_render_context(&module); - - #[cfg(not(feature = "flat-vector"))] - let render_task = self.get_render_context(); - - render_task - }; + let mut render_task = self.get_render_context(&module); render_task.use_stable_glyph_id = false; // accumulate the height of pages let mut acc_height = 0u32; for (idx, page) in pages.iter().enumerate() { - let size = Self::page_size(output.pages[idx].size().into()); + let size_f32 = output.pages[idx].size().into(); + let size = Self::page_size(size_f32); let attributes = vec![ ("transform", format!("translate(0, {})", acc_height)), @@ -362,7 +593,7 @@ impl SvgTask { ("data-page-height", size.y.to_string()), ]; - let page_svg = render_task.render_item(page); + let page_svg = render_task.render_item(RenderState::new_size(size_f32), page); svg_body.push(SvgText::Content(Arc::new(SvgTextNode { attributes, @@ -372,3 +603,152 @@ impl SvgTask { } } } + +/// Maps a coordinate in a unit size square to a coordinate in the pattern. +fn correct_pattern_pos(x: f32) -> f32 { + (x + 0.5) / 2.0 +} + +#[derive(Default)] +struct SvgPath2DBuilder(pub String); + +/// See: https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths +impl SvgPath2DBuilder { + #[allow(dead_code)] + pub fn rect(&mut self, x: f32, y: f32, w: f32, h: f32) { + write!( + &mut self.0, + "M {} {} H {} V {} H {} Z", + x, + y, + x + w, + y + h, + x + ) + .unwrap(); + } +} + +impl SvgPath2DBuilder { + fn move_to(&mut self, x: f32, y: f32) { + write!(&mut self.0, "M {} {} ", x, y).unwrap(); + } + + fn line_to(&mut self, x: f32, y: f32) { + write!(&mut self.0, "L {} {} ", x, y).unwrap(); + } + + /// Creates an arc path. + fn arc( + &mut self, + radius: (f32, f32), + x_axis_rot: f32, + large_arc_flag: u32, + sweep_flag: u32, + pos: (f32, f32), + ) { + write!( + &mut self.0, + "A {rx} {ry} {x_axis_rot} {large_arc_flag} {sweep_flag} {x} {y} ", + rx = radius.0, + ry = radius.1, + x = pos.0, + y = pos.1, + ) + .unwrap(); + } + + fn close(&mut self) { + write!(&mut self.0, "Z ").unwrap(); + } +} + +/// The number of segments in a conic gradient. +/// This is a heuristic value that seems to work well. +/// Smaller values could be interesting for optimization. +const CONIC_SEGMENT: usize = 360; + +/// A subgradient for conic gradients. +#[derive(Hash, PartialEq, Eq, Debug, Clone, Copy)] +struct SVGSubGradient { + /// The center point of the gradient. + center: Axes, + /// The start point of the subgradient. + t0: Angle, + /// The end point of the subgradient. + t1: Angle, + /// The color at the start point of the subgradient. + c0: Color, + /// The color at the end point of the subgradient. + c1: Color, +} + +/// Sample the stops at a given position. +// todo: use native approach +fn sample_color_stops(gradient: &GradientItem, t: f32) -> Color { + let t = t.clamp(0.0, 1.0); + let mut low = 0; + let mut high = gradient.stops.len(); + + let mixing_space = gradient.space.into(); + let stops = &gradient.stops; + + while low < high { + let mid = (low + high) / 2; + if stops[mid].1 .0 < t { + low = mid + 1; + } else { + high = mid; + } + } + + if low == 0 { + low = 1; + } + let (col_0, pos_0) = &stops[low - 1]; + let (col_1, pos_1) = &stops[low]; + let t = (t - pos_0.0) / (pos_1.0 - pos_0.0); + let col_0 = col_0.typst(); + let col_1 = col_1.typst(); + + let out = Color::mix_iter( + [ + WeightedColor::new(col_0, (1.0 - t) as f64), + WeightedColor::new(col_1, t as f64), + ], + mixing_space, + ) + .unwrap(); + + // Special case for handling multi-turn hue interpolation. + if mixing_space == ColorSpace::Hsl || mixing_space == ColorSpace::Hsv { + let hue_0 = col_0.to_space(mixing_space).to_vec4()[0]; + let hue_1 = col_1.to_space(mixing_space).to_vec4()[0]; + + // Check if we need to interpolate over the 360° boundary. + if (hue_0 - hue_1).abs() > 180.0 { + let hue_0 = if hue_0 < hue_1 { hue_0 + 360.0 } else { hue_0 }; + let hue_1 = if hue_1 < hue_0 { hue_1 + 360.0 } else { hue_1 }; + + let hue = hue_0 * (1.0 - t) + hue_1 * t; + + if mixing_space == ColorSpace::Hsl { + let [_, saturation, lightness, alpha] = out.to_hsl().to_vec4(); + return Color::Hsl(Hsl::new(hue, saturation, lightness, alpha)); + } else if mixing_space == ColorSpace::Hsv { + let [_, saturation, value, alpha] = out.to_hsv().to_vec4(); + return Color::Hsv(Hsv::new(hue, saturation, value, alpha)); + } + } + } + + out +} + +struct RatioRepr(f32); + +impl std::fmt::Display for RatioRepr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:.3}%", self.0 * 100.0) + } +} diff --git a/exporter/svg/src/frontend/typst.svg.css b/exporter/svg/src/frontend/typst.svg.css index 8f912f8b..ff41b3a0 100644 --- a/exporter/svg/src/frontend/typst.svg.css +++ b/exporter/svg/src/frontend/typst.svg.css @@ -30,10 +30,10 @@ svg { cursor: pointer; pointer-events: all; } -.outline_glyph path { +.outline_glyph path, path.outline_glyph { fill: var(--glyph_fill); } -.outline_glyph path { +.outline_glyph path, path.outline_glyph { transition: 0.2s fill; } .hover .typst-text { diff --git a/exporter/svg/src/utils.rs b/exporter/svg/src/utils.rs index f0c3d446..059ac6d1 100644 --- a/exporter/svg/src/utils.rs +++ b/exporter/svg/src/utils.rs @@ -22,9 +22,9 @@ pub trait ToCssExt { impl ToCssExt for Color { fn to_css(self) -> String { - let color = self.to_rgba(); - if color.a == 255 { - let shorter = format!("#{:02x}{:02x}{:02x}", color.r, color.g, color.b); + let [r, g, b, a] = self.to_vec4_u8(); + if a == 255 { + let shorter = format!("#{:02x}{:02x}{:02x}", r, g, b); if shorter.chars().nth(1) == shorter.chars().nth(2) && shorter.chars().nth(3) == shorter.chars().nth(4) && shorter.chars().nth(5) == shorter.chars().nth(6) @@ -39,10 +39,7 @@ impl ToCssExt for Color { return shorter; } - format!( - "#{:02x}{:02x}{:02x}{:02x}", - color.r, color.g, color.b, color.a - ) + format!("#{:02x}{:02x}{:02x}{:02x}", r, g, b, a) } } diff --git a/fuzzers/corpora/bugs/block-width-box_00.typ b/fuzzers/corpora/bugs/block-width-box_00.typ new file mode 100644 index 00000000..bc407621 --- /dev/null +++ b/fuzzers/corpora/bugs/block-width-box_00.typ @@ -0,0 +1,7 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#block(width: 100%, fill: red, box("a box")) + +#block(width: 100%, fill: red, [#box("a box") #box()]) diff --git a/fuzzers/corpora/bugs/equation-numbering-reference_00.typ b/fuzzers/corpora/bugs/equation-numbering-reference_00.typ new file mode 100644 index 00000000..a37de962 --- /dev/null +++ b/fuzzers/corpora/bugs/equation-numbering-reference_00.typ @@ -0,0 +1,13 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// +// #set page(height: 70pt) +// +// $ +// Delta = b^2 - 4 a c +// $ +// +// // Error: 14-24 cannot reference equation without numbering +// // Hint: 14-24 you can enable equation numbering with `#set math.equation(numbering: "1.")` +// Looks at the @quadratic formula. \ No newline at end of file diff --git a/fuzzers/corpora/bugs/grid-2_00.typ b/fuzzers/corpora/bugs/grid-2_00.typ index dceee975..acce7253 100644 --- a/fuzzers/corpora/bugs/grid-2_00.typ +++ b/fuzzers/corpora/bugs/grid-2_00.typ @@ -9,7 +9,7 @@ rect(width: 100%, fill: red), rect(width: 100%, fill: blue), rect(width: 100%, height: 80%, fill: green), - [hello \ darkness #parbreak my \ old \ friend \ I], + [hello \ darkness #parbreak() my \ old \ friend \ I], rect(width: 100%, height: 20%, fill: blue), polygon(fill: red, (0%, 0%), (100%, 0%), (100%, 20%)) ) diff --git a/fuzzers/corpora/bugs/layout-infinite-lengths_00.typ b/fuzzers/corpora/bugs/layout-infinite-lengths_00.typ new file mode 100644 index 00000000..cd0dae8b --- /dev/null +++ b/fuzzers/corpora/bugs/layout-infinite-lengths_00.typ @@ -0,0 +1,8 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// +// #set page(width: auto, height: auto) +// +// // Error: cannot expand into infinite width +// #layout(size => grid(columns: (size.width, size.height))[a][b][c][d]) \ No newline at end of file diff --git a/fuzzers/corpora/bugs/layout-infinite-lengths_01.typ b/fuzzers/corpora/bugs/layout-infinite-lengths_01.typ new file mode 100644 index 00000000..778f9e9a --- /dev/null +++ b/fuzzers/corpora/bugs/layout-infinite-lengths_01.typ @@ -0,0 +1,8 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// +// #set page(width: auto, height: auto) +// +// // Error: 17-66 cannot create grid with infinite height +// #layout(size => grid(rows: (size.width, size.height))[a][b][c][d]) \ No newline at end of file diff --git a/fuzzers/corpora/bugs/layout-infinite-lengths_02.typ b/fuzzers/corpora/bugs/layout-infinite-lengths_02.typ new file mode 100644 index 00000000..c57cc25e --- /dev/null +++ b/fuzzers/corpora/bugs/layout-infinite-lengths_02.typ @@ -0,0 +1,8 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// +// #set page(width: auto, height: auto) +// +// // Error: 17-41 cannot create line with infinite length +// #layout(size => line(length: size.width)) \ No newline at end of file diff --git a/fuzzers/corpora/bugs/layout-infinite-lengths_03.typ b/fuzzers/corpora/bugs/layout-infinite-lengths_03.typ new file mode 100644 index 00000000..2774697f --- /dev/null +++ b/fuzzers/corpora/bugs/layout-infinite-lengths_03.typ @@ -0,0 +1,8 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// +// #set page(width: auto, height: auto) +// +// // Error: 17-54 cannot create polygon with infinite size +// #layout(size => polygon((0pt,0pt), (0pt, size.width))) \ No newline at end of file diff --git a/fuzzers/corpora/bugs/mat-aug-color_00.typ b/fuzzers/corpora/bugs/mat-aug-color_00.typ new file mode 100644 index 00000000..cd7562af --- /dev/null +++ b/fuzzers/corpora/bugs/mat-aug-color_00.typ @@ -0,0 +1,12 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// https://github.com/typst/typst/issues/2268 +// The augment line should be of the same color as the text +#set text( + font: "New Computer Modern", + lang: "en", + fill: yellow, +) + +$mat(augment: #1, M, v) arrow.r.squiggly mat(augment: #1, R, b)$ diff --git a/fuzzers/corpora/bugs/math-eval_00.typ b/fuzzers/corpora/bugs/math-eval_00.typ new file mode 100644 index 00000000..16bff7f4 --- /dev/null +++ b/fuzzers/corpora/bugs/math-eval_00.typ @@ -0,0 +1,8 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// Evaluating a math expr should renders the same as an equation + +#eval(mode: "math", "f(a) = cases(a + b\, space space x >= 3,a + b\, space space x = 5)") + +$f(a) = cases(a + b\, space space x >= 3,a + b\, space space x = 5)$ diff --git a/fuzzers/corpora/bugs/math-number-spacing_00.typ b/fuzzers/corpora/bugs/math-number-spacing_00.typ new file mode 100644 index 00000000..e7374c95 --- /dev/null +++ b/fuzzers/corpora/bugs/math-number-spacing_00.typ @@ -0,0 +1,10 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +$ +10degree \ +10 degree \ +10.1degree \ +10.1 degree +$ diff --git a/fuzzers/corpora/bugs/math-shift_00.typ b/fuzzers/corpora/bugs/math-shift_00.typ new file mode 100644 index 00000000..415ea7b7 --- /dev/null +++ b/fuzzers/corpora/bugs/math-shift_00.typ @@ -0,0 +1,8 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// https://github.com/typst/typst/issues/2214 +// The math content should also be affected by the TextElem baseline. + +hello #text(baseline: -5pt)[123 #sym.WW\orld]\ +hello #text(baseline: -5pt)[$123 WW#text[or]$ld]\ diff --git a/fuzzers/corpora/bugs/math-text-break_00.typ b/fuzzers/corpora/bugs/math-text-break_00.typ new file mode 100644 index 00000000..49204dae --- /dev/null +++ b/fuzzers/corpora/bugs/math-text-break_00.typ @@ -0,0 +1,5 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +$ x := "a\nb\nc\nd\ne" $ diff --git a/fuzzers/corpora/bugs/new-cm-svg_00.typ b/fuzzers/corpora/bugs/new-cm-svg_00.typ new file mode 100644 index 00000000..43f0de6d --- /dev/null +++ b/fuzzers/corpora/bugs/new-cm-svg_00.typ @@ -0,0 +1,5 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +#set text(font: "New Computer Modern") +#image("/assets/files/diagram.svg") diff --git a/fuzzers/corpora/bugs/newline-mode_00.typ b/fuzzers/corpora/bugs/newline-mode_00.typ new file mode 100644 index 00000000..11cc7c15 --- /dev/null +++ b/fuzzers/corpora/bugs/newline-mode_00.typ @@ -0,0 +1,14 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#{ + "hello" + .clusters() + if false { + + } + else { + ("1", "2") + } +} diff --git a/fuzzers/corpora/bugs/newline-mode_01.typ b/fuzzers/corpora/bugs/newline-mode_01.typ new file mode 100644 index 00000000..7fc87c90 --- /dev/null +++ b/fuzzers/corpora/bugs/newline-mode_01.typ @@ -0,0 +1,13 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#"hello" + .codepoints() + +#if false { + +} +else { + ("1", "2") +} diff --git a/fuzzers/corpora/bugs/pagebreak-bibliography_00.typ b/fuzzers/corpora/bugs/pagebreak-bibliography_00.typ new file mode 100644 index 00000000..3b1ba0e2 --- /dev/null +++ b/fuzzers/corpora/bugs/pagebreak-bibliography_00.typ @@ -0,0 +1,6 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#pagebreak(weak: true) +#bibliography("/assets/files/works.bib") diff --git a/fuzzers/corpora/bugs/pagebreak-numbering_00.typ b/fuzzers/corpora/bugs/pagebreak-numbering_00.typ new file mode 100644 index 00000000..a50c5726 --- /dev/null +++ b/fuzzers/corpora/bugs/pagebreak-numbering_00.typ @@ -0,0 +1,16 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// https://github.com/typst/typst/issues/2095 +// The empty page 2 should not have a page number + +#set page(numbering: none) +This and next page should not be numbered + +#pagebreak(weak: true, to: "odd") + +#set page(numbering: "1") +#counter(page).update(1) + +This page should + diff --git a/fuzzers/corpora/bugs/pagebreak-set-style_00.typ b/fuzzers/corpora/bugs/pagebreak-set-style_00.typ new file mode 100644 index 00000000..3f4d65f1 --- /dev/null +++ b/fuzzers/corpora/bugs/pagebreak-set-style_00.typ @@ -0,0 +1,15 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// https://github.com/typst/typst/issues/2162 +// The styles should not be applied to the pagebreak empty page, +// it should only be applied after that. + +#pagebreak(to: "even") // We should now skip to page 2 + +Some text on page 2 + +#pagebreak(to: "even") // We should now skip to page 4 + +#set page(fill: orange) // This sets the color of the page starting from page 4 +Some text on page 4 diff --git a/fuzzers/corpora/bugs/place-spacing_00.typ b/fuzzers/corpora/bugs/place-spacing_00.typ new file mode 100644 index 00000000..27592f1e --- /dev/null +++ b/fuzzers/corpora/bugs/place-spacing_00.typ @@ -0,0 +1,9 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#show figure: set block(spacing: 4em) + +Paragraph before float. +#figure(rect(), placement: bottom) +Paragraph after float. diff --git a/fuzzers/corpora/bugs/place-spacing_01.typ b/fuzzers/corpora/bugs/place-spacing_01.typ new file mode 100644 index 00000000..7bb3369b --- /dev/null +++ b/fuzzers/corpora/bugs/place-spacing_01.typ @@ -0,0 +1,9 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#show place: set block(spacing: 4em) + +Paragraph before place. +#place(rect()) +Paragraph after place. diff --git a/fuzzers/corpora/bugs/raw-color-overwrite_00.typ b/fuzzers/corpora/bugs/raw-color-overwrite_00.typ new file mode 100644 index 00000000..46eed62f --- /dev/null +++ b/fuzzers/corpora/bugs/raw-color-overwrite_00.typ @@ -0,0 +1,14 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + + +#show raw: set text(fill: blue) + +`Hello, World!` + +```rs +fn main() { + println!("Hello, World!"); +} +``` \ No newline at end of file diff --git a/fuzzers/corpora/layout/align_03.typ b/fuzzers/corpora/layout/align_03.typ index fbfbc96e..989765d7 100644 --- a/fuzzers/corpora/layout/align_03.typ +++ b/fuzzers/corpora/layout/align_03.typ @@ -3,6 +3,6 @@ #show: test-page // Ref: false -#test(type(center), "alignment") -#test(type(horizon), "alignment") -#test(type(center + horizon), "2d alignment") +#test(type(center), alignment) +#test(type(horizon), alignment) +#test(type(center + horizon), alignment) diff --git a/fuzzers/corpora/layout/align_06.typ b/fuzzers/corpora/layout/align_06.typ new file mode 100644 index 00000000..6434f769 --- /dev/null +++ b/fuzzers/corpora/layout/align_06.typ @@ -0,0 +1,6 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// +// // Error: 8-30 cannot add a vertical and a 2D alignment +// #align(top + (bottom + right), [A]) \ No newline at end of file diff --git a/fuzzers/corpora/layout/cjk-latin-spacing_00.typ b/fuzzers/corpora/layout/cjk-latin-spacing_00.typ new file mode 100644 index 00000000..acc868ca --- /dev/null +++ b/fuzzers/corpora/layout/cjk-latin-spacing_00.typ @@ -0,0 +1,19 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// Test CJK-Latin spacing. + +#set page(width: 50pt + 10pt, margin: (x: 5pt)) +#set text(lang: "zh", font: "Noto Serif CJK SC", cjk-latin-spacing: auto) +#set par(justify: true) + +中文,中12文1中,文12中文 + +中文,中ab文a中,文ab中文 + +#set text(cjk-latin-spacing: none) + +中文,中12文1中,文12中文 + +中文,中ab文a中,文ab中文 + diff --git a/fuzzers/corpora/layout/cjk-punctuation-adjustment_00.typ b/fuzzers/corpora/layout/cjk-punctuation-adjustment_00.typ new file mode 100644 index 00000000..fb489e9e --- /dev/null +++ b/fuzzers/corpora/layout/cjk-punctuation-adjustment_00.typ @@ -0,0 +1,14 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +#set page(width: 15em) + +// In the following example, the space between 》! and ? should be squeezed. +// because zh-CN follows GB style +#set text(lang: "zh", region: "CN", font: "Noto Serif CJK SC") +原来,你也玩《原神》!? + +// However, in the following example, the space between 》! and ? should not be squeezed. +// because zh-TW does not follow GB style +#set text(lang: "zh", region: "TW", font: "Noto Serif CJK TC") +原來,你也玩《原神》! ? \ No newline at end of file diff --git a/fuzzers/corpora/layout/cjk-punctuation-adjustment_01.typ b/fuzzers/corpora/layout/cjk-punctuation-adjustment_01.typ new file mode 100644 index 00000000..ba2e426d --- /dev/null +++ b/fuzzers/corpora/layout/cjk-punctuation-adjustment_01.typ @@ -0,0 +1,9 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + + +#set text(lang: "zh", region: "CN", font: "Noto Serif CJK SC") +《书名〈章节〉》 // the space between 〉 and 》 should be squeezed + +〔茸毛〕:很细的毛 // the space between 〕 and : should be squeezed diff --git a/fuzzers/corpora/layout/cjk-punctuation-adjustment_02.typ b/fuzzers/corpora/layout/cjk-punctuation-adjustment_02.typ new file mode 100644 index 00000000..4296bdf3 --- /dev/null +++ b/fuzzers/corpora/layout/cjk-punctuation-adjustment_02.typ @@ -0,0 +1,23 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#set page(width: 21em) +#set text(lang: "zh", region: "CN", font: "Noto Serif CJK SC") + +// These examples contain extensive use of Chinese punctuation marks, +// from 《Which parentheses should be used when applying parentheses?》. +// link: https://archive.md/2bb1N + + +(〔中〕医、〔中〕药、技)系列评审 + +(长三角[长江三角洲])(GB/T 16159—2012《汉语拼音正词法基本规则》) + +【爱因斯坦(Albert Einstein)】物理学家 + +〔(2009)民申字第1622号〕 + +“江南海北长相忆,浅水深山独掩扉。”([唐]刘长卿《会赦后酬主簿所问》) + +参看1378页〖象形文字〗。(《现代汉语词典》修订本) diff --git a/fuzzers/corpora/layout/clip_00.typ b/fuzzers/corpora/layout/clip_00.typ index 8b718471..e0200a73 100644 --- a/fuzzers/corpora/layout/clip_00.typ +++ b/fuzzers/corpora/layout/clip_00.typ @@ -8,5 +8,5 @@ world 1 Space -Hello #box(width: 1em, height: 1em, clip: true)[#rect(width: 3em, height: 3em, fill: red)] +Hello #box(width: 1em, height: 1em, clip: true)[#rect(width: 3em, height: 3em, fill: red)] world 2 diff --git a/fuzzers/corpora/layout/clip_01.typ b/fuzzers/corpora/layout/clip_01.typ index 23533688..c9691955 100644 --- a/fuzzers/corpora/layout/clip_01.typ +++ b/fuzzers/corpora/layout/clip_01.typ @@ -4,7 +4,7 @@ // Test cliping text #block(width: 5em, height: 2em, clip: false, stroke: 1pt + black)[ - But, soft! what light through + But, soft! what light through ] #v(2em) diff --git a/fuzzers/corpora/layout/clip_02.typ b/fuzzers/corpora/layout/clip_02.typ index 64ba9881..f65d4237 100644 --- a/fuzzers/corpora/layout/clip_02.typ +++ b/fuzzers/corpora/layout/clip_02.typ @@ -2,7 +2,7 @@ #import "/contrib/templates/std-tests/preset.typ": * #show: test-page -// Test cliping svg glyphs +// Test clipping svg glyphs Emoji: #box(height: 0.5em, stroke: 1pt + black)[🐪, 🌋, 🏞] Emoji: #box(height: 0.5em, clip: true, stroke: 1pt + black)[🐪, 🌋, 🏞] diff --git a/fuzzers/corpora/layout/clip_04.typ b/fuzzers/corpora/layout/clip_04.typ new file mode 100644 index 00000000..7d10dd5d --- /dev/null +++ b/fuzzers/corpora/layout/clip_04.typ @@ -0,0 +1,16 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test clipping with `radius`. + +#set page(height: 60pt) + +#box( + radius: 5pt, + stroke: 2pt + black, + width: 20pt, + height: 20pt, + clip: true, + image("/assets/files/rhino.png", width: 30pt) +) diff --git a/fuzzers/corpora/layout/enum-align_03.typ b/fuzzers/corpora/layout/enum-align_03.typ index eb77fbfb..551c8bad 100644 --- a/fuzzers/corpora/layout/enum-align_03.typ +++ b/fuzzers/corpora/layout/enum-align_03.typ @@ -1,11 +1,10 @@ #import "/contrib/templates/std-tests/preset.typ": * #show: test-page -// -// // Test valid number align values (horizontal) -// #set enum(number-align: start) -// #set enum(number-align: end) -// #set enum(number-align: left) -// #set enum(number-align: right) -// // Error: 25-28 alignment must be horizontal -// #set enum(number-align: top) \ No newline at end of file + +// Test valid number align values (horizontal) +// Ref: false +#set enum(number-align: start) +#set enum(number-align: end) +#set enum(number-align: left) +#set enum(number-align: right) diff --git a/fuzzers/corpora/layout/enum-align_04.typ b/fuzzers/corpora/layout/enum-align_04.typ new file mode 100644 index 00000000..d44bb6f2 --- /dev/null +++ b/fuzzers/corpora/layout/enum-align_04.typ @@ -0,0 +1,6 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// +// // Error: 25-28 expected `start`, `left`, `center`, `right`, or `end`, found top +// #set enum(number-align: top) \ No newline at end of file diff --git a/fuzzers/corpora/layout/page-number-align_00.typ b/fuzzers/corpora/layout/page-number-align_00.typ new file mode 100644 index 00000000..a1cf4404 --- /dev/null +++ b/fuzzers/corpora/layout/page-number-align_00.typ @@ -0,0 +1,12 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#set page( + height: 100pt, + margin: 30pt, + numbering: "(1)", + number-align: top + right, +) + +#block(width: 100%, height: 100%, fill: aqua.lighten(50%)) diff --git a/fuzzers/corpora/layout/page-number-align_01.typ b/fuzzers/corpora/layout/page-number-align_01.typ new file mode 100644 index 00000000..1f32cda9 --- /dev/null +++ b/fuzzers/corpora/layout/page-number-align_01.typ @@ -0,0 +1,12 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#set page( + height: 100pt, + margin: 30pt, + numbering: "[1]", + number-align: bottom + left, +) + +#block(width: 100%, height: 100%, fill: aqua.lighten(50%)) diff --git a/fuzzers/corpora/layout/page-number-align_02.typ b/fuzzers/corpora/layout/page-number-align_02.typ new file mode 100644 index 00000000..014fcc25 --- /dev/null +++ b/fuzzers/corpora/layout/page-number-align_02.typ @@ -0,0 +1,6 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// +// // Error: 25-39 page number cannot be `horizon`-aligned +// #set page(number-align: left + horizon) \ No newline at end of file diff --git a/fuzzers/corpora/layout/pagebreak-weak_00.typ b/fuzzers/corpora/layout/pagebreak-weak_00.typ new file mode 100644 index 00000000..2fa0121c --- /dev/null +++ b/fuzzers/corpora/layout/pagebreak-weak_00.typ @@ -0,0 +1,11 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// After place +// Should result in three pages. +First +#pagebreak(weak: true) +#place(right)[placed A] +#pagebreak(weak: true) +Third diff --git a/fuzzers/corpora/layout/pagebreak-weak_01.typ b/fuzzers/corpora/layout/pagebreak-weak_01.typ new file mode 100644 index 00000000..857c8351 --- /dev/null +++ b/fuzzers/corpora/layout/pagebreak-weak_01.typ @@ -0,0 +1,12 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// After only ignorables & invisibles +// Should result in two pages. +First +#pagebreak(weak: true) +#counter(page).update(1) +#metadata("Some") +#pagebreak(weak: true) +Second diff --git a/fuzzers/corpora/layout/pagebreak-weak_02.typ b/fuzzers/corpora/layout/pagebreak-weak_02.typ new file mode 100644 index 00000000..3fdc7e05 --- /dev/null +++ b/fuzzers/corpora/layout/pagebreak-weak_02.typ @@ -0,0 +1,12 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// After only ignorables, but regular break +// Should result in three pages. +First +#pagebreak() +#counter(page).update(1) +#metadata("Some") +#pagebreak() +Third diff --git a/fuzzers/corpora/layout/par-justify_05.typ b/fuzzers/corpora/layout/par-justify_05.typ new file mode 100644 index 00000000..97e3c545 --- /dev/null +++ b/fuzzers/corpora/layout/par-justify_05.typ @@ -0,0 +1,11 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test that runts are avoided when it's not too costly to do so. +#set page(width: 124pt) +#set par(justify: true) +#for i in range(0, 20) { + "a b c " +} +#"d" diff --git a/fuzzers/corpora/layout/par-justify_06.typ b/fuzzers/corpora/layout/par-justify_06.typ new file mode 100644 index 00000000..b6bb3eaf --- /dev/null +++ b/fuzzers/corpora/layout/par-justify_06.typ @@ -0,0 +1,12 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test that justification cannot lead to a leading space +#set par(justify: true) +#set text(size: 12pt) +#set page(width: 45mm, height: auto) + +lorem ipsum 1234, lorem ipsum dolor sit amet + +#" leading whitespace should still be displayed" diff --git a/fuzzers/corpora/layout/par-justify_07.typ b/fuzzers/corpora/layout/par-justify_07.typ new file mode 100644 index 00000000..bf97e870 --- /dev/null +++ b/fuzzers/corpora/layout/par-justify_07.typ @@ -0,0 +1,15 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test that justification doesn't break code blocks + +#set par(justify: true) + +```cpp +int main() { + printf("Hello world\n"); + return 0; +} +``` + diff --git a/fuzzers/corpora/layout/place-float-auto_03.typ b/fuzzers/corpora/layout/place-float-auto_03.typ new file mode 100644 index 00000000..ed4264c6 --- /dev/null +++ b/fuzzers/corpora/layout/place-float-auto_03.typ @@ -0,0 +1,6 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// +// // Error: 2-36 floating placement must be `auto`, `top`, or `bottom` +// #place(horizon, float: true)[Hello] \ No newline at end of file diff --git a/fuzzers/corpora/layout/place-float-auto_04.typ b/fuzzers/corpora/layout/place-float-auto_04.typ new file mode 100644 index 00000000..7a4f1557 --- /dev/null +++ b/fuzzers/corpora/layout/place-float-auto_04.typ @@ -0,0 +1,6 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// +// // Error: 2-27 floating placement must be `auto`, `top`, or `bottom` +// #place(float: true)[Hello] \ No newline at end of file diff --git a/fuzzers/corpora/layout/place-float-auto_05.typ b/fuzzers/corpora/layout/place-float-auto_05.typ new file mode 100644 index 00000000..2799e10f --- /dev/null +++ b/fuzzers/corpora/layout/place-float-auto_05.typ @@ -0,0 +1,6 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// +// // Error: 2-34 floating placement must be `auto`, `top`, or `bottom` +// #place(right, float: true)[Hello] \ No newline at end of file diff --git a/fuzzers/corpora/layout/place-float-columns_00.typ b/fuzzers/corpora/layout/place-float-columns_00.typ new file mode 100644 index 00000000..1e4fc2f7 --- /dev/null +++ b/fuzzers/corpora/layout/place-float-columns_00.typ @@ -0,0 +1,20 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#set page(height: 200pt, width: 300pt) +#show: columns.with(2) + += Introduction +#figure( + placement: bottom, + caption: [A glacier], + image("/assets/files/glacier.jpg", width: 50%), +) +#lorem(45) +#figure( + placement: top, + caption: [A rectangle], + rect[Hello!], +) +#lorem(20) diff --git a/fuzzers/corpora/layout/spacing_02.typ b/fuzzers/corpora/layout/spacing_02.typ index dc6ed1b8..f26fdd77 100644 --- a/fuzzers/corpora/layout/spacing_02.typ +++ b/fuzzers/corpora/layout/spacing_02.typ @@ -2,7 +2,11 @@ #import "/contrib/templates/std-tests/preset.typ": * #show: test-page -// Test RTL spacing. -#set text(dir: rtl) -A #h(10pt) B \ -A #h(1fr) B +// Test spacing collapsing with different font sizes. +#grid(columns: 2)[ + #text(size: 12pt, block(below: 1em)[A]) + #text(size: 8pt, block(above: 1em)[B]) +][ + #text(size: 12pt, block(below: 1em)[A]) + #text(size: 8pt, block(above: 1.25em)[B]) +] diff --git a/fuzzers/corpora/layout/spacing_03.typ b/fuzzers/corpora/layout/spacing_03.typ index 8721efe2..dc6ed1b8 100644 --- a/fuzzers/corpora/layout/spacing_03.typ +++ b/fuzzers/corpora/layout/spacing_03.typ @@ -1,7 +1,8 @@ #import "/contrib/templates/std-tests/preset.typ": * #show: test-page -// -// // Missing spacing. -// // Error: 11-13 missing argument: amount -// Totally #h() ignored \ No newline at end of file + +// Test RTL spacing. +#set text(dir: rtl) +A #h(10pt) B \ +A #h(1fr) B diff --git a/fuzzers/corpora/layout/spacing_04.typ b/fuzzers/corpora/layout/spacing_04.typ new file mode 100644 index 00000000..8721efe2 --- /dev/null +++ b/fuzzers/corpora/layout/spacing_04.typ @@ -0,0 +1,7 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// +// // Missing spacing. +// // Error: 11-13 missing argument: amount +// Totally #h() ignored \ No newline at end of file diff --git a/fuzzers/corpora/layout/table_03.typ b/fuzzers/corpora/layout/table_03.typ index 49601fa8..8cb339c0 100644 --- a/fuzzers/corpora/layout/table_03.typ +++ b/fuzzers/corpora/layout/table_03.typ @@ -2,5 +2,33 @@ #import "/contrib/templates/std-tests/preset.typ": * #show: test-page -// Ref: false -#table() +// Test inset. +#table( + columns: 3, + inset: 10pt, + [A], [B], [C] +) + +#table( + columns: 3, + inset: (y: 10pt), + [A], [B], [C] +) + +#table( + columns: 3, + inset: (left: 20pt, rest: 10pt), + [A], [B], [C] +) + +#table( + columns: 2, + inset: ( + left: 20pt, + right: 5pt, + top: 10pt, + bottom: 3pt, + ), + [A], + [B], +) diff --git a/fuzzers/corpora/layout/table_04.typ b/fuzzers/corpora/layout/table_04.typ index b79cdf10..49601fa8 100644 --- a/fuzzers/corpora/layout/table_04.typ +++ b/fuzzers/corpora/layout/table_04.typ @@ -1,6 +1,6 @@ #import "/contrib/templates/std-tests/preset.typ": * #show: test-page -// -// // Error: 14-19 expected color, none, array, or function, found string -// #table(fill: "hey") \ No newline at end of file + +// Ref: false +#table() diff --git a/fuzzers/corpora/layout/table_05.typ b/fuzzers/corpora/layout/table_05.typ new file mode 100644 index 00000000..a0b20599 --- /dev/null +++ b/fuzzers/corpora/layout/table_05.typ @@ -0,0 +1,6 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// +// // Error: 14-19 expected color, gradient, none, array, or function, found string +// #table(fill: "hey") \ No newline at end of file diff --git a/fuzzers/corpora/math/attach-p2_03.typ b/fuzzers/corpora/math/attach-p2_03.typ index f2c65ea6..88d9e607 100644 --- a/fuzzers/corpora/math/attach-p2_03.typ +++ b/fuzzers/corpora/math/attach-p2_03.typ @@ -8,5 +8,5 @@ $ x_1 p_1 frak(p)_1 2_1 dot_1 lg_1 !_1 \\_1 ]_1 "ip"_1 op("iq")_1 \ x^1 b^1 frak(b)^1 2^1 dot^1 lg^1 !^1 \\^1 ]^1 "ib"^1 op("id")^1 \ x_1 y_1 "_"_1 x^1 l^1 "`"^1 attach(I,tl:1,bl:1,tr:1,br:1) - scripts(sum)_1^1 integral_1^1 |1/2|_1^1 \ + scripts(sum)_1^1 integral_1^1 abs(1/2)_1^1 \ x^1_1, "("b y")"^1_1 != (b y)^1_1, "[∫]"_1 [integral]_1 $ diff --git a/fuzzers/corpora/math/block-alignment_00.typ b/fuzzers/corpora/math/block-alignment_00.typ new file mode 100644 index 00000000..ce665e28 --- /dev/null +++ b/fuzzers/corpora/math/block-alignment_00.typ @@ -0,0 +1,17 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test unnumbered +#let eq(alignment) = { + show math.equation: set align(alignment) + $ a + b = c $ +} + +#eq(center) +#eq(left) +#eq(right) + +#set text(dir: rtl) +#eq(start) +#eq(end) diff --git a/fuzzers/corpora/math/block-alignment_01.typ b/fuzzers/corpora/math/block-alignment_01.typ new file mode 100644 index 00000000..82c17a69 --- /dev/null +++ b/fuzzers/corpora/math/block-alignment_01.typ @@ -0,0 +1,19 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test numbered +#let eq(alignment) = { + show math.equation: set align(alignment) + $ a + b = c $ +} + +#set math.equation(numbering: "(1)") + +#eq(center) +#eq(left) +#eq(right) + +#set text(dir: rtl) +#eq(start) +#eq(end) diff --git a/fuzzers/corpora/math/cancel_04.typ b/fuzzers/corpora/math/cancel_04.typ index 34d690a1..152dd24a 100644 --- a/fuzzers/corpora/math/cancel_04.typ +++ b/fuzzers/corpora/math/cancel_04.typ @@ -4,5 +4,5 @@ // Resized and styled #set page(width: 200pt, height: auto) -$a + cancel(x, length: #200%) - cancel(x, length: #50%, stroke: #{red + 1.1pt})$ -$ b + cancel(x, length: #150%) - cancel(a + b + c, length: #50%, stroke: #{blue + 1.2pt}) $ +$a + cancel(x, length: #200%) - cancel(x, length: #50%, stroke: #(red + 1.1pt))$ +$ b + cancel(x, length: #150%) - cancel(a + b + c, length: #50%, stroke: #(blue + 1.2pt)) $ diff --git a/fuzzers/corpora/math/cancel_05.typ b/fuzzers/corpora/math/cancel_05.typ index d5122d26..a94b1716 100644 --- a/fuzzers/corpora/math/cancel_05.typ +++ b/fuzzers/corpora/math/cancel_05.typ @@ -2,6 +2,5 @@ #import "/contrib/templates/std-tests/preset.typ": * #show: test-page -// Rotated -$x + cancel(y, rotation: #90deg) - cancel(z, rotation: #135deg)$ -$ e + cancel((j + e)/(f + e)) - cancel((j + e)/(f + e), rotation: #30deg) $ +// Specifying cancel line angle with an absolute angle +$cancel(x, angle: #0deg) + cancel(x, angle: #45deg) + cancel(x, angle: #90deg) + cancel(x, angle: #135deg)$ diff --git a/fuzzers/corpora/math/cancel_06.typ b/fuzzers/corpora/math/cancel_06.typ new file mode 100644 index 00000000..c2658fe3 --- /dev/null +++ b/fuzzers/corpora/math/cancel_06.typ @@ -0,0 +1,7 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Specifying cancel line angle with a function +$x + cancel(y, angle: #{angle => angle + 90deg}) - cancel(z, angle: #(angle => angle + 135deg))$ +$ e + cancel((j + e)/(f + e)) - cancel((j + e)/(f + e), angle: #(angle => angle + 30deg)) $ diff --git a/fuzzers/corpora/math/content_04.typ b/fuzzers/corpora/math/content_04.typ index 15eb79e5..5971066a 100644 --- a/fuzzers/corpora/math/content_04.typ +++ b/fuzzers/corpora/math/content_04.typ @@ -4,9 +4,9 @@ // Test boxes without a baseline act as if the baseline is at the base #{ - box(stroke: 0.2pt, $a #box(stroke: 0.2pt, $a$)$) - h(12pt) - box(stroke: 0.2pt, $a #box(stroke: 0.2pt, $g$)$) - h(12pt) - box(stroke: 0.2pt, $g #box(stroke: 0.2pt, $g$)$) + box(stroke: 0.2pt, $a #box(stroke: 0.2pt, $a$)$) + h(12pt) + box(stroke: 0.2pt, $a #box(stroke: 0.2pt, $g$)$) + h(12pt) + box(stroke: 0.2pt, $g #box(stroke: 0.2pt, $g$)$) } diff --git a/fuzzers/corpora/math/delimited_00.typ b/fuzzers/corpora/math/delimited_00.typ index 7b903f41..81bd7e8f 100644 --- a/fuzzers/corpora/math/delimited_00.typ +++ b/fuzzers/corpora/math/delimited_00.typ @@ -3,5 +3,5 @@ #show: test-page // Test automatic matching. -$ (a) + {b/2} + |a|/2 + (b) $ -$f(x/2) < zeta(c^2 + |a + b/2|)$ +$ (a) + {b/2} + abs(a)/2 + (b) $ +$f(x/2) < zeta(c^2 + abs(a + b/2))$ diff --git a/fuzzers/corpora/math/delimited_03.typ b/fuzzers/corpora/math/delimited_03.typ index a15e5a86..b84a5141 100644 --- a/fuzzers/corpora/math/delimited_03.typ +++ b/fuzzers/corpora/math/delimited_03.typ @@ -4,4 +4,4 @@ // Test fence confusion. $ |x + |y| + z/a| \ - |x + lr(|y|) + z/a| $ + lr(|x + |y| + z/a|) $ diff --git a/fuzzers/corpora/math/frac_04.typ b/fuzzers/corpora/math/frac_04.typ index f7635166..5ca116a0 100644 --- a/fuzzers/corpora/math/frac_04.typ +++ b/fuzzers/corpora/math/frac_04.typ @@ -1,6 +1,6 @@ #import "/contrib/templates/std-tests/preset.typ": * #show: test-page -// -// // Error: 8-13 missing argument: lower -// $ binom(x^2) $ \ No newline at end of file + +// Test multinomial coefficients. +$ binom(n, k_1, k_2, k_3) $ diff --git a/fuzzers/corpora/math/frac_05.typ b/fuzzers/corpora/math/frac_05.typ index 4b6f9b45..f7635166 100644 --- a/fuzzers/corpora/math/frac_05.typ +++ b/fuzzers/corpora/math/frac_05.typ @@ -1,7 +1,6 @@ #import "/contrib/templates/std-tests/preset.typ": * #show: test-page - -// Test dif. -$ (dif y)/(dif x), dif/x, x/dif, dif/dif \ - frac(dif y, dif x), frac(dif, x), frac(x, dif), frac(dif, dif) $ +// +// // Error: 8-13 missing argument: lower +// $ binom(x^2) $ \ No newline at end of file diff --git a/fuzzers/corpora/math/frac_06.typ b/fuzzers/corpora/math/frac_06.typ index 75c4ed68..4b6f9b45 100644 --- a/fuzzers/corpora/math/frac_06.typ +++ b/fuzzers/corpora/math/frac_06.typ @@ -2,5 +2,6 @@ #import "/contrib/templates/std-tests/preset.typ": * #show: test-page -// Test associativity. -$ 1/2/3 = (1/2)/3 = 1/(2/3) $ +// Test dif. +$ (dif y)/(dif x), dif/x, x/dif, dif/dif \ + frac(dif y, dif x), frac(dif, x), frac(x, dif), frac(dif, dif) $ diff --git a/fuzzers/corpora/math/frac_07.typ b/fuzzers/corpora/math/frac_07.typ index 5dafa22e..75c4ed68 100644 --- a/fuzzers/corpora/math/frac_07.typ +++ b/fuzzers/corpora/math/frac_07.typ @@ -2,10 +2,5 @@ #import "/contrib/templates/std-tests/preset.typ": * #show: test-page -// Test precedence. -$ a_1/b_2, 1/f(x), zeta(x)/2, "foo"[|x|]/2 \ - 1.2/3.7, 2.3^3.4 \ - 🏳️‍🌈[x]/2, f [x]/2, phi [x]/2, 🏳️‍🌈 [x]/2 \ - +[x]/2, 1(x)/2, 2[x]/2 \ - (a)b/2, b(a)[b]/2 \ - n!/2, 5!/2, n !/2, 1/n!, 1/5! $ +// Test associativity. +$ 1/2/3 = (1/2)/3 = 1/(2/3) $ diff --git a/fuzzers/corpora/math/frac_08.typ b/fuzzers/corpora/math/frac_08.typ new file mode 100644 index 00000000..5dafa22e --- /dev/null +++ b/fuzzers/corpora/math/frac_08.typ @@ -0,0 +1,11 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test precedence. +$ a_1/b_2, 1/f(x), zeta(x)/2, "foo"[|x|]/2 \ + 1.2/3.7, 2.3^3.4 \ + 🏳️‍🌈[x]/2, f [x]/2, phi [x]/2, 🏳️‍🌈 [x]/2 \ + +[x]/2, 1(x)/2, 2[x]/2 \ + (a)b/2, b(a)[b]/2 \ + n!/2, 5!/2, n !/2, 1/n!, 1/5! $ diff --git a/fuzzers/corpora/math/matrix-gaps_00.typ b/fuzzers/corpora/math/matrix-gaps_00.typ new file mode 100644 index 00000000..06f92b5f --- /dev/null +++ b/fuzzers/corpora/math/matrix-gaps_00.typ @@ -0,0 +1,6 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#set math.mat(row-gap: 1em, column-gap: 2em) +$ mat(1, 2; 3, 4) $ diff --git a/fuzzers/corpora/math/matrix-gaps_01.typ b/fuzzers/corpora/math/matrix-gaps_01.typ new file mode 100644 index 00000000..e1025012 --- /dev/null +++ b/fuzzers/corpora/math/matrix-gaps_01.typ @@ -0,0 +1,6 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#set math.mat(gap: 1em) +$ mat(1, 2; 3, 4) $ diff --git a/fuzzers/corpora/math/matrix-gaps_02.typ b/fuzzers/corpora/math/matrix-gaps_02.typ new file mode 100644 index 00000000..a7df9176 --- /dev/null +++ b/fuzzers/corpora/math/matrix-gaps_02.typ @@ -0,0 +1,6 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#set math.cases(gap: 1em) +$ x = cases(1, 2) $ diff --git a/fuzzers/corpora/math/matrix-gaps_03.typ b/fuzzers/corpora/math/matrix-gaps_03.typ new file mode 100644 index 00000000..e291c82f --- /dev/null +++ b/fuzzers/corpora/math/matrix-gaps_03.typ @@ -0,0 +1,6 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#set math.vec(gap: 1em) +$ vec(1, 2) $ diff --git a/fuzzers/corpora/math/matrix_07.typ b/fuzzers/corpora/math/matrix_07.typ new file mode 100644 index 00000000..ea6a3564 --- /dev/null +++ b/fuzzers/corpora/math/matrix_07.typ @@ -0,0 +1,19 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + + +// Test matrix line drawing (augmentation). +#grid( + columns: 2, + gutter: 10pt, + + $ mat(10, 2, 3, 4; 5, 6, 7, 8; augment: #3) $, + $ mat(10, 2, 3, 4; 5, 6, 7, 8; augment: #(-1)) $, + $ mat(100, 2, 3; 4, 5, 6; 7, 8, 9; augment: #(hline: 2)) $, + $ mat(100, 2, 3; 4, 5, 6; 7, 8, 9; augment: #(hline: -1)) $, + $ mat(100, 2, 3; 4, 5, 6; 7, 8, 9; augment: #(hline: 1, vline: 1)) $, + $ mat(100, 2, 3; 4, 5, 6; 7, 8, 9; augment: #(hline: -2, vline: -2)) $, + $ mat(100, 2, 3; 4, 5, 6; 7, 8, 9; augment: #(vline: 2, stroke: 1pt + blue)) $, + $ mat(100, 2, 3; 4, 5, 6; 7, 8, 9; augment: #(vline: -1, stroke: 1pt + blue)) $, +) diff --git a/fuzzers/corpora/math/matrix_08.typ b/fuzzers/corpora/math/matrix_08.typ new file mode 100644 index 00000000..e0a282b6 --- /dev/null +++ b/fuzzers/corpora/math/matrix_08.typ @@ -0,0 +1,13 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + + +// Test using matrix line drawing with a set rule. +#set math.mat(augment: (hline: 2, vline: 1, stroke: 2pt + green)) +$ mat(1, 0, 0, 0; 0, 1, 0, 0; 0, 0, 1, 1) $ + +#set math.mat(augment: 2) +$ mat(1, 0, 0, 0; 0, 1, 0, 0; 0, 0, 1, 1) $ + +#set math.mat(augment: none) diff --git a/fuzzers/corpora/math/matrix_09.typ b/fuzzers/corpora/math/matrix_09.typ new file mode 100644 index 00000000..e5d26330 --- /dev/null +++ b/fuzzers/corpora/math/matrix_09.typ @@ -0,0 +1,6 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// +// // Error: 3-37 cannot draw a vertical line after column 3 of a matrix with 3 columns +// $ mat(1, 0, 0; 0, 1, 1; augment: #3) $, \ No newline at end of file diff --git a/fuzzers/corpora/math/opticalsize_07.typ b/fuzzers/corpora/math/opticalsize_07.typ new file mode 100644 index 00000000..26ae23ab --- /dev/null +++ b/fuzzers/corpora/math/opticalsize_07.typ @@ -0,0 +1,6 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test attaching primes only +$a' = a^', a_', a_'''^''^'$ \ No newline at end of file diff --git a/fuzzers/corpora/math/prime_00.typ b/fuzzers/corpora/math/prime_00.typ new file mode 100644 index 00000000..e3f00d36 --- /dev/null +++ b/fuzzers/corpora/math/prime_00.typ @@ -0,0 +1,12 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// Test prime symbols after code mode. +#let g = $f$ +#let gg = $f$ + +$ + #(g)' #g' #g ' \ + #g''''''''''''''''' \ + gg' +$ diff --git a/fuzzers/corpora/math/spacing_00.typ b/fuzzers/corpora/math/spacing_00.typ index 829151c4..bc29edcc 100644 --- a/fuzzers/corpora/math/spacing_00.typ +++ b/fuzzers/corpora/math/spacing_00.typ @@ -5,7 +5,7 @@ // Test spacing cases. $ä, +, c, (, )$ \ $=), (+), {times}$ -$⟧<⟦, |-|, [=$ \ +$⟧<⟦, abs(-), [=$ \ $a=b, a==b$ \ $-a, +a$ \ $a not b$ \ diff --git a/fuzzers/corpora/math/spacing_04.typ b/fuzzers/corpora/math/spacing_04.typ index 915a61e8..98c0938c 100644 --- a/fuzzers/corpora/math/spacing_04.typ +++ b/fuzzers/corpora/math/spacing_04.typ @@ -8,4 +8,4 @@ $a equiv b + c - d => e log 5 op("ln") 6$ \ $a cancel(equiv) b overline(+) c arrow(-) d hat(=>) e cancel(log) 5 dot(op("ln")) 6$ \ $a overbrace(equiv) b underline(+) c grave(-) d underbracket(=>) e circle(log) 5 caron(op("ln")) 6$ \ \ -$a attach(equiv, tl: a, tr: b) b attach(limits(+), t: a, b: b) c tilde(-) d breve(=>) e attach(limits(log), t: a, b: b) 5 attach(op("ln"), tr: a, bl: b) 6$ +$a attach(equiv, tl: a, tr: b) b attach(limits(+), t: a, b: b) c tilde(-) d breve(=>) e attach(limits(log), t: a, b: b) 5 attach(op("ln"), tr: a, bl: b) 6$ \ No newline at end of file diff --git a/fuzzers/corpora/math/spacing_05.typ b/fuzzers/corpora/math/spacing_05.typ new file mode 100644 index 00000000..cb863154 --- /dev/null +++ b/fuzzers/corpora/math/spacing_05.typ @@ -0,0 +1,10 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test weak spacing +$integral f(x) dif x$, +// Not weak +$integral f(x) thin dif x$, +// Both are weak, collide +$integral f(x) #h(0.166em, weak: true)dif x$ diff --git a/fuzzers/corpora/meta/bibliography_01.typ b/fuzzers/corpora/meta/bibliography_01.typ index 78304041..5a8482f6 100644 --- a/fuzzers/corpora/meta/bibliography_01.typ +++ b/fuzzers/corpora/meta/bibliography_01.typ @@ -4,5 +4,5 @@ #set page(width: 200pt) = Details -See also #cite("arrgh", "distress", [p. 22]), @arrgh[p. 4], and @distress[p. 5]. +See also #cite("arrgh", "distress", supplement: [p. 22]), @arrgh[p. 4], and @distress[p. 5]. #bibliography("/assets/files/works.bib") diff --git a/fuzzers/corpora/meta/document_01.typ b/fuzzers/corpora/meta/document_01.typ index 4b2933c3..6ecdcd96 100644 --- a/fuzzers/corpora/meta/document_01.typ +++ b/fuzzers/corpora/meta/document_01.typ @@ -4,4 +4,4 @@ // This, too. // Ref: false -#set document(author: ("A", "B")) +#set document(author: ("A", "B"), date: datetime.today()) diff --git a/fuzzers/corpora/meta/document_02.typ b/fuzzers/corpora/meta/document_02.typ index c6cbaad7..96e00c31 100644 --- a/fuzzers/corpora/meta/document_02.typ +++ b/fuzzers/corpora/meta/document_02.typ @@ -2,7 +2,5 @@ #import "/contrib/templates/std-tests/preset.typ": * #show: test-page // -// // This, too. -// // Error: 23-29 expected string, found integer -// #set document(author: (123,)) -// What's up? \ No newline at end of file +// // Error: 21-28 expected datetime or none, found string +// #set document(date: "today") \ No newline at end of file diff --git a/fuzzers/corpora/meta/document_03.typ b/fuzzers/corpora/meta/document_03.typ index 08b4871f..c6cbaad7 100644 --- a/fuzzers/corpora/meta/document_03.typ +++ b/fuzzers/corpora/meta/document_03.typ @@ -2,7 +2,7 @@ #import "/contrib/templates/std-tests/preset.typ": * #show: test-page // -// Hello -// -// // Error: 2-30 document set rules must appear before any content -// #set document(title: "Hello") \ No newline at end of file +// // This, too. +// // Error: 23-29 expected string, found integer +// #set document(author: (123,)) +// What's up? \ No newline at end of file diff --git a/fuzzers/corpora/meta/document_04.typ b/fuzzers/corpora/meta/document_04.typ index 653418c5..08b4871f 100644 --- a/fuzzers/corpora/meta/document_04.typ +++ b/fuzzers/corpora/meta/document_04.typ @@ -2,5 +2,7 @@ #import "/contrib/templates/std-tests/preset.typ": * #show: test-page // -// // Error: 10-12 can only be used in set rules -// #document() \ No newline at end of file +// Hello +// +// // Error: 2-30 document set rules must appear before any content +// #set document(title: "Hello") \ No newline at end of file diff --git a/fuzzers/corpora/meta/document_05.typ b/fuzzers/corpora/meta/document_05.typ index 615bddc5..653418c5 100644 --- a/fuzzers/corpora/meta/document_05.typ +++ b/fuzzers/corpora/meta/document_05.typ @@ -2,7 +2,5 @@ #import "/contrib/templates/std-tests/preset.typ": * #show: test-page // -// #box[ -// // Error: 4-32 document set rules are not allowed inside of containers -// #set document(title: "Hello") -// ] \ No newline at end of file +// // Error: 10-12 can only be used in set rules +// #document() \ No newline at end of file diff --git a/fuzzers/corpora/meta/document_06.typ b/fuzzers/corpora/meta/document_06.typ index bb723336..615bddc5 100644 --- a/fuzzers/corpora/meta/document_06.typ +++ b/fuzzers/corpora/meta/document_06.typ @@ -3,6 +3,6 @@ #show: test-page // // #box[ -// // Error: 4-18 page configuration is not allowed inside of containers -// #set page("a4") +// // Error: 4-32 document set rules are not allowed inside of containers +// #set document(title: "Hello") // ] \ No newline at end of file diff --git a/fuzzers/corpora/meta/document_07.typ b/fuzzers/corpora/meta/document_07.typ index cfc296f0..bb723336 100644 --- a/fuzzers/corpora/meta/document_07.typ +++ b/fuzzers/corpora/meta/document_07.typ @@ -3,6 +3,6 @@ #show: test-page // // #box[ -// // Error: 4-15 pagebreaks are not allowed inside of containers -// #pagebreak() +// // Error: 4-18 page configuration is not allowed inside of containers +// #set page("a4") // ] \ No newline at end of file diff --git a/fuzzers/corpora/meta/document_08.typ b/fuzzers/corpora/meta/document_08.typ new file mode 100644 index 00000000..cfc296f0 --- /dev/null +++ b/fuzzers/corpora/meta/document_08.typ @@ -0,0 +1,8 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// +// #box[ +// // Error: 4-15 pagebreaks are not allowed inside of containers +// #pagebreak() +// ] \ No newline at end of file diff --git a/fuzzers/corpora/meta/figure-caption_00.typ b/fuzzers/corpora/meta/figure-caption_00.typ new file mode 100644 index 00000000..156921ab --- /dev/null +++ b/fuzzers/corpora/meta/figure-caption_00.typ @@ -0,0 +1,11 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test figure.caption element +#show figure.caption: emph + +#figure( + [Not italicized], + caption: [Italicized], +) diff --git a/fuzzers/corpora/meta/figure-caption_01.typ b/fuzzers/corpora/meta/figure-caption_01.typ new file mode 100644 index 00000000..6248ac99 --- /dev/null +++ b/fuzzers/corpora/meta/figure-caption_01.typ @@ -0,0 +1,16 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test figure.caption element for specific figure kinds +#show figure.caption.where(kind: table): underline + +#figure( + [Not a table], + caption: [Not underlined], +) + +#figure( + table[A table], + caption: [Underlined], +) diff --git a/fuzzers/corpora/meta/figure-caption_02.typ b/fuzzers/corpora/meta/figure-caption_02.typ new file mode 100644 index 00000000..88887f7b --- /dev/null +++ b/fuzzers/corpora/meta/figure-caption_02.typ @@ -0,0 +1,34 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test creating custom figure and custom caption + +#let gap = 0.7em +#show figure.where(kind: "custom"): it => rect(inset: gap, { + align(center, it.body) + v(gap, weak: true) + line(length: 100%) + v(gap, weak: true) + align(center, it.caption) +}) + +#figure( + [A figure], + kind: "custom", + caption: [Hi], + supplement: [A], +) + +#show figure.caption: it => emph[ + #it.body + (#it.supplement + #it.counter.display(it.numbering)) +] + +#figure( + [Another figure], + kind: "custom", + caption: [Hi], + supplement: [B], +) diff --git a/fuzzers/corpora/meta/figure_02.typ b/fuzzers/corpora/meta/figure_02.typ index 1cbcfefc..e8c40e11 100644 --- a/fuzzers/corpora/meta/figure_02.typ +++ b/fuzzers/corpora/meta/figure_02.typ @@ -7,7 +7,7 @@ #show figure.where(kind: "theorem"): it => { let name = none if not it.caption == none { - name = [ #emph(it.caption)] + name = [ #emph(it.caption.body)] } else { name = [] } diff --git a/fuzzers/corpora/meta/figure_04.typ b/fuzzers/corpora/meta/figure_04.typ new file mode 100644 index 00000000..461ab672 --- /dev/null +++ b/fuzzers/corpora/meta/figure_04.typ @@ -0,0 +1,11 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test custom separator for figure caption +#set figure.caption(separator: [ --- ]) + +#figure( + table(columns: 2)[a][b], + caption: [The table with custom separator.], +) diff --git a/fuzzers/corpora/meta/figure_05.typ b/fuzzers/corpora/meta/figure_05.typ new file mode 100644 index 00000000..613dc919 --- /dev/null +++ b/fuzzers/corpora/meta/figure_05.typ @@ -0,0 +1,24 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test localized default separator +#set text(lang: "fr", region: "CH") + +#figure( + circle(), + caption: [Un cercle.], +) +#set text(lang: "es") + +#figure( + polygon.regular(size: 1cm, vertices: 3), + caption: [Un triángulo.], +) + +#set text(lang: "fr", region: "CA") + +#figure( + square(), + caption: [Un carré.], +) diff --git a/fuzzers/corpora/meta/numbering_03.typ b/fuzzers/corpora/meta/numbering_03.typ index 30714d1f..17a84b67 100644 --- a/fuzzers/corpora/meta/numbering_03.typ +++ b/fuzzers/corpora/meta/numbering_03.typ @@ -3,7 +3,7 @@ #show: test-page #set text(lang: "zh", font: ("Linux Libertine", "Noto Serif CJK SC")) -#for i in range(9,21, step: 2){ +#for i in range(9, 21, step: 2){ numbering("一", i) [ and ] numbering("壹", i) diff --git a/fuzzers/corpora/meta/outline-entry_02.typ b/fuzzers/corpora/meta/outline-entry_02.typ index 5c8c3c7e..57d284a9 100644 --- a/fuzzers/corpora/meta/outline-entry_02.typ +++ b/fuzzers/corpora/meta/outline-entry_02.typ @@ -4,5 +4,5 @@ // // // Error: 2-23 cannot outline cite // #outline(target: cite) -// #cite("arrgh", "distress", [p. 22]) +// #cite("arrgh", "distress", supplement: [p. 22]) // #bibliography("/assets/files/works.bib") \ No newline at end of file diff --git a/fuzzers/corpora/meta/page-label_00.typ b/fuzzers/corpora/meta/page-label_00.typ new file mode 100644 index 00000000..a6d1b5e5 --- /dev/null +++ b/fuzzers/corpora/meta/page-label_00.typ @@ -0,0 +1,50 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +#set page(margin: (bottom: 20pt, rest: 10pt)) +#let filler = lorem(20) + +// (i) - (ii). No style opt. because of suffix. +#set page(numbering: "(i)") +#filler +#pagebreak() +#filler + +// 3 - 4. Style opt. Page Label should use /D style. +#set page(numbering: "1") +#filler +#pagebreak() +#filler + +// I - IV. Style opt. Page Label should use /R style and start at 1 again. +#set page(numbering: "I / I") +#counter(page).update(1) +#filler +#pagebreak() +#filler +#pagebreak() +#filler +#pagebreak() +#filler + +// Pre: ほ, Pre: ろ, Pre: は, Pre: に. No style opt. Uses prefix field entirely. +// Counter update without numbering change. +#set page(numbering: "Pre: い") +#filler +#pagebreak() +#filler +#counter(page).update(2) +#filler +#pagebreak() +#filler +#pagebreak() +#filler + +// aa & ba. Style opt only for values <= 26. Page Label uses lower alphabet style. +// Repeats letter each 26 pages or uses numbering directly as prefix. +#set page(numbering: "a") +#counter(page).update(27) +#filler +#pagebreak() +#counter(page).update(53) +#filler diff --git a/fuzzers/corpora/meta/query-figure_00.typ b/fuzzers/corpora/meta/query-figure_00.typ index 864a3b94..0aa04cd7 100644 --- a/fuzzers/corpora/meta/query-figure_00.typ +++ b/fuzzers/corpora/meta/query-figure_00.typ @@ -18,7 +18,7 @@ Figure #numbering(it.numbering, ..counter(figure).at(it.location())): - #it.caption + #it.caption.body #box(width: 1fr, repeat[.]) #counter(page).at(it.location()).first() \ ] diff --git a/fuzzers/corpora/meta/state_03.typ b/fuzzers/corpora/meta/state_03.typ index 79396e71..3f178b10 100644 --- a/fuzzers/corpora/meta/state_03.typ +++ b/fuzzers/corpora/meta/state_03.typ @@ -3,10 +3,8 @@ #show: test-page // Make sure that a warning is produced if the layout fails to converge. -// Warning: -3:1-6:1 layout did not converge within 5 attempts -// Hint: -3:1-6:1 check if any states or queries are updating themselves -#let s = state("x", 1) -#locate(loc => { - s.update(s.final(loc) + 1) -}) +// Warning: layout did not converge within 5 attempts +// Hint: check if any states or queries are updating themselves +#let s = state("s", 1) +#locate(loc => s.update(s.final(loc) + 1)) #s.display() diff --git a/fuzzers/corpora/text/deco_01.typ b/fuzzers/corpora/text/deco_01.typ index 9d106b47..6a01d7d4 100644 --- a/fuzzers/corpora/text/deco_01.typ +++ b/fuzzers/corpora/text/deco_01.typ @@ -3,9 +3,8 @@ #show: test-page #let redact = strike.with(stroke: 10pt, extent: 0.05em) -#let highlight = strike.with(stroke: 10pt + rgb("abcdef88"), extent: 0.05em) +#let highlight-custom = strike.with(stroke: 10pt + rgb("abcdef88"), extent: 0.05em) // Abuse thickness and transparency for redacting and highlighting stuff. Sometimes, we work #redact[in secret]. -There might be #highlight[redacted] things. - underline() +There might be #highlight-custom[redacted] things. diff --git a/fuzzers/corpora/text/deco_03.typ b/fuzzers/corpora/text/deco_03.typ new file mode 100644 index 00000000..1cb36fb2 --- /dev/null +++ b/fuzzers/corpora/text/deco_03.typ @@ -0,0 +1,8 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test highlight. +This is the built-in #highlight[highlight with default color]. +We can also specify a customized value +#highlight(fill: green.lighten(80%))[to highlight]. diff --git a/fuzzers/corpora/text/deco_04.typ b/fuzzers/corpora/text/deco_04.typ new file mode 100644 index 00000000..077c2373 --- /dev/null +++ b/fuzzers/corpora/text/deco_04.typ @@ -0,0 +1,9 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test default highlight bounds. +#highlight[ace], +#highlight[base], +#highlight[super], +#highlight[phone #sym.integral] diff --git a/fuzzers/corpora/text/deco_05.typ b/fuzzers/corpora/text/deco_05.typ new file mode 100644 index 00000000..62188702 --- /dev/null +++ b/fuzzers/corpora/text/deco_05.typ @@ -0,0 +1,10 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test a tighter highlight. +#set highlight(top-edge: "x-height", bottom-edge: "baseline") +#highlight[ace], +#highlight[base], +#highlight[super], +#highlight[phone #sym.integral] diff --git a/fuzzers/corpora/text/deco_06.typ b/fuzzers/corpora/text/deco_06.typ new file mode 100644 index 00000000..3447b238 --- /dev/null +++ b/fuzzers/corpora/text/deco_06.typ @@ -0,0 +1,8 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test a bounds highlight. +#set highlight(top-edge: "bounds", bottom-edge: "bounds") +#highlight[abc] +#highlight[abc #sym.integral] diff --git a/fuzzers/corpora/text/deco_07.typ b/fuzzers/corpora/text/deco_07.typ new file mode 100644 index 00000000..8f5564c3 --- /dev/null +++ b/fuzzers/corpora/text/deco_07.typ @@ -0,0 +1,7 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test underline background +#set underline(background: true, stroke: (thickness: 0.5em, paint: red, cap: "round")) +#underline[This is in the background] diff --git a/fuzzers/corpora/text/deco_08.typ b/fuzzers/corpora/text/deco_08.typ new file mode 100644 index 00000000..75214bf5 --- /dev/null +++ b/fuzzers/corpora/text/deco_08.typ @@ -0,0 +1,8 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test overline background +#set overline(background: true, stroke: (thickness: 0.5em, paint: red, cap: "round")) +#overline[This is in the background] + diff --git a/fuzzers/corpora/text/deco_09.typ b/fuzzers/corpora/text/deco_09.typ new file mode 100644 index 00000000..57871fdd --- /dev/null +++ b/fuzzers/corpora/text/deco_09.typ @@ -0,0 +1,7 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test strike background +#set strike(background: true, stroke: 5pt + red) +#strike[This is in the background] diff --git a/fuzzers/corpora/text/numbers_00.typ b/fuzzers/corpora/text/numbers_00.typ new file mode 100644 index 00000000..6c5dd767 --- /dev/null +++ b/fuzzers/corpora/text/numbers_00.typ @@ -0,0 +1,33 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test numbers in text mode. +12 \ +12.0 \ +3.14 \ +1234567890 \ +0123456789 \ +0 \ +0.0 \ ++0 \ ++0.0 \ +-0 \ +-0.0 \ +-1 \ +-3.14 \ +-9876543210 \ +-0987654321 \ +٣٫١٤ \ +-٣٫١٤ \ +-¾ \ +#text(fractions: true)[-3/2] \ +2022 - 2023 \ +2022 -- 2023 \ +2022--2023 \ +2022-2023 \ +٢٠٢٢ - ٢٠٢٣ \ +٢٠٢٢ -- ٢٠٢٣ \ +٢٠٢٢--٢٠٢٣ \ +٢٠٢٢-٢٠٢٣ \ +-500 -- -400 diff --git a/fuzzers/corpora/text/numbers_01.typ b/fuzzers/corpora/text/numbers_01.typ new file mode 100644 index 00000000..882e98e3 --- /dev/null +++ b/fuzzers/corpora/text/numbers_01.typ @@ -0,0 +1,14 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test integers. +#12 \ +#1234567890 \ +#0123456789 \ +#0 \ +#(-0) \ +#(-1) \ +#(-9876543210) \ +#(-0987654321) \ +#(4 - 8) diff --git a/fuzzers/corpora/text/numbers_02.typ b/fuzzers/corpora/text/numbers_02.typ new file mode 100644 index 00000000..6a4579e6 --- /dev/null +++ b/fuzzers/corpora/text/numbers_02.typ @@ -0,0 +1,16 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test floats. +#12.0 \ +#3.14 \ +#1234567890.0 \ +#0123456789.0 \ +#0.0 \ +#(-0.0) \ +#(-1.0) \ +#(-9876543210.0) \ +#(-0987654321.0) \ +#(-3.14) \ +#(4.0 - 8.0) diff --git a/fuzzers/corpora/text/numbers_03.typ b/fuzzers/corpora/text/numbers_03.typ new file mode 100644 index 00000000..4db8c6a5 --- /dev/null +++ b/fuzzers/corpora/text/numbers_03.typ @@ -0,0 +1,14 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test the `str` function with integers. +#str(12) \ +#str(1234567890) \ +#str(0123456789) \ +#str(0) \ +#str(-0) \ +#str(-1) \ +#str(-9876543210) \ +#str(-0987654321) \ +#str(4 - 8) diff --git a/fuzzers/corpora/text/numbers_04.typ b/fuzzers/corpora/text/numbers_04.typ new file mode 100644 index 00000000..290ec230 --- /dev/null +++ b/fuzzers/corpora/text/numbers_04.typ @@ -0,0 +1,16 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test the `str` function with floats. +#str(12.0) \ +#str(3.14) \ +#str(1234567890.0) \ +#str(0123456789.0) \ +#str(0.0) \ +#str(-0.0) \ +#str(-1.0) \ +#str(-9876543210.0) \ +#str(-0987654321.0) \ +#str(-3.14) \ +#str(4.0 - 8.0) diff --git a/fuzzers/corpora/text/numbers_05.typ b/fuzzers/corpora/text/numbers_05.typ new file mode 100644 index 00000000..14301384 --- /dev/null +++ b/fuzzers/corpora/text/numbers_05.typ @@ -0,0 +1,14 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test the `repr` function with integers. +#repr(12) \ +#repr(1234567890) \ +#repr(0123456789) \ +#repr(0) \ +#repr(-0) \ +#repr(-1) \ +#repr(-9876543210) \ +#repr(-0987654321) \ +#repr(4 - 8) diff --git a/fuzzers/corpora/text/numbers_06.typ b/fuzzers/corpora/text/numbers_06.typ new file mode 100644 index 00000000..b5251639 --- /dev/null +++ b/fuzzers/corpora/text/numbers_06.typ @@ -0,0 +1,16 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test the `repr` function with floats. +#repr(12.0) \ +#repr(3.14) \ +#repr(1234567890.0) \ +#repr(0123456789.0) \ +#repr(0.0) \ +#repr(-0.0) \ +#repr(-1.0) \ +#repr(-9876543210.0) \ +#repr(-0987654321.0) \ +#repr(-3.14) \ +#repr(4.0 - 8.0) diff --git a/fuzzers/corpora/text/quote_00.typ b/fuzzers/corpora/text/quote_00.typ new file mode 100644 index 00000000..c9e7891b --- /dev/null +++ b/fuzzers/corpora/text/quote_00.typ @@ -0,0 +1,9 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Text direction affects author positioning +And I quote: #quote(attribution: [René Descartes])[cogito, ergo sum]. + +#set text(lang: "ar") +#quote(attribution: [عالم])[مرحبًا] diff --git a/fuzzers/corpora/text/quote_01.typ b/fuzzers/corpora/text/quote_01.typ new file mode 100644 index 00000000..2e1a1f7e --- /dev/null +++ b/fuzzers/corpora/text/quote_01.typ @@ -0,0 +1,10 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Text direction affects block alignment +#set quote(block: true) +#quote(attribution: [René Descartes])[cogito, ergo sum] + +#set text(lang: "ar") +#quote(attribution: [عالم])[مرحبًا] diff --git a/fuzzers/corpora/text/quote_02.typ b/fuzzers/corpora/text/quote_02.typ new file mode 100644 index 00000000..b0d4e552 --- /dev/null +++ b/fuzzers/corpora/text/quote_02.typ @@ -0,0 +1,10 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Spacing with other blocks +#set quote(block: true) + +#lorem(10) +#quote(lorem(10)) +#lorem(10) diff --git a/fuzzers/corpora/text/quote_03.typ b/fuzzers/corpora/text/quote_03.typ new file mode 100644 index 00000000..58bd114e --- /dev/null +++ b/fuzzers/corpora/text/quote_03.typ @@ -0,0 +1,8 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Inline citation +#bibliography("/assets/files/works.bib") + +#quote(attribution: )[In a hole in the ground there lived a hobbit.] diff --git a/fuzzers/corpora/text/quote_04.typ b/fuzzers/corpora/text/quote_04.typ new file mode 100644 index 00000000..86504f54 --- /dev/null +++ b/fuzzers/corpora/text/quote_04.typ @@ -0,0 +1,9 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Citation-format: label or numeric +#set quote(block: true) +#bibliography("/assets/files/works.bib", style: "ieee") + +#quote(attribution: )[In a hole in the ground there lived a hobbit.] diff --git a/fuzzers/corpora/text/quote_05.typ b/fuzzers/corpora/text/quote_05.typ new file mode 100644 index 00000000..670aab9d --- /dev/null +++ b/fuzzers/corpora/text/quote_05.typ @@ -0,0 +1,9 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Citation-format: note +#set quote(block: true) +#bibliography("/assets/files/works.bib", style: "chicago-notes") + +#quote(attribution: )[In a hole in the ground there lived a hobbit.] diff --git a/fuzzers/corpora/text/quote_06.typ b/fuzzers/corpora/text/quote_06.typ new file mode 100644 index 00000000..65ff3d70 --- /dev/null +++ b/fuzzers/corpora/text/quote_06.typ @@ -0,0 +1,9 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Citation-format: author-date or author +#set quote(block: true) +#bibliography("/assets/files/works.bib", style: "apa") + +#quote(attribution: )[In a hole in the ground there lived a hobbit.] diff --git a/fuzzers/corpora/text/quotes_06.typ b/fuzzers/corpora/text/quotes_06.typ new file mode 100644 index 00000000..4a49992e --- /dev/null +++ b/fuzzers/corpora/text/quotes_06.typ @@ -0,0 +1,8 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test nested double and single quotes. +"'test statement'" \ +"'test' statement" \ +"statement 'test'" diff --git a/fuzzers/corpora/text/raw-align_02.typ b/fuzzers/corpora/text/raw-align_02.typ index 6b81cfa7..737ef400 100644 --- a/fuzzers/corpora/text/raw-align_02.typ +++ b/fuzzers/corpora/text/raw-align_02.typ @@ -2,5 +2,5 @@ #import "/contrib/templates/std-tests/preset.typ": * #show: test-page // -// // Error: 17-20 alignment must be horizontal +// // Error: 17-20 expected `start`, `left`, `center`, `right`, or `end`, found top // #set raw(align: top) \ No newline at end of file diff --git a/fuzzers/corpora/text/raw-line_00.typ b/fuzzers/corpora/text/raw-line_00.typ new file mode 100644 index 00000000..7d0cf928 --- /dev/null +++ b/fuzzers/corpora/text/raw-line_00.typ @@ -0,0 +1,26 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#set page(width: 200pt) + +```rs +fn main() { + println!("Hello, world!"); +} +``` + +#show raw.line: it => { + box(stack( + dir: ltr, + box(width: 15pt)[#it.number], + it.body, + )) + linebreak() +} + +```rs +fn main() { + println!("Hello, world!"); +} +``` diff --git a/fuzzers/corpora/text/raw-line_01.typ b/fuzzers/corpora/text/raw-line_01.typ new file mode 100644 index 00000000..d458d267 --- /dev/null +++ b/fuzzers/corpora/text/raw-line_01.typ @@ -0,0 +1,33 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#set page(width: 200pt) +#show raw: it => stack(dir: ttb, ..it.lines) +#show raw.line: it => { + box( + width: 100%, + height: 1.75em, + inset: 0.25em, + fill: if calc.rem(it.number, 2) == 0 { + luma(90%) + } else { + white + }, + align(horizon, stack( + dir: ltr, + box(width: 15pt)[#it.number], + it.body, + )) + ) +} + +```typ +#show raw.line: block.with( + fill: luma(60%) +); + +Hello, world! + += A heading for good measure +``` diff --git a/fuzzers/corpora/text/raw-line_02.typ b/fuzzers/corpora/text/raw-line_02.typ new file mode 100644 index 00000000..e82f50e6 --- /dev/null +++ b/fuzzers/corpora/text/raw-line_02.typ @@ -0,0 +1,19 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#set page(width: 200pt) +#show raw.line: set text(fill: red) + +```py +import numpy as np + +def f(x): + return x**2 + +x = np.linspace(0, 10, 100) +y = f(x) + +print(x) +print(y) +``` diff --git a/fuzzers/corpora/text/raw-line_03.typ b/fuzzers/corpora/text/raw-line_03.typ new file mode 100644 index 00000000..7c9bc49b --- /dev/null +++ b/fuzzers/corpora/text/raw-line_03.typ @@ -0,0 +1,38 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Ref: false + +// Test line extraction works. + +#show raw: code => { + for i in code.lines { + test(i.count, 10) + } + + test(code.lines.at(0).text, "import numpy as np") + test(code.lines.at(1).text, "") + test(code.lines.at(2).text, "def f(x):") + test(code.lines.at(3).text, " return x**2") + test(code.lines.at(4).text, "") + test(code.lines.at(5).text, "x = np.linspace(0, 10, 100)") + test(code.lines.at(6).text, "y = f(x)") + test(code.lines.at(7).text, "") + test(code.lines.at(8).text, "print(x)") + test(code.lines.at(9).text, "print(y)") + test(code.lines.at(10, default: none), none) +} + +```py +import numpy as np + +def f(x): + return x**2 + +x = np.linspace(0, 10, 100) +y = f(x) + +print(x) +print(y) +``` diff --git a/fuzzers/corpora/text/raw-tabs_00.typ b/fuzzers/corpora/text/raw-tabs_00.typ new file mode 100644 index 00000000..f26e07d2 --- /dev/null +++ b/fuzzers/corpora/text/raw-tabs_00.typ @@ -0,0 +1,12 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#set raw(tab-size: 8) + +```tsv +Year Month Day +2000 2 3 +2001 2 1 +2002 3 10 +``` diff --git a/fuzzers/corpora/text/smartquotes_00.typ b/fuzzers/corpora/text/smartquotes_00.typ new file mode 100644 index 00000000..99c9f456 --- /dev/null +++ b/fuzzers/corpora/text/smartquotes_00.typ @@ -0,0 +1,10 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Use language quotes for missing keys, allow partial reset +#set smartquote(quotes: "«»") +"Double and 'Single' Quotes" + +#set smartquote(quotes: (double: auto, single: "«»")) +"Double and 'Single' Quotes" diff --git a/fuzzers/corpora/text/smartquotes_01.typ b/fuzzers/corpora/text/smartquotes_01.typ new file mode 100644 index 00000000..8b95c42f --- /dev/null +++ b/fuzzers/corpora/text/smartquotes_01.typ @@ -0,0 +1,10 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Allow 2 graphemes +#set smartquote(quotes: "a\u{0301}a\u{0301}") +"Double and 'Single' Quotes" + +#set smartquote(quotes: (single: "a\u{0301}a\u{0301}")) +"Double and 'Single' Quotes" diff --git a/fuzzers/corpora/text/smartquotes_02.typ b/fuzzers/corpora/text/smartquotes_02.typ new file mode 100644 index 00000000..0ad846ac --- /dev/null +++ b/fuzzers/corpora/text/smartquotes_02.typ @@ -0,0 +1,6 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// +// // Error: 25-28 expected 2 characters, found 1 character +// #set smartquote(quotes: "'") \ No newline at end of file diff --git a/fuzzers/corpora/text/smartquotes_03.typ b/fuzzers/corpora/text/smartquotes_03.typ new file mode 100644 index 00000000..bc52ae7e --- /dev/null +++ b/fuzzers/corpora/text/smartquotes_03.typ @@ -0,0 +1,6 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// +// // Error: 25-35 expected 2 quotes, found 4 quotes +// #set smartquote(quotes: ("'",) * 4) \ No newline at end of file diff --git a/fuzzers/corpora/text/smartquotes_04.typ b/fuzzers/corpora/text/smartquotes_04.typ new file mode 100644 index 00000000..45a2bbed --- /dev/null +++ b/fuzzers/corpora/text/smartquotes_04.typ @@ -0,0 +1,6 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// +// // Error: 25-45 expected 2 quotes, found 4 quotes +// #set smartquote(quotes: (single: ("'",) * 4)) \ No newline at end of file diff --git a/fuzzers/corpora/visualize/gradient-conic_00.typ b/fuzzers/corpora/visualize/gradient-conic_00.typ new file mode 100644 index 00000000..b9507466 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-conic_00.typ @@ -0,0 +1,8 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#square( + size: 50pt, + fill: gradient.conic(..color.map.rainbow, space: color.hsv), +) diff --git a/fuzzers/corpora/visualize/gradient-conic_01.typ b/fuzzers/corpora/visualize/gradient-conic_01.typ new file mode 100644 index 00000000..fc650859 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-conic_01.typ @@ -0,0 +1,8 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#square( + size: 50pt, + fill: gradient.conic(..color.map.rainbow, space: color.hsv, center: (10%, 10%)), +) diff --git a/fuzzers/corpora/visualize/gradient-conic_02.typ b/fuzzers/corpora/visualize/gradient-conic_02.typ new file mode 100644 index 00000000..9d86c075 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-conic_02.typ @@ -0,0 +1,8 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#square( + size: 50pt, + fill: gradient.conic(..color.map.rainbow, space: color.hsv, center: (90%, 90%)), +) diff --git a/fuzzers/corpora/visualize/gradient-conic_03.typ b/fuzzers/corpora/visualize/gradient-conic_03.typ new file mode 100644 index 00000000..ebd90190 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-conic_03.typ @@ -0,0 +1,8 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#square( + size: 50pt, + fill: gradient.conic(..color.map.rainbow, space: color.hsv, angle: 90deg), +) diff --git a/fuzzers/corpora/visualize/gradient-dir_00.typ b/fuzzers/corpora/visualize/gradient-dir_00.typ new file mode 100644 index 00000000..aafa0742 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-dir_00.typ @@ -0,0 +1,14 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#set page(width: 900pt) +#for i in range(0, 360, step: 15){ + box( + height: 100pt, + width: 100pt, + fill: gradient.linear(angle: i * 1deg, (red, 0%), (blue, 100%)), + align(center + horizon)[Angle: #i degrees], + ) + h(30pt) +} diff --git a/fuzzers/corpora/visualize/gradient-presets_00.typ b/fuzzers/corpora/visualize/gradient-presets_00.typ new file mode 100644 index 00000000..183de084 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-presets_00.typ @@ -0,0 +1,34 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#set page(width: 200pt, height: auto, margin: 0pt) +#set text(fill: white, size: 18pt) +#set text(top-edge: "bounds", bottom-edge: "bounds") + +#let presets = ( + ("turbo", color.map.turbo), + ("cividis", color.map.cividis), + ("rainbow", color.map.rainbow), + ("spectral", color.map.spectral), + ("viridis", color.map.viridis), + ("inferno", color.map.inferno), + ("magma", color.map.magma), + ("plasma", color.map.plasma), + ("rocket", color.map.rocket), + ("mako", color.map.mako), + ("vlag", color.map.vlag), + ("icefire", color.map.icefire), + ("flare", color.map.flare), + ("crest", color.map.crest), +) + +#stack( + spacing: 3pt, + ..presets.map(((name, preset)) => block( + width: 100%, + height: 20pt, + fill: gradient.linear(..preset), + align(center + horizon, smallcaps(name)), + )) +) diff --git a/fuzzers/corpora/visualize/gradient-radial_00.typ b/fuzzers/corpora/visualize/gradient-radial_00.typ new file mode 100644 index 00000000..a9eb2eac --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-radial_00.typ @@ -0,0 +1,9 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + + +#square( + size: 100pt, + fill: gradient.radial(..color.map.rainbow, space: color.hsl), +) \ No newline at end of file diff --git a/fuzzers/corpora/visualize/gradient-radial_01.typ b/fuzzers/corpora/visualize/gradient-radial_01.typ new file mode 100644 index 00000000..955f9eb9 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-radial_01.typ @@ -0,0 +1,24 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + + +#grid( + columns: 2, + square( + size: 50pt, + fill: gradient.radial(..color.map.rainbow, space: color.hsl, center: (0%, 0%)), + ), + square( + size: 50pt, + fill: gradient.radial(..color.map.rainbow, space: color.hsl, center: (0%, 100%)), + ), + square( + size: 50pt, + fill: gradient.radial(..color.map.rainbow, space: color.hsl, center: (100%, 0%)), + ), + square( + size: 50pt, + fill: gradient.radial(..color.map.rainbow, space: color.hsl, center: (100%, 100%)), + ), +) diff --git a/fuzzers/corpora/visualize/gradient-radial_02.typ b/fuzzers/corpora/visualize/gradient-radial_02.typ new file mode 100644 index 00000000..9ac02195 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-radial_02.typ @@ -0,0 +1,13 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + + +#square( + size: 50pt, + fill: gradient.radial(..color.map.rainbow, space: color.hsl, radius: 10%), +) +#square( + size: 50pt, + fill: gradient.radial(..color.map.rainbow, space: color.hsl, radius: 72%), +) diff --git a/fuzzers/corpora/visualize/gradient-radial_03.typ b/fuzzers/corpora/visualize/gradient-radial_03.typ new file mode 100644 index 00000000..acaca3b4 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-radial_03.typ @@ -0,0 +1,12 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#circle( + radius: 25pt, + fill: gradient.radial(white, rgb("#8fbc8f"), focal-center: (35%, 35%), focal-radius: 5%), +) +#circle( + radius: 25pt, + fill: gradient.radial(white, rgb("#8fbc8f"), focal-center: (75%, 35%), focal-radius: 5%), +) diff --git a/fuzzers/corpora/visualize/gradient-relative-conic_00.typ b/fuzzers/corpora/visualize/gradient-relative-conic_00.typ new file mode 100644 index 00000000..4d09de53 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-relative-conic_00.typ @@ -0,0 +1,16 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// The image should look as if there is a single gradient that is being used for +// both the page and the rectangles. +#let grad = gradient.conic(red, blue, green, purple, relative: "parent"); +#let my-rect = rect(width: 50%, height: 50%, fill: grad) +#set page( + height: 200pt, + width: 200pt, + fill: grad, + background: place(top + left, my-rect), +) +#place(top + right, my-rect) +#place(bottom + center, rotate(45deg, my-rect)) diff --git a/fuzzers/corpora/visualize/gradient-relative-conic_01.typ b/fuzzers/corpora/visualize/gradient-relative-conic_01.typ new file mode 100644 index 00000000..7a4a7240 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-relative-conic_01.typ @@ -0,0 +1,16 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// The image should look as if there are multiple gradients, one for each +// rectangle. +#let grad = gradient.conic(red, blue, green, purple, relative: "self"); +#let my-rect = rect(width: 50%, height: 50%, fill: grad) +#set page( + height: 200pt, + width: 200pt, + fill: grad, + background: place(top + left, my-rect), +) +#place(top + right, my-rect) +#place(bottom + center, rotate(45deg, my-rect)) diff --git a/fuzzers/corpora/visualize/gradient-relative-linear_00.typ b/fuzzers/corpora/visualize/gradient-relative-linear_00.typ new file mode 100644 index 00000000..2aef6eb4 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-relative-linear_00.typ @@ -0,0 +1,16 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// The image should look as if there is a single gradient that is being used for +// both the page and the rectangles. +#let grad = gradient.linear(red, blue, green, purple, relative: "parent"); +#let my-rect = rect(width: 50%, height: 50%, fill: grad) +#set page( + height: 200pt, + width: 200pt, + fill: grad, + background: place(top + left, my-rect), +) +#place(top + right, my-rect) +#place(bottom + center, rotate(45deg, my-rect)) diff --git a/fuzzers/corpora/visualize/gradient-relative-linear_01.typ b/fuzzers/corpora/visualize/gradient-relative-linear_01.typ new file mode 100644 index 00000000..a6eb06b2 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-relative-linear_01.typ @@ -0,0 +1,16 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// The image should look as if there are multiple gradients, one for each +// rectangle. +#let grad = gradient.linear(red, blue, green, purple, relative: "self"); +#let my-rect = rect(width: 50%, height: 50%, fill: grad) +#set page( + height: 200pt, + width: 200pt, + fill: grad, + background: place(top + left, my-rect), +) +#place(top + right, my-rect) +#place(bottom + center, rotate(45deg, my-rect)) diff --git a/fuzzers/corpora/visualize/gradient-relative-radial_00.typ b/fuzzers/corpora/visualize/gradient-relative-radial_00.typ new file mode 100644 index 00000000..4f7b0f6d --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-relative-radial_00.typ @@ -0,0 +1,16 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// The image should look as if there is a single gradient that is being used for +// both the page and the rectangles. +#let grad = gradient.radial(red, blue, green, purple, relative: "parent"); +#let my-rect = rect(width: 50%, height: 50%, fill: grad) +#set page( + height: 200pt, + width: 200pt, + fill: grad, + background: place(top + left, my-rect), +) +#place(top + right, my-rect) +#place(bottom + center, rotate(45deg, my-rect)) diff --git a/fuzzers/corpora/visualize/gradient-relative-radial_01.typ b/fuzzers/corpora/visualize/gradient-relative-radial_01.typ new file mode 100644 index 00000000..dce99026 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-relative-radial_01.typ @@ -0,0 +1,16 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// The image should look as if there are multiple gradients, one for each +// rectangle. +#let grad = gradient.radial(red, blue, green, purple, relative: "self"); +#let my-rect = rect(width: 50%, height: 50%, fill: grad) +#set page( + height: 200pt, + width: 200pt, + fill: grad, + background: place(top + left, my-rect), +) +#place(top + right, my-rect) +#place(bottom + center, rotate(45deg, my-rect)) diff --git a/fuzzers/corpora/visualize/gradient-repeat_00.typ b/fuzzers/corpora/visualize/gradient-repeat_00.typ new file mode 100644 index 00000000..430f42b4 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-repeat_00.typ @@ -0,0 +1,9 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#rect( + height: 40pt, + width: 100%, + fill: gradient.linear(..color.map.inferno).repeat(2, mirror: true) +) diff --git a/fuzzers/corpora/visualize/gradient-repeat_01.typ b/fuzzers/corpora/visualize/gradient-repeat_01.typ new file mode 100644 index 00000000..f0348dd8 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-repeat_01.typ @@ -0,0 +1,9 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#rect( + height: 40pt, + width: 100%, + fill: gradient.linear(..color.map.rainbow).repeat(2, mirror: true), +) diff --git a/fuzzers/corpora/visualize/gradient-repeat_02.typ b/fuzzers/corpora/visualize/gradient-repeat_02.typ new file mode 100644 index 00000000..a9dcf8f7 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-repeat_02.typ @@ -0,0 +1,9 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#rect( + height: 40pt, + width: 100%, + fill: gradient.linear(..color.map.rainbow).repeat(5, mirror: true) +) diff --git a/fuzzers/corpora/visualize/gradient-repeat_03.typ b/fuzzers/corpora/visualize/gradient-repeat_03.typ new file mode 100644 index 00000000..e24a7949 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-repeat_03.typ @@ -0,0 +1,9 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#rect( + height: 40pt, + width: 100%, + fill: gradient.linear(..color.map.rainbow).sharp(10).repeat(5, mirror: false) +) diff --git a/fuzzers/corpora/visualize/gradient-repeat_04.typ b/fuzzers/corpora/visualize/gradient-repeat_04.typ new file mode 100644 index 00000000..e0fbb9fd --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-repeat_04.typ @@ -0,0 +1,9 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#rect( + height: 40pt, + width: 100%, + fill: gradient.linear(..color.map.rainbow).sharp(10).repeat(5, mirror: true) +) diff --git a/fuzzers/corpora/visualize/gradient-sharp_00.typ b/fuzzers/corpora/visualize/gradient-sharp_00.typ new file mode 100644 index 00000000..ec314015 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-sharp_00.typ @@ -0,0 +1,16 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#square( + size: 100pt, + fill: gradient.linear(..color.map.rainbow, space: color.hsl).sharp(10), +) +#square( + size: 100pt, + fill: gradient.radial(..color.map.rainbow, space: color.hsl).sharp(10), +) +#square( + size: 100pt, + fill: gradient.conic(..color.map.rainbow, space: color.hsl).sharp(10), +) diff --git a/fuzzers/corpora/visualize/gradient-sharp_01.typ b/fuzzers/corpora/visualize/gradient-sharp_01.typ new file mode 100644 index 00000000..a0acaae4 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-sharp_01.typ @@ -0,0 +1,16 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#square( + size: 100pt, + fill: gradient.linear(..color.map.rainbow, space: color.hsl).sharp(10, smoothness: 40%), +) +#square( + size: 100pt, + fill: gradient.radial(..color.map.rainbow, space: color.hsl).sharp(10, smoothness: 40%), +) +#square( + size: 100pt, + fill: gradient.conic(..color.map.rainbow, space: color.hsl).sharp(10, smoothness: 40%), +) diff --git a/fuzzers/corpora/visualize/gradient-stroke_00.typ b/fuzzers/corpora/visualize/gradient-stroke_00.typ new file mode 100644 index 00000000..b655cfb5 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-stroke_00.typ @@ -0,0 +1,5 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#align(center + top, square(size: 50pt, fill: black, stroke: 5pt + gradient.linear(red, blue))) diff --git a/fuzzers/corpora/visualize/gradient-stroke_01.typ b/fuzzers/corpora/visualize/gradient-stroke_01.typ new file mode 100644 index 00000000..a0d0b7b3 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-stroke_01.typ @@ -0,0 +1,12 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#align( + center + bottom, + square( + size: 50pt, + fill: gradient.radial(red, blue, radius: 70.7%, focal-center: (10%, 10%)), + stroke: 10pt + gradient.radial(red, blue, radius: 70.7%, focal-center: (10%, 10%)) + ) +) diff --git a/fuzzers/corpora/visualize/gradient-stroke_02.typ b/fuzzers/corpora/visualize/gradient-stroke_02.typ new file mode 100644 index 00000000..1fd3b8b5 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-stroke_02.typ @@ -0,0 +1,12 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#align( + center + bottom, + square( + size: 50pt, + fill: black, + stroke: 10pt + gradient.conic(red, blue) + ) +) diff --git a/fuzzers/corpora/visualize/gradient-stroke_03.typ b/fuzzers/corpora/visualize/gradient-stroke_03.typ new file mode 100644 index 00000000..8b9a5c1d --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-stroke_03.typ @@ -0,0 +1,9 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test gradient on lines +#set page(width: 100pt, height: 100pt) +#line(length: 100%, stroke: 1pt + gradient.linear(red, blue)) +#line(length: 100%, angle: 10deg, stroke: 1pt + gradient.linear(red, blue)) +#line(length: 100%, angle: 10deg, stroke: 1pt + gradient.linear(red, blue, relative: "parent")) diff --git a/fuzzers/corpora/visualize/gradient-text-cjk.typ b/fuzzers/corpora/visualize/gradient-text-cjk.typ new file mode 100644 index 00000000..f3aca161 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-text-cjk.typ @@ -0,0 +1,19 @@ + + +#set page(width: 140pt, height: auto, margin: 10pt) +#set par(justify: true) +#set text(fill: gradient.radial(red, blue), size: 10pt) + +锄禾日当午, +汗滴禾下土。 + +谁知盘中餐, +粒粒皆辛苦。 + + +锄禾日当午, +汗滴禾下土。 + +谁知盘中餐, +粒粒皆辛苦。 + diff --git a/fuzzers/corpora/visualize/gradient-text-decorations_00.typ b/fuzzers/corpora/visualize/gradient-text-decorations_00.typ new file mode 100644 index 00000000..161c5dd3 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-text-decorations_00.typ @@ -0,0 +1,10 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + + +#set text(fill: gradient.linear(red, blue)) + +Hello #underline[World]! \ +Hello #overline[World]! \ +Hello #strike[World]! \ diff --git a/fuzzers/corpora/visualize/gradient-text-emoji.typ b/fuzzers/corpora/visualize/gradient-text-emoji.typ new file mode 100644 index 00000000..64f924cd --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-text-emoji.typ @@ -0,0 +1,19 @@ + + +#set page(width: 140pt, height: auto, margin: 10pt) +#set par(justify: true) +#set text(fill: gradient.radial(red, blue), size: 10pt) + +锄禾日当午, +汗滴禾下土。 + +谁知盘中餐, +粒粒皆🏞‍🌋。 + + +锄禾日当午, +汗滴禾下土。 + +谁知盘中餐, +粒粒皆辛苦。 + diff --git a/fuzzers/corpora/visualize/gradient-text-other_00.typ b/fuzzers/corpora/visualize/gradient-text-other_00.typ new file mode 100644 index 00000000..2351b998 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-text-other_00.typ @@ -0,0 +1,9 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#set page(width: 200pt, height: auto, margin: 10pt) +#set par(justify: true) +#set text(fill: gradient.radial(red, blue)) +#lorem(30) + diff --git a/fuzzers/corpora/visualize/gradient-text-other_01.typ b/fuzzers/corpora/visualize/gradient-text-other_01.typ new file mode 100644 index 00000000..f8d0290f --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-text-other_01.typ @@ -0,0 +1,8 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#set page(width: 200pt, height: auto, margin: 10pt) +#set par(justify: true) +#set text(fill: gradient.conic(red, blue, angle: 45deg)) +#lorem(30) diff --git a/fuzzers/corpora/visualize/gradient-text-perf.typ b/fuzzers/corpora/visualize/gradient-text-perf.typ new file mode 100644 index 00000000..0c2373ed --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-text-perf.typ @@ -0,0 +1,17 @@ + + +#set page(width: 140pt, height: auto, margin: 10pt) +#set par(justify: true) +#set text(fill: gradient.radial(red, blue), size: 10pt) + +#lorem(1000) + +#lorem(1000) + +#lorem(1000) + +#lorem(1000) + +#lorem(1000) + +#lorem(1000) diff --git a/fuzzers/corpora/visualize/gradient-text_00.typ b/fuzzers/corpora/visualize/gradient-text_00.typ new file mode 100644 index 00000000..12a314e1 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-text_00.typ @@ -0,0 +1,10 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page +// +// // Ref: false +// // Make sure they don't work when `relative: "self"`. +// +// // Hint: 17-61 make sure to set `relative: auto` on your text fill +// // Error: 17-61 gradients on text must be relative to the parent +// #set text(fill: gradient.linear(red, blue, relative: "self")) \ No newline at end of file diff --git a/fuzzers/corpora/visualize/gradient-text_01.typ b/fuzzers/corpora/visualize/gradient-text_01.typ new file mode 100644 index 00000000..504417c4 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-text_01.typ @@ -0,0 +1,12 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test that gradient fills on text work for globally defined gradients. + +#set page(width: 200pt, height: auto, margin: 10pt, background: { + rect(width: 100%, height: 30pt, fill: gradient.linear(red, blue)) +}) +#set par(justify: true) +#set text(fill: gradient.linear(red, blue)) +#lorem(30) diff --git a/fuzzers/corpora/visualize/gradient-text_02.typ b/fuzzers/corpora/visualize/gradient-text_02.typ new file mode 100644 index 00000000..af3ddc0b --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-text_02.typ @@ -0,0 +1,12 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Sanity check that the direction works on text. + +#set page(width: 200pt, height: auto, margin: 10pt, background: { + rect(height: 100%, width: 30pt, fill: gradient.linear(dir: btt, red, blue)) +}) +#set par(justify: true) +#set text(fill: gradient.linear(dir: btt, red, blue)) +#lorem(30) diff --git a/fuzzers/corpora/visualize/gradient-text_03.typ b/fuzzers/corpora/visualize/gradient-text_03.typ new file mode 100644 index 00000000..daa0a6fa --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-text_03.typ @@ -0,0 +1,10 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test that gradient fills on text work for locally defined gradients. + +#set page(width: auto, height: auto, margin: 10pt) +#show box: set text(fill: gradient.linear(..color.map.rainbow)) + +Hello, #box[World]! diff --git a/fuzzers/corpora/visualize/gradient-text_04.typ b/fuzzers/corpora/visualize/gradient-text_04.typ new file mode 100644 index 00000000..15520265 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-text_04.typ @@ -0,0 +1,10 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +// Test that gradients fills on text work with transforms. + +#set page(width: auto, height: auto, margin: 10pt) +#show box: set text(fill: gradient.linear(..color.map.rainbow)) + +#rotate(45deg, box[World]) diff --git a/fuzzers/corpora/visualize/gradient-transform_00.typ b/fuzzers/corpora/visualize/gradient-transform_00.typ new file mode 100644 index 00000000..7917ad65 --- /dev/null +++ b/fuzzers/corpora/visualize/gradient-transform_00.typ @@ -0,0 +1,13 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#let grad = gradient.linear(red, blue, green, purple, relative: "parent"); +#let my-rect = rect(width: 50pt, height: 50pt, fill: grad) +#set page( + height: 200pt, + width: 200pt, +) +#place(top + right, scale(x: 200%, y: 130%, my-rect)) +#place(bottom + center, rotate(45deg, my-rect)) +#place(horizon + center, scale(x: 200%, y: 130%, rotate(45deg, my-rect))) diff --git a/fuzzers/corpora/visualize/image_08.typ b/fuzzers/corpora/visualize/image_08.typ index 6566dc5d..4fb1b7b8 100644 --- a/fuzzers/corpora/visualize/image_08.typ +++ b/fuzzers/corpora/visualize/image_08.typ @@ -2,5 +2,5 @@ #import "/contrib/templates/std-tests/preset.typ": * #show: test-page // -// // Error: 2-25 failed to parse svg: found closing tag 'g' instead of 'style' in line 4 +// // Error: 2-25 failed to parse SVG (found closing tag 'g' instead of 'style' in line 4) // #image("/assets/files/bad.svg") \ No newline at end of file diff --git a/fuzzers/corpora/visualize/image_10.typ b/fuzzers/corpora/visualize/image_10.typ index 4a477bef..76e9a6b1 100644 --- a/fuzzers/corpora/visualize/image_10.typ +++ b/fuzzers/corpora/visualize/image_10.typ @@ -2,5 +2,5 @@ #import "/contrib/templates/std-tests/preset.typ": * #show: test-page // -// // Error: 2-168 failed to parse svg: missing root node +// // Error: 2-168 failed to parse SVG (missing root node) // #image.decode(``.text, format: "svg") \ No newline at end of file diff --git a/fuzzers/corpora/visualize/image_13.typ b/fuzzers/corpora/visualize/image_13.typ index a7c81d3e..de56fa55 100644 --- a/fuzzers/corpora/visualize/image_13.typ +++ b/fuzzers/corpora/visualize/image_13.typ @@ -2,5 +2,5 @@ #import "/contrib/templates/std-tests/preset.typ": * #show: test-page // -// // Error: 2-83 failed to decode image +// // Error: 2-83 failed to decode image (Format error decoding Png: Invalid PNG signature.) // #image.decode(read("/assets/files/tiger.jpg", encoding: none), format: "png", width: 80%) \ No newline at end of file diff --git a/fuzzers/corpora/visualize/polygon_00.typ b/fuzzers/corpora/visualize/polygon_00.typ index a274ea80..ce0887d2 100644 --- a/fuzzers/corpora/visualize/polygon_00.typ +++ b/fuzzers/corpora/visualize/polygon_00.typ @@ -9,6 +9,7 @@ #polygon() #polygon((0em, 0pt)) #polygon((0pt, 0pt), (10pt, 0pt)) +#polygon.regular(size: 0pt, vertices: 9) #polygon((5pt, 0pt), (0pt, 10pt), (10pt, 10pt)) #polygon( @@ -27,3 +28,6 @@ // Self-intersections #polygon((0pt, 10pt), (30pt, 20pt), (0pt, 30pt), (20pt, 0pt), (20pt, 35pt)) + +// Regular polygon; should have equal side lengths +#for k in range(3, 9) {polygon.regular(size: 30pt, vertices: k,)} diff --git a/fuzzers/corpora/visualize/shape-fill-stroke_02.typ b/fuzzers/corpora/visualize/shape-fill-stroke_02.typ index 99d43218..b6f3a734 100644 --- a/fuzzers/corpora/visualize/shape-fill-stroke_02.typ +++ b/fuzzers/corpora/visualize/shape-fill-stroke_02.typ @@ -5,8 +5,51 @@ // Test stroke composition. #set square(stroke: 4pt) #set text(font: "Roboto") -#square( - stroke: (left: red, top: yellow, right: green, bottom: blue), - radius: 100%, align(center+horizon)[*G*], - inset: 8pt +#stack( + dir: ltr, + square( + stroke: (left: red, top: yellow, right: green, bottom: blue), + radius: 50%, align(center+horizon)[*G*], + inset: 8pt + ), + h(0.5cm), + square( + stroke: (left: red, top: yellow + 8pt, right: green, bottom: blue + 2pt), + radius: 50%, align(center+horizon)[*G*], + inset: 8pt + ), + h(0.5cm), + square( + stroke: (left: red, top: yellow, right: green, bottom: blue), + radius: 100%, align(center+horizon)[*G*], + inset: 8pt + ), +) + +// Join between different solid strokes +#set square(size: 20pt, stroke: 2pt) +#set square(stroke: (left: green + 4pt, top: black + 2pt, right: blue, bottom: black + 2pt)) +#stack( + dir: ltr, + square(), + h(0.2cm), + square(radius: (top-left: 0pt, rest: 1pt)), + h(0.2cm), + square(radius: (top-left: 0pt, rest: 8pt)), + h(0.2cm), + square(radius: (top-left: 0pt, rest: 100pt)), +) + + +// Join between solid and dotted strokes +#set square(stroke: (left: green + 4pt, top: black + 2pt, right: (paint: blue, dash: "dotted"), bottom: (paint: black, dash: "dotted"))) +#stack( + dir: ltr, + square(), + h(0.2cm), + square(radius: (top-left: 0pt, rest: 1pt)), + h(0.2cm), + square(radius: (top-left: 0pt, rest: 8pt)), + h(0.2cm), + square(radius: (top-left: 0pt, rest: 100pt)), ) diff --git a/fuzzers/corpora/visualize/shape-rect_01.typ b/fuzzers/corpora/visualize/shape-rect_01.typ index eb205a7a..8eb6c5d3 100644 --- a/fuzzers/corpora/visualize/shape-rect_01.typ +++ b/fuzzers/corpora/visualize/shape-rect_01.typ @@ -29,7 +29,7 @@ #stack( dir: ltr, spacing: 1fr, - rect(width: 2cm, radius: 60%), + rect(width: 2cm, radius: 30%), rect(width: 1cm, radius: (left: 10pt, right: 5pt)), rect(width: 1.25cm, radius: ( top-left: 2pt, diff --git a/fuzzers/corpora/visualize/shape-rounded_00.typ b/fuzzers/corpora/visualize/shape-rounded_00.typ index dfb7573b..1f78a8fe 100644 --- a/fuzzers/corpora/visualize/shape-rounded_00.typ +++ b/fuzzers/corpora/visualize/shape-rounded_00.typ @@ -2,6 +2,53 @@ #import "/contrib/templates/std-tests/preset.typ": * #show: test-page -// Ensure that radius is clamped. -#rect(radius: -20pt) -#square(radius: 30pt) +#set square(size: 20pt, stroke: 4pt) + +// no radius for non-rounded corners +#stack( + dir: ltr, + square(), + h(10pt), + square(radius: 0pt), + h(10pt), + square(radius: -10pt), +) + +#stack( + dir: ltr, + square(), + h(10pt), + square(radius: 0%), + h(10pt), + square(radius: -10%), +) + + +// small values for small radius +#stack( + dir: ltr, + square(radius: 1pt), + h(10pt), + square(radius: 5%), + h(10pt), + square(radius: 2pt), +) + +// large values for large radius or circle +#stack( + dir: ltr, + square(radius: 8pt), + h(10pt), + square(radius: 10pt), + h(10pt), + square(radius: 12pt), +) + +#stack( + dir: ltr, + square(radius: 45%), + h(10pt), + square(radius: 50%), + h(10pt), + square(radius: 55%), +) diff --git a/fuzzers/corpora/visualize/svg-text_01.typ b/fuzzers/corpora/visualize/svg-text_01.typ new file mode 100644 index 00000000..41ec17e2 --- /dev/null +++ b/fuzzers/corpora/visualize/svg-text_01.typ @@ -0,0 +1,11 @@ + +#import "/contrib/templates/std-tests/preset.typ": * +#show: test-page + +#set page(width: 250pt) +#show image: set text(font: ("Roboto", "Noto Serif CJK SC")) + +#figure( + image("/assets/files/chinese.svg"), + caption: [Bilingual text] +) diff --git a/github-pages/docs/data-flow-standalone.artifact.svg b/github-pages/docs/data-flow-standalone.artifact.svg index 421323be..8287816e 100644 --- a/github-pages/docs/data-flow-standalone.artifact.svg +++ b/github-pages/docs/data-flow-standalone.artifact.svg @@ -30,10 +30,10 @@ svg { cursor: pointer; pointer-events: all; } -.outline_glyph path { +.outline_glyph path, path.outline_glyph { fill: var(--glyph_fill); } -.outline_glyph path { +.outline_glyph path, path.outline_glyph { transition: 0.2s fill; } .hover .typst-text { @@ -69,4 +69,4 @@ svg { margin: -1.5vw; } } -precompile with theme and screen settingscompile to svg directly render ·render to svg render to canvas Typst DocumentsPreprocessed ArtifactSVG Document ( <svg/> )Canvas ( <canvas/> )Figure: Browser-side module needed: : compiler; : renderer. \ No newline at end of file +precompile with theme and screen settingscompile to svg directly render ·render to svg render to canvas Typst DocumentsPreprocessed ArtifactSVG Document ( <svg/> )Canvas ( <canvas/> )Figure: Browser-side module needed: : compiler; : renderer. \ No newline at end of file diff --git a/github-pages/docs/data-flow-standalone.dark.artifact.svg b/github-pages/docs/data-flow-standalone.dark.artifact.svg index 1a843b0e..68eb4922 100644 --- a/github-pages/docs/data-flow-standalone.dark.artifact.svg +++ b/github-pages/docs/data-flow-standalone.dark.artifact.svg @@ -30,10 +30,10 @@ svg { cursor: pointer; pointer-events: all; } -.outline_glyph path { +.outline_glyph path, path.outline_glyph { fill: var(--glyph_fill); } -.outline_glyph path { +.outline_glyph path, path.outline_glyph { transition: 0.2s fill; } .hover .typst-text { @@ -69,4 +69,4 @@ svg { margin: -1.5vw; } } -precompile with theme and screen settingscompile to svg directly render ·render to svg render to canvas Typst DocumentsPreprocessed ArtifactSVG Document ( <svg/> )Canvas ( <canvas/> )Figure: Browser-side module needed: : compiler; : renderer. \ No newline at end of file +precompile with theme and screen settingscompile to svg directly render ·render to svg render to canvas Typst DocumentsPreprocessed ArtifactSVG Document ( <svg/> )Canvas ( <canvas/> )Figure: Browser-side module needed: : compiler; : renderer. \ No newline at end of file diff --git a/github-pages/docs/ir-features.artifact.svg b/github-pages/docs/ir-features.artifact.svg index 65ca6ab7..d3f13124 100644 --- a/github-pages/docs/ir-features.artifact.svg +++ b/github-pages/docs/ir-features.artifact.svg @@ -30,10 +30,10 @@ svg { cursor: pointer; pointer-events: all; } -.outline_glyph path { +.outline_glyph path, path.outline_glyph { fill: var(--glyph_fill); } -.outline_glyph path { +.outline_glyph path, path.outline_glyph { transition: 0.2s fill; } .hover .typst-text { @@ -69,4 +69,4 @@ svg { margin: -1.5vw; } } -Data sharingT&deabfdead beefdeadbeeffill=#ffffill=#000Artifact Streaming..𝜆𝑥=𝑥d𝑥..𝜆𝑥=𝑥d𝑥Incremental Rendering𝜆𝑥=𝑥d𝑥....𝜆=𝑐𝑥2Figure: Features of the Vector Format. \ No newline at end of file +Data sharingT&deabfdead beefdeadbeeffill=#ffffill=#000Artifact Streaming..𝜆𝑥=𝑥d𝑥..𝜆𝑥=𝑥d𝑥Incremental Rendering𝜆𝑥=𝑥d𝑥....𝜆=𝑐𝑥2Figure: Features of the Vector Format. \ No newline at end of file diff --git a/github-pages/docs/ir-features.dark.artifact.svg b/github-pages/docs/ir-features.dark.artifact.svg index 53c5fd4b..2e843a6b 100644 --- a/github-pages/docs/ir-features.dark.artifact.svg +++ b/github-pages/docs/ir-features.dark.artifact.svg @@ -30,10 +30,10 @@ svg { cursor: pointer; pointer-events: all; } -.outline_glyph path { +.outline_glyph path, path.outline_glyph { fill: var(--glyph_fill); } -.outline_glyph path { +.outline_glyph path, path.outline_glyph { transition: 0.2s fill; } .hover .typst-text { @@ -69,4 +69,4 @@ svg { margin: -1.5vw; } } -Data sharingT&deabfdead beefdeadbeeffill=#ffffill=#000Artifact Streaming..𝜆𝑥=𝑥d𝑥..𝜆𝑥=𝑥d𝑥Incremental Rendering𝜆𝑥=𝑥d𝑥....𝜆=𝑐𝑥2Figure: Features of the Vector Format. \ No newline at end of file +Data sharingT&deabfdead beefdeadbeeffill=#ffffill=#000Artifact Streaming..𝜆𝑥=𝑥d𝑥..𝜆𝑥=𝑥d𝑥Incremental Rendering𝜆𝑥=𝑥d𝑥....𝜆=𝑐𝑥2Figure: Features of the Vector Format. \ No newline at end of file diff --git a/packages/typst.react/package.json b/packages/typst.react/package.json index d017200b..2e746b77 100644 --- a/packages/typst.react/package.json +++ b/packages/typst.react/package.json @@ -15,14 +15,14 @@ "homepage": "https://myriad-dreamin.github.io/typst.ts/", "exports": { ".": { - "require": "./dist/src/lib/index.mjs", - "import": "./dist/src/lib/index.mjs", - "types": "./dist/src/lib/index.d.mts" + "types": "./dist/index.d.ts", + "require": "./dist/index.js", + "import": "./dist/index.js" }, "./*": { - "require": "./dist/src/lib/*.mjs", - "import": "./dist/src/lib/*.mjs", - "types": "./dist/src/lib/*.d.mts" + "types": "./dist/*.d.ts", + "require": "./dist/*.js", + "import": "./dist/*.js" } }, "files": [ diff --git a/packages/typst.ts/package.json b/packages/typst.ts/package.json index fac7502e..e0480bf5 100644 --- a/packages/typst.ts/package.json +++ b/packages/typst.ts/package.json @@ -17,13 +17,13 @@ "types": "./dist/esm/index.d.mts", "exports": { ".": { + "types": "./dist/esm/index.d.mts", "require": "./dist/cjs/index.cjs", - "import": "./dist/esm/index.mjs", - "types": "./dist/esm/index.d.mts" + "import": "./dist/esm/index.mjs" }, "./*": [ "./*", - "./*.d.ts" + "./*.d.mts" ] }, "files": [ diff --git a/server/dev/src/main.rs b/server/dev/src/main.rs index 14f28cf7..6a367b46 100644 --- a/server/dev/src/main.rs +++ b/server/dev/src/main.rs @@ -13,6 +13,7 @@ fn main() { .filter_level(log::LevelFilter::Info) .filter_module("typst::", log::LevelFilter::Warn) .filter_module("typst_library::", log::LevelFilter::Warn) + .filter_module("tracing::", log::LevelFilter::Off) .try_init(); let opts = Opts::parse(); diff --git a/server/remote/src/main.rs b/server/remote/src/main.rs index 834aa852..d93811c0 100644 --- a/server/remote/src/main.rs +++ b/server/remote/src/main.rs @@ -11,6 +11,7 @@ use typst_ts_remote_server::{ fn main() { let _ = env_logger::builder() .filter_level(log::LevelFilter::Info) + .filter_module("tracing::", log::LevelFilter::Off) .try_init(); let opts = Opts::parse(); diff --git a/tests/common/src/std_artifact.rs b/tests/common/src/std_artifact.rs index cbfb45fc..341734dd 100644 --- a/tests/common/src/std_artifact.rs +++ b/tests/common/src/std_artifact.rs @@ -5,9 +5,11 @@ pub const STD_TEST_FILES: &[(&str, &str)] = &[ ("bugs", "args-sink_00"), ("bugs", "args-underscore_00"), ("bugs", "bidi-tofus_00"), + ("bugs", "block-width-box_00"), ("bugs", "cite-locate_00"), ("bugs", "clamp-panic_00"), ("bugs", "columns-1_00"), + ("bugs", "equation-numbering-reference_00"), ("bugs", "flow-1_00"), ("bugs", "flow-2_00"), ("bugs", "flow-3_00"), @@ -21,13 +23,31 @@ pub const STD_TEST_FILES: &[(&str, &str)] = &[ ("bugs", "grid-3_00"), ("bugs", "hide-meta_00"), ("bugs", "hide-meta_01"), + ("bugs", "layout-infinite-lengths_00"), + ("bugs", "layout-infinite-lengths_01"), + ("bugs", "layout-infinite-lengths_02"), + ("bugs", "layout-infinite-lengths_03"), ("bugs", "line-align_00"), + ("bugs", "mat-aug-color_00"), + ("bugs", "math-eval_00"), + ("bugs", "math-number-spacing_00"), ("bugs", "math-realize_00"), ("bugs", "math-realize_01"), ("bugs", "math-realize_02"), + ("bugs", "math-shift_00"), + ("bugs", "math-text-break_00"), + ("bugs", "new-cm-svg_00"), + ("bugs", "newline-mode_00"), + ("bugs", "newline-mode_01"), + ("bugs", "pagebreak-bibliography_00"), + ("bugs", "pagebreak-numbering_00"), + ("bugs", "pagebreak-set-style_00"), ("bugs", "parameter-pattern_00"), ("bugs", "place-base_00"), ("bugs", "place-pagebreak_00"), + ("bugs", "place-spacing_00"), + ("bugs", "place-spacing_01"), + ("bugs", "raw-color-overwrite_00"), ("bugs", "smartquotes-in-outline_00"), ("bugs", "smartquotes-on-newline_00"), ("bugs", "square-base_00"), @@ -39,13 +59,19 @@ pub const STD_TEST_FILES: &[(&str, &str)] = &[ ("layout", "align_03"), ("layout", "align_04"), ("layout", "align_05"), + ("layout", "align_06"), ("layout", "block-sizing_00"), ("layout", "block-sizing_01"), ("layout", "block-spacing_00"), + ("layout", "cjk-latin-spacing_00"), + ("layout", "cjk-punctuation-adjustment_00"), + ("layout", "cjk-punctuation-adjustment_01"), + ("layout", "cjk-punctuation-adjustment_02"), ("layout", "clip_00"), ("layout", "clip_01"), ("layout", "clip_02"), ("layout", "clip_03"), + ("layout", "clip_04"), ("layout", "columns_00"), ("layout", "columns_01"), ("layout", "columns_02"), @@ -67,6 +93,7 @@ pub const STD_TEST_FILES: &[(&str, &str)] = &[ ("layout", "enum-align_01"), ("layout", "enum-align_02"), ("layout", "enum-align_03"), + ("layout", "enum-align_04"), ("layout", "enum-numbering_00"), ("layout", "enum-numbering_01"), ("layout", "enum-numbering_02"), @@ -133,6 +160,9 @@ pub const STD_TEST_FILES: &[(&str, &str)] = &[ ("layout", "page-margin_00"), ("layout", "page-margin_01"), ("layout", "page-marginals_00"), + ("layout", "page-number-align_00"), + ("layout", "page-number-align_01"), + ("layout", "page-number-align_02"), ("layout", "page-style_00"), ("layout", "page-style_01"), ("layout", "page-style_02"), @@ -145,6 +175,9 @@ pub const STD_TEST_FILES: &[(&str, &str)] = &[ ("layout", "page_05"), ("layout", "pagebreak-parity_00"), ("layout", "pagebreak-parity_01"), + ("layout", "pagebreak-weak_00"), + ("layout", "pagebreak-weak_01"), + ("layout", "pagebreak-weak_02"), ("layout", "pagebreak_00"), ("layout", "pagebreak_01"), ("layout", "pagebreak_02"), @@ -173,6 +206,9 @@ pub const STD_TEST_FILES: &[(&str, &str)] = &[ ("layout", "par-justify_02"), ("layout", "par-justify_03"), ("layout", "par-justify_04"), + ("layout", "par-justify_05"), + ("layout", "par-justify_06"), + ("layout", "par-justify_07"), ("layout", "par-knuth_00"), ("layout", "par-simple_00"), ("layout", "par_00"), @@ -183,6 +219,10 @@ pub const STD_TEST_FILES: &[(&str, &str)] = &[ ("layout", "place-float-auto_00"), ("layout", "place-float-auto_01"), ("layout", "place-float-auto_02"), + ("layout", "place-float-auto_03"), + ("layout", "place-float-auto_04"), + ("layout", "place-float-auto_05"), + ("layout", "place-float-columns_00"), ("layout", "place-float-figure_00"), ("layout", "place-nested_00"), ("layout", "place-nested_01"), @@ -200,6 +240,7 @@ pub const STD_TEST_FILES: &[(&str, &str)] = &[ ("layout", "spacing_01"), ("layout", "spacing_02"), ("layout", "spacing_03"), + ("layout", "spacing_04"), ("layout", "stack-1_00"), ("layout", "stack-1_01"), ("layout", "stack-1_02"), @@ -211,6 +252,7 @@ pub const STD_TEST_FILES: &[(&str, &str)] = &[ ("layout", "table_02"), ("layout", "table_03"), ("layout", "table_04"), + ("layout", "table_05"), ("layout", "terms_00"), ("layout", "terms_01"), ("layout", "terms_02"), @@ -253,12 +295,15 @@ pub const STD_TEST_FILES: &[(&str, &str)] = &[ ("math", "attach-p3_04"), ("math", "attach-p3_05"), ("math", "attach-p3_06"), + ("math", "block-alignment_00"), + ("math", "block-alignment_01"), ("math", "cancel_00"), ("math", "cancel_01"), ("math", "cancel_02"), ("math", "cancel_03"), ("math", "cancel_04"), ("math", "cancel_05"), + ("math", "cancel_06"), ("math", "cases_00"), ("math", "class_00"), ("math", "class_01"), @@ -288,6 +333,7 @@ pub const STD_TEST_FILES: &[(&str, &str)] = &[ ("math", "frac_05"), ("math", "frac_06"), ("math", "frac_07"), + ("math", "frac_08"), ("math", "matrix-alignment_00"), ("math", "matrix-alignment_01"), ("math", "matrix-alignment_02"), @@ -295,6 +341,10 @@ pub const STD_TEST_FILES: &[(&str, &str)] = &[ ("math", "matrix-alignment_04"), ("math", "matrix-alignment_05"), ("math", "matrix-alignment_06"), + ("math", "matrix-gaps_00"), + ("math", "matrix-gaps_01"), + ("math", "matrix-gaps_02"), + ("math", "matrix-gaps_03"), ("math", "matrix_00"), ("math", "matrix_01"), ("math", "matrix_02"), @@ -302,6 +352,9 @@ pub const STD_TEST_FILES: &[(&str, &str)] = &[ ("math", "matrix_04"), ("math", "matrix_05"), ("math", "matrix_06"), + ("math", "matrix_07"), + ("math", "matrix_08"), + ("math", "matrix_09"), ("math", "multiline_00"), ("math", "multiline_01"), ("math", "multiline_02"), @@ -324,6 +377,8 @@ pub const STD_TEST_FILES: &[(&str, &str)] = &[ ("math", "opticalsize_04"), ("math", "opticalsize_05"), ("math", "opticalsize_06"), + ("math", "opticalsize_07"), + ("math", "prime_00"), ("math", "root_00"), ("math", "root_01"), ("math", "root_02"), @@ -335,6 +390,7 @@ pub const STD_TEST_FILES: &[(&str, &str)] = &[ ("math", "spacing_02"), ("math", "spacing_03"), ("math", "spacing_04"), + ("math", "spacing_05"), ("math", "style_00"), ("math", "style_01"), ("math", "style_02"), @@ -374,10 +430,16 @@ pub const STD_TEST_FILES: &[(&str, &str)] = &[ ("meta", "document_05"), ("meta", "document_06"), ("meta", "document_07"), + ("meta", "document_08"), + ("meta", "figure-caption_00"), + ("meta", "figure-caption_01"), + ("meta", "figure-caption_02"), ("meta", "figure_00"), ("meta", "figure_01"), ("meta", "figure_02"), ("meta", "figure_03"), + ("meta", "figure_04"), + ("meta", "figure_05"), ("meta", "footnote-break_00"), ("meta", "footnote-columns_00"), ("meta", "footnote-container_00"), @@ -426,6 +488,7 @@ pub const STD_TEST_FILES: &[(&str, &str)] = &[ ("meta", "outline-indent_01"), ("meta", "outline-indent_02"), ("meta", "outline_00"), + ("meta", "page-label_00"), ("meta", "query-before-after_00"), ("meta", "query-before-after_01"), ("meta", "query-figure_00"), @@ -447,6 +510,13 @@ pub const STD_TEST_FILES: &[(&str, &str)] = &[ ("text", "deco_00"), ("text", "deco_01"), ("text", "deco_02"), + ("text", "deco_03"), + ("text", "deco_04"), + ("text", "deco_05"), + ("text", "deco_06"), + ("text", "deco_07"), + ("text", "deco_08"), + ("text", "deco_09"), ("text", "edge_00"), ("text", "edge_01"), ("text", "edge_02"), @@ -518,17 +588,37 @@ pub const STD_TEST_FILES: &[(&str, &str)] = &[ ("text", "lorem_02"), ("text", "microtype_00"), ("text", "microtype_01"), + ("text", "numbers_00"), + ("text", "numbers_01"), + ("text", "numbers_02"), + ("text", "numbers_03"), + ("text", "numbers_04"), + ("text", "numbers_05"), + ("text", "numbers_06"), + ("text", "quote_00"), + ("text", "quote_01"), + ("text", "quote_02"), + ("text", "quote_03"), + ("text", "quote_04"), + ("text", "quote_05"), + ("text", "quote_06"), ("text", "quotes_00"), ("text", "quotes_01"), ("text", "quotes_02"), ("text", "quotes_03"), ("text", "quotes_04"), ("text", "quotes_05"), + ("text", "quotes_06"), ("text", "raw-align_00"), ("text", "raw-align_01"), ("text", "raw-align_02"), ("text", "raw-code_00"), + ("text", "raw-line_00"), + ("text", "raw-line_01"), + ("text", "raw-line_02"), + ("text", "raw-line_03"), ("text", "raw-syntaxes_00"), + ("text", "raw-tabs_00"), ("text", "raw-theme_00"), ("text", "raw_00"), ("text", "raw_01"), @@ -549,6 +639,11 @@ pub const STD_TEST_FILES: &[(&str, &str)] = &[ ("text", "shift_00"), ("text", "shift_01"), ("text", "shift_02"), + ("text", "smartquotes_00"), + ("text", "smartquotes_01"), + ("text", "smartquotes_02"), + ("text", "smartquotes_03"), + ("text", "smartquotes_04"), ("text", "space_00"), ("text", "space_01"), ("text", "space_02"), @@ -564,6 +659,42 @@ pub const STD_TEST_FILES: &[(&str, &str)] = &[ ("text", "tracking-spacing_03"), ("text", "tracking-spacing_04"), ("text", "tracking-spacing_05"), + ("visualize", "gradient-conic_00"), + ("visualize", "gradient-conic_01"), + ("visualize", "gradient-conic_02"), + ("visualize", "gradient-conic_03"), + ("visualize", "gradient-dir_00"), + ("visualize", "gradient-presets_00"), + ("visualize", "gradient-radial_00"), + ("visualize", "gradient-radial_01"), + ("visualize", "gradient-radial_02"), + ("visualize", "gradient-radial_03"), + ("visualize", "gradient-relative-conic_00"), + ("visualize", "gradient-relative-conic_01"), + ("visualize", "gradient-relative-linear_00"), + ("visualize", "gradient-relative-linear_01"), + ("visualize", "gradient-relative-radial_00"), + ("visualize", "gradient-relative-radial_01"), + ("visualize", "gradient-repeat_00"), + ("visualize", "gradient-repeat_01"), + ("visualize", "gradient-repeat_02"), + ("visualize", "gradient-repeat_03"), + ("visualize", "gradient-repeat_04"), + ("visualize", "gradient-sharp_00"), + ("visualize", "gradient-sharp_01"), + ("visualize", "gradient-stroke_00"), + ("visualize", "gradient-stroke_01"), + ("visualize", "gradient-stroke_02"), + ("visualize", "gradient-stroke_03"), + ("visualize", "gradient-text-decorations_00"), + ("visualize", "gradient-text-other_00"), + ("visualize", "gradient-text-other_01"), + ("visualize", "gradient-text_00"), + ("visualize", "gradient-text_01"), + ("visualize", "gradient-text_02"), + ("visualize", "gradient-text_03"), + ("visualize", "gradient-text_04"), + ("visualize", "gradient-transform_00"), ("visualize", "image_00"), ("visualize", "image_01"), ("visualize", "image_02"), @@ -625,5 +756,6 @@ pub const STD_TEST_FILES: &[(&str, &str)] = &[ ("visualize", "stroke_05"), ("visualize", "stroke_06"), ("visualize", "stroke_07"), - ("visualize", "svg-text_00") + ("visualize", "svg-text_00"), + ("visualize", "svg-text_01") ]; diff --git a/tests/integration/src/tests.rs b/tests/integration/src/tests.rs index d129a61e..3e491974 100644 --- a/tests/integration/src/tests.rs +++ b/tests/integration/src/tests.rs @@ -562,7 +562,7 @@ mod tests { --- name: layout_transform_00_artifact_ir data_content_phash: "phash-gradient:AAAAAAAAAAAASGgAAIIAzGgBzWgBYWsAuGsBgNkCVPMACLIAAEAAkDIAwLIAlHoAkHQAmDUAFD0AAE0AEC0AAAAAAAAAAAAA" - text_content_hash: "sha256:7edb598501de9a2babff930a6d3cfc64d0e4edc90999d7cced03eae759a520ac" + text_content_hash: "sha256:3cf0c26e1d53973b2cb02aaa6084116ac7e4ff1b54c1bf5cb944f40ddcfafe61" "###); check_canvas_render_test_point!(@r###" --- @@ -586,13 +586,13 @@ mod tests { --- name: math_main_artifact_ir data_content_phash: "phash-gradient:AAAAgNwAAMQAmAYA2M4AAMgAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAABAA" - text_content_hash: "sha256:b00908ed4b0670c6eba39a3476a22b94b9dae9cd057c39e8e9e95a72c177cba2" + text_content_hash: "sha256:1a243912d1aa482060284b7154af2b7ebacfe76268abbf4e90547c93a07559e6" "###); check_canvas_render_test_point!(@r###" --- name: math_undergradmath_artifact_ir - data_content_phash: "phash-gradient:YM7N5Kz17Cb3MM92MMdmmEbkmMzmSA5jzI7YsCf/ZI5k5Iz8qI7ceIbpNI98qM/WwAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - text_content_hash: "sha256:9521d1dcaaadb4af01dfd55b90afe9562a5c9c651111711700e5a874b2dd0f9e" + data_content_phash: "phash-gradient:YM7N5Kz17Cb3MM92MMdmmEbkmMzmSA5jzI7YsIf3ZI5k5Iz8qI7ceIbpNI98qG/WwAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + text_content_hash: "sha256:d12bf636df32b6f8cf615f8be058dd3141d48c02b76ac6b861be4f598269d1d3" "###); // todo: the size of cjk font file is quite big // check_canvas_render_test_point!(@r###" @@ -616,8 +616,8 @@ mod tests { check_canvas_render_test_point!(@r###" --- name: text_deco_01_artifact_ir - data_content_phash: "phash-gradient:AAAAhAAkYCQChABklCVlICZlJEtkSABg2AsJ0FoawJ5b2CIlAAMAVF0BoNECIOkCWGMBoIwAoC0AKC4AOC0AAAwAIAIAAAAA" - text_content_hash: "sha256:683a8f00ef1a4cb609f8cdf6afb11342e1a00f0e6685f187e97c76e3a2b4eb39" + data_content_phash: "phash-gradient:AAAAAAAghAAAYCRihABklCVlaSZlIG9kAAhgwGQKSAMpwEo6yNxawA4byAABAEMSUA0B1N0AIPECYOkCCGMBAJAAACEAAAAA" + text_content_hash: "sha256:7d06f127160753245ec72b1f3fc062bea7e30fa559048a71912b674ce698e8e5" "###); check_canvas_render_test_point!(@r###" --- @@ -698,7 +698,7 @@ mod tests { check_canvas_render_test_point!(@r###" --- name: visualize_polygon_00_artifact_ir - data_content_phash: "phash-gradient:AAAAAAAAgAAAYAYAgAEAgBMAYAYAQF4AgB8AgAEAgAsAgAEAQAcK4AYAAPAfAPAPwAAA5GABgAwBIMAAAMEeANYfwMMAAMAK" + data_content_phash: "phash-gradient:AAAAYAYAgAMAgB8AAB4AgEEEwAcAAPYPwPAP4AAAAMEeAMMeEPgAAPAHpMcGJPAACHAvAIcHAIofAPAfAPAvAMoPBNIvBMIP" text_content_hash: "sha256:7697c705e134fe39094c2ad9d6076210e20079cb32d7479079961e97237081d1" "###); check_canvas_render_test_point!(@r###" @@ -717,7 +717,7 @@ mod tests { --- name: visualize_shape-aspect_01_artifact_ir data_content_phash: "phash-gradient:AAAAAAQAAAAAiA4AJK0B3AwCkM8FlCULJBQCFJkWFIkUFAkUFMkNFMkNRIkFFAkUxNkUxJkWJDQKVC0DKM4EABACCIYAAAAA" - text_content_hash: "sha256:947b9874583143663e9972ece2d1cfb35f47050b2115567a6c8a96258489a4e6" + text_content_hash: "sha256:bedfe90689b8d4883245ebb688ce48e306cec12e697921a47d6cffcb775d8961" "###); check_canvas_render_test_point!(@r###" --- diff --git a/tools/rkyv-assertions/src/main.rs b/tools/rkyv-assertions/src/main.rs index af048b9c..4f2201d0 100644 --- a/tools/rkyv-assertions/src/main.rs +++ b/tools/rkyv-assertions/src/main.rs @@ -55,5 +55,6 @@ fn main() { ArchivedPage, ArchivedLayoutRegion, ArchivedBuildInfo, + ArchivedColorItem, ); }