diff --git a/Cargo.lock b/Cargo.lock index 1faaec4..2bb44fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" + [[package]] name = "addr2line" version = "0.24.2" @@ -17,6 +23,18 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -26,6 +44,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "aligned-vec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" + [[package]] name = "allocator-api2" version = "0.2.21" @@ -62,6 +86,35 @@ dependencies = [ "num-traits", ] +[[package]] +name = "arabic_reshaper" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb963411bc62b3888e69ad0d821ac03a3176ec7d74c2096bb5555a4cc419545b" + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + [[package]] name = "arrayvec" version = "0.7.6" @@ -125,6 +178,29 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "av1-grain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98922d6a4cfbcb08820c69d8eeccc05bb1f29bfa06b4f5b1dbfe9a868bd7608e" +dependencies = [ + "arrayvec", +] + [[package]] name = "backtrace" version = "0.3.75" @@ -190,6 +266,12 @@ dependencies = [ "which", ] +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + [[package]] name = "bitflags" version = "1.3.2" @@ -205,6 +287,12 @@ dependencies = [ "serde", ] +[[package]] +name = "bitstream-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" + [[package]] name = "block-buffer" version = "0.10.4" @@ -214,6 +302,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "built" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b" + [[package]] name = "bumpalo" version = "3.17.0" @@ -232,12 +326,24 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" +[[package]] +name = "bytemuck" +version = "1.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" + [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.10.1" @@ -308,6 +414,8 @@ version = "1.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1" dependencies = [ + "jobserver", + "libc", "shlex", ] @@ -320,6 +428,16 @@ dependencies = [ "nom", ] +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -352,6 +470,12 @@ dependencies = [ "libloading", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "command_attr" version = "0.5.3" @@ -436,6 +560,25 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-queue" version = "0.3.12" @@ -451,6 +594,12 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crunchy" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" + [[package]] name = "crypto-common" version = "0.1.6" @@ -546,6 +695,15 @@ dependencies = [ "serde", ] +[[package]] +name = "emojis" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99e1f1df1f181f2539bac8bf027d31ca5ffbf9e559e3f2d09413b9107b5c02f4" +dependencies = [ + "phf", +] + [[package]] name = "encoding_rs" version = "0.8.35" @@ -602,6 +760,21 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "exr" +version = "1.73.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0" +dependencies = [ + "bit_field", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + [[package]] name = "fast-srgb8" version = "1.0.0" @@ -614,6 +787,15 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + [[package]] name = "flate2" version = "1.1.1" @@ -813,6 +995,16 @@ dependencies = [ "wasi 0.14.2+wasi-0.2.4", ] +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gimli" version = "0.31.1" @@ -863,11 +1055,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] name = "hashbrown" @@ -1293,6 +1499,61 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "image" +version = "0.25.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "exr", + "gif", + "image-webp", + "num-traits", + "png", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b77d01e822461baa8409e156015a1d91735549f0f2c17691bd2d996bef238f7f" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imagetext" +version = "2.1.1" +source = "git+https://github.com/nathanielfernandes/imagetext#3532b1367823c52e93aadbf389044cf1ac411365" +dependencies = [ + "emojis", + "hashbrown 0.14.5", + "image", + "log", + "once_cell", + "regex", + "rusttype", + "tiny-skia", + "unicode-segmentation", +] + +[[package]] +name = "imgref" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" + [[package]] name = "indexmap" version = "2.9.0" @@ -1303,6 +1564,17 @@ dependencies = [ "hashbrown 0.15.3", ] +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "ipnet" version = "2.11.0" @@ -1324,6 +1596,22 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "jobserver" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.3", + "libc", +] + +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" + [[package]] name = "js-sys" version = "0.3.77" @@ -1371,6 +1659,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "levenshtein" version = "1.0.5" @@ -1383,6 +1677,16 @@ version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +[[package]] +name = "libfuzzer-sys" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf78f52d400cf2d84a3a973a78a592b4adc535739e0a5597a0da6f0c357adc75" +dependencies = [ + "arbitrary", + "cc", +] + [[package]] name = "libloading" version = "0.8.7" @@ -1444,6 +1748,15 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + [[package]] name = "magick_rust" version = "1.0.0" @@ -1453,6 +1766,16 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + [[package]] name = "md-5" version = "0.10.6" @@ -1545,6 +1868,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -1575,6 +1899,12 @@ dependencies = [ "tempfile", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "nom" version = "7.1.3" @@ -1585,6 +1915,22 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + +[[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-bigint-dig" version = "0.8.4" @@ -1608,6 +1954,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "num-integer" version = "0.1.46" @@ -1628,6 +1985,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1697,6 +2065,15 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "owned_ttf_parser" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e6affeb1632d6ff6a23d2cd40ffed138e82f1532571a26f527c8a284bb2fbb" +dependencies = [ + "ttf-parser", +] + [[package]] name = "palette" version = "0.7.6" @@ -1750,6 +2127,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -1846,6 +2229,19 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[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 = "potential_utf" version = "0.1.2" @@ -1889,6 +2285,25 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "profiling" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" +dependencies = [ + "quote", + "syn 2.0.101", +] + [[package]] name = "pulldown-cmark" version = "0.9.6" @@ -1900,6 +2315,21 @@ dependencies = [ "unicase", ] +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quote" version = "1.0.40" @@ -1945,6 +2375,76 @@ dependencies = [ "getrandom 0.2.16", ] +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "once_cell", + "paste", + "profiling", + "rand", + "rand_chacha", + "simd_helpers", + "system-deps", + "thiserror 1.0.69", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6a5f31fcf7500f9401fea858ea4ab5525c99f2322cfcee732c0e6c74208c0c6" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.5.12" @@ -2098,6 +2598,12 @@ dependencies = [ "tower-service", ] +[[package]] +name = "rgb" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" + [[package]] name = "ring" version = "0.17.14" @@ -2268,6 +2774,16 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rusttype" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff8374aa04134254b7995b63ad3dc41c7f7236f69528b28553da7d72efaa967" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + [[package]] name = "rustversion" version = "1.0.20" @@ -2397,6 +2913,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2504,6 +3029,21 @@ dependencies = [ "rand_core", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + [[package]] name = "siphasher" version = "1.0.1" @@ -2790,6 +3330,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" + [[package]] name = "stringprep" version = "0.1.5" @@ -2897,12 +3443,31 @@ dependencies = [ "libc", ] +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + [[package]] name = "tagptr" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + [[package]] name = "tempfile" version = "3.20.0" @@ -2956,6 +3521,17 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + [[package]] name = "time" version = "0.3.41" @@ -2987,6 +3563,32 @@ dependencies = [ "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", + "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.8.1" @@ -3122,6 +3724,40 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tower" version = "0.5.2" @@ -3193,6 +3829,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "ttf-parser" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd" + [[package]] name = "tungstenite" version = "0.21.0" @@ -3308,6 +3950,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "unicode-width" version = "0.1.14" @@ -3350,12 +3998,29 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4bf03e0ca70d626ecc4ba6b0763b934b6f2976e8c744088bb3c1d646fbb1ad0" +[[package]] +name = "v_frame" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + [[package]] name = "version_check" version = "0.9.5" @@ -3490,8 +4155,11 @@ dependencies = [ name = "watcat" version = "0.1.0" dependencies = [ + "arabic_reshaper", "dotenvy", "http-cache-reqwest", + "image", + "imagetext", "lastfm", "magick_rust", "palette", @@ -3500,6 +4168,7 @@ dependencies = [ "serenity", "sqlx", "tokio", + "unicode-bidi", ] [[package]] @@ -3536,6 +4205,12 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + [[package]] name = "which" version = "4.4.2" @@ -3900,6 +4575,15 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +[[package]] +name = "winnow" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" @@ -4034,3 +4718,27 @@ dependencies = [ "quote", "syn 2.0.101", ] + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" +dependencies = [ + "zune-core", +] diff --git a/Cargo.toml b/Cargo.toml index 1046939..f68335e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,11 @@ reqwest = "0.12" reqwest-middleware = "0.4.2" http-cache-reqwest = "0.15.1" palette = "0.7.6" +# text rendering dependencies +arabic_reshaper = "0.4.2" +unicode-bidi = "0.3.18" +image = "0.25.6" +imagetext = { git = "https://github.com/nathanielfernandes/imagetext" } [dependencies.magick_rust] path = "./magick-rust" diff --git a/README.md b/README.md index 6e9c9dd..b0318ae 100644 --- a/README.md +++ b/README.md @@ -28,4 +28,6 @@ init the database ``sqlx migrate run`` TODO ---- -literally just add the text ++ ~~literally just add the text~~ ++ add easter egg ++ make various parameters user-configurable diff --git a/fonts/Heebo-Regular.ttf b/fonts/Heebo-Regular.ttf new file mode 100644 index 0000000..332f96e Binary files /dev/null and b/fonts/Heebo-Regular.ttf differ diff --git a/fonts/Heebo-SemiBold.ttf b/fonts/Heebo-SemiBold.ttf new file mode 100644 index 0000000..71ae334 Binary files /dev/null and b/fonts/Heebo-SemiBold.ttf differ diff --git a/fonts/NotoEmoji-Medium.ttf b/fonts/NotoEmoji-Medium.ttf new file mode 100644 index 0000000..007e7ab Binary files /dev/null and b/fonts/NotoEmoji-Medium.ttf differ diff --git a/fonts/NotoEmoji-Regular.ttf b/fonts/NotoEmoji-Regular.ttf new file mode 100644 index 0000000..69f3811 Binary files /dev/null and b/fonts/NotoEmoji-Regular.ttf differ diff --git a/fonts/NotoSans-Regular.ttf b/fonts/NotoSans-Regular.ttf new file mode 100644 index 0000000..7da1a0f Binary files /dev/null and b/fonts/NotoSans-Regular.ttf differ diff --git a/fonts/NotoSans-SemiBold.ttf b/fonts/NotoSans-SemiBold.ttf new file mode 100644 index 0000000..d062b93 Binary files /dev/null and b/fonts/NotoSans-SemiBold.ttf differ diff --git a/fonts/NotoSansArabic-Regular.ttf b/fonts/NotoSansArabic-Regular.ttf new file mode 100644 index 0000000..659ced9 Binary files /dev/null and b/fonts/NotoSansArabic-Regular.ttf differ diff --git a/fonts/NotoSansArabic-SemiBold.ttf b/fonts/NotoSansArabic-SemiBold.ttf new file mode 100644 index 0000000..3ce6508 Binary files /dev/null and b/fonts/NotoSansArabic-SemiBold.ttf differ diff --git a/fonts/NotoSansHK-Medium.otf b/fonts/NotoSansHK-Medium.otf new file mode 100644 index 0000000..064f075 Binary files /dev/null and b/fonts/NotoSansHK-Medium.otf differ diff --git a/fonts/NotoSansHK-Regular.otf b/fonts/NotoSansHK-Regular.otf new file mode 100644 index 0000000..622c653 Binary files /dev/null and b/fonts/NotoSansHK-Regular.otf differ diff --git a/fonts/NotoSansJP-Medium.otf b/fonts/NotoSansJP-Medium.otf new file mode 100644 index 0000000..490cc70 Binary files /dev/null and b/fonts/NotoSansJP-Medium.otf differ diff --git a/fonts/NotoSansJP-Regular.otf b/fonts/NotoSansJP-Regular.otf new file mode 100644 index 0000000..4af556a Binary files /dev/null and b/fonts/NotoSansJP-Regular.otf differ diff --git a/fonts/NotoSansKR-Medium.otf b/fonts/NotoSansKR-Medium.otf new file mode 100644 index 0000000..653cbfd Binary files /dev/null and b/fonts/NotoSansKR-Medium.otf differ diff --git a/fonts/NotoSansKR-Regular.otf b/fonts/NotoSansKR-Regular.otf new file mode 100644 index 0000000..b8c1142 Binary files /dev/null and b/fonts/NotoSansKR-Regular.otf differ diff --git a/fonts/NotoSansSC-Medium.otf b/fonts/NotoSansSC-Medium.otf new file mode 100644 index 0000000..fafdfeb Binary files /dev/null and b/fonts/NotoSansSC-Medium.otf differ diff --git a/fonts/NotoSansSC-Regular.otf b/fonts/NotoSansSC-Regular.otf new file mode 100644 index 0000000..fc0fda9 Binary files /dev/null and b/fonts/NotoSansSC-Regular.otf differ diff --git a/fonts/NotoSansTC-Medium.otf b/fonts/NotoSansTC-Medium.otf new file mode 100644 index 0000000..82f03b1 Binary files /dev/null and b/fonts/NotoSansTC-Medium.otf differ diff --git a/fonts/NotoSansTC-Regular.otf b/fonts/NotoSansTC-Regular.otf new file mode 100644 index 0000000..63c3422 Binary files /dev/null and b/fonts/NotoSansTC-Regular.otf differ diff --git a/fonts/Symbola.ttf b/fonts/Symbola.ttf new file mode 100644 index 0000000..51d9a88 Binary files /dev/null and b/fonts/Symbola.ttf differ diff --git a/fonts/Unifont.otf b/fonts/Unifont.otf new file mode 100644 index 0000000..3d0dcd3 Binary files /dev/null and b/fonts/Unifont.otf differ diff --git a/src/avatar_mask.png b/src/avatar_mask.png index d223d20..6437265 100644 Binary files a/src/avatar_mask.png and b/src/avatar_mask.png differ diff --git a/src/main.rs b/src/main.rs index dcc6c5f..9fbadac 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,7 @@ use serenity::model::id::UserId; use serenity::prelude::*; extern crate magick_rust; -use magick_rust::{ColorspaceType, CompositeOperator, MagickWand, PixelWand, magick_wand_genesis}; +use magick_rust::{ColorspaceType, CompositeOperator, MagickWand, PixelWand, magick_wand_genesis, FilterType}; use std::env; use std::sync::Once; @@ -23,6 +23,11 @@ use palette::white_point::D65; extern crate sqlx; use sqlx::sqlite::SqlitePool; +use imagetext::fontdb::FontDB; +use imagetext::superfont::SuperFont; + +mod text; + static START: Once = Once::new(); struct Handler; @@ -126,6 +131,11 @@ async fn fmi(ctx: &Context, arg: &str, id: UserId, avatar: Option) -> Re Some(track) => track, None => return Reply::Text("Nothing playing.".to_string()), }; + let track_info = text::TrackInfo { + title: track.name, + artist: track.artist.name, + album: track.album, + }; let image_uri = match track.image.extralarge { Some(iu) => iu, None => return Reply::Text("Error: getting image uri failed".to_string()), @@ -141,6 +151,7 @@ async fn fmi(ctx: &Context, arg: &str, id: UserId, avatar: Option) -> Re let art_wand = MagickWand::new(); let mut art_wand_cluster = MagickWand::new(); let mask_wand = MagickWand::new(); + let text_image_wand = MagickWand::new(); handle_magick_result!( base_color.set_color("#7f7f7f"), @@ -191,7 +202,7 @@ async fn fmi(ctx: &Context, arg: &str, id: UserId, avatar: Option) -> Re .collect::>(); let mut accent_color = PixelWand::new(); handle_magick_result!( - accent_color.set_color("#ffffff"), + accent_color.set_color("cielab(0.0,0.0,0.0)"), "Failed to init accent color" ); if let Some(color) = other_colors.first() { @@ -202,6 +213,16 @@ async fn fmi(ctx: &Context, arg: &str, id: UserId, avatar: Option) -> Re ); } + + let use_dark_color = match validate_color(&colors[0]) { + Some(color) => { + let black_delta_e = Lab::::new(0.0, 0.0, 0.0).improved_difference(color); + let white_delta_e = Lab::::new(100.0, 0.0, 0.0).improved_difference(color); + black_delta_e > white_delta_e + } + _ => false + }; + handle_magick_result!(white.set_color("#ffffff"), "Failed to init white"); handle_magick_result!( mask_wand.read_image_blob(include_bytes!("accent_mask.png")), @@ -242,7 +263,7 @@ async fn fmi(ctx: &Context, arg: &str, id: UserId, avatar: Option) -> Re "Failed to mask avatar" ); handle_magick_result!( - avatar_wand.adaptive_resize_image(64, 64), + avatar_wand.resize_image(64, 64, FilterType::RobidouxSharp), "Failed to resize avatar" ); handle_magick_result!( @@ -250,8 +271,22 @@ async fn fmi(ctx: &Context, arg: &str, id: UserId, avatar: Option) -> Re "Failed to set avatar colorspace" ); + let (image_width, image_height) = (548, 147); + let data = ctx.data.write().await; + let fonts = data + .get::().expect("Failed to load fonts"); + let text_image_blob = match text::fmi_text(image_width, image_height, track_info, fonts, 19.0, use_dark_color) { + Ok(bytes) => bytes, + Err(e) => return Reply::Text(e), + }; + handle_magick_result!(text_image_wand.read_image_blob(&text_image_blob), "Failed to load text image"); handle_magick_result!( - main_wand.new_image(548, 147, &colors[0]), + text_image_wand.transform_image_colorspace(ColorspaceType::Lab), + "Failed to set avatar colorspace" + ); + + handle_magick_result!( + main_wand.new_image(image_width as usize, image_height as usize, &colors[0]), "Failed to create wand" ); handle_magick_result!( @@ -274,15 +309,22 @@ async fn fmi(ctx: &Context, arg: &str, id: UserId, avatar: Option) -> Re main_wand.compose_images(&mask_wand, CompositeOperator::SrcOver, false, 401, 0), "Failed to combine mask" ); + #[cfg(debug_assertions)] + if env::var("DEBUG_IMAGE").unwrap_or("0".to_string()) == "1" { + handle_magick_result!( + main_wand.compose_images( + &art_wand_cluster, + CompositeOperator::SrcOver, + false, + 160, + 12 + ), + "Failed to combine debug image" + ); + } handle_magick_result!( - main_wand.compose_images( - &art_wand_cluster, - CompositeOperator::SrcOver, - false, - 160, - 12 - ), - "Failed to combine debug image" + main_wand.compose_images(&text_image_wand, CompositeOperator::SrcOver, false, 0, 0), + "Failed to combine mask" ); handle_magick_result!( main_wand.compose_images(&avatar_wand, CompositeOperator::SrcOver, false, 473, 73), @@ -330,16 +372,21 @@ impl EventHandler for Handler { } } -struct HttpContainer; -impl TypeMapKey for HttpContainer { +struct HttpHaver; +impl TypeMapKey for HttpHaver { type Value = reqwest_middleware::ClientWithMiddleware; } -struct PoolContainer; -impl TypeMapKey for PoolContainer { +struct PoolHaver; +impl TypeMapKey for PoolHaver { type Value = SqlitePool; } +struct FontsHaver; +impl TypeMapKey for FontsHaver { + type Value = (SuperFont<'static>, SuperFont<'static>); +} + struct DBResponse { lastfm_username: String, } @@ -348,7 +395,7 @@ async fn get_lastfm_username(ctx: &Context, id: UserId) -> Option { log!("get db user {}", id); let data = ctx.data.write().await; let pool = data - .get::() + .get::() .expect("Failed to get pool container"); let id = i64::from(id); let resp = sqlx::query_as!( @@ -375,7 +422,7 @@ async fn set_lastfm_username(ctx: &Context, id: UserId, user: String) { log!("set db user {} {}", id, user); let data = ctx.data.write().await; let pool = data - .get::() + .get::() .expect("Failed to get pool container"); let id = i64::from(id); sqlx::query!( @@ -395,7 +442,7 @@ async fn get_image(ctx: &Context, url: &str) -> Result, reqwest_middlewa log!("get {}", url); let data = ctx.data.write().await; let http = data - .get::() + .get::() .expect("Failed to get http client"); match http.get(url).send().await { Ok(resp) => { @@ -428,10 +475,6 @@ async fn main() { | GatewayIntents::DIRECT_MESSAGES | GatewayIntents::MESSAGE_CONTENT; - let mut discord_client = Client::builder(&token, intents) - .event_handler(Handler) - .await - .expect("Err creating Discord client"); let http = reqwest_middleware::ClientBuilder::new(reqwest::Client::new()) .with(Cache(HttpCache { mode: CacheMode::Default, @@ -439,10 +482,44 @@ async fn main() { options: HttpCacheOptions::default(), })) .build(); + + FontDB::load_from_dir("fonts"); //singlet instance??? wha????? + let regular_fonts = FontDB::query(" + NotoSans-Regular + NotoSansHK-Regular + NotoSansJP-Regular + NotoSansKR-Regular + NotoSansSC-Regular + NotoSansTC-Regular + NotoSansArabic-Regular + Heebo-Regular + NotoEmoji-Regular + Symbola + Unifont + ").expect("Failed to load regular fonts"); + let bold_fonts = FontDB::query(" + NotoSans-SemiBold + NotoSansJP-Medium + NotoSansKR-Medium + NotoSansSC-Medium + NotoSansTC-Medium + NotoSansArabic-SemiBold + Heebo-SemiBold + NotoEmoji-Medium + Symbola + Unifont + ").expect("Failed to load bold fonts"); + + let mut discord_client = Client::builder(&token, intents) + .event_handler(Handler) + .await + .expect("Err creating Discord client"); + { let mut data = discord_client.data.write().await; - data.insert::(pool); - data.insert::(http); + data.insert::(pool); + data.insert::(http); + data.insert::((regular_fonts, bold_fonts)); } if let Err(why) = discord_client.start().await { diff --git a/src/text.rs b/src/text.rs new file mode 100644 index 0000000..269e315 --- /dev/null +++ b/src/text.rs @@ -0,0 +1,121 @@ +// this code is mostly just blindly ported from +// https://github.com/adamanldo/Cosmo/blob/946af25dcac8a81cb7b49d1a4f83731a5f7c7b46/cogs/utils/fmi_text.py +// since I'm using the same text rendering library, my code is almost one-to-one + +use arabic_reshaper::arabic_reshape; +use imagetext::measure::text_width; +use imagetext::superfont::SuperFont; +use imagetext::wrap::{text_wrap, WrapStyle}; +use imagetext::prelude::{scale, BLACK, WHITE, Outline, draw_text_wrapped, TextAlign, stroke}; +use image::{RgbaImage, ExtendedColorType, ImageEncoder}; +use image::codecs::png::PngEncoder; +use unicode_bidi::BidiInfo; + +enum TextField { + Title, + Artist, + Album, +} + +fn process_text( + text: &str, + field: &TextField, + font: &SuperFont<'static>, + font_size: f32, +) -> (Vec, i32) { + let mut text = text.to_string(); + if text + .chars() + .any(|x| matches!(x as u32, 0x590..=0x5fe | 0x600..=0x6ff0)) + { + if text.chars().any(|x| matches!(x as u32, 0x600..=0x6ff0)) { + text = arabic_reshape(text.as_str()); + } + let bidi = BidiInfo::new(text.as_str(), None); + text = bidi + .paragraphs + .iter() + .map(|para| { + let line = para.range.clone(); + bidi.reorder_line(para, line) + }) + .collect(); + } + let (width, allowed_lines) = match field { + TextField::Title => (350, 2), + TextField::Artist => (312, 1), + TextField::Album => (280, 2), + }; + let mut wrapped_strs = text_wrap( + text.as_str(), + width, + font, + scale(font_size), + WrapStyle::Character, + text_width, + ); + if wrapped_strs.len() > allowed_lines { + wrapped_strs.truncate(allowed_lines); + let text_len = wrapped_strs[allowed_lines - 1].len() - 3; + wrapped_strs[allowed_lines - 1] = match allowed_lines { + 1..=2 => format!( + "{}...", + wrapped_strs[allowed_lines - 1].get(..text_len).unwrap() + ), + _ => unreachable!(), + }; + } + (wrapped_strs, width) +} + +fn draw_info(image: &mut RgbaImage, text: String, text_type: TextField, font: &SuperFont<'static>, font_size: f32, dark_text: bool) -> Result<(), String> { + let (lines, width) = process_text(&text, &text_type, font, font_size); + let text = lines.join(""); + let y = match text_type { + TextField::Title => 24.0, + TextField::Artist => 73.0, + TextField::Album => 96.0, + }; + let color = if dark_text { &BLACK } else { &WHITE }; + match draw_text_wrapped( + image, + color, + Outline::Solid { + stroke: &stroke(0.5), + fill: color, + }, + 146.0, + y, + 0.0, + 0.0, + width as f32, + scale(font_size), + font, + &text, + 1.0, + TextAlign::Left, + WrapStyle::Character, + ) { + Ok(_) => Ok(()), + Err(e) => Err(e.to_string()), + } +} + +pub struct TrackInfo { + pub title: String, + pub artist: String, + pub album: String, +} + +pub fn fmi_text(width: u32, height: u32, track: TrackInfo, fonts: &(SuperFont<'static>, SuperFont<'static>), font_size: f32, dark_text: bool) -> Result, String> { + let mut img = RgbaImage::new(width, height); + draw_info(&mut img, track.title, TextField::Title, &fonts.1.clone(), font_size, dark_text)?; + draw_info(&mut img, track.artist, TextField::Artist, &fonts.0.clone(), font_size, dark_text)?; + draw_info(&mut img, track.album, TextField::Album, &fonts.0.clone(), font_size, dark_text)?; + let mut blob = Vec::::new(); + let png_encoder = PngEncoder::new(&mut blob); + match png_encoder.write_image(&img, width, height, ExtendedColorType::Rgba8) { + Ok(()) => Ok(blob), + Err(_) => Err("Failed to encode text png".to_string()), + } +}