diff --git a/.gitignore b/.gitignore index d20fd85..b6e7d2a 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,9 @@ target # Contains mutation testing data **/mutants.out*/ +# Input files for Rustwell +*.fountain + # Output files from Rustwell *.html *.pdf diff --git a/Cargo.lock b/Cargo.lock index 1d35ab9..a172826 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,20 +17,11 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - [[package]] name = "anstream" -version = "0.6.21" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ "anstyle", "anstyle-parse", @@ -43,15 +34,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anstyle-parse" -version = "0.2.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" dependencies = [ "utf8parse", ] @@ -62,7 +53,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -73,25 +64,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.2", -] - -[[package]] -name = "approx" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" -dependencies = [ - "num-traits", -] - -[[package]] -name = "ar_archive_writer" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c269894b6fe5e9d7ada0cf69b5bf847ff35bc25fc271f08e1d080fce80339a" -dependencies = [ - "object 0.32.2", + "windows-sys", ] [[package]] @@ -112,12 +85,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "az" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" - [[package]] name = "backtrace" version = "0.3.76" @@ -128,7 +95,7 @@ dependencies = [ "cfg-if", "libc", "miniz_oxide", - "object 0.37.3", + "object", "rustc-demangle", "windows-link", ] @@ -139,44 +106,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "biblatex" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d0c374feba1b9a59042a7c1cf00ce7c34b977b9134fe7c42b08e5183729f66" -dependencies = [ - "paste", - "roman-numerals-rs", - "strum", - "unic-langid", - "unicode-normalization", - "unscanny", -] - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bit-set" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" - [[package]] name = "bitflags" version = "1.3.2" @@ -185,30 +114,21 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" -dependencies = [ - "serde_core", -] +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "bumpalo" -version = "3.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" - -[[package]] -name = "by_address" -version = "1.2.1" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "bytemuck" -version = "1.24.0" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" dependencies = [ "bytemuck_derive", ] @@ -230,82 +150,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" -[[package]] -name = "cc" -version = "1.2.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f50d563227a1c37cc0a263f64eca3334388c01c5e4c4861a9def205c614383c" -dependencies = [ - "find-msvc-tools", - "shlex", -] - [[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" -[[package]] -name = "chinese-number" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e964125508474a83c95eb935697abbeb446ff4e9d62c71ce880e3986d1c606b" -dependencies = [ - "chinese-variant", - "enum-ordinalize", - "num-bigint", - "num-traits", -] - -[[package]] -name = "chinese-variant" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58b52a9840ffff5d4d0058ae529fa066a75e794e3125546acfc61c23ad755e49" - -[[package]] -name = "ciborium" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" -dependencies = [ - "ciborium-io", - "ciborium-ll", - "serde", -] - -[[package]] -name = "ciborium-io" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" - -[[package]] -name = "ciborium-ll" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" -dependencies = [ - "ciborium-io", - "half", -] - -[[package]] -name = "citationberg" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f6597e8bdbca37f1f56e5a80d15857b0932aead21a78d20de49e99e74933046" -dependencies = [ - "quick-xml", - "serde", -] - [[package]] name = "clap" -version = "4.5.53" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" dependencies = [ "clap_builder", "clap_derive", @@ -313,9 +168,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.53" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ "anstream", "anstyle", @@ -325,9 +180,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.49" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" dependencies = [ "heck", "proc-macro2", @@ -337,24 +192,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" - -[[package]] -name = "cobs" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" -dependencies = [ - "thiserror", -] - -[[package]] -name = "codex" -version = "0.2.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9589e1effc5cacbea347899645c654158b03b2053d24bb426fd3128ced6e423c" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "color-eyre" @@ -391,33 +231,9 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] name = "colorchoice" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" - -[[package]] -name = "comemo" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "649d7b2d867b569729c03c0f6968db10bc95921182a1f2b2012b1b549492f39d" -dependencies = [ - "comemo-macros", - "parking_lot", - "rustc-hash", - "siphasher", - "slab", -] - -[[package]] -name = "comemo-macros" -version = "0.5.0" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51c87fc7e85487493ddedae1a3a34b897c77ad8825375b79265a8a162c28d535" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "core_maths" @@ -438,2638 +254,595 @@ dependencies = [ ] [[package]] -name = "crossbeam-deque" -version = "0.8.6" +name = "equivalent" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] -name = "crossbeam-epoch" -version = "0.9.18" +name = "euclid" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06" dependencies = [ - "crossbeam-utils", + "num-traits", ] [[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crunchy" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" - -[[package]] -name = "csv" -version = "1.4.0" +name = "eyre" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" dependencies = [ - "csv-core", - "itoa", - "ryu", - "serde_core", + "indenter", + "once_cell", ] [[package]] -name = "csv-core" -version = "0.1.13" +name = "fdeflate" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" dependencies = [ - "memchr", + "simd-adler32", ] [[package]] -name = "data-url" -version = "0.3.2" +name = "flate2" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", + "zlib-rs", +] [[package]] -name = "deranged" -version = "0.5.5" +name = "float-cmp" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" dependencies = [ - "powerfmt", + "num-traits", ] [[package]] -name = "displaydoc" -version = "0.2.5" +name = "font-types" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "39a654f404bbcbd48ea58c617c2993ee91d1cb63727a37bf2323a4edeed1b8c5" dependencies = [ - "proc-macro2", - "quote", - "syn", + "bytemuck", ] [[package]] -name = "ecow" -version = "0.2.6" +name = "gif" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78e4f79b296fbaab6ce2e22d52cb4c7f010fe0ebe7a32e34fa25885fd797bd02" +checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" dependencies = [ - "serde", + "color_quant", + "weezl", ] [[package]] -name = "either" -version = "1.15.0" +name = "gimli" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" [[package]] -name = "embedded-io" -version = "0.4.0" +name = "hashbrown" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] -name = "embedded-io" -version = "0.6.1" +name = "heck" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] -name = "enum-ordinalize" -version = "4.3.2" +name = "image-webp" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1091a7bb1f8f2c4b28f1fe2cef4980ca2d410a3d727d67ecc3178c9b0800f0" +checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" dependencies = [ - "enum-ordinalize-derive", + "byteorder-lite", + "quick-error", ] [[package]] -name = "enum-ordinalize-derive" -version = "4.3.2" +name = "imagesize" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "09e54e57b4c48b40f7aec75635392b12b3421fa26fe8b4332e63138ed278459c" [[package]] -name = "equivalent" -version = "1.0.2" +name = "indenter" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" [[package]] -name = "euclid" -version = "0.22.11" +name = "indexmap" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ - "num-traits", + "equivalent", + "hashbrown", ] [[package]] -name = "eyre" -version = "0.6.12" +name = "is_terminal_polyfill" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "krilla" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ddd739cf930f99a70ee89ec51b21fab894b5f142dc9672e0e9a27947b2bc093" dependencies = [ - "indenter", + "base64", + "bumpalo", + "flate2", + "float-cmp", + "gif", + "image-webp", + "imagesize", + "indexmap", "once_cell", + "pdf-writer", + "png", + "rustc-hash", + "rustybuzz", + "siphasher", + "skrifa", + "smallvec", + "subsetter", + "tiny-skia-path", + "xmp-writer", + "yoke", + "zune-jpeg", ] [[package]] -name = "fancy-regex" -version = "0.16.2" +name = "kurbo" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "998b056554fbe42e03ae0e152895cd1a7e1002aec800fdc6635d20270260c46f" +checksum = "ce9729cc38c18d86123ab736fd2e7151763ba226ac2490ec092d1dd148825e32" dependencies = [ - "bit-set", - "regex-automata", - "regex-syntax", + "arrayvec", + "euclid", + "smallvec", ] [[package]] -name = "fast-srgb8" -version = "1.0.0" +name = "lazy_static" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] -name = "fastrand" -version = "2.3.0" +name = "libc" +version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" [[package]] -name = "fdeflate" -version = "0.3.7" +name = "libm" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" -dependencies = [ - "simd-adler32", -] +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] -name = "find-msvc-tools" -version = "0.1.5" +name = "log" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] -name = "flate2" -version = "1.1.5" +name = "memchr" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" -dependencies = [ - "crc32fast", - "libz-rs-sys", - "miniz_oxide", -] - -[[package]] -name = "float-cmp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" - -[[package]] -name = "float-cmp" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" -dependencies = [ - "num-traits", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "font-types" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39a654f404bbcbd48ea58c617c2993ee91d1cb63727a37bf2323a4edeed1b8c5" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "fontconfig-parser" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" -dependencies = [ - "roxmltree", -] +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] -name = "fontdb" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" -dependencies = [ - "fontconfig-parser", - "log", - "memmap2", - "slotmap", - "tinyvec", - "ttf-parser", -] - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "gif" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" -dependencies = [ - "color_quant", - "weezl", -] - -[[package]] -name = "gif" -version = "0.14.1" +name = "miniz_oxide" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ - "color_quant", - "weezl", + "adler2", + "simd-adler32", ] [[package]] -name = "gimli" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" - -[[package]] -name = "glidesort" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2e102e6eb644d3e0b186fc161e4460417880a0a0b87d235f2e5b8fb30f2e9e0" - -[[package]] -name = "half" -version = "2.7.1" +name = "num-traits" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ - "cfg-if", - "crunchy", - "zerocopy", + "autocfg", ] [[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" - -[[package]] -name = "hayagriva" -version = "0.9.1" +name = "object" +version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cb69425736f184173b3ca6e27fcba440a61492a790c786b1c6af7e06a03e575" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ - "biblatex", - "ciborium", - "citationberg", - "indexmap", - "paste", - "roman-numerals-rs", - "serde", - "serde_yaml", - "thiserror", - "unic-langid", - "unicode-segmentation", - "unscanny", - "url", + "memchr", ] [[package]] -name = "hayro" -version = "0.4.0" +name = "once_cell" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048488ba88552bb0fb2a7e4001c64d5bed65d1a92167186a1bb9151571f32e60" -dependencies = [ - "bytemuck", - "hayro-interpret", - "image", - "kurbo 0.12.0", -] +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] -name = "hayro-font" -version = "0.3.0" +name = "once_cell_polyfill" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10e7e97ce840a6a70e7901e240ec65ba61106b66b37a4a1b899a2ce484248463" -dependencies = [ - "log", - "phf", -] +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] -name = "hayro-interpret" -version = "0.4.0" +name = "owo-colors" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56204c972d08e844f3db13b1e14be769f846e576699b46d4f4637cc4f8f70102" -dependencies = [ - "bitflags 2.10.0", - "hayro-font", - "hayro-syntax", - "kurbo 0.12.0", - "log", - "moxcms", - "phf", - "rustc-hash", - "siphasher", - "skrifa", - "smallvec", - "yoke 0.8.1", -] +checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" [[package]] -name = "hayro-svg" -version = "0.2.0" +name = "pdf-writer" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c673304cec6e0dfd3b4f71fccecd45646899aa70279b62d3f933842abc4ac5" +checksum = "92a79477295a713c2ed425aa82a8b5d20cec3fdee203706cbe6f3854880c1c81" dependencies = [ - "base64", - "hayro-interpret", - "image", - "kurbo 0.12.0", - "siphasher", - "xmlwriter", + "bitflags 2.11.0", + "itoa", + "memchr", + "ryu", ] [[package]] -name = "hayro-syntax" -version = "0.4.0" +name = "pin-project-lite" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9e5c7dbc0f11dc42775d1a6cc00f5f5137b90b6288dd7fe5f71d17b14d10be" -dependencies = [ - "flate2", - "kurbo 0.12.0", - "log", - "rustc-hash", - "smallvec", - "zune-jpeg 0.4.21", -] +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] -name = "hayro-write" -version = "0.3.0" +name = "png" +version = "0.17.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc05d8b4bc878b9aee48d980ecb25ed08f1dd9fad6da5ab4d9b7c56ec03a0cf6" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", "flate2", - "hayro-syntax", - "log", - "pdf-writer", -] - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hypher" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74e25026c579b170c59f8d3ddfc523d7dab0abe079f09eb8edaebd2417044f60" - -[[package]] -name = "icu_collections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" -dependencies = [ - "displaydoc", - "serde", - "yoke 0.7.5", - "zerofrom", - "zerovec 0.10.4", -] - -[[package]] -name = "icu_collections" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" -dependencies = [ - "displaydoc", - "potential_utf", - "yoke 0.8.1", - "zerofrom", - "zerovec 0.11.5", -] - -[[package]] -name = "icu_locale_core" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" -dependencies = [ - "displaydoc", - "litemap 0.8.1", - "tinystr 0.8.2", - "writeable 0.6.2", - "zerovec 0.11.5", -] - -[[package]] -name = "icu_locid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" -dependencies = [ - "displaydoc", - "litemap 0.7.5", - "tinystr 0.7.6", - "writeable 0.5.5", - "zerovec 0.10.4", -] - -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider 1.5.0", - "tinystr 0.7.6", - "zerovec 0.10.4", + "miniz_oxide", ] [[package]] -name = "icu_locid_transform_data" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" - -[[package]] -name = "icu_normalizer" -version = "2.1.1" +name = "proc-macro2" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ - "icu_collections 2.1.1", - "icu_normalizer_data", - "icu_properties 2.1.2", - "icu_provider 2.1.1", - "smallvec", - "zerovec 0.11.5", + "unicode-ident", ] [[package]] -name = "icu_normalizer_data" -version = "2.1.1" +name = "quick-error" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] -name = "icu_properties" -version = "1.5.1" +name = "quote" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ - "displaydoc", - "icu_collections 1.5.0", - "icu_locid_transform", - "icu_properties_data 1.5.1", - "icu_provider 1.5.0", - "serde", - "tinystr 0.7.6", - "zerovec 0.10.4", + "proc-macro2", ] [[package]] -name = "icu_properties" -version = "2.1.2" +name = "read-fonts" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +checksum = "6717cf23b488adf64b9d711329542ba34de147df262370221940dfabc2c91358" dependencies = [ - "icu_collections 2.1.1", - "icu_locale_core", - "icu_properties_data 2.1.2", - "icu_provider 2.1.1", - "zerotrie 0.2.3", - "zerovec 0.11.5", + "bytemuck", + "font-types", ] [[package]] -name = "icu_properties_data" -version = "1.5.1" +name = "rustc-demangle" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" [[package]] -name = "icu_properties_data" +name = "rustc-hash" version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" - -[[package]] -name = "icu_provider" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", - "postcard", - "serde", - "stable_deref_trait", - "tinystr 0.7.6", - "writeable 0.5.5", - "yoke 0.7.5", - "zerofrom", - "zerovec 0.10.4", -] - -[[package]] -name = "icu_provider" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable 0.6.2", - "yoke 0.8.1", - "zerofrom", - "zerotrie 0.2.3", - "zerovec 0.11.5", -] - -[[package]] -name = "icu_provider_adapters" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6324dfd08348a8e0374a447ebd334044d766b1839bb8d5ccf2482a99a77c0bc" -dependencies = [ - "icu_locid", - "icu_locid_transform", - "icu_provider 1.5.0", - "tinystr 0.7.6", - "zerovec 0.10.4", -] +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" [[package]] -name = "icu_provider_blob" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c24b98d1365f55d78186c205817631a4acf08d7a45bdf5dc9dcf9c5d54dccf51" +name = "rustwell" +version = "0.2.0" dependencies = [ - "icu_provider 1.5.0", - "postcard", - "serde", - "writeable 0.5.5", - "zerotrie 0.1.3", - "zerovec 0.10.4", + "bitflags 2.11.0", + "krilla", + "unicode-properties", ] [[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +name = "rustwell-cli" +version = "0.2.0" dependencies = [ - "proc-macro2", - "quote", - "syn", + "clap", + "color-eyre", + "rustwell", ] [[package]] -name = "icu_segmenter" -version = "1.5.0" +name = "rustybuzz" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a717725612346ffc2d7b42c94b820db6908048f39434504cb130e8b46256b0de" +checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" dependencies = [ + "bitflags 2.11.0", + "bytemuck", "core_maths", - "displaydoc", - "icu_collections 1.5.0", - "icu_locid", - "icu_provider 1.5.0", - "icu_segmenter_data", - "serde", - "utf8_iter", - "zerovec 0.10.4", -] - -[[package]] -name = "icu_segmenter_data" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e52775179941363cc594e49ce99284d13d6948928d8e72c755f55e98caa1eb" - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties 2.1.2", -] - -[[package]] -name = "image" -version = "0.25.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" -dependencies = [ - "bytemuck", - "byteorder-lite", - "color_quant", - "gif 0.14.1", - "image-webp", - "moxcms", - "num-traits", - "png 0.18.0", - "zune-core 0.5.0", - "zune-jpeg 0.5.8", -] - -[[package]] -name = "image-webp" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" -dependencies = [ - "byteorder-lite", - "quick-error", -] - -[[package]] -name = "imagesize" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" - -[[package]] -name = "imagesize" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09e54e57b4c48b40f7aec75635392b12b3421fa26fe8b4332e63138ed278459c" - -[[package]] -name = "indenter" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" - -[[package]] -name = "indexmap" -version = "2.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" -dependencies = [ - "equivalent", - "hashbrown", - "serde", - "serde_core", -] - -[[package]] -name = "infer" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" - -[[package]] -name = "itoa" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee5b5339afb4c41626dde77b7a611bd4f2c202b897852b4bcf5d03eddc61010" - -[[package]] -name = "kamadak-exif" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1130d80c7374efad55a117d715a3af9368f0fa7a2c54573afc15a188cd984837" -dependencies = [ - "mutate_once", -] - -[[package]] -name = "krilla" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0ddfec86fec13d068075e14f22a7e217c281f3ed69ddcb427bf3f5d504fd674" -dependencies = [ - "base64", - "bumpalo", - "comemo", - "flate2", - "float-cmp 0.10.0", - "gif 0.13.3", - "hayro-write", - "image-webp", - "imagesize 0.14.0", - "indexmap", - "once_cell", - "pdf-writer", - "png 0.17.16", - "rayon", - "rustc-hash", - "rustybuzz", - "siphasher", - "skrifa", - "smallvec", - "subsetter", - "tiny-skia-path", - "xmp-writer", - "yoke 0.8.1", - "zune-jpeg 0.5.8", -] - -[[package]] -name = "krilla-svg" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f485e1a850201a01dcd8d73e7cf09f2cd4c4cc85c2cd296359094d49336d8ef7" -dependencies = [ - "flate2", - "fontdb", - "krilla", - "png 0.17.16", - "resvg", - "tiny-skia", - "usvg", -] - -[[package]] -name = "kurbo" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62" -dependencies = [ - "arrayvec", - "euclid", - "smallvec", -] - -[[package]] -name = "kurbo" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce9729cc38c18d86123ab736fd2e7151763ba226ac2490ec092d1dd148825e32" -dependencies = [ - "arrayvec", - "euclid", - "smallvec", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "libc" -version = "0.2.178" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" - -[[package]] -name = "libm" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" - -[[package]] -name = "libz-rs-sys" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c10501e7805cee23da17c7790e59df2870c0d4043ec6d03f67d31e2b53e77415" -dependencies = [ - "zlib-rs", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - -[[package]] -name = "lipsum" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "636860251af8963cc40f6b4baadee105f02e21b28131d76eba8e40ce84ab8064" -dependencies = [ - "rand", - "rand_chacha", -] - -[[package]] -name = "litemap" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" -dependencies = [ - "serde", -] - -[[package]] -name = "litemap" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" - -[[package]] -name = "memchr" -version = "2.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" - -[[package]] -name = "memmap2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" -dependencies = [ - "libc", -] - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "moxcms" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97" -dependencies = [ - "num-traits", - "pxfm", -] - -[[package]] -name = "mutate_once" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13d2233c9842d08cfe13f9eac96e207ca6a2ea10b80259ebe8ad0268be27d2af" - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "object" -version = "0.32.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" -dependencies = [ - "memchr", -] - -[[package]] -name = "object" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "once_cell_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" - -[[package]] -name = "owo-colors" -version = "4.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52" - -[[package]] -name = "palette" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6" -dependencies = [ - "approx", - "fast-srgb8", - "libm", - "palette_derive", -] - -[[package]] -name = "palette_derive" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5030daf005bface118c096f510ffb781fc28f9ab6a32ab224d8631be6851d30" -dependencies = [ - "by_address", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "pdf-writer" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92a79477295a713c2ed425aa82a8b5d20cec3fdee203706cbe6f3854880c1c81" -dependencies = [ - "bitflags 2.10.0", - "itoa", - "memchr", - "ryu", -] - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "phf" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" -dependencies = [ - "phf_macros", - "phf_shared", - "serde", -] - -[[package]] -name = "phf_generator" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" -dependencies = [ - "fastrand", - "phf_shared", -] - -[[package]] -name = "phf_macros" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "phf_shared" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pico-args" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" - -[[package]] -name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "plist" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" -dependencies = [ - "base64", - "indexmap", - "quick-xml", - "serde", - "time", -] - -[[package]] -name = "png" -version = "0.17.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "png" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" -dependencies = [ - "bitflags 2.10.0", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "portable-atomic" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f59e70c4aef1e55797c2e8fd94a4f2a973fc972cfde0e0b05f683667b0cd39dd" - -[[package]] -name = "postcard" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" -dependencies = [ - "cobs", - "embedded-io 0.4.0", - "embedded-io 0.6.1", - "serde", -] - -[[package]] -name = "potential_utf" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" -dependencies = [ - "zerovec 0.11.5", -] - -[[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.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - -[[package]] -name = "proc-macro2" -version = "1.0.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "psm" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d11f2fedc3b7dafdc2851bc52f277377c5473d378859be234bc7ebb593144d01" -dependencies = [ - "ar_archive_writer", - "cc", -] - -[[package]] -name = "pxfm" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8" -dependencies = [ - "num-traits", -] - -[[package]] -name = "qcms" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edecfcd5d755a5e5d98e24cf43113e7cdaec5a070edd0f6b250c03a573da30fa" - -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - -[[package]] -name = "quick-xml" -version = "0.38.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "quote" -version = "1.0.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" - -[[package]] -name = "rayon" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "read-fonts" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717cf23b488adf64b9d711329542ba34de147df262370221940dfabc2c91358" -dependencies = [ - "bytemuck", - "font-types", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags 2.10.0", -] - -[[package]] -name = "regex" -version = "1.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" - -[[package]] -name = "resvg" -version = "0.45.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8928798c0a55e03c9ca6c4c6846f76377427d2c1e1f7e6de3c06ae57942df43" -dependencies = [ - "gif 0.13.3", - "image-webp", - "log", - "pico-args", - "rgb", - "svgtypes", - "tiny-skia", - "usvg", - "zune-jpeg 0.4.21", -] - -[[package]] -name = "rgb" -version = "0.8.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "roman-numerals-rs" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85cd47a33a4510b1424fe796498e174c6a9cf94e606460ef022a19f3e4ff85e" - -[[package]] -name = "roxmltree" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" - -[[package]] -name = "rust_decimal" -version = "1.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35affe401787a9bd846712274d97654355d21b2a2c092a3139aabe31e9022282" -dependencies = [ - "arrayvec", - "num-traits", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - -[[package]] -name = "rustwell" -version = "0.2.0" -dependencies = [ - "bitflags 2.10.0", - "typst", - "typst-pdf", - "unicode-properties", -] - -[[package]] -name = "rustwell-cli" -version = "0.2.0" -dependencies = [ - "clap", - "color-eyre", - "rustwell", -] - -[[package]] -name = "rustybuzz" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" -dependencies = [ - "bitflags 2.10.0", - "bytemuck", - "core_maths", - "log", - "smallvec", - "ttf-parser", - "unicode-bidi-mirroring", - "unicode-ccc", - "unicode-properties", - "unicode-script", -] - -[[package]] -name = "ryu" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62049b2877bf12821e8f9ad256ee38fdc31db7387ec2d3b3f403024de2034aea" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6af14725505314343e673e9ecb7cd7e8a36aa9791eb936235a3567cc31447ae4" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_spanned" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_yaml" -version = "0.9.34+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" -dependencies = [ - "indexmap", - "itoa", - "ryu", - "serde", - "unsafe-libyaml", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "simd-adler32" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" - -[[package]] -name = "simplecss" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c" -dependencies = [ - "log", -] - -[[package]] -name = "siphasher" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" - -[[package]] -name = "skrifa" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c31071dedf532758ecf3fed987cdb4bd9509f900e026ab684b4ecb81ea49841" -dependencies = [ - "bytemuck", - "read-fonts", -] - -[[package]] -name = "slab" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" - -[[package]] -name = "slotmap" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" -dependencies = [ - "version_check", -] - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "stacker" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1f8b29fb42aafcea4edeeb6b2f2d7ecd0d969c48b4cf0d2e64aafc471dd6e59" -dependencies = [ - "cc", - "cfg-if", - "libc", - "psm", - "windows-sys 0.59.0", -] - -[[package]] -name = "strict-num" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" -dependencies = [ - "float-cmp 0.9.0", -] - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "strum" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "subsetter" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6895a12ac5599bb6057362f00e8a3cf1daab4df33f553a55690a44e4fed8d0" -dependencies = [ - "kurbo 0.12.0", - "rustc-hash", - "skrifa", - "write-fonts", -] - -[[package]] -name = "svgtypes" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc" -dependencies = [ - "kurbo 0.11.3", - "siphasher", -] - -[[package]] -name = "syn" -version = "2.0.111" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "syntect" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "656b45c05d95a5704399aeef6bd0ddec7b2b3531b7c9e900abbf7c4d2190c925" -dependencies = [ - "bincode", - "fancy-regex", - "flate2", - "fnv", - "once_cell", - "plist", - "regex-syntax", - "serde", - "serde_derive", - "serde_json", - "thiserror", - "walkdir", - "yaml-rust", -] - -[[package]] -name = "thin-vec" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "144f754d318415ac792f9d69fc87abbbfc043ce2ef041c60f16ad828f638717d" - -[[package]] -name = "thiserror" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "time" -version = "0.3.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" - -[[package]] -name = "time-macros" -version = "0.2.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tiny-skia" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" -dependencies = [ - "arrayref", - "arrayvec", - "bytemuck", - "cfg-if", - "log", - "png 0.17.16", - "tiny-skia-path", -] - -[[package]] -name = "tiny-skia-path" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" -dependencies = [ - "arrayref", - "bytemuck", - "strict-num", -] - -[[package]] -name = "tinystr" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" -dependencies = [ - "displaydoc", - "serde", - "zerovec 0.10.4", -] - -[[package]] -name = "tinystr" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" -dependencies = [ - "displaydoc", - "serde_core", - "zerovec 0.11.5", -] - -[[package]] -name = "tinyvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "toml" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.22.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "toml_write", - "winnow", -] - -[[package]] -name = "toml_write" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" - -[[package]] -name = "tracing" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" -dependencies = [ - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-error" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" -dependencies = [ - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" -dependencies = [ - "sharded-slab", - "thread_local", - "tracing-core", -] - -[[package]] -name = "ttf-parser" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" -dependencies = [ - "core_maths", -] - -[[package]] -name = "two-face" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e51b6e60e545cfdae5a4639ff423818f52372211a8d9a3e892b4b0761f76b2" -dependencies = [ - "serde", - "serde_derive", - "syntect", -] - -[[package]] -name = "typed-arena" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" - -[[package]] -name = "typst" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f6511ee598476f4f322b4d13891083d96dbacb8f9c2b908604c7094ba390653" -dependencies = [ - "comemo", - "ecow", - "rustc-hash", - "typst-eval", - "typst-html", - "typst-layout", - "typst-library", - "typst-macros", - "typst-realize", - "typst-syntax", - "typst-timing", - "typst-utils", -] - -[[package]] -name = "typst-assets" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5613cb719a6222fe9b74027c3625d107767ec187bff26b8fc931cf58942c834f" - -[[package]] -name = "typst-eval" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "687757487dfc0c1e941344d5024cf7a28364e70c3e304faad89ac65597f62526" -dependencies = [ - "comemo", - "ecow", - "indexmap", - "rustc-hash", - "stacker", - "toml", - "typst-library", - "typst-macros", - "typst-syntax", - "typst-timing", - "typst-utils", - "unicode-segmentation", -] - -[[package]] -name = "typst-html" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e29f8da4f964d4c90739c3c1e0288b0ba1bccc3cc50623a6d558300b86ca8aad" -dependencies = [ - "bumpalo", - "comemo", - "ecow", - "palette", - "rustc-hash", - "time", - "typst-assets", - "typst-library", - "typst-macros", - "typst-svg", - "typst-syntax", - "typst-timing", - "typst-utils", -] - -[[package]] -name = "typst-layout" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cab0200105831a9158e63718a0f6141c78cb2c1722ed17d19ad28941e3b8491" -dependencies = [ - "az", - "bumpalo", - "codex", - "comemo", - "ecow", - "either", - "hypher", - "icu_properties 1.5.1", - "icu_provider 1.5.0", - "icu_provider_adapters", - "icu_provider_blob", - "icu_segmenter", - "kurbo 0.12.0", - "memchr", - "rustc-hash", - "rustybuzz", - "smallvec", - "ttf-parser", - "typst-assets", - "typst-library", - "typst-macros", - "typst-syntax", - "typst-timing", - "typst-utils", - "unicode-bidi", - "unicode-math-class", - "unicode-script", - "unicode-segmentation", -] - -[[package]] -name = "typst-library" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e276a5de53020c43efe2111ec236252e54ea4480b5ac18063e663dfbe03d9d1b" -dependencies = [ - "az", - "bitflags 2.10.0", - "bumpalo", - "chinese-number", - "ciborium", - "codex", - "comemo", - "csv", - "ecow", - "flate2", - "fontdb", - "glidesort", - "hayagriva", - "hayro-syntax", - "icu_properties 1.5.1", - "icu_provider 1.5.0", - "icu_provider_blob", - "image", - "indexmap", - "kamadak-exif", - "kurbo 0.12.0", - "lipsum", - "memchr", - "palette", - "phf", - "png 0.17.16", - "qcms", - "rayon", - "regex", - "regex-syntax", - "roxmltree", - "rust_decimal", - "rustc-hash", - "rustybuzz", - "serde", - "serde_json", - "serde_yaml", - "siphasher", - "smallvec", - "syntect", - "time", - "toml", - "ttf-parser", - "two-face", - "typed-arena", - "typst-assets", - "typst-macros", - "typst-syntax", - "typst-timing", - "typst-utils", - "unicode-math-class", - "unicode-normalization", - "unicode-segmentation", - "unscanny", - "usvg", - "utf8_iter", - "wasmi", - "xmlwriter", -] - -[[package]] -name = "typst-macros" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "141cbd1027129fbf6bda1013f52a264df7befc7388cc8f47767d65e803fd3a59" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "typst-pdf" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37c8a4630754767cd10d48e8b8186e7dc784631a30a3a93521edf7d77aebd0c0" -dependencies = [ - "az", - "bytemuck", - "comemo", - "ecow", - "image", - "indexmap", - "infer", - "krilla", - "krilla-svg", - "rustc-hash", - "serde", - "smallvec", - "typst-assets", - "typst-library", - "typst-macros", - "typst-syntax", - "typst-timing", - "typst-utils", -] - -[[package]] -name = "typst-realize" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7ffe964757fb93d2e98978aa2a74ee85b0f94c8643e8f3550737258b58f39d8" -dependencies = [ - "arrayvec", - "bumpalo", - "comemo", - "ecow", - "regex", - "typst-library", - "typst-macros", - "typst-syntax", - "typst-timing", - "typst-utils", -] - -[[package]] -name = "typst-svg" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e46b811837ade1f0243ef0d8bf3fb06d166443090eac22c28643f374c2ccdc9d" -dependencies = [ - "base64", - "comemo", - "ecow", - "flate2", - "hayro", - "hayro-svg", - "image", - "rustc-hash", - "ttf-parser", - "typst-assets", - "typst-library", - "typst-macros", - "typst-timing", - "typst-utils", - "xmlparser", - "xmlwriter", -] - -[[package]] -name = "typst-syntax" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a95d9192060e23b1e491b0b94dff676acddc92a4d672aeb8ca3890a5a734e879" -dependencies = [ - "ecow", - "rustc-hash", - "serde", - "toml", - "typst-timing", - "typst-utils", - "unicode-ident", - "unicode-math-class", - "unicode-script", - "unicode-segmentation", - "unscanny", -] - -[[package]] -name = "typst-timing" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be94f8faf19841b49574ef5c7fd7a12e2deb7c3d8deba5a596f35d2222024cd" -dependencies = [ - "parking_lot", - "serde", - "serde_json", -] - -[[package]] -name = "typst-utils" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3966c92e8fa48c7ce898130d07000d985f18206d92b250f0f939287fbccdee3" -dependencies = [ - "once_cell", - "portable-atomic", - "rayon", - "rustc-hash", - "siphasher", - "thin-vec", - "unicode-math-class", -] - -[[package]] -name = "unic-langid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28ba52c9b05311f4f6e62d5d9d46f094bd6e84cb8df7b3ef952748d752a7d05" -dependencies = [ - "unic-langid-impl", - "unic-langid-macros", -] - -[[package]] -name = "unic-langid-impl" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce1bf08044d4b7a94028c93786f8566047edc11110595914de93362559bc658" -dependencies = [ - "serde", - "tinystr 0.8.2", -] - -[[package]] -name = "unic-langid-macros" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5957eb82e346d7add14182a3315a7e298f04e1ba4baac36f7f0dbfedba5fc25" -dependencies = [ - "proc-macro-hack", - "tinystr 0.8.2", - "unic-langid-impl", - "unic-langid-macros-impl", -] - -[[package]] -name = "unic-langid-macros-impl" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1249a628de3ad34b821ecb1001355bca3940bcb2f88558f1a8bd82e977f75b5" -dependencies = [ - "proc-macro-hack", - "quote", - "syn", - "unic-langid-impl", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" - -[[package]] -name = "unicode-bidi-mirroring" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfa6e8c60bb66d49db113e0125ee8711b7647b5579dc7f5f19c42357ed039fe" - -[[package]] -name = "unicode-ccc" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e" - -[[package]] -name = "unicode-ident" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" - -[[package]] -name = "unicode-math-class" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d246cf599d5fae3c8d56e04b20eb519adb89a8af8d0b0fbcded369aa3647d65" - -[[package]] -name = "unicode-normalization" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-properties" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" - -[[package]] -name = "unicode-script" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" - -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + "log", + "smallvec", + "ttf-parser", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-properties", + "unicode-script", +] [[package]] -name = "unicode-vo" -version = "0.1.0" +name = "ryu" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] -name = "unsafe-libyaml" -version = "0.2.11" +name = "sharded-slab" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] [[package]] -name = "unscanny" -version = "0.1.0" +name = "simd-adler32" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9df2af067a7953e9c3831320f35c1cc0600c30d44d9f7a12b01db1cd88d6b47" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" [[package]] -name = "url" -version = "2.5.7" +name = "siphasher" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" [[package]] -name = "usvg" -version = "0.45.1" +name = "skrifa" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80be9b06fbae3b8b303400ab20778c80bbaf338f563afe567cf3c9eea17b47ef" +checksum = "8c31071dedf532758ecf3fed987cdb4bd9509f900e026ab684b4ecb81ea49841" dependencies = [ - "base64", - "data-url", - "flate2", - "fontdb", - "imagesize 0.13.0", - "kurbo 0.11.3", - "log", - "pico-args", - "roxmltree", - "rustybuzz", - "simplecss", - "siphasher", - "strict-num", - "svgtypes", - "tiny-skia-path", - "unicode-bidi", - "unicode-script", - "unicode-vo", - "xmlwriter", + "bytemuck", + "read-fonts", ] [[package]] -name = "utf8_iter" -version = "1.0.4" +name = "smallvec" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] -name = "utf8parse" -version = "0.2.2" +name = "stable_deref_trait" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] -name = "valuable" +name = "strict-num" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" [[package]] -name = "version_check" -version = "0.9.5" +name = "strsim" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] -name = "walkdir" -version = "2.5.0" +name = "subsetter" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +checksum = "cb6895a12ac5599bb6057362f00e8a3cf1daab4df33f553a55690a44e4fed8d0" dependencies = [ - "same-file", - "winapi-util", + "kurbo", + "rustc-hash", + "skrifa", + "write-fonts", ] [[package]] -name = "wasmi" -version = "0.51.5" +name = "syn" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb321403ce594274827657a908e13d1d9918aa02257b8bf8391949d9764023ff" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ - "spin", - "wasmi_collections", - "wasmi_core", - "wasmi_ir", - "wasmparser", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] -name = "wasmi_collections" -version = "0.51.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9b8e98e45a2a534489f8225e765cbf1cb9a3078072605e58158910cf4749172" - -[[package]] -name = "wasmi_core" -version = "0.51.5" +name = "synstructure" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c25f375c0cdf14810eab07f532f61f14d4966f09c747a55067fdf3196e8512e6" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ - "libm", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "wasmi_ir" -version = "0.51.5" +name = "thread_local" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624e2a68a4293ecb8f564260b68394b29cf3b3edba6bce35532889a2cb33c3d9" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ - "wasmi_core", + "cfg-if", ] [[package]] -name = "wasmparser" -version = "0.228.0" +name = "tiny-skia-path" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4abf1132c1fdf747d56bbc1bb52152400c70f336870f968b85e89ea422198ae3" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" dependencies = [ - "bitflags 2.10.0", + "arrayref", + "bytemuck", + "strict-num", ] [[package]] -name = "weezl" -version = "0.1.12" +name = "tracing" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] [[package]] -name = "winapi-util" -version = "0.1.11" +name = "tracing-core" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ - "windows-sys 0.61.2", + "once_cell", + "valuable", ] [[package]] -name = "windows-link" +name = "tracing-error" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" +dependencies = [ + "tracing", + "tracing-subscriber", +] [[package]] -name = "windows-sys" -version = "0.59.0" +name = "tracing-subscriber" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" dependencies = [ - "windows-targets", + "sharded-slab", + "thread_local", + "tracing-core", ] [[package]] -name = "windows-sys" -version = "0.61.2" +name = "ttf-parser" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" dependencies = [ - "windows-link", + "core_maths", ] [[package]] -name = "windows-targets" -version = "0.52.6" +name = "unicode-bidi-mirroring" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] +checksum = "5dfa6e8c60bb66d49db113e0125ee8711b7647b5579dc7f5f19c42357ed039fe" [[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" +name = "unicode-ccc" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e" [[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" +name = "unicode-ident" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] -name = "windows_i686_gnu" -version = "0.52.6" +name = "unicode-properties" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" [[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" +name = "unicode-script" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" [[package]] -name = "windows_i686_msvc" -version = "0.52.6" +name = "utf8parse" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" +name = "valuable" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" +name = "weezl" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" [[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" +name = "windows-link" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] -name = "winnow" -version = "0.7.14" +name = "windows-sys" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "memchr", + "windows-link", ] [[package]] @@ -3080,62 +853,17 @@ checksum = "886614b5ce857341226aa091f3c285e450683894acaaa7887f366c361efef79d" dependencies = [ "font-types", "indexmap", - "kurbo 0.12.0", + "kurbo", "log", "read-fonts", ] -[[package]] -name = "writeable" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" - -[[package]] -name = "writeable" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" - -[[package]] -name = "xmlparser" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" - -[[package]] -name = "xmlwriter" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" - [[package]] name = "xmp-writer" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce9e2f4a404d9ebffc0a9832cf4f50907220ba3d7fffa9099261a5cab52f2dd7" -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - -[[package]] -name = "yoke" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive 0.7.5", - "zerofrom", -] - [[package]] name = "yoke" version = "0.8.1" @@ -3143,22 +871,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ "stable_deref_trait", - "yoke-derive 0.8.1", + "yoke-derive", "zerofrom", ] -[[package]] -name = "yoke-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - [[package]] name = "yoke-derive" version = "0.8.1" @@ -3171,26 +887,6 @@ dependencies = [ "synstructure", ] -[[package]] -name = "zerocopy" -version = "0.8.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "zerofrom" version = "0.1.6" @@ -3212,113 +908,23 @@ dependencies = [ "synstructure", ] -[[package]] -name = "zerotrie" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb594dd55d87335c5f60177cee24f19457a5ec10a065e0a3014722ad252d0a1f" -dependencies = [ - "displaydoc", - "litemap 0.7.5", - "serde", - "zerovec 0.10.4", -] - -[[package]] -name = "zerotrie" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" -dependencies = [ - "displaydoc", - "yoke 0.8.1", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" -dependencies = [ - "serde", - "yoke 0.7.5", - "zerofrom", - "zerovec-derive 0.10.3", -] - -[[package]] -name = "zerovec" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" -dependencies = [ - "serde", - "yoke 0.8.1", - "zerofrom", - "zerovec-derive 0.11.2", -] - -[[package]] -name = "zerovec-derive" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "zlib-rs" -version = "0.5.5" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40990edd51aae2c2b6907af74ffb635029d5788228222c4bb811e9351c0caad3" - -[[package]] -name = "zmij" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0095ecd462946aa3927d9297b63ef82fb9a5316d7a37d134eeb36e58228615a" - -[[package]] -name = "zune-core" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" +checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513" [[package]] name = "zune-core" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "111f7d9820f05fd715df3144e254d6fc02ee4088b0644c0ffd0efc9e6d9d2773" - -[[package]] -name = "zune-jpeg" -version = "0.4.21" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" -dependencies = [ - "zune-core 0.4.12", -] +checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" [[package]] name = "zune-jpeg" -version = "0.5.8" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35aee689668bf9bd6f6f3a6c60bb29ba1244b3b43adfd50edd554a371da37d5" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" dependencies = [ - "zune-core 0.5.0", + "zune-core", ] diff --git a/README.md b/README.md index ff387e8..3e7fae1 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,4 @@ Rustwell currently implements the entirely of the *Fountain* specification. It o The original CSS code for the html output was created by [Jonathan Poritsky](https://www.candlerblog.com/), but has been expanded upon for this endeavour. -The typst template is based on the [celluloid](https://typst.app/universe/package/celluloid) template for typst by [Casey Dahlin](mailto:sadmac@google.com), licensed under [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0) license. The template used for Rustwell is a heavily modified version of celluloid, and is not considered fit for use outside of the scope of Rustwell. The original license can be found [here](./licenses/OFL-Courier-Prime.txt). - [Courier Prime](https://quoteunquoteapps.com/courierprime/), designed by Alan Dague-Greene, is distributed along with Rustwell for the `pdf`-exporter to have a baseline font. The original license can be found [here](./licenses/OFL-Courier-Prime.txt). diff --git a/crates/rustwell-cli/src/main.rs b/crates/rustwell-cli/src/main.rs index bc9737a..f2da3fd 100644 --- a/crates/rustwell-cli/src/main.rs +++ b/crates/rustwell-cli/src/main.rs @@ -6,7 +6,6 @@ use rustwell::ExporterExt; use rustwell::HtmlExporter; use rustwell::PdfExporter; use rustwell::Screenplay; -use rustwell::TypstExporter; use std::fs::File; use std::io; @@ -28,6 +27,10 @@ struct Cli { #[arg(short = 't', long = "target", value_enum)] target: Option, + /// Paper size specific for pdf output + #[arg(short = 'p', long = "papersize", value_enum)] + papersize: Option, + /// Alias for stdout (same as `-o -`) #[arg(long = "stdout")] stdout: bool, @@ -39,11 +42,16 @@ struct Cli { #[derive(Debug, Clone, Copy, ValueEnum)] enum Target { - Typst, Html, Pdf, } +#[derive(Debug, Clone, Copy, ValueEnum)] +enum PaperSize { + A4, + Letter, +} + fn main() -> Result<()> { color_eyre::install()?; let cli = Cli::parse(); @@ -76,9 +84,8 @@ fn decide_exporter(cli: &Cli) -> Box { }), Target::Pdf => Box::new(PdfExporter { synopses: cli.synopses, - }), - Target::Typst => Box::new(TypstExporter { - synopses: cli.synopses, + paper_size: decide_paper_size(cli.papersize), + ..Default::default() }), } } @@ -109,7 +116,6 @@ fn detect_target_from_path(path: &str) -> Result { .to_ascii_lowercase(); let t = match ext.as_str() { - "typ" => Target::Typst, "html" | "htm" => Target::Html, "pdf" => Target::Pdf, _ => bail!("unkown extension '.{}'; specify -t/--target", ext), @@ -140,3 +146,11 @@ fn detect_name_from_path(path: &str) -> Result<&str> { .and_then(|s| s.to_str()) .unwrap_or_default()) } + +fn decide_paper_size(size: Option) -> rustwell::PaperSize { + match size { + Some(PaperSize::A4) => rustwell::A4, + Some(PaperSize::Letter) => rustwell::LETTER, + None => rustwell::PaperSize::default(), + } +} diff --git a/crates/rustwell/Cargo.toml b/crates/rustwell/Cargo.toml index 0e4a647..7c4aa44 100644 --- a/crates/rustwell/Cargo.toml +++ b/crates/rustwell/Cargo.toml @@ -13,6 +13,5 @@ path = "src/lib.rs" [dependencies] bitflags = "2" +krilla = "0.7.0" unicode-properties = "0.1.4" -typst = "0.14.2" -typst-pdf = "0.14.2" diff --git a/crates/rustwell/src/export.rs b/crates/rustwell/src/export.rs index 01cfe5a..32f7328 100644 --- a/crates/rustwell/src/export.rs +++ b/crates/rustwell/src/export.rs @@ -10,7 +10,6 @@ pub mod html; pub mod pdf; -pub mod typst; use std::{ fs::File, diff --git a/crates/rustwell/src/export/pdf.rs b/crates/rustwell/src/export/pdf.rs index 6a56fb5..ad84cc4 100644 --- a/crates/rustwell/src/export/pdf.rs +++ b/crates/rustwell/src/export/pdf.rs @@ -1,35 +1,1105 @@ use std::io::Write; -use typst_pdf::PdfOptions; +use krilla::{ + Document, + color::rgb, + destination::XyzDestination, + geom::{PathBuilder, Point, Rect}, + num::NormalizedF32, + outline::{Outline, OutlineNode}, + page::PageSettings, + paint::Fill, + surface::Surface, + text::Font, +}; -use crate::{Exporter, export::typst::TypstExporter, screenplay::Screenplay}; +use crate::{ + Exporter, Screenplay, + rich_string::RichString, + screenplay::{Dialogue, DialogueElement, Element, Span, TitlePage}, +}; + +const FONT_SIZE: usize = 12; // standard screenplay size +const FONT_WIDTH: f32 = 7.2; // 12 * 0.6 (Courier Prime's aspect ratio) + +/// The font bundled together with Rustwell; Courier Prime. +/// Includes the data of the font styles Regular, Bold, Italic +/// and BoldItalic, in stated order. +const FONTS: [&[u8]; 4] = [ + include_bytes!("fonts/CourierPrime-Regular.ttf"), + include_bytes!("fonts/CourierPrime-Bold.ttf"), + include_bytes!("fonts/CourierPrime-Italic.ttf"), + include_bytes!("fonts/CourierPrime-BoldItalic.ttf"), +]; + +/// A family of fonts with the standard variants. +struct FontFamily { + pub regular: Font, + pub bold: Font, + pub italic: Font, + pub bold_italic: Font, +} + +/// Dimensions of a paper in points (pts). +pub struct PaperSize { + pub x: usize, + pub y: usize, +} + +/// The size of an `A4` paper in points (pts). +pub const A4: PaperSize = PaperSize { x: 595, y: 842 }; // A4 size in pts +/// The size of a `US letter` paper in points (pts). +pub const LETTER: PaperSize = PaperSize { x: 612, y: 792 }; // Letter size in pts + +impl Default for PaperSize { + fn default() -> Self { + A4 + } +} + +/// The margin at the top of a page. Applicable on every page. In points. +const TOP_MARGIN: usize = 72; +/// The margin at the bottom of a page. Applicable on every page. In points. +const BOTTOM_MARGIN: usize = 72; + +/// Left- and right margins, in points, going inwards - meaning left margin is relative to the left +/// side, and the right margin is relative to the right side. +struct Margin { + pub left: f32, + pub right: f32, +} + +/// Collection of margins for the dialogue components. +struct DialogueMargins { + pub character: Margin, + pub parenthetical: Margin, + pub line: Margin, +} + +/// Collection of margins for the dual dialogue components. +struct DualDialogueMargins { + pub left: DialogueMargins, + pub right: DialogueMargins, +} + +/// Collection of all margins for all different screenplay [`Elements`]. +struct Margins { + pub heading: Margin, + pub action: Margin, + pub dialogue: DialogueMargins, + pub dual_dialogue: DualDialogueMargins, + pub lyrics: Margin, + pub transition: Margin, + pub centered: Margin, + pub synopsis: Margin, + pub page_number: Margin, +} + +/// The standard margins for all different screenplay [`Elements`]. +const MARGINS: Margins = Margins { + heading: Margin { + left: 108.0, + right: 72.0, + }, + action: Margin { + left: 108.0, + right: 72.0, + }, + dialogue: DialogueMargins { + character: Margin { + left: 252.0, + right: 108.0, + }, + parenthetical: Margin { + left: 223.2, + right: 180.0, + }, + line: Margin { + left: 180.0, + right: 144.0, + }, + }, + dual_dialogue: DualDialogueMargins { + left: DialogueMargins { + character: Margin { + left: 198.0, + right: 288.0, + }, + parenthetical: Margin { + left: 162.0, + right: 324.0, + }, + line: Margin { + left: 144.0, + right: 288.0, + }, + }, + right: DialogueMargins { + character: Margin { + left: 414.0, + right: 72.0, + }, + parenthetical: Margin { + left: 378.0, + right: 90.0, + }, + line: Margin { + left: 360.0, + right: 72.0, + }, + }, + }, + lyrics: Margin { + left: 180.0, + right: 144.0, + }, + transition: Margin { + left: 144.0, + right: 144.0, + }, + centered: Margin { + left: 144.0, + right: 144.0, + }, + synopsis: Margin { + left: 108.0, + right: 72.0, + }, + page_number: Margin { + left: 108.0, + right: 72.0, + }, +}; + +struct LayoutInfo<'a> { + pub size: &'a PaperSize, + pub fonts: &'a FontFamily, +} /// A [`Screenplay`] exporter for `pdf` /// -/// Uses [`typst`] to create the `pdf` by first exporting the [`Screenplay`] to -/// a [`typst`] document. -/// /// The variables configure the exporter #[derive(Default)] pub struct PdfExporter { - /// Wheter to include synopses in the output + /// Whether to include synopses in the output pub synopses: bool, + /// What size (type) of paper (e.g. A4 or US letter) + pub paper_size: PaperSize, } impl Exporter for PdfExporter { + /// The `.pdf` extension. fn file_extension(&self) -> &'static str { "pdf" } - /// Exports a `pdf` file and writes it to the provided writer. This is done by first constructing - /// a [typst] document and then, using [typst], exporting that to an actual `pdf` document. + /// Exports a `pdf` file and writes it to the provided writer. The pdf creation can fail if + /// certain elements do not fit within a single page. fn export(&self, screenplay: &Screenplay, writer: &mut dyn Write) -> std::io::Result<()> { - let compiled_doc = TypstExporter { - synopses: self.synopses, - } - .compile_document(screenplay)?; - let pdf = typst_pdf::pdf(&compiled_doc, &PdfOptions::default()) - .map_err(|_| std::io::Error::other("failed to create typst pdf"))?; + let mut document = Document::new(); + + let fonts = FontFamily { + regular: Font::new(FONTS[0].into(), 0).unwrap(), + bold: Font::new(FONTS[1].into(), 0).unwrap(), + italic: Font::new(FONTS[2].into(), 0).unwrap(), + bold_italic: Font::new(FONTS[3].into(), 0).unwrap(), + }; + + let layout_info = LayoutInfo { + size: &self.paper_size, + fonts: &fonts, + }; + + self.generate_pdf(&mut document, &layout_info, screenplay)?; + + let pdf = document + .finish() + .map_err(|_| std::io::Error::other("failed to create pdf"))?; writer.write_all(&pdf) } } + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +enum Alignment { + LeftToRight, + RightToLeft, + Centered, +} + +impl PdfExporter { + /// Generates a `pdf` document from a [`Screenplay`]. Runs in (more or less) a single pass. + fn generate_pdf( + &self, + document: &mut Document, + layout_info: &LayoutInfo, + screenplay: &Screenplay, + ) -> std::io::Result<()> { + let mut element_iter = screenplay.elements.iter().peekable(); + + // The index for which page in the document is currently being written. + let mut page_idx = 0; + + // The maximum number of writable lines which can fit on a page, considering the top and + // bottom margins. + let max_lines_per_page = + (layout_info.size.y - (TOP_MARGIN + BOTTOM_MARGIN)) / FONT_SIZE - 1; + // If an element does not fit within a page this will be Some(index), where index is pointing + // to the breakpoint in the breakpoint list which should be on the start of the next page. + let mut residual_breakpoint_idx = None; + let mut residual_dialogue_idx = None; + + let mut residual_dual_dialogue_idx = (None, None); + let mut residual_dual_breakpoint_idx = (None, None); + + let mut outline = Outline::new(); + + if let Some(t) = &screenplay.titlepage { + page_idx += 1; + write_titlepage(t, layout_info, max_lines_per_page, document)?; + } + + // Page loop, creates a new page and writes everything it can on it. + while element_iter.peek().is_some() { + let mut page = document.start_page_with( + PageSettings::from_wh(layout_info.size.x as f32, layout_info.size.y as f32) + .unwrap(), + ); + let mut surface = page.surface(); + let mut line_idx = 0; + + // Writes the page number. + if (screenplay.titlepage.is_none() && page_idx > 0) + || (screenplay.titlepage.is_some() && page_idx > 1) + { + let residual_page_number = write_element_custom_top_margin( + layout_info, + &format!( + "{}.", + if screenplay.titlepage.is_some() { + page_idx + } else { + page_idx + 1 + } + ) + .into(), + &MARGINS.page_number, + &mut 0, + &mut 0, + 1, + &mut surface, + Alignment::RightToLeft, + 36, + )?; + + // Page number cannot be larger than what fits on a single line on the page. + if residual_page_number.is_some() { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "There cannot be more pages than the number which fits on a page.", + )); + } + } + + // Element loop, iterates through the screenplay elements. + while let Some(Span { + start_line: _, + end_line: _, + inner: element, + }) = element_iter.peek() + { + if line_idx >= max_lines_per_page { + break; + } + + let mut breakpoint_idx = match residual_breakpoint_idx { + Some(i) => { + // If we're in a dialogue element, we need to preserve + // `residual_breakpoint_idx`. + if !matches!(element, Element::Dialogue(_)) { + residual_breakpoint_idx = None; + } + i + } + None => 0, + }; + + /// Macro for the most common usage of write_element(...), as most types of + /// [`Element`]s call this function with almost identical parameters. + macro_rules! write_element { + ($content:expr, $margin:expr, $text_direction:expr) => { + residual_breakpoint_idx = write_element( + layout_info, + $content, + $margin, + &mut breakpoint_idx, + &mut line_idx, + max_lines_per_page, + &mut surface, + $text_direction, + )? + }; + } + + match &element { + Element::Heading { slug, number } => { + if number.is_some() { + let initial_line_index = line_idx; + + let left_number_margin = Margin { + left: 54.0, + right: layout_info.size.x as f32 - MARGINS.heading.left + 18.0, + }; + let right_number_margin = Margin { + left: layout_info.size.x as f32 - MARGINS.heading.right + 18.0, + right: 18.0, + }; + + let rich_number = &number.as_ref().unwrap().into(); + + write_element( + layout_info, + rich_number, + &left_number_margin, + &mut 0, + &mut initial_line_index.clone(), + max_lines_per_page, + &mut surface, + Alignment::LeftToRight, + )?; + + write_element( + layout_info, + rich_number, + &right_number_margin, + &mut 0, + &mut initial_line_index.clone(), + max_lines_per_page, + &mut surface, + Alignment::RightToLeft, + )?; + } + outline.push_child(OutlineNode::new( + slug.to_plain_string(), + XyzDestination::new( + page_idx, + Point { + x: MARGINS.heading.left, + y: (TOP_MARGIN + (line_idx * FONT_SIZE) - FONT_SIZE) as f32, + }, + ), + )); + write_element!(slug, &MARGINS.heading, Alignment::LeftToRight); + } + Element::Action(s) => { + write_element!(s, &MARGINS.action, Alignment::LeftToRight); + } + Element::Dialogue(dialogue) => { + let premature_exit = write_dialogue( + layout_info, + dialogue, + &mut residual_dialogue_idx, + &mut residual_breakpoint_idx, + max_lines_per_page, + &mut line_idx, + &mut surface, + &MARGINS.dialogue, + )?; + if residual_dialogue_idx.is_some() || premature_exit { + break; + } + } + Element::DualDialogue(dialogue0, dialogue1) => { + let mut initial_line_index = line_idx; + let mut premature_exit = false; + // Ensures dual dialogue is only written it either both have not been + // written, or if this specifically is to be continued on a new page. + if (residual_dual_dialogue_idx.0.is_none() + && residual_dual_dialogue_idx.1.is_none()) + || residual_dual_dialogue_idx.0.is_some() + { + premature_exit = premature_exit + || write_dialogue( + layout_info, + dialogue0, + &mut residual_dual_dialogue_idx.0, + &mut residual_dual_breakpoint_idx.0, + max_lines_per_page, + &mut line_idx, + &mut surface, + &MARGINS.dual_dialogue.left, + )?; + } + if (residual_dual_dialogue_idx.1.is_none() + && residual_dual_dialogue_idx.0.is_none()) + || residual_dual_dialogue_idx.1.is_some() + { + premature_exit = premature_exit + || write_dialogue( + layout_info, + dialogue1, + &mut residual_dual_dialogue_idx.1, + &mut residual_dual_breakpoint_idx.1, + max_lines_per_page, + &mut initial_line_index, + &mut surface, + &MARGINS.dual_dialogue.right, + )?; + } + line_idx = line_idx.max(initial_line_index); + if residual_dual_dialogue_idx.0.is_some() + || residual_dual_dialogue_idx.1.is_some() + || premature_exit + { + break; + } + } + Element::Lyrics(s) => { + write_element!(s, &MARGINS.lyrics, Alignment::RightToLeft); + } + Element::Transition(s) => { + write_element!(s, &MARGINS.transition, Alignment::RightToLeft); + } + Element::CenteredText(s) => { + write_element!(s, &MARGINS.centered, Alignment::Centered); + } + Element::Synopsis(s) => { + if self.synopses { + surface.set_fill(Some(Fill { + paint: rgb::Color::new(143, 143, 143).into(), + opacity: NormalizedF32::new(0.5).unwrap(), + rule: Default::default(), + })); + write_element!(s, &MARGINS.synopsis, Alignment::LeftToRight); + surface.set_fill(None); + } + } + Element::PageBreak => { + element_iter.next(); + break; + } + } + + // Newline separator between all elements + line_idx += 1; + + if residual_breakpoint_idx.is_some() { + continue; + } + + element_iter.next(); + } + + surface.finish(); + page.finish(); + page_idx += 1; + } + document.set_outline(outline); + + Ok(()) + } +} + +/// Writes a diologue [`Element`] to the `pdf` document. If a dialogue spans multiple pages it will +/// write the character name with the extension `(CONT'D)` on each new page. Returns a +/// [Option] which is true if the whole dialogue element did not fit on the same page. +fn write_dialogue( + layout_info: &LayoutInfo, + dialogue: &Dialogue, + residual_dialogue: &mut Option, + residual_index: &mut Option, + max_lines: usize, + line_index: &mut usize, + surface: &mut Surface, + dialogue_margins: &DialogueMargins, +) -> std::io::Result { + let mut character_name = dialogue.character.clone(); + match (*residual_dialogue, &dialogue.extension) { + (Some(_), _) => { + character_name.append(" (CONT'D)".into()); + } + (None, Some(ext)) => { + character_name.append(" (".into()); + character_name.append(ext.clone()); + character_name.append(")".into()); + } + _ => (), + }; + let span = glyph_span( + layout_info.size, + dialogue_margins.character.left, + dialogue_margins.character.right, + ); + let name_lines_count = break_points(&character_name, span).len() + 1; + + if name_lines_count >= max_lines { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Character name cannot be longer than a whole page.", + )); + } + + if *line_index + name_lines_count + 1 >= max_lines { + return Ok(true); + } + + write_element( + layout_info, + &character_name, + &dialogue_margins.character, + &mut 0, + line_index, + max_lines, + surface, + Alignment::LeftToRight, + )?; + + let mut dialogue_index = residual_dialogue.unwrap_or(0); + while dialogue_index < dialogue.elements.len() { + if *line_index >= max_lines { + *residual_dialogue = Some(dialogue_index); + // If a dialogue continues on the next page, writes `(MORE)` at the bottom of the + // current page, inside the bottom margin. + write_element( + layout_info, + &"(MORE)".into(), + &dialogue_margins.character, + &mut 0, + line_index, + max_lines + 1, + surface, + Alignment::LeftToRight, + )?; + + return Ok(true); + } + let mut breakpoint_index = match *residual_index { + Some(i) => { + *residual_index = None; + i + } + None => 0, + }; + + let (content, margin) = match &dialogue.elements[dialogue_index] { + DialogueElement::Parenthetical(s) => (s, &dialogue_margins.parenthetical), + DialogueElement::Line(s) => (s, &dialogue_margins.line), + }; + + *residual_index = write_element( + layout_info, + content, + margin, + &mut breakpoint_index, + line_index, + max_lines, + surface, + Alignment::LeftToRight, + )?; + + if residual_index.is_some() { + continue; + } + + dialogue_index += 1; + } + + *residual_dialogue = None; + Ok(false) +} + +/// Writes a [`Element`] to the `pdf` document. If it cannot fit the whole element on the page it +/// will return [`Some`] with an index to which [`BreakPoint`] the function made it to. Calls +/// [`write_element_custom_top_margin`] with the standard top margin. +fn write_element( + layout_info: &LayoutInfo, + content: &RichString, + margin: &Margin, + breakpoint_index: &mut usize, + line_index: &mut usize, + max_lines: usize, + surface: &mut Surface, + text_direction: Alignment, +) -> std::io::Result> { + write_element_custom_top_margin( + layout_info, + content, + margin, + breakpoint_index, + line_index, + max_lines, + surface, + text_direction, + TOP_MARGIN, + ) +} + +/// Writes a [`Element`] to the `pdf` document. If it cannot fit the whole element on the page it +/// will return [`Some`] with an index to which [`BreakPoint`] the function made it to. Allows for +/// using a non-default top margin (for page numbers). +fn write_element_custom_top_margin( + layout_info: &LayoutInfo, + content: &RichString, + margin: &Margin, + breakpoint_index: &mut usize, + line_index: &mut usize, + max_lines: usize, + surface: &mut Surface, + text_direction: Alignment, + top_margin: usize, +) -> std::io::Result> { + let left_margin = margin.left; + let right_margin = margin.right; + let span = glyph_span(layout_info.size, left_margin, right_margin); + let breakpoints = break_points(content, span); + while *breakpoint_index <= breakpoints.len() { + if *line_index >= max_lines { + return Ok(Some(*breakpoint_index)); + } + + let start_index = if *breakpoint_index == 0 { + 0 + } else { + breakpoints[*breakpoint_index - 1].index + }; + write_line( + layout_info, + surface, + left_margin, + (FONT_SIZE * *line_index + top_margin) as f32, + content, + start_index, + breakpoints.get(*breakpoint_index), + text_direction, + margin, + )?; + *breakpoint_index += 1; + *line_index += 1; + } + Ok(None) +} + +/// Writes a single line (not to be confused with [`DialogueElement::Line`]) onto the page. +fn write_line( + layout_info: &LayoutInfo, + surface: &mut Surface, + mut x: f32, + y: f32, + content: &RichString, + mut start_index: usize, // the "global" index to the rich string + breakpoint: Option<&BreakPoint>, + text_direction: Alignment, + margin: &Margin, +) -> std::io::Result<()> { + match content.get_char(start_index) { + // Newlines are handled as part of the layout engine, so it should never write ut a newline + // character. + Some(c) => { + if c == '\n' { + start_index += 1 + } + } + None => { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Could not get character from source.", + )); + } + } + + let (breakpoint_index, break_word) = match breakpoint { + Some(b) => (b.index, b.break_type == BreakType::BreakWord), + None => (content.char_count(), false), + }; + + // If we use a nonstandard [`Alignment`] we must move the `x` value of which we start writing + // the line on. + match text_direction { + Alignment::LeftToRight => (), + Alignment::RightToLeft => { + let line_length = breakpoint_index - start_index; + let line_span = line_length as f32 * FONT_WIDTH; + x += layout_info.size.x as f32 - (margin.left + margin.right) - line_span; + } + Alignment::Centered => { + let line_length = breakpoint_index - start_index; + let line_span = (line_length / 2) as f32 * FONT_WIDTH; + x = (layout_info.size.x / 2) as f32 - line_span; + } + } + + // Keeps track of the position on the line which the writer is currently at. + let mut glyph_index = 0; + // Writes until reaching the breakpoint position. + while start_index < breakpoint_index { + let (string_element, relative_index) = match content.get_element_from_index(start_index) { + Some(res) => res, + None => { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Could not get rich string element.", + )); + } + }; + + let element_length = string_element.text.chars().count(); + + // The local index in the rich string element at which to break the line. + let relative_break_index = + if breakpoint_index - start_index >= element_length - relative_index { + element_length + } else { + breakpoint_index - (start_index - relative_index) + }; + let font = match (string_element.is_bold(), string_element.is_italic()) { + (false, false) => &layout_info.fonts.regular, + (true, false) => &layout_info.fonts.bold, + (false, true) => &layout_info.fonts.italic, + (true, true) => &layout_info.fonts.bold_italic, + }; + let mut char_indices = string_element.text.char_indices(); + let start_byte_index = char_indices.nth(relative_index).unwrap().0; + let end_byte_index = match char_indices.nth(relative_break_index - relative_index - 1) { + Some((i, _)) => i, + None => string_element.text.len(), + }; + + surface.draw_text( + Point::from_xy(x + (glyph_index as f32 * FONT_WIDTH), y), + font.clone(), + FONT_SIZE as f32, + &string_element.text[start_byte_index..end_byte_index], + false, + krilla::text::TextDirection::LeftToRight, + ); + + let glyphs_written = relative_break_index - relative_index; + + // Draws a line under the characters (to create an underline effect). + if string_element.is_underline() { + let underline = { + let mut pb = PathBuilder::new(); + let r = Rect::from_xywh( + x + (glyph_index as f32 * FONT_WIDTH), + y + 0.5, + glyphs_written as f32 * FONT_WIDTH, + 0.75, + ) + .unwrap(); + pb.push_rect(r); + pb.close(); + pb.finish().unwrap() + }; + surface.draw_path(&underline); + } + + glyph_index += glyphs_written; + start_index += glyphs_written; + } + + // Adds a `-` at the end of a line if the linebreak took place inside a word. + if break_word { + surface.draw_text( + Point::from_xy(x + (glyph_index as f32 * FONT_WIDTH), y), + layout_info.fonts.regular.clone(), + FONT_SIZE as f32, + "-", + false, + krilla::text::TextDirection::LeftToRight, + ); + } + + Ok(()) +} + +/// Margins for the [`TitlePage`] elements. +struct TitlePageMargins { + pub title: Margin, + pub credit: Margin, + pub authors: Margin, + pub source: Margin, + pub draft_date: Margin, + pub contact: Margin, +} + +/// Standard margins for the [`TitlePage`] elements. +const TITLE_PAGE_MARGINS: TitlePageMargins = TitlePageMargins { + title: Margin { + left: 72.0, + right: 72.0, + }, + credit: Margin { + left: 72.0, + right: 72.0, + }, + authors: Margin { + left: 72.0, + right: 72.0, + }, + source: Margin { + left: 72.0, + right: 72.0, + }, + draft_date: Margin { + left: 315.0, + right: 72.0, + }, + contact: Margin { + left: 72.0, + right: 315.0, + }, +}; + +/// Writes the [`TitlePage`] to the `pdf` document. Fails if everything does not fit on one page, +/// but allows for overlapping contact information and draft dates with the rest of the elements. +fn write_titlepage( + titlepage: &TitlePage, + layout_info: &LayoutInfo, + max_lines: usize, + document: &mut Document, +) -> std::io::Result<()> { + let mut page = document.start_page_with( + PageSettings::from_wh(layout_info.size.x as f32, layout_info.size.y as f32).unwrap(), + ); + let mut surface = page.surface(); + + let mut line_idx = max_lines / 3; + + /// Writes the [`TitlePage`] elements using the [`write_element`] function. Will add newlines + /// between each type of element, as per (some) standards. + macro_rules! write_title_element { + // For the elements which are centered an written below each other. + ($element:ident) => { + if !titlepage.$element.is_empty() { + for s in &titlepage.$element { + let residual = write_element( + layout_info, + s, + &TITLE_PAGE_MARGINS.$element, + &mut 0, + &mut line_idx, + max_lines, + &mut surface, + Alignment::Centered, + )?; + + if residual.is_some() { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Title page cannot be longer than a single page.", + )); + } + } + line_idx += 1; + } + }; + // For elements which have specific alignments and are written from the bottom up. + ($element:ident, $alignment:expr) => { + if !titlepage.$element.is_empty() { + let mut total_lines = titlepage.$element.len(); + for s in &titlepage.$element { + total_lines += break_points( + s, + glyph_span( + layout_info.size, + TITLE_PAGE_MARGINS.$element.left, + TITLE_PAGE_MARGINS.$element.right, + ), + ) + .len(); + + if total_lines > max_lines { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Title page cannot be longer than a single page.", + )); + } + } + line_idx = max_lines - total_lines; + + for s in &titlepage.$element { + write_element( + layout_info, + s, + &TITLE_PAGE_MARGINS.$element, + &mut 0, + &mut line_idx, + max_lines, + &mut surface, + $alignment, + )?; + } + } + }; + } + + write_title_element!(title); + write_title_element!(credit); + write_title_element!(authors); + write_title_element!(source); + + write_title_element!(contact, Alignment::LeftToRight); + write_title_element!(draft_date, Alignment::RightToLeft); + + surface.finish(); + page.finish(); + Ok(()) +} + +/// Calculates the span in a margin. Returns how many characters with the standard font width will +/// fit between the left and right margin. +fn glyph_span(size: &PaperSize, left_margin: f32, right_margin: f32) -> usize { + ((size.x as f32 - (left_margin + right_margin)) / FONT_WIDTH) as usize +} + +/// The different ways to break a line into multiple lines. +#[derive(Debug, PartialEq, Eq, Clone, Hash)] +enum BreakType { + /// Standard type of line break, by just adding a newline somewhere. + NewLine, + /// Break the line by adding a newline inside a word. + BreakWord, +} + +/// Where in the string a line break should occur. +#[derive(Debug, PartialEq, Eq, Clone, Hash)] +struct BreakPoint { + pub index: usize, + /// If it should add a newline after a word, or inside a word. + pub break_type: BreakType, +} + +/// Given a [`RichString`] and a number of allowed glyphs on a single line, calculates where +/// eventual [`BreakPoint`]s should be placed in the string. +/// +/// Greedily places [`BreakPoint`]s. Will only place one inside a word if the word does not fit on +/// a line by itself. Respects newline characters in the string. +fn break_points(content: &RichString, span: usize) -> Vec { + debug_assert!(span >= 2); + + let mut brekpoints = Vec::with_capacity(content.char_count() / span + 1); + let mut last_whitespace_char = (0, 0); + let mut line_len = 0; + for (i, glyph) in content.iter().enumerate() { + line_len += 1; + if glyph == '\n' { + brekpoints.push(BreakPoint { + index: i, + break_type: BreakType::NewLine, + }); + line_len = 0; + continue; + } + + if glyph.is_whitespace() || glyph == '-' { + last_whitespace_char = (brekpoints.len() + 1, i); + continue; + } + + if line_len >= span { + if brekpoints.len() + 1 != last_whitespace_char.0 { + brekpoints.push(BreakPoint { + index: i, + break_type: BreakType::BreakWord, + }); + line_len = 0; + continue; + } + + brekpoints.push(BreakPoint { + index: last_whitespace_char.1 + 1, + break_type: BreakType::NewLine, + }); + line_len = i - last_whitespace_char.1; + } + } + brekpoints +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn breaks_simple() { + let mut rs = RichString::new(); + rs.push_str("hello world"); + + let breakpoints = break_points(&rs, 6); + let correct = vec![BreakPoint { + index: 6, + break_type: BreakType::NewLine, + }]; + + assert_eq!(breakpoints, correct); + } + + #[test] + fn breaks_simple_with_newline() { + let mut rs = RichString::new(); + rs.push_str("hello\nworld"); + + let breakpoints = break_points(&rs, 100); + let correct = vec![BreakPoint { + index: 5, + break_type: BreakType::NewLine, + }]; + + assert_eq!(breakpoints, correct); + } + + #[test] + fn breaks_simple_breakword() { + let mut rs = RichString::new(); + rs.push_str("helloworld"); + + let breakpoints = break_points(&rs, 6); + let correct = vec![BreakPoint { + index: 5, + break_type: BreakType::BreakWord, + }]; + + assert_eq!(breakpoints, correct); + } + + #[test] + fn breaks_simple_utilizing_hyphen() { + let mut rs = RichString::new(); + rs.push_str("hello-world"); + + let breakpoints = break_points(&rs, 7); + let correct = vec![BreakPoint { + index: 6, + break_type: BreakType::NewLine, + }]; + + assert_eq!(breakpoints, correct); + } + + #[test] + fn breaks_rich() { + let mut rs = RichString::new(); + rs.push_str("he**ll**o wor*ld*"); + + let breakpoints = break_points(&rs, 6); + let correct = vec![BreakPoint { + index: 6, + break_type: BreakType::NewLine, + }]; + + assert_eq!(breakpoints, correct); + } + + #[test] + fn breaks_rich_longer() { + let mut rs = RichString::new(); + rs.push_str("Bosse går till **affären** och köper lite mjölk, vilket han tycker är väldigt gott att äta."); + + let breakpoints = break_points(&rs, 60); + let correct = vec![BreakPoint { + index: 56, + break_type: BreakType::NewLine, + }]; + + assert_eq!(breakpoints, correct); + } +} diff --git a/crates/rustwell/src/export/template.typ b/crates/rustwell/src/export/template.typ deleted file mode 100644 index 15c60f4..0000000 --- a/crates/rustwell/src/export/template.typ +++ /dev/null @@ -1,172 +0,0 @@ -// This template is directly based on the 'celluloid' typst template -// by Casey Dahlin. See https://typst.app/universe/package/celluloid for -// the original template. This template has been heavily modified and is -// not fit for use outside of Rustwell, we instead refer to using the -// fantastic celluloid template for writing screenplays in typst. Below -// is the original copyright notice from celluloid. - -// Copyright (C) 2025 Casey Dahlin -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#let line_spacing(blanks) = 0.65em + blanks * 1.23em -#let dialogue_counter = counter("dialogue") - -#let screenplay( - titlepage: false, - title: none, - credit: none, - authors: none, - source: none, - draft_date: none, - contact: none, - doc, -) = { - set page( - margin: ( top: 1in, left: 1.5in, right: 1in, bottom: 0.5in), - header-ascent: 0.5in, - ) - set text( - font: "Courier Prime", - size: 12pt, - ) - set par(spacing: line_spacing(1)) - - show heading: h => { - set text(size: 12pt, weight: "regular") - set block(spacing: line_spacing(1)) - h - } - - if titlepage { - page({ - align(center, { - upper(title) - - if credit != none { - block(credit, above: 1in) - } - if authors != none { - block(authors, spacing: 0.5in) - } - if source != none { - block(source, spacing: 0.6in) - } - if draft_date != none { - block(draft_date, spacing: 0.9in) - } - }) - - if contact != none { - align(bottom, box(align(left, contact))) - } - counter(page).update(0) - }, margin: ( top: 2in, bottom: 1in )) - } - - set page( - header: context { - if counter(page).get().at(0) > 0 { - align(right, counter(page).display("1.")) - } - }, - ) - - doc -} - -#let dialogue_raw(character, dialogue, paren: none, left_inset, left_inset_name, right_inset_name) = { - context { - set par(spacing: line_spacing(0)) - dialogue_counter.step() - let dialogue_count = dialogue_counter.get().at(0) - let dialogue_header_counter = counter("dialogue_header" + str(dialogue_count)) - let dialogue_footer_counter = counter("dialogue_footer" + str(dialogue_count)) - grid( - grid.header(block(par([#upper(context { - dialogue_header_counter.step() - let paren = if dialogue_header_counter.get() != (0,) { - "CONT’D" - } else { - paren - } - character - if paren != none { - [ (#paren)] - } - })]), inset: (left: left_inset))), - block(dialogue, spacing: line_spacing(0)), - grid.footer(block({ - dialogue_footer_counter.step() - context { - if dialogue_footer_counter.get() != dialogue_footer_counter.final() { - [(MORE)] - } - } - }, inset: (left: left_inset), spacing: line_spacing(0))), - inset: (left: left_inset_name, right: right_inset_name), - gutter: line_spacing(0), - ) - } -} - -#let dialogue(character, dialogue, paren: none) = dialogue_raw(character, dialogue, paren: paren, 1.5in, 1in, 1.5in) - -#let dual_dialogue(character1, dialogue1, paren1: none, character2, dialogue2, paren2: none) = grid( - columns: 2, - dialogue_raw(character1, dialogue1, paren: paren1, 1in, 0.5in, 1in), - dialogue_raw(character2, dialogue2, paren: paren2, 1in, 0.5in, 1in), -) - -#let lyrics(cont) = { - block( - upper(cont), - above: line_spacing(0), - inset: (left: 1in, right: 1.5in), - ) -} - -#let parenthetical(content) = { - context{ - block( - par([#content], hanging-indent: measure("(").width), - inset: (left: 0.5in, right: 1in), spacing: line_spacing(0) - ) - } -} - -#let scene(cont, number: none) = { - grid( - columns: (0em, 1fr, 0em), - place(dx: -3em, align(right, number)), - heading(block(upper(cont), above: line_spacing(2), below: line_spacing(1))), - place(dx: 1em, number), - ) -} - -#let centered(cont) = { - block({ - align(center, block(cont)) - }, breakable: false, width: 100%, below: line_spacing(2)) -} - -#let transition(name) = { - align(right, block([#upper(name)], spacing: line_spacing(1), inset: (right: 2em))) -} - -#let synopsis(cont) = { - block( - inset: (left: 1em, right: 1em), - text(fill: luma(100), cont) - ) -} diff --git a/crates/rustwell/src/export/typst.rs b/crates/rustwell/src/export/typst.rs deleted file mode 100644 index 7ccb0e1..0000000 --- a/crates/rustwell/src/export/typst.rs +++ /dev/null @@ -1,371 +0,0 @@ -use std::{collections::HashMap, io::Write}; - -use typst::{ - self, Library, LibraryExt, - diag::{FileError, FileResult}, - foundations::{Bytes, Datetime}, - layout::PagedDocument, - syntax::{FileId, Source, VirtualPath}, - text::{Font, FontBook, FontInfo}, - utils::LazyHash, -}; - -use crate::{ - Exporter, - rich_string::{self, RichString}, - screenplay::{DialogueElement, Element, Screenplay, Span}, -}; - -/// The contents of the [typst] template `template.typ` found in the -/// export module. -const TEMPLATE: &str = include_str!("template.typ"); - -/// A [`Screenplay`] exporter for `typst` -/// -/// The [`Exporter`] implementation for [`Exporter::export`] exports [`typst`] -/// source code. -/// -/// The variables configure the exporter -#[derive(Default)] -pub struct TypstExporter { - /// If synopses should be included in the output - pub synopses: bool, -} - -impl Exporter for TypstExporter { - fn file_extension(&self) -> &'static str { - "typ" - } - - /// Exports [`typst`] source code - fn export(&self, screenplay: &Screenplay, writer: &mut dyn Write) -> std::io::Result<()> { - self.export_typst(screenplay, writer) - } -} - -impl TypstExporter { - /// Exports the provided [Screenplay] as a pure [typst] document that can be - /// manually compiled with any [typst]-compiler. The document will not be very - /// readable nor be provided with comments explaining anything. This is mainly included - /// for debugging. - pub fn export_typst( - &self, - screenplay: &Screenplay, - mut writer: impl Write, - ) -> std::io::Result<()> { - let content = self.format_as_typst(screenplay); - write!(writer, "{content}") - } - - /// Generates a [PagedDocument], which is a layouted [typst] document which can then - /// be exported and written with any [typst] exporter, like [typst_pdf]. - pub fn compile_document(&self, screenplay: &Screenplay) -> std::io::Result { - let (fontbook, fonts) = create_fontbook(); - let content = self.format_as_typst(screenplay); - let worldplay = WorldPlay::new(content, &fontbook, &fonts); - let pd: Result = typst::compile(&worldplay).output; - match pd { - Ok(p) => Ok(p), - Err(_) => Err(std::io::Error::other("failed to compile typst document")), - } - } - - /// Formats the [Screenplay] as a [typst] document, meaning it essentially gets - /// converted into [typst]-compilable code. - fn format_as_typst(&self, screenplay: &Screenplay) -> String { - let formatted_elements = screenplay - .elements - .iter() - .map( - |Span { - start_line: _, - end_line: _, - inner: e, - }| self.export_element(e), - ) - .collect::>(); - let titlepage = self.export_titlepage(screenplay); - format!("{TEMPLATE}\n{titlepage}\n{}", formatted_elements.join("\n")) - } - - /// Exports the [crate::screenplay::TitlePage] in the provided [Screenplay] to [typst] code. - /// This function also provides the necessary `#show: screenplay.with(...)` that - /// handles the page layout for the whole screenplay. - fn export_titlepage(&self, screenplay: &Screenplay) -> String { - if let Some(titlepage) = &screenplay.titlepage { - let title = self.format_titlepage_element(&titlepage.title); - let credit = self.format_titlepage_element(&titlepage.credit); - let authors = self.format_titlepage_element(&titlepage.authors); - let source = self.format_titlepage_element(&titlepage.source); - let draft_date = self.format_titlepage_element(&titlepage.draft_date); - let contact = self.format_titlepage_element(&titlepage.contact); - format!( - r#"#show: screenplay.with( - titlepage: true, - title: {title}, - credit: {credit}, - authors: {authors}, - source: {source}, - draft_date: {draft_date}, - contact: {contact}, -)"# - ) - } else { - "#show: screenplay.with(titlepage: false)".to_string() - } - } - - /// Exports a single [Element] as [typst] code. Primarily done by calling the associated - /// [typst] function found in the template. - fn export_element(&self, element: &Element) -> String { - match element { - Element::Heading { slug, number } => { - if let Some(num) = number { - format!( - r#"#scene(number: "{}")[{}]"#, - self.replace_escaping(num), - self.format_rich_string(slug) - ) - } else { - format!("#scene[{}]", self.format_rich_string(slug)) - } - } - Element::Action(s) => self.format_rich_string(s), - Element::Dialogue(dialogue) => format!( - "#dialogue(paren: {})[{}][{}]", - self.format_character_extension(&dialogue.extension), - self.format_rich_string(&dialogue.character), - self.format_dialogue(&dialogue.elements), - ), - Element::DualDialogue(dialogue1, dialogue2) => format!( - "#dual_dialogue(paren1: {}, paren2: {})[{}][{}][{}][{}]", - self.format_character_extension(&dialogue1.extension), - self.format_character_extension(&dialogue2.extension), - self.format_rich_string(&dialogue1.character), - self.format_dialogue(&dialogue1.elements), - self.format_rich_string(&dialogue2.character), - self.format_dialogue(&dialogue2.elements), - ), - Element::Lyrics(s) => format!("#lyrics[{}]", self.format_rich_string(s)), - Element::Transition(s) => format!("#transition[{}]", self.format_rich_string(s)), - Element::CenteredText(s) => format!("#centered[{}]", self.format_rich_string(s)), - Element::Synopsis(s) => { - if self.synopses { - format!("#synopsis[{}]", self.format_rich_string(s)) - } else { - "".to_string() - } - } - Element::PageBreak => "#pagebreak()".to_string(), - } - } - - /// Formats the dialogue into [typst] code. - fn format_dialogue(&self, dialogue: &[DialogueElement]) -> String { - dialogue - .iter() - .map(|d| self.format_dialogue_element(d)) - .collect::>() - .join(" ") - } - - /// Formats the character extension (`(V.O)`, for example) that is - /// next to a character's name in a dialogue. - fn format_character_extension(&self, opt_ext: &Option) -> String { - if let Some(ext) = opt_ext { - format!("[{}]", self.format_rich_string(ext)) - } else { - "none".to_string() - } - } - - /// Formats a [DialogueElement] into a [typst] code. - fn format_dialogue_element(&self, element: &DialogueElement) -> String { - match element { - DialogueElement::Parenthetical(s) => { - format!("#parenthetical[{}]", self.format_rich_string(s)) - } - DialogueElement::Line(s) => self.format_rich_string(s), - } - } - - /// Formats a [RichString] into a [typst]-[String]. - fn format_rich_string(&self, str: &RichString) -> String { - str.elements - .iter() - .map(|e| self.format_rich_element(e)) - .collect::>() - .concat() - } - - /// Formats a [RichString] [rich_string::Element] into a [typst]-[String]. - /// All elements will be explicitly contained in a `#text("{element.text}")` - /// function from [typst], with styling using `weight: "bold"`, `style: "italic"` - /// and `#underline[#text(...)]`. - /// - /// This function also iterates over each string twice to replace all escaping - /// characters `\` and `"` with `\\` and `\*` respectively. - fn format_rich_element(&self, element: &rich_string::Element) -> String { - // Assumes newlines '\n' will only occur sole elements - if element.text == "\n" { - return "\\ ".to_string(); - } - - let mut out = format!( - "#text({}{}\"{}\")", - if element.is_bold() { - "weight: \"bold\"," - } else { - "" - }, - if element.is_italic() { - "style: \"italic\"," - } else { - "" - }, - self.replace_escaping(&element.text) - ); - if element.is_underline() { - out = format!("#underline[{}]", out); - } - - out - } - - /// This function also iterates over each string twice to replace all escaping - /// characters `\` and `"` with `\\` and `\*` respectively. - fn replace_escaping(&self, s: &str) -> String { - s.replace("\\", "\\\\").replace("\"", "\\\"") - } - - /// Formats a single [crate::screenplay::TitlePage] element into [typst] code. - /// If no value has been declared it will return `"none"`. - fn format_titlepage_element(&self, element: &[RichString]) -> String { - if element.is_empty() { - return "none".to_string(); - } - format!( - "[{}]", - element - .iter() - .map(|e| self.format_rich_string(e)) - .collect::>() - .join("\\ ") - ) - } -} - -/// Internal [typst::World] which is basically the whole underlying structure of the [typst] -/// document. This is significantly more slimmed down than a real [typst::World] is, as -/// everything not needed for Rustwell has been stripped away. -struct WorldPlay<'a> { - library: LazyHash, - book: &'a LazyHash, - source: HashMap, - fonts: &'a Vec, -} - -/// `MAIN` contains the "filename" of the main file, which in [typst] **has** to be `/main.typ`. -const MAIN: &str = "/main.typ"; - -/// The font bundled together with Rustwell; Courier Prime. -/// Includes the data of the font styles Regular, Bold, Italic -/// and BoldItalic, in stated order. -const FONTS: [&[u8]; 4] = [ - include_bytes!("fonts/CourierPrime-Regular.ttf"), - include_bytes!("fonts/CourierPrime-Bold.ttf"), - include_bytes!("fonts/CourierPrime-Italic.ttf"), - include_bytes!("fonts/CourierPrime-BoldItalic.ttf"), -]; - -impl<'a> WorldPlay<'a> { - fn new(content: String, book: &'a LazyHash, fonts: &'a Vec) -> Self { - let mut sources = HashMap::with_capacity(1); - let main = create_source(MAIN, content); - let main_id = main.id(); - sources.insert(main_id, main); - - Self { - library: LazyHash::new(Library::default()), - book, - fonts, - source: sources, - } - } -} - -impl typst::World for WorldPlay<'_> { - /// The standard library. - /// - /// Can be created through `Library::build()`. - fn library(&self) -> &LazyHash { - &self.library - } - - /// Metadata about all known fonts. - fn book(&self) -> &LazyHash { - self.book - } - - /// Get the file id of the main source file. - fn main(&self) -> FileId { - create_file_id(MAIN) - } - - /// Try to access the specified source file. - fn source(&self, id: FileId) -> FileResult { - match self.source.get(&id) { - Some(s) => Ok(s.clone()), - None => FileResult::Err(FileError::NotSource), - } - } - - /// Try to access the specified file. - /// WARNING: This function will only return [FileError] as it is - /// is not implemented, nor needed for Rustwell. - fn file(&self, _: FileId) -> FileResult { - FileResult::Err(FileError::NotSource) - } - - /// Try to access the font with the given index in the font book. - fn font(&self, index: usize) -> Option { - self.fonts.get(index).cloned() - } - - /// Gets the current system time. - /// WARNING: This function will only return [None] as it is - /// is not implemented, nor needed for Rustwell. - fn today(&self, _: Option) -> Option { - None - } -} - -/// Creates a [Source] based on a document. -fn create_source(filename: &str, content: String) -> Source { - let file_id = create_file_id(filename); - Source::new(file_id, content) -} - -/// Creates a [FileId] based on a filename. -fn create_file_id(filename: &str) -> FileId { - FileId::new(None, VirtualPath::new(filename)) -} - -/// Creates a [FontBook] which indexes the returned [Vec]. -fn create_fontbook() -> (LazyHash, Vec) { - let mut fonts = Vec::new(); - let mut fontbook = FontBook::new(); - - for font_data in FONTS.iter() { - let font = match Font::new(Bytes::new(font_data), 0) { - Some(f) => f, - None => continue, - }; - fonts.push(font); - - let info = FontInfo::new(font_data, 0).expect("Could not parse font"); - fontbook.push(info); - } - - (LazyHash::new(fontbook), fonts) -} diff --git a/crates/rustwell/src/lib.rs b/crates/rustwell/src/lib.rs index 83eb74a..830cfb7 100644 --- a/crates/rustwell/src/lib.rs +++ b/crates/rustwell/src/lib.rs @@ -42,8 +42,10 @@ pub use screenplay::Screenplay; pub use export::Exporter; pub use export::ExporterExt; pub use export::html::HtmlExporter; +pub use export::pdf::A4; +pub use export::pdf::LETTER; +pub use export::pdf::PaperSize; pub use export::pdf::PdfExporter; -pub use export::typst::TypstExporter; /// Parses a Fountain source string into a [Screenplay] structure. /// diff --git a/crates/rustwell/src/rich_string.rs b/crates/rustwell/src/rich_string.rs index feec28f..b4c409c 100644 --- a/crates/rustwell/src/rich_string.rs +++ b/crates/rustwell/src/rich_string.rs @@ -20,6 +20,7 @@ //! ``` use std::collections::HashMap; +use std::{fmt::Display, str::Chars}; use bitflags::bitflags; use unicode_properties::{GeneralCategoryGroup, UnicodeGeneralCategory}; @@ -64,6 +65,98 @@ impl RichString { } } + /// The total length of a [`RichString`], meaning the total number of [`char`]s. + pub fn char_count(&self) -> usize { + let mut len = 0; + for e in &self.elements { + len += e.text.chars().count(); + } + len + } + + /// Gets a [`char`] from a "global" index, meaning the index when viewing the [`RichString`] as + /// a single string without any style attributes taken into account. + /// + /// # Examples + /// + /// ``` + /// use rustwell::rich_string::RichString; + /// + /// let mut rs = RichString::from("He**llo**"); + /// + /// assert_eq!(rs.get_char(1), Some('e')); + /// assert_eq!(rs.get_char(3), Some('l')); + /// assert_eq!(rs.get_char(5), None); + /// ``` + pub fn get_char(&self, mut index: usize) -> Option { + if index >= self.char_count() { + return None; + } + for e in &self.elements { + if index >= e.text.chars().count() { + index -= e.text.chars().count(); + continue; + } + return e.text.chars().nth(index); + } + None + } + + /// Given a "global" index, gets the [`Element`] which contains it, and the "local" index + /// pointing to that character in the element. + /// + /// # Examples + /// + /// ``` + /// use rustwell::rich_string::RichString; + /// + /// let mut rs = RichString::from("He**ll**o"); + /// + /// assert!(matches!(rs.get_element_from_index(1), Some((_, 1)))); + /// assert!(matches!(rs.get_element_from_index(2), Some((_, 0)))); + /// assert!(matches!(rs.get_element_from_index(3), Some((_, 1)))); + /// assert!(matches!(rs.get_element_from_index(4), Some((_, 0)))); + /// ``` + pub fn get_element_from_index(&self, mut index: usize) -> Option<(&Element, usize)> { + if index >= self.char_count() { + return None; + } + for e in &self.elements { + if index >= e.text.chars().count() { + index -= e.text.chars().count(); + continue; + } + return Some((e, index)); + } + None + } + + /// Creates an [`char`] iterator over the [`RichString`], without the style attributes of each + /// [`char`] taken into account. + pub fn iter(&'_ self) -> RichIterator<'_> { + RichIterator { + rich_string: self, + element_idx: 0, + chars_iterator: self.elements[0].text.chars(), + } + } + + /// Appends a [`RichString`] to self. Will merge the last [`Element`] of self, and the + /// first of the other if they have the same style attributes. + pub fn append(&mut self, mut other: Self) { + if let Some(e) = other.elements.first() + && let Some(l) = self.elements.last_mut() + { + if e.attributes == l.attributes { + l.text.push_str(&e.text); + other.elements.drain(..1); + self.elements.append(&mut other.elements); + return; + } + } + self.elements.append(&mut other.elements); + } + /// Pushes a string onto the [`RichString`]. Will divide the string into /// multiple elements with different styles if input string can be parsed with styles. pub fn push_str(&mut self, str: impl AsRef) { @@ -73,6 +166,27 @@ impl RichString { self.push_parsed(&tokens, &delimiters, &matches); } + /// Converts the [`RichString`] to a plain [`String`] by just combining the string elements + /// without adding delimiters. + /// + /// # Examples + /// + /// ``` + /// use rustwell::rich_string::RichString; + /// + /// let mut rs = RichString::from("He**ll**o"); + /// + /// assert_eq!(rs.to_plain_string(), "Hello".to_string()); + /// ``` + pub fn to_plain_string(&self) -> String { + let mut str = String::with_capacity(self.char_count()); + for element in &self.elements { + str.push_str(&element.text); + } + + str + } + /// Creates a list of "tokens" and [`Delimiter`] runs. /// /// By token is meant [`&str`] slices divided at delimiters. @@ -315,6 +429,34 @@ impl Default for RichString { } } +impl Display for RichString { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut str = String::with_capacity(self.char_count()); + + for element in &self.elements { + macro_rules! attr_to_delim { + ($attr:ident, $delimiter:expr) => { + if element.$attr() { $delimiter } else { "" } + }; + } + + let element_text = format!( + "{}{}{}{}{}{}{}", + attr_to_delim!(is_bold, "**"), + attr_to_delim!(is_italic, "*"), + attr_to_delim!(is_underline, "_"), + element.text, + attr_to_delim!(is_underline, "_"), + attr_to_delim!(is_italic, "*"), + attr_to_delim!(is_bold, "**"), + ); + str.extend(element_text.chars()); + } + + write!(f, "{str}") + } +} + impl From for RichString where T: AsRef, @@ -326,6 +468,31 @@ where } } +/// An intermediate iterator which allows for seamless iteration over the [Chars] inside a +/// [`RichString`]. +pub struct RichIterator<'a> { + rich_string: &'a RichString, + element_idx: usize, + chars_iterator: Chars<'a>, +} + +impl<'a> Iterator for RichIterator<'a> { + type Item = char; + + fn next(&mut self) -> Option { + let next = self.chars_iterator.next(); + if next.is_some() { + return next; + } + self.element_idx += 1; + if self.element_idx >= self.rich_string.elements.len() { + return None; + } + self.chars_iterator = self.rich_string.elements[self.element_idx].text.chars(); + self.chars_iterator.next() + } +} + /// A [`RichString`] component, containing a [String] and the style attributes /// belonging to said string. #[must_use] @@ -397,6 +564,20 @@ struct Match { mod tests { use super::*; + #[test] + fn appends_same_attributes() { + let mut rs: RichString = "a*b*".into(); + let other: RichString = "*c*d".into(); + rs.append(other); + assert_eq!(rs.elements[1].text, "bc".to_string()) + } + + #[test] + fn displays_with_delims() { + let rs: RichString = "H**e**_ll_**_o_**".into(); + assert_eq!(rs, rs.to_string().into()) + } + mod tokenize { use super::*;