From ed6550fdb08eae92bffab6b6b137b1e0eebf62c6 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Fri, 10 Jun 2022 23:53:20 +0200 Subject: [PATCH] Bump dependencies --- Cargo.lock | 537 ++++------------------ Cargo.toml | 22 +- src/diag.rs | 38 +- src/eval/mod.rs | 11 +- src/export/mod.rs | 1 - src/export/pdf.rs | 103 +++-- src/export/subset.rs | 814 ---------------------------------- src/font.rs | 27 +- src/geom/paint.rs | 10 +- src/image.rs | 4 +- src/library/layout/page.rs | 5 +- src/library/math/rex.rs | 6 +- src/library/structure/list.rs | 6 +- src/library/text/raw.rs | 7 +- src/library/text/shaping.rs | 6 +- src/main.rs | 7 +- src/parse/mod.rs | 5 +- src/parse/parser.rs | 11 +- src/parse/tokens.rs | 17 +- src/syntax/mod.rs | 26 +- src/syntax/span.rs | 58 ++- tests/typeset.rs | 5 +- 22 files changed, 309 insertions(+), 1417 deletions(-) delete mode 100644 src/export/subset.rs diff --git a/Cargo.lock b/Cargo.lock index 2f35c121b..7f0b81a6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,15 +23,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "approx" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" -dependencies = [ - "num-traits", -] - [[package]] name = "arrayref" version = "0.3.6" @@ -92,12 +83,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "bumpalo" -version = "3.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" - [[package]] name = "bytemuck" version = "1.9.1" @@ -150,28 +135,6 @@ dependencies = [ "matches", ] -[[package]] -name = "decorum" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "281759d3c8a14f5c3f0c49363be56810fcd7f910422f97f2db850c2920fde5cf" -dependencies = [ - "approx", - "num-traits", - "serde", - "serde_derive", -] - -[[package]] -name = "deflate" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" -dependencies = [ - "adler32", - "byteorder", -] - [[package]] name = "deflate" version = "1.0.0" @@ -219,14 +182,12 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39522e96686d38f4bc984b9198e3a0613264abaebaff2c5c918bfa6b6da09af" +checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" dependencies = [ - "cfg-if", "crc32fast", - "libc", - "miniz_oxide 0.5.1", + "miniz_oxide", ] [[package]] @@ -241,25 +202,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "font" -version = "0.1.0" -source = "git+https://github.com/pdf-rs/font#f0ea791543140ccbe0d4fb20bac363ab66c53f68" -dependencies = [ - "decorum", - "indexmap", - "itertools", - "log", - "nom", - "pathfinder_color", - "pathfinder_content", - "pathfinder_geometry", - "pdf_encoding", - "rand 0.7.3", - "slotmap", - "tuple", -] - [[package]] name = "fxhash" version = "0.2.1" @@ -269,19 +211,6 @@ dependencies = [ "byteorder", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - [[package]] name = "getrandom" version = "0.2.6" @@ -290,14 +219,18 @@ checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" dependencies = [ "cfg-if", "libc", - "wasi 0.10.2+wasi-snapshot-preview1", + "wasi", ] [[package]] -name = "hashbrown" -version = "0.11.2" +name = "gif" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +checksum = "c3a7187e78088aead22ceedeee99779455b23fc231fe13ec443f99bb71694e5b" +dependencies = [ + "color_quant", + "weezl", +] [[package]] name = "hypher" @@ -315,28 +248,19 @@ dependencies = [ [[package]] name = "image" -version = "0.23.14" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" +checksum = "28edd9d7bc256be2502e325ac0628bde30b7001b9b52e0abe31a1a9dc2701212" dependencies = [ "bytemuck", "byteorder", "color_quant", + "gif", "jpeg-decoder", "num-iter", "num-rational", "num-traits", - "png 0.16.8", -] - -[[package]] -name = "indexmap" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" -dependencies = [ - "autocfg", - "hashbrown", + "png", ] [[package]] @@ -348,12 +272,6 @@ dependencies = [ "either", ] -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - [[package]] name = "itoa" version = "1.0.2" @@ -362,18 +280,9 @@ checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" [[package]] name = "jpeg-decoder" -version = "0.1.22" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" - -[[package]] -name = "js-sys" -version = "0.3.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397" -dependencies = [ - "wasm-bindgen", -] +checksum = "9478aa10f73e7528198d75109c8be5cd7d15fb530238040148d5f9a22d4c5b3b" [[package]] name = "kurbo" @@ -390,12 +299,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "lexical-core" version = "0.7.6" @@ -415,22 +318,13 @@ version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" -[[package]] -name = "line-wrap" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" -dependencies = [ - "safemem", -] - [[package]] name = "lipsum" -version = "0.8.0" -source = "git+https://github.com/reknih/lipsum#c97ce95ba01ed2cce1d1b0b230b6b78295b0720b" +version = "0.8.2" +source = "git+https://github.com/reknih/lipsum#d6d8f2cba12f8dee8c8ed4af62858cdb061c0801" dependencies = [ - "rand 0.8.5", - "rand_chacha 0.3.1", + "rand", + "rand_chacha", ] [[package]] @@ -456,37 +350,18 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memmap2" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057a3db23999c867821a7a59feb06a578fcb03685e983dff90daf9e7d24ac08f" +checksum = "d5172b50c23043ff43dd53e51392f36519d9b35a8f3a410d30ece5d1aedd58ae" dependencies = [ "libc", ] [[package]] name = "miniz_oxide" -version = "0.3.7" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" -dependencies = [ - "adler32", -] - -[[package]] -name = "miniz_oxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg", -] - -[[package]] -name = "miniz_oxide" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" +checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" dependencies = [ "adler", ] @@ -525,9 +400,9 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.3.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" dependencies = [ "autocfg", "num-integer", @@ -543,90 +418,23 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_threads" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ - "libc", -] - [[package]] name = "once_cell" -version = "1.10.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" - -[[package]] -name = "pathfinder_color" -version = "0.5.0" -source = "git+https://github.com/servo/pathfinder/#038a3476d803fd77a6e66a74117b5b8803a2cb49" -dependencies = [ - "pathfinder_simd", -] - -[[package]] -name = "pathfinder_content" -version = "0.5.0" -source = "git+https://github.com/servo/pathfinder/#038a3476d803fd77a6e66a74117b5b8803a2cb49" -dependencies = [ - "arrayvec 0.5.2", - "bitflags", - "image", - "log", - "pathfinder_color", - "pathfinder_geometry", - "pathfinder_simd", - "smallvec", -] - -[[package]] -name = "pathfinder_geometry" -version = "0.5.1" -source = "git+https://github.com/servo/pathfinder/#038a3476d803fd77a6e66a74117b5b8803a2cb49" -dependencies = [ - "log", - "pathfinder_simd", -] - -[[package]] -name = "pathfinder_simd" -version = "0.5.1" -source = "git+https://github.com/servo/pathfinder/#038a3476d803fd77a6e66a74117b5b8803a2cb49" -dependencies = [ - "rustc_version", -] +checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" [[package]] name = "pdf-writer" -version = "0.4.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36d760a6f2ac90811cba1006a298e8a7e5ce2c922bb5dc7f7000911a4a6b60f4" +checksum = "249f9b33a3192626f2cd9f4b0cd66c1ec32d65968d58cf4d8239977feddddead" dependencies = [ "bitflags", - "itoa 0.4.8", + "itoa", "ryu", ] -[[package]] -name = "pdf_encoding" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed7468173909bb32dbc74ca454c82dfdfe994ad1133ddf78d6c31715c9b88c40" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "pest" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" -dependencies = [ - "ucd-trie", -] - [[package]] name = "pico-args" version = "0.4.2" @@ -636,37 +444,11 @@ checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" [[package]] name = "pixglyph" version = "0.1.0" -source = "git+https://github.com/typst/pixglyph#8ee0d4517d887125e9184916780ac230e40a042a" +source = "git+https://github.com/typst/pixglyph#f1aae13ae622f4640a1cfac231525f02978fc305" dependencies = [ "ttf-parser", ] -[[package]] -name = "plist" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd39bc6cdc9355ad1dc5eeedefee696bb35c34caf21768741e81826c0bbd7225" -dependencies = [ - "base64", - "indexmap", - "line-wrap", - "serde", - "time", - "xml-rs", -] - -[[package]] -name = "png" -version = "0.16.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" -dependencies = [ - "bitflags", - "crc32fast", - "deflate 0.8.6", - "miniz_oxide 0.3.7", -] - [[package]] name = "png" version = "0.17.5" @@ -675,8 +457,8 @@ checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" dependencies = [ "bitflags", "crc32fast", - "deflate 1.0.0", - "miniz_oxide 0.5.1", + "deflate", + "miniz_oxide", ] [[package]] @@ -703,36 +485,13 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "rand_core 0.6.3", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", + "rand_core", ] [[package]] @@ -742,16 +501,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.3", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", + "rand_core", ] [[package]] @@ -760,15 +510,6 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - [[package]] name = "rctree" version = "0.4.0" @@ -790,16 +531,16 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.6", + "getrandom", "redox_syscall", "thiserror", ] [[package]] name = "regex" -version = "1.5.5" +version = "1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" dependencies = [ "aho-corasick", "memchr", @@ -808,20 +549,21 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" [[package]] name = "resvg" -version = "0.20.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d94a32ca845cdda27237a40beba9bd3d3858ac8fc5356eb9442bdeecfe34d9e0" +checksum = "2e702d1e8e00a3a0717b96244cba840f34f542d8f23097c8903266c4e2975658" dependencies = [ + "gif", "jpeg-decoder", "log", "pico-args", - "png 0.17.5", + "png", "rgb", "svgtypes", "tiny-skia", @@ -831,9 +573,11 @@ dependencies = [ [[package]] name = "rex" version = "0.1.2" -source = "git+https://github.com/laurmaedje/ReX#7362b0cbb229211d6206198d80382a9b23eda993" +source = "git+https://github.com/laurmaedje/ReX#6e4bada20d2b1685940e2d752630cb9ad3d797b0" dependencies = [ - "font", + "itertools", + "nom", + "ttf-parser", "unicode-math", ] @@ -855,20 +599,11 @@ dependencies = [ "xmlparser", ] -[[package]] -name = "rustc_version" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" -dependencies = [ - "semver", -] - [[package]] name = "rustybuzz" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44561062e583c4873162861261f16fd1d85fe927c4904d71329a4fe43dc355ef" +checksum = "25ff94f20221325d000e552781713e53b0d85c1d9551b6f420d12daf5a08eace" dependencies = [ "bitflags", "bytemuck", @@ -895,12 +630,6 @@ dependencies = [ "bytemuck", ] -[[package]] -name = "safemem" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" - [[package]] name = "same-file" version = "1.0.6" @@ -910,24 +639,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - [[package]] name = "serde" version = "1.0.137" @@ -954,7 +665,7 @@ version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" dependencies = [ - "itoa 1.0.2", + "itoa", "ryu", "serde", ] @@ -974,12 +685,6 @@ version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" -[[package]] -name = "slotmap" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bf34684c5767b87de9119790e92e9a1d60056be2ceeaf16a8e6ef13082aeab1" - [[package]] name = "smallvec" version = "1.8.0" @@ -992,14 +697,19 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "subsetter" +version = "0.1.0" +source = "git+https://github.com/typst/subsetter#51c0eaf166631917bb29b0a1bddac276472c8e6c" + [[package]] name = "svg2pdf" -version = "0.2.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7feae49dae1a460ecd13b50e4389204672daac9c7133fd830132f44486ab84d" +checksum = "2f7148684e1eb24d211bf6a5bd6dad43957d665b1fcfc77daf9a72961fe41d33" dependencies = [ "image", - "miniz_oxide 0.4.4", + "miniz_oxide", "pdf-writer", "usvg", ] @@ -1015,9 +725,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942" +checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf" dependencies = [ "proc-macro2", "quote", @@ -1026,9 +736,9 @@ dependencies = [ [[package]] name = "syntect" -version = "4.6.0" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b20815bbe80ee0be06e6957450a841185fcf690fe0178f14d77a05ce2caa031" +checksum = "c6c454c27d9d7d9a84c7803aaa3c50cd088d2906fe3c6e42da3209aa623576a8" dependencies = [ "bincode", "bitflags", @@ -1036,12 +746,12 @@ dependencies = [ "flate2", "fnv", "lazy_static", - "lazycell", - "plist", + "once_cell", "regex-syntax", "serde", "serde_derive", "serde_json", + "thiserror", "walkdir", ] @@ -1074,45 +784,25 @@ dependencies = [ "syn", ] -[[package]] -name = "time" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd" -dependencies = [ - "itoa 1.0.2", - "libc", - "num_threads", -] - [[package]] name = "tiny-skia" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bcfd4339bdd4545eabed74b208f2f1555f2e6540fb58135c01f46c0940aa138" +checksum = "78b3e1db967020dd509b49cecc024025beba2b1b6cf204618610ba266269d6b9" dependencies = [ "arrayref", "arrayvec 0.5.2", "bytemuck", "cfg-if", - "png 0.17.5", + "png", "safe_arch", ] [[package]] name = "ttf-parser" -version = "0.12.3" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ae2f58a822f08abdaf668897e96a5656fe72f5a9ce66422423e8849384872e6" - -[[package]] -name = "tuple" -version = "0.5.1" -source = "git+https://github.com/s3bk/tuple/#fdf8b4400ffb10506c711018a3cb918412a3c8c1" -dependencies = [ - "num-traits", - "serde", -] +checksum = "42d4b50cba812f0f04f0707bb6a0eaa5fae4ae05d90fc2a377998d2f21e77a1c" [[package]] name = "typed-arena" @@ -1127,7 +817,6 @@ dependencies = [ "bytemuck", "codespan-reporting", "dirs", - "either", "flate2", "fxhash", "hypher", @@ -1136,7 +825,7 @@ dependencies = [ "kurbo", "lipsum", "memmap2", - "miniz_oxide 0.4.4", + "miniz_oxide", "once_cell", "pdf-writer", "pico-args", @@ -1148,6 +837,7 @@ dependencies = [ "rustybuzz", "same-file", "serde", + "subsetter", "svg2pdf", "syntect", "tiny-skia", @@ -1173,12 +863,6 @@ dependencies = [ "syn", ] -[[package]] -name = "ucd-trie" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" - [[package]] name = "unicode-bidi" version = "0.3.8" @@ -1244,13 +928,14 @@ checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" [[package]] name = "unscanny" version = "0.1.0" -source = "git+https://github.com/typst/unscanny#168fa7a05fe2931f86a788e26d7bfb67185767b5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9df2af067a7953e9c3831320f35c1cc0600c30d44d9f7a12b01db1cd88d6b47" [[package]] name = "usvg" -version = "0.20.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f064d38f79ff69e3160e2fba884e4ede897061c15178041a3976371c68cab1" +checksum = "a261d60a7215fa339482047cc3dafd4e22e2bf34396aaebef2b707355bbb39c0" dependencies = [ "base64", "data-url", @@ -1283,12 +968,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" @@ -1296,58 +975,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] -name = "wasm-bindgen" -version = "0.2.80" +name = "weezl" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4" -dependencies = [ - "bumpalo", - "lazy_static", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" +checksum = "9c97e489d8f836838d497091de568cf16b117486d529ec5579233521065bd5e4" [[package]] name = "winapi" @@ -1386,12 +1017,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a" -[[package]] -name = "xml-rs" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" - [[package]] name = "xmlparser" version = "0.13.3" diff --git a/Cargo.toml b/Cargo.toml index 494a912f3..8db050c2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,20 +15,19 @@ typst-macros = { path = "./macros" } # Utilities bytemuck = "1" -either = "1" fxhash = "0.2" lipsum = { git = "https://github.com/reknih/lipsum", default-features = false } once_cell = "1" serde = { version = "1", features = ["derive"] } typed-arena = "2" -unscanny = { git = "https://github.com/typst/unscanny" } +unscanny = "0.1" regex = "1" # Text and font handling hypher = "0.1" kurbo = "0.8" -ttf-parser = "0.12" -rustybuzz = "0.4" +ttf-parser = "0.15" +rustybuzz = "0.5" unicode-bidi = "0.3.5" unicode-segmentation = "1" unicode-xid = "0.2" @@ -36,22 +35,23 @@ unicode-script = "0.5" xi-unicode = "0.3" # Raster and vector graphics handling -image = { version = "0.23", default-features = false, features = ["png", "jpeg"] } -usvg = { version = "0.20", default-features = false } +image = { version = "0.24", default-features = false, features = ["png", "jpeg", "gif"] } +usvg = { version = "0.22", default-features = false } # External implementation of user-facing features -syntect = { version = "4.6", default-features = false, features = ["dump-load", "parsing", "regex-fancy", "assets"] } +syntect = { version = "5", default-features = false, features = ["default-syntaxes", "regex-fancy"] } rex = { git = "https://github.com/laurmaedje/ReX" } # PDF export -miniz_oxide = "0.4" -pdf-writer = "0.4" -svg2pdf = "0.2" +miniz_oxide = "0.5" +pdf-writer = "0.6" +subsetter = { git = "https://github.com/typst/subsetter" } +svg2pdf = "0.4" # Raster export / rendering tiny-skia = "0.6.2" pixglyph = { git = "https://github.com/typst/pixglyph" } -resvg = { version = "0.20", default-features = false } +resvg = { version = "0.22", default-features = false } roxmltree = "0.14" flate2 = "1" diff --git a/src/diag.rs b/src/diag.rs index b9fcebdf9..76cad792b 100644 --- a/src/diag.rs +++ b/src/diag.rs @@ -1,9 +1,9 @@ //! Diagnostics. use std::fmt::{self, Display, Formatter}; -use std::ops::Range; use crate::syntax::{Span, Spanned}; +use crate::Context; /// Early-return with a [`TypError`]. #[macro_export] @@ -39,8 +39,6 @@ pub type StrResult = Result; pub struct Error { /// The erroneous node in the source code. pub span: Span, - /// Where in the node the error should be annotated. - pub pos: ErrorPos, /// A diagnostic message describing the problem. pub message: String, /// The trace of function calls leading to the error. @@ -52,35 +50,12 @@ impl Error { pub fn new(span: Span, message: impl Into) -> Self { Self { span, - pos: ErrorPos::Full, trace: vec![], message: message.into(), } } } -/// Where in a node an error should be annotated. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum ErrorPos { - /// At the start of the node. - Start, - /// Over the full width of the node. - Full, - /// At the end of the node. - End, -} - -impl ErrorPos { - /// Apply this to a node's byte range. - pub fn apply(self, range: Range) -> Range { - match self { - ErrorPos::Start => range.start .. range.start, - ErrorPos::Full => range, - ErrorPos::End => range.end .. range.end, - } - } -} - /// A part of an error's [trace](Error::trace). #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] pub enum Tracepoint { @@ -124,18 +99,25 @@ where /// Enrich a [`TypResult`] with a tracepoint. pub trait Trace { /// Add the tracepoint to all errors that lie outside the `span`. - fn trace(self, make_point: F, span: Span) -> Self + fn trace(self, ctx: &Context, make_point: F, span: Span) -> Self where F: Fn() -> Tracepoint; } impl Trace for TypResult { - fn trace(self, make_point: F, span: Span) -> Self + fn trace(self, ctx: &Context, make_point: F, span: Span) -> Self where F: Fn() -> Tracepoint, { self.map_err(|mut errors| { + let range = ctx.sources.range(span); for error in errors.iter_mut() { + // Skip traces that surround the error. + let error_range = ctx.sources.range(error.span); + if range.start <= error_range.start && range.end >= error_range.end { + continue; + } + error.trace.push(Spanned::new(make_point(), span)); } errors diff --git a/src/eval/mod.rs b/src/eval/mod.rs index d39fc3ad3..b1cf9e0bb 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -606,7 +606,7 @@ impl Eval for FuncCall { Value::Dict(dict) => dict.get(&args.into_key()?).at(self.span())?.clone(), Value::Func(func) => { let point = || Tracepoint::Call(func.name().map(ToString::to_string)); - func.call(vm, args).trace(point, self.span())? + func.call(vm, args).trace(vm.ctx, point, self.span())? } v => bail!( @@ -629,12 +629,13 @@ impl Eval for MethodCall { Ok(if methods::is_mutating(&method) { let args = self.args().eval(vm)?; let mut value = self.receiver().access(vm)?; - methods::call_mut(&mut value, &method, args, span).trace(point, span)?; + methods::call_mut(&mut value, &method, args, span) + .trace(vm.ctx, point, span)?; Value::None } else { let value = self.receiver().eval(vm)?; let args = self.args().eval(vm)?; - methods::call(vm, value, &method, args, span).trace(point, span)? + methods::call(vm, value, &method, args, span).trace(vm.ctx, point, span)? }) } } @@ -980,7 +981,9 @@ fn import(vm: &mut Machine, path: &str, span: Span) -> TypResult { // Evaluate the file. let route = vm.route.clone(); - let module = evaluate(vm.ctx, id, route).trace(|| Tracepoint::Import, span)?; + let module = + evaluate(vm.ctx, id, route).trace(vm.ctx, || Tracepoint::Import, span)?; + vm.deps.extend(module.deps.iter().cloned()); Ok(module) diff --git a/src/export/mod.rs b/src/export/mod.rs index b782ae131..d690f4dbb 100644 --- a/src/export/mod.rs +++ b/src/export/mod.rs @@ -2,7 +2,6 @@ mod pdf; mod render; -mod subset; pub use pdf::*; pub use render::*; diff --git a/src/export/pdf.rs b/src/export/pdf.rs index aa3e08763..4495bceb3 100644 --- a/src/export/pdf.rs +++ b/src/export/pdf.rs @@ -3,6 +3,7 @@ use std::cmp::Eq; use std::collections::{BTreeMap, HashMap, HashSet}; use std::hash::Hash; +use std::io::Cursor; use std::sync::Arc; use image::{DynamicImage, GenericImageView, ImageFormat, ImageResult, Rgba}; @@ -14,7 +15,6 @@ use pdf_writer::writers::ColorSpace; use pdf_writer::{Content, Filter, Finish, Name, PdfWriter, Rect, Ref, Str, TextStr}; use ttf_parser::{name_id, GlyphId, Tag}; -use super::subset::subset; use crate::font::{find_name, FaceId, FontStore}; use crate::frame::{Destination, Element, Frame, Group, Role, Text}; use crate::geom::{ @@ -24,6 +24,7 @@ use crate::geom::{ use crate::image::{Image, ImageId, ImageStore, RasterImage}; use crate::library::prelude::EcoString; use crate::library::text::Lang; +use crate::util::SliceExt; use crate::Context; /// Export a collection of frames into a PDF file. @@ -39,7 +40,7 @@ pub fn pdf(ctx: &Context, frames: &[Arc]) -> Vec { /// Identifies the color space definitions. const SRGB: Name<'static> = Name(b"srgb"); -const SRGB_GRAY: Name<'static> = Name(b"srgbgray"); +const D65_GRAY: Name<'static> = Name(b"d65gray"); /// An exporter for a whole PDF document. struct PdfExporter<'a> { @@ -155,23 +156,37 @@ impl<'a> PdfExporter<'a> { // Write the CID font referencing the font descriptor. let mut cid = self.writer.cid_font(cid_ref); - cid.subtype(subtype) - .base_font(base_font) - .system_info(system_info) - .font_descriptor(descriptor_ref) - .widths() - .consecutive(0, { - let num_glyphs = ttf.number_of_glyphs(); - (0 .. num_glyphs).map(|g| { - let x = ttf.glyph_hor_advance(GlyphId(g)).unwrap_or(0); - face.to_em(x).to_font_units() - }) - }); + cid.subtype(subtype); + cid.base_font(base_font); + cid.system_info(system_info); + cid.font_descriptor(descriptor_ref); + cid.default_width(0.0); if subtype == CidFontType::Type2 { cid.cid_to_gid_map_predefined(Name(b"Identity")); } + // Extract the widths of all glyphs. + let num_glyphs = ttf.number_of_glyphs(); + let mut widths = vec![0.0; num_glyphs as usize]; + for &g in glyphs { + let x = ttf.glyph_hor_advance(GlyphId(g)).unwrap_or(0); + widths[g as usize] = face.to_em(x).to_font_units(); + } + + // Write all non-zero glyph widths. + let mut first = 0; + let mut width_writer = cid.widths(); + for (w, group) in widths.group_by_key(|&w| w) { + let end = first + group.len(); + if w != 0.0 { + let last = end - 1; + width_writer.same(first as u16, last as u16, w); + } + first = end; + } + + width_writer.finish(); cid.finish(); let mut flags = FontFlags::empty(); @@ -217,7 +232,9 @@ impl<'a> PdfExporter<'a> { // Compute a reverse mapping from glyphs to unicode. let cmap = { let mut mapping = BTreeMap::new(); - for subtable in ttf.character_mapping_subtables() { + for subtable in + ttf.tables().cmap.into_iter().flat_map(|table| table.subtables) + { if subtable.is_unicode() { subtable.codepoints(|n| { if let Some(c) = std::char::from_u32(n) { @@ -245,16 +262,24 @@ impl<'a> PdfExporter<'a> { .filter(Filter::FlateDecode); // Subset and write the face's bytes. - let buffer = face.buffer(); - let subsetted = subset(buffer, face.index(), glyphs); - let data = deflate(subsetted.as_deref().unwrap_or(buffer)); - let mut font_stream = self.writer.stream(data_ref, &data); + let data = face.buffer(); + let subsetted = { + let glyphs: Vec<_> = glyphs.iter().copied().collect(); + let profile = subsetter::Profile::pdf(&glyphs); + subsetter::subset(data, face.index(), profile) + }; + + // Compress and write the face's byte. + let data = subsetted.as_deref().unwrap_or(data); + let data = deflate(data); + let mut stream = self.writer.stream(data_ref, &data); + stream.filter(Filter::FlateDecode); if subtype == CidFontType::Type0 { - font_stream.pair(Name(b"Subtype"), Name(b"OpenType")); + stream.pair(Name(b"Subtype"), Name(b"OpenType")); } - font_stream.filter(Filter::FlateDecode).finish(); + stream.finish(); } } @@ -346,13 +371,15 @@ impl<'a> PdfExporter<'a> { .uri(Str(uri.as_str().as_bytes())); } Destination::Internal(loc) => { - let index = loc.page - 1; - let height = self.page_heights[index]; - link.action() - .action_type(ActionType::GoTo) - .destination_direct() - .page(self.page_refs[index]) - .xyz(loc.pos.x.to_f32(), height - loc.pos.y.to_f32(), None); + if (1 ..= self.page_heights.len()).contains(&loc.page) { + let index = loc.page - 1; + let height = self.page_heights[index]; + link.action() + .action_type(ActionType::GoTo) + .destination_direct() + .page(self.page_refs[index]) + .xyz(loc.pos.x.to_f32(), height - loc.pos.y.to_f32(), None); + } } } } @@ -360,9 +387,9 @@ impl<'a> PdfExporter<'a> { annotations.finish(); page_writer.finish(); - self.writer - .stream(content_id, &deflate(&page.content.finish())) - .filter(Filter::FlateDecode); + let data = page.content.finish(); + let data = deflate(&data); + self.writer.stream(content_id, &data).filter(Filter::FlateDecode); } fn write_page_tree(&mut self) { @@ -374,7 +401,7 @@ impl<'a> PdfExporter<'a> { let mut resources = pages.resources(); let mut spaces = resources.color_spaces(); spaces.insert(SRGB).start::().srgb(); - spaces.insert(SRGB_GRAY).start::().srgb_gray(); + spaces.insert(D65_GRAY).start::().d65_gray(); spaces.finish(); let mut fonts = resources.fonts(); @@ -855,7 +882,7 @@ impl<'a, 'b> PageExporter<'a, 'b> { let Paint::Solid(color) = fill; match color { Color::Luma(c) => { - self.set_fill_color_space(SRGB_GRAY); + self.set_fill_color_space(D65_GRAY); self.content.set_fill_gray(f(c.0)); } Color::Rgba(c) => { @@ -883,7 +910,7 @@ impl<'a, 'b> PageExporter<'a, 'b> { let Paint::Solid(color) = stroke.paint; match color { Color::Luma(c) => { - self.set_stroke_color_space(SRGB_GRAY); + self.set_stroke_color_space(D65_GRAY); self.content.set_stroke_gray(f(c.0)); } Color::Rgba(c) => { @@ -916,16 +943,16 @@ fn encode_image(img: &RasterImage) -> ImageResult<(Vec, Filter, bool)> { Ok(match (img.format, &img.buf) { // 8-bit gray JPEG. (ImageFormat::Jpeg, DynamicImage::ImageLuma8(_)) => { - let mut data = vec![]; + let mut data = Cursor::new(vec![]); img.buf.write_to(&mut data, img.format)?; - (data, Filter::DctDecode, false) + (data.into_inner(), Filter::DctDecode, false) } // 8-bit Rgb JPEG (Cmyk JPEGs get converted to Rgb earlier). (ImageFormat::Jpeg, DynamicImage::ImageRgb8(_)) => { - let mut data = vec![]; + let mut data = Cursor::new(vec![]); img.buf.write_to(&mut data, img.format)?; - (data, Filter::DctDecode, true) + (data.into_inner(), Filter::DctDecode, true) } // TODO: Encode flate streams with PNG-predictor? diff --git a/src/export/subset.rs b/src/export/subset.rs deleted file mode 100644 index e688d9cc1..000000000 --- a/src/export/subset.rs +++ /dev/null @@ -1,814 +0,0 @@ -//! OpenType font subsetting. - -use std::borrow::Cow; -use std::collections::HashSet; -use std::iter; -use std::ops::Range; - -use ttf_parser::parser::{ - FromData, LazyArray16, LazyArray32, Offset, Offset16, Offset32, Stream, F2DOT14, -}; -use ttf_parser::Tag; - -/// Subset a font face for PDF embedding. -/// -/// This will remove the outlines of all glyphs that are not part of the given -/// slice. Furthmore, all character mapping and layout tables are dropped as -/// shaping has already happened. -/// -/// Returns `None` if the font data is fatally broken (in which case -/// `ttf-parser` would probably already have rejected the font, so this -/// shouldn't happen if the font data has already passed through `ttf-parser`). -pub fn subset(data: &[u8], index: u32, glyphs: &HashSet) -> Option> { - Some(Subsetter::new(data, index, glyphs)?.subset()) -} - -struct Subsetter<'a> { - data: &'a [u8], - magic: Magic, - records: LazyArray16<'a, TableRecord>, - num_glyphs: u16, - glyphs: &'a HashSet, - tables: Vec<(Tag, Cow<'a, [u8]>)>, -} - -impl<'a> Subsetter<'a> { - /// Parse the font header and create a new subsetter. - fn new(data: &'a [u8], index: u32, glyphs: &'a HashSet) -> Option { - let mut s = Stream::new(data); - - let mut magic = s.read::()?; - if magic == Magic::Collection { - // Parse font collection header if necessary. - s.skip::(); - let num_faces = s.read::()?; - let offsets = s.read_array32::(num_faces)?; - let offset = offsets.get(index)?.to_usize(); - - s = Stream::new_at(data, offset)?; - magic = s.read::()?; - if magic == Magic::Collection { - return None; - } - } - - // Read number of table records. - let count = s.read::()?; - - // Skip boring parts of header. - s.skip::(); - s.skip::(); - s.skip::(); - - // Read the table records. - let records = s.read_array16::(count)?; - let mut subsetter = Self { - data, - magic, - records, - num_glyphs: 0, - glyphs, - tables: vec![], - }; - - // Find out number of glyphs. - let maxp = subsetter.table_data(MAXP)?; - subsetter.num_glyphs = Stream::read_at::(maxp, 4)?; - - Some(subsetter) - } - - /// Encode the subsetted font file. - fn subset(mut self) -> Vec { - self.subset_tables(); - - // Start writing a brand new font. - let mut w = Vec::new(); - w.write(self.magic); - - // Write table directory. - let count = self.tables.len() as u16; - let entry_selector = (count as f32).log2().floor() as u16; - let search_range = 2u16.pow(u32::from(entry_selector)) * 16; - let range_shift = count * 16 - search_range; - w.write(count); - w.write(search_range); - w.write(entry_selector); - w.write(range_shift); - - // Tables shall be sorted by tag. - self.tables.sort_by_key(|&(tag, _)| tag); - - // This variable will hold the offset to the checksum adjustment field - // in the head table, which we'll have to write in the end (after - // checksumming the whole font). - let mut checksum_adjustment_offset = None; - - // Write table records. - let mut offset = 12 + self.tables.len() * TableRecord::SIZE; - for (tag, data) in &mut self.tables { - if *tag == HEAD { - // Zero out checksum field in head table. - data.to_mut()[8 .. 12].fill(0); - checksum_adjustment_offset = Some(offset + 8); - } - - let len = data.len(); - w.write(TableRecord { - tag: *tag, - checksum: checksum(data), - offset: offset as u32, - length: len as u32, - }); - - // Increase offset, plus padding zeros to align to 4 bytes. - offset += len; - while offset % 4 != 0 { - offset += 1; - } - } - - // Write tables. - for (_, data) in &self.tables { - // Write data plus padding zeros to align to 4 bytes. - w.extend(data.as_ref()); - while w.len() % 4 != 0 { - w.push(0); - } - } - - // Write checksumAdjustment field in head table. - if let Some(i) = checksum_adjustment_offset { - let sum = checksum(&w); - let val = 0xB1B0AFBA_u32.wrapping_sub(sum); - w[i .. i + 4].copy_from_slice(&val.to_be_bytes()); - } - - w - } - - /// Subset, drop and copy tables. - fn subset_tables(&mut self) { - // Remove unnecessary name information. - let handled_post = post::subset(self).is_some(); - - // Remove unnecessary glyph outlines. - let handled_glyf_loca = glyf::subset(self).is_some(); - let handled_cff1 = cff::subset_v1(self).is_some(); - - for record in self.records { - // If `handled` is true, we don't take any further action, if it's - // false, we copy the table. - #[rustfmt::skip] - let handled = match &record.tag.to_bytes() { - // Drop: Glyphs are already mapped. - b"cmap" => true, - - // Drop: Layout is already finished. - b"GPOS" | b"GSUB" | b"BASE" | b"JSTF" | b"MATH" | - b"ankr" | b"kern" | b"kerx" | b"mort" | b"morx" | - b"trak" | b"bsln" | b"just" | b"feat" | b"prop" => true, - - // Drop: They don't render in PDF viewers anyway. - // TODO: We probably have to convert fonts with one of these - // tables into Type 3 fonts where glyphs are described by either - // PDF graphics operators or XObject images. - b"CBDT" | b"CBLC" | b"COLR" | b"CPAL" | b"sbix" | b"SVG " => true, - - // Subsetted: Subsetting happens outside the loop, but if it - // failed, we simply copy the affected table(s). - b"post" => handled_post, - b"loca" | b"glyf" => handled_glyf_loca, - b"CFF " => handled_cff1, - - // Copy: All other tables are simply copied. - _ => false, - }; - - if !handled { - if let Some(data) = self.table_data(record.tag) { - self.push_table(record.tag, data); - } - } - } - } - - /// Retrieve the table data for a table. - fn table_data(&mut self, tag: Tag) -> Option<&'a [u8]> { - let (_, record) = self.records.binary_search_by(|record| record.tag.cmp(&tag))?; - let start = record.offset as usize; - let end = start + (record.length as usize); - self.data.get(start .. end) - } - - /// Push a new table. - fn push_table(&mut self, tag: Tag, data: impl Into>) { - self.tables.push((tag, data.into())); - } -} - -// Some common tags. -const HEAD: Tag = Tag::from_bytes(b"head"); -const MAXP: Tag = Tag::from_bytes(b"maxp"); -const POST: Tag = Tag::from_bytes(b"post"); -const LOCA: Tag = Tag::from_bytes(b"loca"); -const GLYF: Tag = Tag::from_bytes(b"glyf"); -const CFF1: Tag = Tag::from_bytes(b"CFF "); - -/// Calculate a checksum over the sliced data as a sum of u32s. If the data -/// length is not a multiple of four, it is treated as if padded with zero to a -/// length that is a multiple of four. -fn checksum(data: &[u8]) -> u32 { - let mut sum = 0u32; - for chunk in data.chunks(4) { - let mut bytes = [0; 4]; - bytes[.. chunk.len()].copy_from_slice(chunk); - sum = sum.wrapping_add(u32::from_be_bytes(bytes)); - } - sum -} - -/// Zero all bytes in a slice. -fn memzero(slice: &mut [u8]) { - for byte in slice { - *byte = 0; - } -} - -/// Convenience trait for writing into a byte buffer. -trait BufExt { - fn write(&mut self, v: T); -} - -impl BufExt for Vec { - fn write(&mut self, v: T) { - v.write(self); - } -} - -/// A trait for writing raw binary data. -trait ToData { - fn write(&self, data: &mut Vec); -} - -impl ToData for u8 { - fn write(&self, data: &mut Vec) { - data.push(*self); - } -} - -impl ToData for u16 { - fn write(&self, data: &mut Vec) { - data.extend(&self.to_be_bytes()); - } -} - -impl ToData for Offset16 { - fn write(&self, data: &mut Vec) { - self.0.write(data); - } -} - -impl ToData for u32 { - fn write(&self, data: &mut Vec) { - data.extend(&self.to_be_bytes()); - } -} - -impl ToData for Offset32 { - fn write(&self, data: &mut Vec) { - self.0.write(data); - } -} - -impl ToData for Tag { - fn write(&self, data: &mut Vec) { - self.as_u32().write(data); - } -} - -/// Font magic number. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -enum Magic { - TrueType, - OpenType, - Collection, -} - -impl FromData for Magic { - const SIZE: usize = 4; - - fn parse(data: &[u8]) -> Option { - match u32::parse(data)? { - 0x00010000 | 0x74727565 => Some(Magic::TrueType), - 0x4F54544F => Some(Magic::OpenType), - 0x74746366 => Some(Magic::Collection), - _ => None, - } - } -} - -impl ToData for Magic { - fn write(&self, data: &mut Vec) { - let value: u32 = match self { - Magic::TrueType => 0x00010000, - Magic::OpenType => 0x4F54544F, - Magic::Collection => 0x74746366, - }; - value.write(data); - } -} - -/// Locates a table in the font file. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -struct TableRecord { - tag: Tag, - checksum: u32, - offset: u32, - length: u32, -} - -impl FromData for TableRecord { - const SIZE: usize = 16; - - fn parse(data: &[u8]) -> Option { - let mut s = Stream::new(data); - Some(TableRecord { - tag: s.read::()?, - checksum: s.read::()?, - offset: s.read::()?, - length: s.read::()?, - }) - } -} - -impl ToData for TableRecord { - fn write(&self, data: &mut Vec) { - self.tag.write(data); - self.checksum.write(data); - self.offset.write(data); - self.length.write(data); - } -} - -mod post { - use super::*; - - /// Subset the post table by removing the name information. - pub(super) fn subset(subsetter: &mut Subsetter) -> Option<()> { - // Table version three is the one without names. - let mut new = 0x00030000_u32.to_be_bytes().to_vec(); - new.extend(subsetter.table_data(POST)?.get(4 .. 32)?); - subsetter.push_table(POST, new); - Some(()) - } -} - -mod glyf { - use super::*; - - /// Subset the glyf and loca tables by clearing out glyph data for - /// unused glyphs. - pub(super) fn subset(subsetter: &mut Subsetter) -> Option<()> { - let head = subsetter.table_data(HEAD)?; - let short = Stream::read_at::(head, 50)? == 0; - if short { - subset_impl::(subsetter) - } else { - subset_impl::(subsetter) - } - } - - fn subset_impl(subsetter: &mut Subsetter) -> Option<()> - where - T: LocaOffset, - { - let loca = subsetter.table_data(LOCA)?; - let glyf = subsetter.table_data(GLYF)?; - - let offsets = LazyArray32::::new(loca); - let glyph_data = |id: u16| { - let from = offsets.get(u32::from(id))?.loca_to_usize(); - let to = offsets.get(u32::from(id) + 1)?.loca_to_usize(); - glyf.get(from .. to) - }; - - // The set of all glyphs we will include in the subset. - let mut subset = HashSet::new(); - - // Because glyphs may depend on other glyphs as components (also with - // multiple layers of nesting), we have to process all glyphs to find - // their components. For notdef and all requested glyphs we simply use - // an iterator, but to track other glyphs that need processing we create - // a work stack. - let mut iter = iter::once(0).chain(subsetter.glyphs.iter().copied()); - let mut work = vec![]; - - // Find composite glyph descriptions. - while let Some(id) = work.pop().or_else(|| iter.next()) { - if subset.insert(id) { - let mut s = Stream::new(glyph_data(id)?); - if let Some(num_contours) = s.read::() { - // Negative means this is a composite glyph. - if num_contours < 0 { - // Skip min/max metrics. - s.read::(); - s.read::(); - s.read::(); - s.read::(); - - // Read component glyphs. - work.extend(component_glyphs(s)); - } - } - } - } - - let mut sub_loca = vec![]; - let mut sub_glyf = vec![]; - - for id in 0 .. subsetter.num_glyphs { - // If the glyph shouldn't be contained in the subset, it will - // still get a loca entry, but the glyf data is simply empty. - sub_loca.write(T::usize_to_loca(sub_glyf.len())?); - if subset.contains(&id) { - sub_glyf.extend(glyph_data(id)?); - } - } - - sub_loca.write(T::usize_to_loca(sub_glyf.len())?); - - subsetter.push_table(LOCA, sub_loca); - subsetter.push_table(GLYF, sub_glyf); - - Some(()) - } - - trait LocaOffset: Sized + FromData + ToData { - fn loca_to_usize(self) -> usize; - fn usize_to_loca(offset: usize) -> Option; - } - - impl LocaOffset for Offset16 { - fn loca_to_usize(self) -> usize { - 2 * usize::from(self.0) - } - - fn usize_to_loca(offset: usize) -> Option { - if offset % 2 == 0 { - (offset / 2).try_into().ok().map(Self) - } else { - None - } - } - } - - impl LocaOffset for Offset32 { - fn loca_to_usize(self) -> usize { - self.0 as usize - } - - fn usize_to_loca(offset: usize) -> Option { - offset.try_into().ok().map(Self) - } - } - - /// Returns an iterator over the component glyphs referenced by the given - /// `glyf` table composite glyph description. - fn component_glyphs(mut s: Stream) -> impl Iterator + '_ { - const ARG_1_AND_2_ARE_WORDS: u16 = 0x0001; - const WE_HAVE_A_SCALE: u16 = 0x0008; - const MORE_COMPONENTS: u16 = 0x0020; - const WE_HAVE_AN_X_AND_Y_SCALE: u16 = 0x0040; - const WE_HAVE_A_TWO_BY_TWO: u16 = 0x0080; - - let mut done = false; - iter::from_fn(move || { - if done { - return None; - } - - let flags = s.read::()?; - let component = s.read::()?; - - if flags & ARG_1_AND_2_ARE_WORDS != 0 { - s.skip::(); - s.skip::(); - } else { - s.skip::(); - } - - if flags & WE_HAVE_A_SCALE != 0 { - s.skip::(); - } else if flags & WE_HAVE_AN_X_AND_Y_SCALE != 0 { - s.skip::(); - s.skip::(); - } else if flags & WE_HAVE_A_TWO_BY_TWO != 0 { - s.skip::(); - s.skip::(); - s.skip::(); - s.skip::(); - } - - done = flags & MORE_COMPONENTS == 0; - Some(component) - }) - } -} - -mod cff { - use super::*; - - /// Subset the CFF table by zeroing glyph data for unused glyphs. - pub(super) fn subset_v1(subsetter: &mut Subsetter) -> Option<()> { - let cff = subsetter.table_data(CFF1)?; - let mut s = Stream::new(cff); - - let (major, _) = (s.read::()?, s.skip::()); - if major != 1 { - return None; - } - - let header_size = s.read::()?; - s = Stream::new_at(cff, usize::from(header_size))?; - - // Skip the name index. - Index::parse_stream(&mut s); - - // Read the top dict. The index should contain only one item. - let top_dict_index = Index::parse_stream(&mut s)?; - let top_dict = Dict::parse(top_dict_index.get(0)?); - - let mut sub_cff = cff.to_vec(); - - // Because completely rebuilding the CFF structure would be pretty - // complex, for now, we employ a peculiar strategy for CFF subsetting: - // We simply replace unused data with zeros. This way, the font - // structure and offsets can stay the same. And while the CFF table - // itself doesn't shrink, the actual embedded font is compressed and - // greatly benefits from the repeated zeros. - zero_char_strings(subsetter, cff, &top_dict, &mut sub_cff); - zero_subr_indices(subsetter, cff, &top_dict, &mut sub_cff); - - subsetter.push_table(CFF1, sub_cff); - - Some(()) - } - - /// Zero unused char strings. - fn zero_char_strings( - subsetter: &Subsetter, - cff: &[u8], - top_dict: &Dict, - sub_cff: &mut [u8], - ) -> Option<()> { - let char_strings_offset = top_dict.get_offset(Op::CHAR_STRINGS)?; - let char_strings = Index::parse(cff.get(char_strings_offset ..)?)?; - - for (id, _, range) in char_strings.iter() { - if !subsetter.glyphs.contains(&id) { - let start = char_strings_offset + range.start; - let end = char_strings_offset + range.end; - memzero(sub_cff.get_mut(start .. end)?); - } - } - - Some(()) - } - - /// Zero unused local subroutine indices. We don't currently remove - /// individual subroutines because finding out which ones are used is - /// complicated. - fn zero_subr_indices( - subsetter: &Subsetter, - cff: &[u8], - top_dict: &Dict, - sub_cff: &mut [u8], - ) -> Option<()> { - // Parse FD Select data structure, which maps from glyph ids to find - // dict indices. - let fd_select_offset = top_dict.get_offset(Op::FD_SELECT)?; - let fd_select = - parse_fd_select(cff.get(fd_select_offset ..)?, subsetter.num_glyphs)?; - - // Clear local subrs from unused font dicts. - let fd_array_offset = top_dict.get_offset(Op::FD_ARRAY)?; - let fd_array = Index::parse(cff.get(fd_array_offset ..)?)?; - - // Determine which font dict's subrs to keep. - let mut sub_fds = HashSet::new(); - for &glyph in subsetter.glyphs { - sub_fds.insert(fd_select.get(usize::from(glyph))?); - } - - for (i, data, _) in fd_array.iter() { - if !sub_fds.contains(&(i as u8)) { - let font_dict = Dict::parse(data); - if let Some(private_range) = font_dict.get_range(Op::PRIVATE) { - let private_dict = Dict::parse(cff.get(private_range.clone())?); - if let Some(subrs_offset) = private_dict.get_offset(Op::SUBRS) { - let start = private_range.start + subrs_offset; - let index = Index::parse(cff.get(start ..)?)?; - let end = start + index.data.len(); - memzero(sub_cff.get_mut(start .. end)?); - } - } - } - } - - Some(()) - } - - /// Returns the font dict index for each glyph. - fn parse_fd_select(data: &[u8], num_glyphs: u16) -> Option> { - let mut s = Stream::new(data); - let format = s.read::()?; - Some(match format { - 0 => Cow::Borrowed(s.read_bytes(usize::from(num_glyphs))?), - 3 => { - let count = usize::from(s.read::()?); - let mut fds = vec![]; - let mut start = s.read::()?; - for _ in 0 .. count { - let fd = s.read::()?; - let end = s.read::()?; - for _ in start .. end { - fds.push(fd); - } - start = end; - } - Cow::Owned(fds) - } - _ => Cow::Borrowed(&[]), - }) - } - - struct Index<'a> { - /// The data of the whole index (including its header). - data: &'a [u8], - /// The data ranges for the actual items. - items: Vec>, - } - - impl<'a> Index<'a> { - fn parse(data: &'a [u8]) -> Option { - let mut s = Stream::new(data); - - let count = usize::from(s.read::()?); - - let mut items = Vec::with_capacity(count); - let mut len = 2; - - if count > 0 { - let offsize = usize::from(s.read::()?); - if !matches!(offsize, 1 ..= 4) { - return None; - } - - // Read an offset and transform it to be relative to the start - // of the index. - let data_offset = 3 + offsize * (count + 1); - let mut read_offset = || { - let mut bytes = [0u8; 4]; - bytes[4 - offsize .. 4].copy_from_slice(s.read_bytes(offsize)?); - Some(data_offset - 1 + u32::from_be_bytes(bytes) as usize) - }; - - let mut last = read_offset()?; - for _ in 0 .. count { - let offset = read_offset()?; - data.get(last .. offset)?; - items.push(last .. offset); - last = offset; - } - - len = last; - } - - Some(Self { data: data.get(.. len)?, items }) - } - - fn parse_stream(s: &'a mut Stream) -> Option { - let index = Index::parse(s.tail()?)?; - s.advance(index.data.len()); - Some(index) - } - - fn get(&self, idx: usize) -> Option<&'a [u8]> { - self.data.get(self.items.get(idx)?.clone()) - } - - fn iter(&self) -> impl Iterator)> + '_ { - self.items - .iter() - .enumerate() - .map(move |(i, item)| (i as u16, &self.data[item.clone()], item.clone())) - } - } - - struct Dict<'a>(Vec>); - - impl<'a> Dict<'a> { - fn parse(data: &'a [u8]) -> Self { - let mut s = Stream::new(data); - Self(iter::from_fn(|| Pair::parse(&mut s)).collect()) - } - - fn get(&self, op: Op) -> Option<&[Operand<'a>]> { - self.0 - .iter() - .find(|pair| pair.op == op) - .map(|pair| pair.operands.as_slice()) - } - - fn get_offset(&self, op: Op) -> Option { - match self.get(op)? { - &[Operand::Int(offset)] if offset > 0 => usize::try_from(offset).ok(), - _ => None, - } - } - - fn get_range(&self, op: Op) -> Option> { - match self.get(op)? { - &[Operand::Int(len), Operand::Int(offset)] if offset > 0 => { - let offset = usize::try_from(offset).ok()?; - let len = usize::try_from(len).ok()?; - Some(offset .. offset + len) - } - _ => None, - } - } - } - - #[derive(Debug)] - struct Pair<'a> { - operands: Vec>, - op: Op, - } - - impl<'a> Pair<'a> { - fn parse(s: &mut Stream<'a>) -> Option { - let mut operands = vec![]; - while s.clone().read::()? > 21 { - operands.push(Operand::parse(s)?); - } - Some(Self { operands, op: Op::parse(s)? }) - } - } - - #[derive(Debug, Eq, PartialEq)] - struct Op(u8, u8); - - impl Op { - const CHAR_STRINGS: Self = Self(17, 0); - const PRIVATE: Self = Self(18, 0); - const SUBRS: Self = Self(19, 0); - const FD_ARRAY: Self = Self(12, 36); - const FD_SELECT: Self = Self(12, 37); - - fn parse(s: &mut Stream) -> Option { - let b0 = s.read::()?; - match b0 { - 12 => Some(Self(b0, s.read::()?)), - 0 ..= 21 => Some(Self(b0, 0)), - _ => None, - } - } - } - - #[derive(Debug)] - enum Operand<'a> { - Int(i32), - Real(&'a [u8]), - } - - impl<'a> Operand<'a> { - fn parse(s: &mut Stream<'a>) -> Option { - let b0 = i32::from(s.read::()?); - Some(match b0 { - 30 => { - let mut len = 0; - for &byte in s.tail()? { - len += 1; - if byte & 0x0f == 0x0f { - break; - } - } - Self::Real(s.read_bytes(len)?) - } - 32 ..= 246 => Self::Int(b0 - 139), - 247 ..= 250 => { - let b1 = i32::from(s.read::()?); - Self::Int((b0 - 247) * 256 + b1 + 108) - } - 251 ..= 254 => { - let b1 = i32::from(s.read::()?); - Self::Int(-(b0 - 251) * 256 - b1 - 108) - } - 28 => Self::Int(i32::from(s.read::()?)), - 29 => Self::Int(s.read::()?), - _ => return None, - }) - } - } -} diff --git a/src/font.rs b/src/font.rs index 0791bb6fe..3592a1a90 100644 --- a/src/font.rs +++ b/src/font.rs @@ -7,7 +7,7 @@ use std::path::{Path, PathBuf}; use std::sync::Arc; use once_cell::sync::OnceCell; -use rex::font::MathFont; +use rex::font::MathHeader; use serde::{Deserialize, Serialize}; use ttf_parser::{name_id, GlyphId, PlatformId, Tag}; use unicode_segmentation::UnicodeSegmentation; @@ -254,7 +254,7 @@ pub struct Face { /// The faces metrics. metrics: FaceMetrics, /// The parsed ReX math font. - math: OnceCell>, + math: OnceCell>, } impl Face { @@ -308,9 +308,14 @@ impl Face { &self.metrics } - /// Access the math font, if any. - pub fn math(&self) -> Option<&MathFont> { - self.math.get_or_init(|| MathFont::parse(self.buffer()).ok()).as_ref() + /// Access the math header, if any. + pub fn math(&self) -> Option<&MathHeader> { + self.math + .get_or_init(|| { + let data = self.ttf().table_data(Tag::from_bytes(b"MATH"))?; + MathHeader::parse(data).ok() + }) + .as_ref() } /// Convert from font units to an em length. @@ -350,7 +355,7 @@ pub struct FaceMetrics { impl FaceMetrics { /// Extract the face's metrics. pub fn from_ttf(ttf: &ttf_parser::Face) -> Self { - let units_per_em = f64::from(ttf.units_per_em().unwrap_or(0)); + let units_per_em = f64::from(ttf.units_per_em()); let to_em = |units| Em::from_units(units, units_per_em); let ascender = to_em(ttf.typographic_ascender().unwrap_or(ttf.ascender())); @@ -517,7 +522,7 @@ impl FaceInfo { // Determine the unicode coverage. let mut codepoints = vec![]; - for subtable in ttf.character_mapping_subtables() { + for subtable in ttf.tables().cmap.into_iter().flat_map(|table| table.subtables) { if subtable.is_unicode() { subtable.codepoints(|c| codepoints.push(c)); } @@ -550,14 +555,14 @@ impl FaceInfo { /// Try to find and decode the name with the given id. pub fn find_name(ttf: &ttf_parser::Face, name_id: u16) -> Option { - ttf.names().find_map(|entry| { - if entry.name_id() == name_id { + ttf.names().into_iter().find_map(|entry| { + if entry.name_id == name_id { if let Some(string) = entry.to_string() { return Some(string); } - if entry.platform_id() == PlatformId::Macintosh && entry.encoding_id() == 0 { - return Some(decode_mac_roman(entry.name())); + if entry.platform_id == PlatformId::Macintosh && entry.encoding_id == 0 { + return Some(decode_mac_roman(entry.name)); } } diff --git a/src/geom/paint.rs b/src/geom/paint.rs index a3a9b0be2..4a7c9e193 100644 --- a/src/geom/paint.rs +++ b/src/geom/paint.rs @@ -181,8 +181,14 @@ impl FromStr for RgbaColor { } impl From for RgbaColor { - fn from(color: SynColor) -> Self { - Self::new(color.r, color.g, color.b, color.a) + fn from(SynColor { r, g, b, a }: SynColor) -> Self { + Self { r, g, b, a } + } +} + +impl From for SynColor { + fn from(RgbaColor { r, g, b, a }: RgbaColor) -> Self { + Self { r, g, b, a } } } diff --git a/src/image.rs b/src/image.rs index 1392ecf11..5b60c8150 100644 --- a/src/image.rs +++ b/src/image.rs @@ -8,7 +8,7 @@ use std::path::Path; use std::sync::Arc; use image::io::Reader as ImageReader; -use image::{DynamicImage, GenericImageView, ImageFormat}; +use image::{DynamicImage, ImageFormat}; use crate::loading::{FileHash, Loader}; @@ -97,7 +97,7 @@ impl Image { match RasterImage::parse(data) { Ok(raster) => return Ok(Self::Raster(raster)), - Err(err) if matches!(ext, "png" | "jpg" | "jpeg") => return Err(err), + Err(err) if matches!(ext, "png" | "jpg" | "jpeg" | "gif") => return Err(err), Err(_) => {} } diff --git a/src/library/layout/page.rs b/src/library/layout/page.rs index 115a1923b..c2fbaba0b 100644 --- a/src/library/layout/page.rs +++ b/src/library/layout/page.rs @@ -201,7 +201,10 @@ impl Cast> for Marginal { Value::Str(v) => Ok(Self::Content(Content::Text(v))), Value::Content(v) => Ok(Self::Content(v)), Value::Func(v) => Ok(Self::Func(v, value.span)), - _ => Err("expected none, content or function")?, + v => Err(format!( + "expected none, content or function, found {}", + v.type_name(), + )), } } } diff --git a/src/library/math/rex.rs b/src/library/math/rex.rs index f839a9e83..47de4b3a6 100644 --- a/src/library/math/rex.rs +++ b/src/library/math/rex.rs @@ -1,5 +1,5 @@ use rex::error::{Error, LayoutError}; -use rex::font::{FontContext, MathFont}; +use rex::font::FontContext; use rex::layout::{LayoutSettings, Style}; use rex::parser::color::RGBA; use rex::render::{Backend, Cursor, Renderer}; @@ -38,7 +38,7 @@ impl Layout for RexNode { let face = ctx.fonts.get(face_id); let ctx = face .math() - .and_then(FontContext::new) + .map(|math| FontContext::new(face.ttf(), math)) .ok_or("font is not suitable for math") .at(span)?; @@ -110,7 +110,7 @@ impl FrameBackend { } impl Backend for FrameBackend { - fn symbol(&mut self, pos: Cursor, gid: u16, scale: f64, _: &MathFont) { + fn symbol(&mut self, pos: Cursor, gid: u16, scale: f64) { self.frame.push( self.transform(pos), Element::Text(Text { diff --git a/src/library/structure/list.rs b/src/library/structure/list.rs index 015ef5209..c4167cf9a 100644 --- a/src/library/structure/list.rs +++ b/src/library/structure/list.rs @@ -238,6 +238,7 @@ impl Cast> for Label { fn cast(value: Spanned) -> StrResult { match value.v { + Value::None => Ok(Self::Content(Content::Empty)), Value::Str(pattern) => { let mut s = Scanner::new(&pattern); let mut prefix; @@ -258,7 +259,10 @@ impl Cast> for Label { } Value::Content(v) => Ok(Self::Content(v)), Value::Func(v) => Ok(Self::Func(v, value.span)), - _ => Err("expected pattern, content or function")?, + v => Err(format!( + "expected string, content or function, found {}", + v.type_name(), + )), } } } diff --git a/src/library/text/raw.rs b/src/library/text/raw.rs index 31db811a4..8db4cf3e6 100644 --- a/src/library/text/raw.rs +++ b/src/library/text/raw.rs @@ -90,7 +90,9 @@ impl Show for RawNode { seq.push(Content::Linebreak { justified: false }); } - for (style, piece) in highlighter.highlight(line, &SYNTAXES) { + for (style, piece) in + highlighter.highlight_line(line, &SYNTAXES).into_iter().flatten() + { seq.push(styled(piece, foreground, style)); } } @@ -177,6 +179,7 @@ pub static THEME: Lazy = Lazy::new(|| Theme { item("string", Some("#298e0d"), None), item("punctuation.shortcut", Some("#1d6c76"), None), item("constant.character.escape", Some("#1d6c76"), None), + item("invalid", Some("#ff0000"), None), ], }); @@ -185,7 +188,7 @@ fn item(scope: &str, color: Option<&str>, font_style: Option) -> Them ThemeItem { scope: scope.parse().unwrap(), style: StyleModifier { - foreground: color.map(|s| s.parse().unwrap()), + foreground: color.map(|s| s.parse::().unwrap().into()), background: None, font_style, }, diff --git a/src/library/text/shaping.rs b/src/library/text/shaping.rs index 1f3d2f55f..591abb5ea 100644 --- a/src/library/text/shaping.rs +++ b/src/library/text/shaping.rs @@ -221,14 +221,12 @@ impl<'a> ShapedText<'a> { let x_advance = face.to_em(ttf.glyph_hor_advance(glyph_id)?); let cluster = self.glyphs.last().map(|g| g.cluster).unwrap_or_default(); self.width += x_advance.at(self.size); - let baseline_shift = self.styles.get(TextNode::BASELINE); - self.glyphs.to_mut().push(ShapedGlyph { face_id, glyph_id: glyph_id.0, x_advance, x_offset: Em::zero(), - y_offset: Em::from_length(baseline_shift, self.size), + y_offset: Em::zero(), cluster, safe_to_break: true, c: '-', @@ -491,7 +489,7 @@ fn shape_tofus(ctx: &mut ShapingContext, base: usize, text: &str, face_id: FaceI glyph_id: 0, x_advance, x_offset: Em::zero(), - y_offset: Em::from_length(ctx.styles.get(TextNode::BASELINE), ctx.size), + y_offset: Em::zero(), cluster: base + cluster, safe_to_break: true, c, diff --git a/src/main.rs b/src/main.rs index d518a03b2..a22d956d3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -239,9 +239,10 @@ fn print_diagnostics( for error in errors { // The main diagnostic. - let diag = Diagnostic::error().with_message(error.message).with_labels(vec![ - Label::primary(error.span.source(), sources.range(error.span)), - ]); + let range = sources.range(error.span); + let diag = Diagnostic::error() + .with_message(error.message) + .with_labels(vec![Label::primary(error.span.source(), range)]); term::emit(&mut w, &config, sources, &diag)?; diff --git a/src/parse/mod.rs b/src/parse/mod.rs index be3af1f8c..98f25cabc 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -11,9 +11,8 @@ pub use tokens::*; use std::collections::HashSet; -use crate::diag::ErrorPos; use crate::syntax::ast::{Associativity, BinOp, UnOp}; -use crate::syntax::{NodeKind, SyntaxNode}; +use crate::syntax::{NodeKind, SpanPos, SyntaxNode}; use crate::util::EcoString; /// Parse a source file. @@ -648,7 +647,7 @@ fn item(p: &mut Parser, keyed: bool) -> ParseResult { msg.push_str(", found "); msg.push_str(kind.as_str()); } - let error = NodeKind::Error(ErrorPos::Full, msg); + let error = NodeKind::Error(SpanPos::Full, msg); marker.end(p, error); p.eat(); marker.perform(p, NodeKind::Named, expr).ok(); diff --git a/src/parse/parser.rs b/src/parse/parser.rs index 722e53ceb..f4b02a9c5 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -3,8 +3,7 @@ use std::mem; use std::ops::Range; use super::{TokenMode, Tokens}; -use crate::diag::ErrorPos; -use crate::syntax::{InnerNode, NodeData, NodeKind, SyntaxNode}; +use crate::syntax::{InnerNode, NodeData, NodeKind, SpanPos, SyntaxNode}; use crate::util::EcoString; /// A convenient token-based parser. @@ -385,7 +384,7 @@ impl Parser<'_> { pub fn unexpected(&mut self) { if let Some(found) = self.peek() { let msg = format_eco!("unexpected {}", found); - let error = NodeKind::Error(ErrorPos::Full, msg); + let error = NodeKind::Error(SpanPos::Full, msg); self.perform(error, Self::eat); } } @@ -399,7 +398,7 @@ impl Parser<'_> { /// Insert an error message that `what` was expected at the marker position. pub fn expected_at(&mut self, marker: Marker, what: &str) { let msg = format_eco!("expected {}", what); - let error = NodeKind::Error(ErrorPos::Full, msg); + let error = NodeKind::Error(SpanPos::Full, msg); self.children.insert(marker.0, NodeData::new(error, 0).into()); } @@ -409,7 +408,7 @@ impl Parser<'_> { match self.peek() { Some(found) => { let msg = format_eco!("expected {}, found {}", thing, found); - let error = NodeKind::Error(ErrorPos::Full, msg); + let error = NodeKind::Error(SpanPos::Full, msg); self.perform(error, Self::eat); } None => self.expected(thing), @@ -481,7 +480,7 @@ impl Marker { msg.push_str(", found "); msg.push_str(child.kind().as_str()); } - let error = NodeKind::Error(ErrorPos::Full, msg); + let error = NodeKind::Error(SpanPos::Full, msg); let inner = mem::take(child); *child = InnerNode::with_child(error, inner).into(); } diff --git a/src/parse/tokens.rs b/src/parse/tokens.rs index 921559093..ff36c6bea 100644 --- a/src/parse/tokens.rs +++ b/src/parse/tokens.rs @@ -4,10 +4,9 @@ use unicode_xid::UnicodeXID; use unscanny::Scanner; use super::resolve::{resolve_hex, resolve_raw, resolve_string}; -use crate::diag::ErrorPos; use crate::geom::{AngleUnit, LengthUnit}; use crate::syntax::ast::{MathNode, RawNode, Unit}; -use crate::syntax::NodeKind; +use crate::syntax::{NodeKind, SpanPos}; use crate::util::EcoString; /// An iterator over the tokens of a string of source code. @@ -287,14 +286,14 @@ impl<'s> Tokens<'s> { NodeKind::Escape(c) } else { NodeKind::Error( - ErrorPos::Full, + SpanPos::Full, "invalid unicode escape sequence".into(), ) } } else { self.terminated = false; NodeKind::Error( - ErrorPos::End, + SpanPos::End, "expected closing brace".into(), ) } @@ -394,7 +393,7 @@ impl<'s> Tokens<'s> { self.terminated = false; NodeKind::Error( - ErrorPos::End, + SpanPos::End, if found == 0 { format_eco!("expected {} {}", remaining, noun) } else { @@ -442,7 +441,7 @@ impl<'s> Tokens<'s> { } else { self.terminated = false; NodeKind::Error( - ErrorPos::End, + SpanPos::End, if !display || (!escaped && dollar) { "expected closing dollar sign".into() } else { @@ -531,7 +530,7 @@ impl<'s> Tokens<'s> { NodeKind::Str(string) } else { self.terminated = false; - NodeKind::Error(ErrorPos::End, "expected quote".into()) + NodeKind::Error(SpanPos::End, "expected quote".into()) } } @@ -677,12 +676,12 @@ mod tests { use super::*; use crate::parse::tests::check; - use ErrorPos::*; use NodeKind::*; use Option::None; + use SpanPos::*; use TokenMode::{Code, Markup}; - fn Error(pos: ErrorPos, message: &str) -> NodeKind { + fn Error(pos: SpanPos, message: &str) -> NodeKind { NodeKind::Error(pos, message.into()) } diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index 9114872d9..7086ad4c4 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -13,7 +13,7 @@ pub use highlight::*; pub use span::*; use self::ast::{MathNode, RawNode, TypedNode, Unit}; -use crate::diag::{Error, ErrorPos}; +use crate::diag::Error; use crate::source::SourceId; use crate::util::EcoString; @@ -82,7 +82,7 @@ impl SyntaxNode { match self.kind() { &NodeKind::Error(pos, ref message) => { - vec![Error { pos, ..Error::new(self.span(), message) }] + vec![Error::new(self.span().with_pos(pos), message)] } _ => self .children() @@ -150,9 +150,7 @@ impl SyntaxNode { pub fn range(&self, span: Span, offset: usize) -> Option> { match self { Self::Inner(inner) => inner.range(span, offset), - Self::Leaf(leaf) => { - (span == leaf.span).then(|| offset .. offset + self.len()) - } + Self::Leaf(leaf) => leaf.range(span, offset), } } @@ -324,8 +322,8 @@ impl InnerNode { /// If the span points into this node, convert it to a byte range. pub fn range(&self, span: Span, mut offset: usize) -> Option> { // Check whether we found it. - if self.data.span == span { - return Some(offset .. offset + self.len()); + if let Some(range) = self.data.range(span, offset) { + return Some(range); } // The parent of a subtree has a smaller span number than all of its @@ -536,6 +534,18 @@ impl NodeData { Err(Unnumberable) } } + + /// If the span points into this node, convert it to a byte range. + pub fn range(&self, span: Span, offset: usize) -> Option> { + (span.with_pos(SpanPos::Full) == self.span).then(|| { + let end = offset + self.len(); + match span.pos() { + SpanPos::Full => offset .. end, + SpanPos::Start => offset .. offset, + SpanPos::End => end .. end, + } + }) + } } impl From for SyntaxNode { @@ -787,7 +797,7 @@ pub enum NodeKind { /// The comment can contain nested block comments. BlockComment, /// Tokens that appear in the wrong place. - Error(ErrorPos, EcoString), + Error(SpanPos, EcoString), /// Unknown character sequences. Unknown(EcoString), } diff --git a/src/syntax/span.rs b/src/syntax/span.rs index 5dcd8fc1c..35425801f 100644 --- a/src/syntax/span.rs +++ b/src/syntax/span.rs @@ -62,20 +62,25 @@ impl Debug for Spanned { pub struct Span(NonZeroU64); impl Span { + // Data layout: + // | 2 bits span pos | 16 bits source id | 46 bits number | + // Number of bits for and minimum and maximum numbers assignable to spans. - const BITS: usize = 48; + const BITS: usize = 46; const DETACHED: u64 = 1; - const MIN: u64 = 2; - const MAX: u64 = (1 << Self::BITS) - 1; /// The full range of numbers available to spans. - pub const FULL: Range = Self::MIN .. Self::MAX + 1; + pub const FULL: Range = 2 .. (1 << Self::BITS); /// Create a new span from a source id and a unique number. /// /// Panics if the `number` is not contained in `FULL`. pub const fn new(id: SourceId, number: u64) -> Self { - assert!(number >= Self::MIN && number <= Self::MAX); + assert!( + Self::FULL.start <= number && number < Self::FULL.end, + "span number outside valid range" + ); + let bits = ((id.into_raw() as u64) << Self::BITS) | number; Self(to_non_zero(bits)) } @@ -85,6 +90,12 @@ impl Span { Self(to_non_zero(Self::DETACHED)) } + /// Return a new span with updated position. + pub const fn with_pos(self, pos: SpanPos) -> Self { + let bits = (self.0.get() & ((1 << 62) - 1)) | ((pos as u64) << 62); + Self(to_non_zero(bits)) + } + /// The id of the source file the span points into. pub const fn source(self) -> SourceId { SourceId::from_raw((self.0.get() >> Self::BITS) as u16) @@ -94,16 +105,37 @@ impl Span { pub const fn number(self) -> u64 { self.0.get() & ((1 << Self::BITS) - 1) } + + /// Where in the node the span points to. + pub const fn pos(self) -> SpanPos { + match self.0.get() >> 62 { + 0 => SpanPos::Full, + 1 => SpanPos::Start, + 2 => SpanPos::End, + _ => panic!("span pos encoding is invalid"), + } + } } /// Convert to a non zero u64. const fn to_non_zero(v: u64) -> NonZeroU64 { match NonZeroU64::new(v) { Some(v) => v, - None => unreachable!(), + None => panic!("span encoding is zero"), } } +/// Where in a node a span points. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum SpanPos { + /// Over the full width of the node. + Full = 0, + /// At the start of the node. + Start = 1, + /// At the end of the node. + End = 2, +} + /// Result of numbering a node within an interval. pub type NumberingResult = Result<(), Unnumberable>; @@ -118,3 +150,17 @@ impl Display for Unnumberable { } impl std::error::Error for Unnumberable {} + +#[cfg(test)] +mod tests { + use super::{SourceId, Span, SpanPos}; + + #[test] + fn test_span_encoding() { + let id = SourceId::from_raw(5); + let span = Span::new(id, 10).with_pos(SpanPos::End); + assert_eq!(span.source(), id); + assert_eq!(span.number(), 10); + assert_eq!(span.pos(), SpanPos::End); + } +} diff --git a/tests/typeset.rs b/tests/typeset.rs index 614449a16..452219a43 100644 --- a/tests/typeset.rs +++ b/tests/typeset.rs @@ -316,10 +316,7 @@ fn test_part( let mut errors: Vec<_> = errors .into_iter() .filter(|error| error.span.source() == id) - .map(|error| { - let range = error.pos.apply(ctx.sources.range(error.span)); - (range, error.message) - }) + .map(|error| (ctx.sources.range(error.span), error.message)) .collect(); errors.sort_by_key(|error| error.0.start);