diff --git a/Cargo.lock b/Cargo.lock index f47a9ce..b2d360e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,7 +7,7 @@ name = "aamva" version = "0.1.0" source = "git+https://github.com/Syfaro/aamva-rs.git#1304b78d2563c885a167a05f69a7a301857b2133" dependencies = [ - "itertools", + "itertools 0.13.0", "jiff", "nom", "num_enum", @@ -122,6 +122,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 = "android-tzdata" version = "0.1.1" @@ -193,6 +199,29 @@ version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +[[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.100", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "asn1-rs" version = "0.5.2" @@ -322,7 +351,7 @@ checksum = "d556ec1359574147ec0c4fc5eb525f3f23263a592b1a9c07e0a75b427de55c97" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -375,6 +404,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 = "axum" version = "0.8.1" @@ -472,9 +524,15 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.6.0" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb97d56060ee67d285efb8001fec9d2a4c710c32efd2e14b5cbb5ba71930fc2d" + +[[package]] +name = "bit_field" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" [[package]] name = "bitflags" @@ -497,6 +555,12 @@ dependencies = [ "typenum", ] +[[package]] +name = "bitstream-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" + [[package]] name = "bitvec" version = "1.0.1" @@ -560,6 +624,12 @@ dependencies = [ "smallvec", ] +[[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" @@ -586,9 +656,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" dependencies = [ "serde", ] @@ -599,6 +669,8 @@ version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" dependencies = [ + "jobserver", + "libc", "shlex", ] @@ -622,6 +694,16 @@ dependencies = [ "uuid", ] +[[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" @@ -688,9 +770,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.31" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" +checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" dependencies = [ "clap_builder", "clap_derive", @@ -707,9 +789,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.31" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863" +checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" dependencies = [ "anstream", "anstyle", @@ -719,14 +801,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.28" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -741,6 +823,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "colorchoice" version = "1.0.3" @@ -762,6 +850,26 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "console_log" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be8aed40e4edbf4d3b4431ab260b63fdc40f5780a4766824329ea0f1eefe3c0f" +dependencies = [ + "log", + "web-sys", +] + [[package]] name = "const-oid" version = "0.7.1" @@ -854,6 +962,25 @@ dependencies = [ "cfg-if", ] +[[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-utils" version = "0.8.21" @@ -953,7 +1080,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -977,7 +1104,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -988,7 +1115,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -1014,7 +1141,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18e4fdb82bd54a12e42fb58a800dcae6b9e13982238ce2296dc3570b92148e1f" dependencies = [ "data-encoding", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -1055,7 +1182,7 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -1097,7 +1224,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -1161,14 +1288,14 @@ dependencies = [ "enum-ordinalize 4.3.0", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] name = "either" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "elliptic-curve" @@ -1213,7 +1340,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -1226,7 +1353,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -1246,7 +1373,7 @@ checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -1257,7 +1384,7 @@ checksum = "ba7795da175654fe16979af73f81f26a8ea27638d8d9823d317016888a63dc4c" dependencies = [ "num-traits", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -1303,6 +1430,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 = "eyre" version = "0.6.12" @@ -1330,9 +1472,9 @@ dependencies = [ [[package]] name = "ff" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ "rand_core 0.6.4", "subtle", @@ -1470,7 +1612,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -1549,6 +1691,16 @@ dependencies = [ "polyval", ] +[[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" @@ -1895,7 +2047,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -1947,12 +2099,37 @@ checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" 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 = "imgref" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" + [[package]] name = "indenter" version = "0.3.3" @@ -1972,9 +2149,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ "equivalent", "hashbrown 0.15.2", @@ -2014,6 +2191,17 @@ dependencies = [ "cfg-if", ] +[[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.100", +] + [[package]] name = "io-kit-sys" version = "0.4.1" @@ -2155,6 +2343,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.13.0" @@ -2164,6 +2361,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.15" @@ -2182,6 +2388,21 @@ dependencies = [ "serde", ] +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" + [[package]] name = "jpeg2k" version = "0.9.1" @@ -2236,7 +2457,7 @@ dependencies = [ "contextual", "educe 0.4.23", "futures", - "indexmap 2.7.1", + "indexmap 2.8.0", "iref", "json-ld-context-processing", "json-ld-core", @@ -2276,7 +2497,7 @@ dependencies = [ "educe 0.4.23", "futures", "hashbrown 0.13.2", - "indexmap 2.7.1", + "indexmap 2.8.0", "iref", "json-ld-syntax", "json-syntax", @@ -2304,7 +2525,7 @@ dependencies = [ "contextual", "educe 0.4.23", "futures", - "indexmap 2.7.1", + "indexmap 2.8.0", "iref", "json-ld-context-processing", "json-ld-core", @@ -2322,7 +2543,7 @@ version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "344f0a6042745d76a358b808878ae0d125a472de30b3eabc9eb82c6cf7f0c23e" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.8.0", "iref", "json-ld-core", "json-syntax", @@ -2342,7 +2563,7 @@ dependencies = [ "decoded-char", "educe 0.4.23", "hashbrown 0.13.2", - "indexmap 2.7.1", + "indexmap 2.8.0", "iref", "json-syntax", "langtag", @@ -2443,6 +2664,12 @@ dependencies = [ "spin 0.9.8", ] +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "lexical" version = "7.0.4" @@ -2518,9 +2745,29 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.170" +version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if", + "windows-targets", +] [[package]] name = "libm" @@ -2557,7 +2804,7 @@ dependencies = [ "proc-macro2", "quote", "static-iref", - "syn 2.0.99", + "syn 2.0.100", "thiserror 1.0.69", ] @@ -2610,6 +2857,15 @@ version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + [[package]] name = "mach2" version = "0.4.2" @@ -2634,6 +2890,22 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" +[[package]] +name = "maybe-owned" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" + +[[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 = "memchr" version = "2.7.4" @@ -2716,6 +2988,12 @@ dependencies = [ "data-encoding-macro", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "nix" version = "0.26.4" @@ -2769,6 +3047,12 @@ 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 = "nu-ansi-term" version = "0.46.0" @@ -2823,6 +3107,17 @@ dependencies = [ "syn 1.0.109", ] +[[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.100", +] + [[package]] name = "num-integer" version = "0.1.46" @@ -2882,7 +3177,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -2896,9 +3191,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.3" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +checksum = "cde51589ab56b20a6f686b2c68f7a0bd6add753d697abf720d63f8db3ab7b1ad" dependencies = [ "portable-atomic", ] @@ -3005,6 +3300,12 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "pathdiff" version = "0.2.3" @@ -3021,6 +3322,32 @@ dependencies = [ "utf8-decode", ] +[[package]] +name = "pdfium-render" +version = "0.8.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5cbb29c282cfbd0a8142ccd3bb0ce8da53e59141ce02a023b980bc72b6c0eec" +dependencies = [ + "bitflags 2.9.0", + "bytemuck", + "bytes", + "chrono", + "console_error_panic_hook", + "console_log", + "image", + "itertools 0.14.0", + "js-sys", + "libloading", + "log", + "maybe-owned", + "once_cell", + "utf16string", + "vecmath", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "pem" version = "3.0.5" @@ -3101,7 +3428,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -3130,7 +3457,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -3145,6 +3472,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piston-float" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad78bf43dcf80e8f950c92b84f938a0fc7590b7f6866fbcbeca781609c115590" + [[package]] name = "pkcs1" version = "0.3.3" @@ -3177,6 +3510,12 @@ dependencies = [ "spki 0.7.3", ] +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + [[package]] name = "png" version = "0.17.16" @@ -3252,11 +3591,11 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.7.35", + "zerocopy 0.8.23", ] [[package]] @@ -3290,9 +3629,9 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ "toml_edit", ] @@ -3330,6 +3669,40 @@ 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.100", +] + +[[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 = "quinn" version = "0.11.6" @@ -3416,7 +3789,7 @@ checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", - "zerocopy 0.8.21", + "zerocopy 0.8.23", ] [[package]] @@ -3472,12 +3845,82 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d20581732dd76fa913c7dff1a2412b714afe3573e94d41c34719de73337cc8ab" +[[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 0.12.1", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive 0.4.2", + "num-traits", + "once_cell", + "paste", + "profiling", + "rand 0.8.5", + "rand_chacha 0.3.1", + "simd_helpers", + "system-deps", + "thiserror 1.0.69", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2413fd96bd0ea5cdeeb37eaf446a22e6ed7b981d792828721e74ded1980a45c6" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + [[package]] name = "raw-btree" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fdead0382b742073dbbb295e93cdf7d25fbe281f0ca07988c64bee27d534a38" +[[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 = "rdf-types" version = "0.22.5" @@ -3486,7 +3929,7 @@ checksum = "4ccfa6b3af8f44db8d700038d47a9e8c8cc4126cdcafc069e82116903420631d" dependencies = [ "contextual", "educe 0.5.11", - "indexmap 2.7.1", + "indexmap 2.8.0", "iref", "langtag", "raw-btree", @@ -3513,10 +3956,12 @@ dependencies = [ "infer", "ipp", "isomdl", + "itertools 0.14.0", "jpeg2k", "jsonwebtoken", "mrtd", "open", + "pdfium-render", "postcard", "rand 0.9.0", "regex", @@ -3651,6 +4096,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "rgb" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" + [[package]] name = "rhai" version = "1.21.0" @@ -3678,14 +4129,14 @@ checksum = "a5a11a05ee1ce44058fa3d5961d05194fdbe3ad6b40f904af764d81b86450e6b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] name = "ring" -version = "0.17.11" +version = "0.17.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da5349ae27d3887ca812fb375b45a4fbb36d8d12d2df394968cd86e35683fe73" +checksum = "70ac5d832aa16abd7d1def883a8545280c20a60f523a370aa3a9617c2b8550ee" dependencies = [ "cc", "cfg-if", @@ -3955,9 +4406,9 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.16" +version = "0.11.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "364fec0df39c49a083c9a8a18a23a6bcfd9af130fe9fe321d18520a0d113e09e" +checksum = "8437fd221bde2d4ca316d61b90e337e9e702b3820b87d63caa9ba6c02bd06d96" dependencies = [ "serde", ] @@ -3970,7 +4421,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -4027,7 +4478,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.7.1", + "indexmap 2.8.0", "serde", "serde_derive", "serde_json", @@ -4044,7 +4495,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -4150,6 +4601,15 @@ 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 = "simple_asn1" version = "0.5.4" @@ -4345,7 +4805,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54107b8d19db3e8c7e65d04910a118172d636ecd1819f4691fa6c0b2428d62bc" dependencies = [ "hex", - "indexmap 2.7.1", + "indexmap 2.8.0", "iref", "json-syntax", "keccak-hash", @@ -4391,7 +4851,7 @@ dependencies = [ "linked-data", "multibase", "num-bigint", - "num-derive", + "num-derive 0.3.3", "num-traits", "p256", "rand 0.8.5", @@ -4425,7 +4885,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a07a869c976c5c84c1fb605a95a153da92a3a0472f4233ea776282f27ef15b11" dependencies = [ "combination", - "indexmap 2.7.1", + "indexmap 2.8.0", "iref", "linked-data", "rdf-types", @@ -4447,7 +4907,7 @@ checksum = "3cc4068497ae43896d41174586dcdc2153a1af2c82856fb308bfaaddc28e5549" dependencies = [ "iref", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -4466,7 +4926,7 @@ dependencies = [ "quote", "serde", "sha2 0.10.8", - "syn 2.0.99", + "syn 2.0.100", "thiserror 1.0.69", ] @@ -4520,9 +4980,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.99" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -4558,7 +5018,20 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml", + "version-compare", ] [[package]] @@ -4567,6 +5040,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + [[package]] name = "thin-vec" version = "0.2.13" @@ -4602,7 +5081,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -4613,7 +5092,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -4626,6 +5105,17 @@ dependencies = [ "once_cell", ] +[[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.39" @@ -4709,7 +5199,7 @@ checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -4737,7 +5227,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -4827,7 +5317,7 @@ version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.8.0", "serde", "serde_spanned", "toml_datetime", @@ -4880,7 +5370,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -5033,6 +5523,15 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" +[[package]] +name = "utf16string" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b62a1e85e12d5d712bf47a85f426b73d303e2d00a90de5f3004df3596e9d216" +dependencies = [ + "byteorder", +] + [[package]] name = "utf8-decode" version = "1.0.1" @@ -5062,12 +5561,38 @@ dependencies = [ "serde", ] +[[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 = "valuable" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "vecmath" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956ae1e0d85bca567dee1dcf87fb1ca2e792792f66f87dced8381f99cd91156a" +dependencies = [ + "piston-float", +] + +[[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" @@ -5120,7 +5645,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", "wasm-bindgen-shared", ] @@ -5155,7 +5680,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5211,6 +5736,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 = "winapi" version = "0.3.9" @@ -5272,7 +5803,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -5283,7 +5814,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -5526,7 +6057,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", "synstructure 0.13.1", ] @@ -5536,17 +6067,16 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "byteorder", "zerocopy-derive 0.7.35", ] [[package]] name = "zerocopy" -version = "0.8.21" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf01143b2dd5d134f11f545cf9f1431b13b749695cb33bcce051e7568f99478" +checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6" dependencies = [ - "zerocopy-derive 0.8.21", + "zerocopy-derive 0.8.23", ] [[package]] @@ -5557,18 +6087,18 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] name = "zerocopy-derive" -version = "0.8.21" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712c8386f4f4299382c9abee219bee7084f78fb939d88b6840fcc1320d5f6da2" +checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -5588,7 +6118,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", "synstructure 0.13.1", ] @@ -5609,7 +6139,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -5631,7 +6161,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -5640,6 +6170,15 @@ 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" diff --git a/Cargo.toml b/Cargo.toml index 8fa1893..c7e06d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,9 @@ publish = false allow-branch = ["main"] sign-tag = true +[features] +pdf = ["pdfium-render"] + [dependencies] aamva = { git = "https://github.com/Syfaro/aamva-rs.git", version = "0.1.0" } async-hid = "0.2.0" @@ -23,10 +26,12 @@ image = { version = "0.25.5", default-features = false } infer = "0.19.0" ipp = { version = "5.2.0", default-features = false, features = ["async", "async-client", "serde"] } isomdl = { git = "https://github.com/Syfaro/isomdl.git", branch = "handover-improvements", version = "0.1.0" } +itertools = "0.14.0" jpeg2k = { version = "0.9.1", features = ["image"] } jsonwebtoken = "9.3.0" mrtd = { version = "0.5.2", features = ["serde"] } open = "5.3.0" +pdfium-render = { version = "0.8.28", optional = true } postcard = { version = "1.1.1", default-features = false, features = ["alloc"] } rand = "0.9.0" regex = "1.11.1" diff --git a/src/action/mod.rs b/src/action/mod.rs index 3832d70..3c14a5e 100644 --- a/src/action/mod.rs +++ b/src/action/mod.rs @@ -1,7 +1,9 @@ use serde::Deserialize; +use serde_with::{DisplayFromStr, serde_as}; use tokio::sync::mpsc; use tokio_util::sync::CancellationToken; use tracing::warn; +use url::Url; use crate::RunningTasks; @@ -27,9 +29,22 @@ impl ConnectionAction { } } +#[serde_as] #[derive(Clone, Debug, Deserialize)] -pub struct ConnectionActionPrint { - pub url: String, +#[serde(untagged)] +pub enum ConnectionActionPrint { + UrlAndData { + #[serde_as(as = "DisplayFromStr")] + url: Url, + data: Vec, + }, + Url { + #[serde_as(as = "DisplayFromStr")] + url: Url, + }, + Data { + data: String, + }, } pub async fn start( @@ -63,9 +78,9 @@ async fn action_task( action = action_rx.recv() => { match action { - Some(ConnectionAction::Print(ConnectionActionPrint { url })) => { + Some(ConnectionAction::Print(print_action)) => { if let Some(printer) = &printer { - printer.print_url(&url).await?; + printer.print(print_action).await?; } else { warn!("got print action but had no printer"); } diff --git a/src/action/print.rs b/src/action/print.rs deleted file mode 100644 index 8c239fd..0000000 --- a/src/action/print.rs +++ /dev/null @@ -1,105 +0,0 @@ -use eyre::{Context, bail}; -use futures::TryStreamExt; -use ipp::prelude::*; -use serde::Deserialize; -use tokio_util::compat::TokioAsyncReadCompatExt; -use tracing::{debug, info, instrument}; - -#[derive(Clone, Debug, Deserialize)] -pub struct PrintConfig { - #[serde(default = "PrintConfig::default_cups_host")] - cups_host: String, - printer_uri: String, - #[serde(default)] - attributes: Vec, -} - -impl PrintConfig { - fn default_cups_host() -> String { - "http://localhost:631".to_string() - } -} - -pub struct Printer { - ipp_client: AsyncIppClient, - ipp_attributes: Vec, - - http_client: reqwest::Client, - printer_uri: Uri, -} - -impl Printer { - #[instrument(skip(config))] - pub async fn new(config: PrintConfig) -> eyre::Result { - let cups_uri = config - .cups_host - .parse() - .wrap_err_with(|| format!("invalid cups uri: {}", config.cups_host))?; - let printer_uri = config - .printer_uri - .parse() - .wrap_err_with(|| format!("invalid printer uri: {}", config.printer_uri))?; - - let ipp_client = AsyncIppClient::builder(cups_uri).build(); - let http_client = reqwest::Client::default(); - - let printer = Self { - ipp_client, - ipp_attributes: config.attributes, - http_client, - printer_uri, - }; - - if !printer - .validate_printer() - .await - .wrap_err_with(|| "could not list printers")? - { - bail!("could not find printer: {}", printer.printer_uri); - } - - Ok(printer) - } - - #[instrument(skip(self))] - pub async fn print_url(&self, url: &str) -> eyre::Result<()> { - let resp = self.http_client.get(url).send().await?.error_for_status()?; - let stream = resp.bytes_stream().map_err(std::io::Error::other); - let reader = tokio_util::io::StreamReader::new(stream); - - let payload = IppPayload::new_async(reader.compat()); - let op = IppOperationBuilder::print_job(self.printer_uri.clone(), payload) - .attributes(self.ipp_attributes.iter().cloned()) - .build(); - let resp = self.ipp_client.send(op).await?; - - info!(status_code = %resp.header().status_code(), "sent print job"); - - Ok(()) - } - - #[instrument(skip(self))] - async fn validate_printer(&self) -> eyre::Result { - let op = IppOperationBuilder::cups().get_printers(); - let resp = self.ipp_client.send(op).await?; - debug!( - status = %resp.header().status_code(), - "got response status" - ); - - let groups = resp.attributes().groups_of(DelimiterTag::PrinterAttributes); - - let printer_uri_str = self.printer_uri.to_string(); - let is_known_printer = groups.into_iter().any(|group| { - let attributes = group.attributes(); - - ["device-uri", "printer-uri-supported"] - .into_iter() - .any(|attribute_name| { - attributes[attribute_name].value().to_string() == printer_uri_str - }) - }); - - Ok(is_known_printer) - } -} diff --git a/src/action/print/mod.rs b/src/action/print/mod.rs new file mode 100644 index 0000000..db43c1a --- /dev/null +++ b/src/action/print/mod.rs @@ -0,0 +1,281 @@ +use std::{io::Cursor, net::SocketAddr, time::Duration}; + +use eyre::{OptionExt, bail}; +use futures::TryStreamExt; +use image::DynamicImage; +use ipp::prelude::*; +use serde::Deserialize; +use serde_with::{DisplayFromStr, serde_as}; +use tokio::{io::AsyncWriteExt, net::TcpSocket}; +use tokio_util::compat::TokioAsyncReadCompatExt; +use tracing::{debug, info, instrument}; + +use super::ConnectionActionPrint; + +mod zpl; + +#[derive(Clone, Debug, Deserialize)] +pub struct PrintConfig { + #[serde(flatten)] + print_type: PrintTypeConfig, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(tag = "print_type", rename_all = "snake_case")] +pub enum PrintTypeConfig { + Cups(PrintTypeCupsConfig), + Zpl(PrintTypeZplConfig), +} + +#[serde_as] +#[derive(Clone, Debug, Deserialize)] +pub struct PrintTypeCupsConfig { + #[serde_as(as = "DisplayFromStr")] + #[serde(default = "PrintTypeCupsConfig::default_host")] + host: Uri, + #[serde_as(as = "DisplayFromStr")] + printer_uri: Uri, + #[serde(default)] + attributes: Vec, +} + +impl PrintTypeCupsConfig { + fn default_host() -> Uri { + "http://localhost:631".parse().unwrap() + } +} + +#[derive(Clone, Debug, Deserialize)] +pub struct PrintTypeZplConfig { + addr: SocketAddr, + rotate: bool, + #[cfg(feature = "pdf")] + label_width: u32, + #[cfg(feature = "pdf")] + label_height: u32, +} + +pub struct Printer { + http_client: reqwest::Client, + connection: PrinterConnection, +} + +enum PrinterConnection { + Cups { + config: PrintTypeCupsConfig, + ipp_client: AsyncIppClient, + }, + Zpl { + config: PrintTypeZplConfig, + }, +} + +impl Printer { + #[instrument(skip(config))] + pub async fn new(config: PrintConfig) -> eyre::Result { + let http_client = reqwest::Client::default(); + + let connection = match config.print_type { + PrintTypeConfig::Cups(cups) => { + let ipp_client = AsyncIppClient::builder(cups.host.clone()).build(); + PrinterConnection::Cups { + config: cups, + ipp_client, + } + } + PrintTypeConfig::Zpl(zpl) => PrinterConnection::Zpl { config: zpl }, + }; + + let printer = Self { + http_client, + connection, + }; + + if !printer.validate_printer().await? { + bail!("could validate printer connection"); + } + + Ok(printer) + } + + #[instrument(skip(self))] + pub async fn print(&self, action: ConnectionActionPrint) -> eyre::Result<()> { + match action { + ConnectionActionPrint::UrlAndData { url, data } => match &self.connection { + PrinterConnection::Cups { .. } => { + eyre::bail!("merging url and data not supported for cups") + } + PrinterConnection::Zpl { config } => { + let images = self.url_to_images(config, url.as_str()).await?; + let images_with_data = images.into_iter().zip(data); + + for (image, mut data) in images_with_data { + let Some(pos) = data.find("^XZ") else { + eyre::bail!("data must have end of label"); + }; + + let field = zpl::image_to_gf(&image, config.rotate); + data.insert_str(pos, &field); + + Self::write_to_socket(config.addr, data.as_bytes()).await?; + } + } + }, + ConnectionActionPrint::Url { url } => match &self.connection { + PrinterConnection::Cups { config, ipp_client } => { + let resp = self.http_client.get(url).send().await?.error_for_status()?; + + let stream = resp.bytes_stream().map_err(std::io::Error::other); + let reader = tokio_util::io::StreamReader::new(stream); + + let payload = IppPayload::new_async(reader.compat()); + let op = IppOperationBuilder::print_job(config.printer_uri.clone(), payload) + .attributes(config.attributes.iter().cloned()) + .build(); + let resp = ipp_client.send(op).await?; + + info!(status_code = %resp.header().status_code(), "sent print job"); + } + PrinterConnection::Zpl { config } => { + for image in self.url_to_images(config, url.as_str()).await? { + let field = zpl::image_to_gf(&image, config.rotate); + + Self::write_to_socket( + config.addr, + format!("^XA^FO0,0{field}^XZ").as_bytes(), + ) + .await?; + } + } + }, + ConnectionActionPrint::Data { data } => match &self.connection { + PrinterConnection::Cups { config, ipp_client } => { + let cursor = Cursor::new(data); + let payload = IppPayload::new(cursor); + let op = IppOperationBuilder::print_job(config.printer_uri.clone(), payload) + .attributes(config.attributes.iter().cloned()) + .build(); + ipp_client.send(op).await?; + } + PrinterConnection::Zpl { config } => { + Self::write_to_socket(config.addr, data.as_bytes()).await?; + } + }, + } + + Ok(()) + } + + #[instrument(skip(self))] + async fn validate_printer(&self) -> eyre::Result { + match &self.connection { + PrinterConnection::Cups { config, ipp_client } => { + let op = IppOperationBuilder::cups().get_printers(); + let resp = ipp_client.send(op).await?; + debug!( + status = %resp.header().status_code(), + "got response status" + ); + + let groups = resp.attributes().groups_of(DelimiterTag::PrinterAttributes); + + let printer_uri_str = config.printer_uri.to_string(); + let is_known_printer = groups.into_iter().any(|group| { + let attributes = group.attributes(); + + ["device-uri", "printer-uri-supported"] + .into_iter() + .any(|attribute_name| { + attributes[attribute_name].value().to_string() == printer_uri_str + }) + }); + + Ok(is_known_printer) + } + PrinterConnection::Zpl { .. } => Ok(true), + } + } + + async fn write_to_socket(addr: SocketAddr, data: &[u8]) -> eyre::Result<()> { + tokio::time::timeout(Duration::from_secs(10), async { + let socket = if addr.is_ipv4() { + TcpSocket::new_v4() + } else { + TcpSocket::new_v6() + }?; + + let mut stream = socket.connect(addr).await?; + stream.write_all(data).await?; + + Ok::<_, eyre::Report>(()) + }) + .await??; + + Ok(()) + } + + async fn url_to_images( + &self, + config: &PrintTypeZplConfig, + url: &str, + ) -> eyre::Result> { + let bytes = self + .http_client + .get(url) + .send() + .await? + .error_for_status()? + .bytes() + .await?; + let content_type = infer::get(&bytes).ok_or_eyre("unknown url content type")?; + + let im = if content_type.matcher_type() == infer::MatcherType::Image { + vec![image::load_from_memory(&bytes)?] + } else { + Self::render_pdf_to_images(config, &bytes)? + }; + + Ok(im) + } + + #[cfg(feature = "pdf")] + fn render_pdf_to_images( + config: &PrintTypeZplConfig, + data: &[u8], + ) -> eyre::Result> { + use itertools::Itertools; + use pdfium_render::prelude::*; + + let pdfium = Pdfium::default(); + let doc = pdfium.load_pdf_from_byte_slice(data, None)?; + + let (width, height) = if config.rotate { + (config.label_height, config.label_width) + } else { + (config.label_width, config.label_height) + }; + + let render_config = PdfRenderConfig::new() + .use_print_quality(true) + .set_target_width(width.try_into().unwrap()) + .set_maximum_width(width.try_into().unwrap()) + .set_target_height(height.try_into().unwrap()); + + doc.pages() + .iter() + .map(|page| { + page.render_with_config(&render_config) + .map(|page| page.as_image()) + }) + .try_collect() + .map_err(eyre::Report::from) + } + + #[cfg(not(feature = "pdf"))] + fn render_pdf_to_images( + _config: &PrintTypeZplConfig, + _data: &[u8], + ) -> eyre::Result> { + eyre::bail!("this build does not support pdf conversion") + } +} diff --git a/src/action/print/zpl.rs b/src/action/print/zpl.rs new file mode 100644 index 0000000..db4142a --- /dev/null +++ b/src/action/print/zpl.rs @@ -0,0 +1,215 @@ +use image::{imageops, DynamicImage, ImageBuffer, Luma, LumaA}; +use itertools::Itertools; +use tracing::{debug, instrument}; + +/// Convert an image to a GF instruction. +/// +/// This also handles the grayscale conversion, including dithering. +#[instrument(skip(im))] +pub fn image_to_gf(im: &DynamicImage, rotate: bool) -> String { + // Convert image to grayscale with alpha channel. We need to make sure that + // transparent pixels are set to white so they aren't printed. + let im = im.to_luma_alpha8(); + + let im = if rotate { imageops::rotate90(&im) } else { im }; + + let (width, height) = im.dimensions(); + debug!(width, height, "got image dimensions"); + + // ZPL requires that all image widths are padded to a multiple of 8. + let padding = width % 8; + let width_padded = width + padding; + debug!(padding, "calculated needed padding"); + + // Create a new image with the padded width, then pull pixels from the + // original image. + let mut cleaned_image = ImageBuffer::, _>::from_fn(width_padded, height, |x, y| { + let pixel = im.get_pixel_checked(x, y).unwrap_or(&LumaA([255, 255])); + + // If the pixel is mostly transparent, make it white. + let val = if pixel.0[1] > 127 { pixel.0[0] } else { 255 }; + Luma([val]) + }); + debug!(width = width_padded, height, "generated image"); + + // The image crate provides a bilevel dithering helper to try and make + // the grayscale to black and white conversion look better. + imageops::dither(&mut cleaned_image, &imageops::BiLevel); + + // Number of bytes that each line uses. + let line_size = (width_padded / 8) as usize; + + // The image data, after compression. + let mut field_data = String::new(); + + // Used to keep track of the value from the last line so we can compress it + // when they are the same. + let mut last_line = String::new(); + + // A buffer for holding bytes for the current line. + let mut buf = Vec::with_capacity(line_size); + // A work in progress byte that gets updated with each pixel. + let mut byte = 0u8; + + for y in 0..height { + buf.clear(); + + for x in 0..width_padded { + let pos = x % 8; + let pixel = cleaned_image.get_pixel(x, y).0[0]; + + // Need to compress the u8 value into a single bit, so use half of + // the maximum value as a threshold and shift it into the + // appropriate bit of our buffer byte. + if pixel < 128 { + byte |= 1 << (7 - pos); + } + + // After processing a complete byte, push it to the buffer. We know + // this will happen every loop because our image's width was padded + // to be a multiple of 8. + if pos == 7 { + buf.push(byte); + byte = 0; + } + } + + // Now that we're done with the line, we can encode it into a hex + // representation and compress that value. + let hex_buf = hex::encode_upper(&buf); + let compressed_line = zpl_compress_line(&hex_buf); + + // ZPL compression says we can use a colon instead of needing to include + // the entire line again if they were the same. + if compressed_line == last_line { + field_data.push(':'); + } else { + field_data.push_str(&compressed_line); + last_line = compressed_line; + } + } + + let total_size = line_size * height as usize; + debug!( + total_size, + compressed_size = field_data.len(), + "got total bytes" + ); + + format!("^GFA,{total_size},{total_size},{line_size},{field_data}^FS") +} + +/// Compress an entire line of data using the ZPL ASCII compression system. +fn zpl_compress_line(inp: &str) -> String { + let mut line = String::new(); + + let mut chars = inp.chars().peekable(); + + while let Some(c) = chars.next() { + // Advance the iterator to consume all of the following characters that + // are the same as the current character. + let following_count = chars.peeking_take_while(|n| *n == c).count(); + + // While compression only makes a difference for more than 1 following + // character, it makes the code cleaner to always use it. + if following_count > 0 { + // If we end on one of these repeating characters, use the special + // case compression. + if (c == '0' || c == 'F') && chars.peek().is_none() { + match c { + '0' => line.push(','), + 'F' => line.push('!'), + _ => unreachable!(), + } + } else { + line.push_str(&zpl_repeat_code(c, following_count + 1)); + } + } else { + line.push(c); + } + } + + line +} + +/// Compress a repeating character using the ZPL ASCII compression scheme. +fn zpl_repeat_code(c: char, mut count: usize) -> String { + let mut s = String::new(); + + // Lookup tables for characters. Each character is positioned such that + // index 0 is 1 instance and is 'g,' index 1 is 2 instances and is 'h,' etc. + const HIGH_CHAR: &[u8; 20] = b"ghijklmnopqrstuvwxyz"; + const LOW_CHAR: &[u8; 19] = b"GHIJKLMNOPQRSTUVWXY"; + + // This scheme has a maximum value, so we just repeat the process if there + // were somehow that many repeating characters in a row. + const MAX_REPEAT: usize = 419; + while count > MAX_REPEAT { + s.push_str(&zpl_repeat_code(c, MAX_REPEAT)); + count -= MAX_REPEAT; + } + + // The compression schemed is organized into two sets, the high and low + // values. These then can be added together to create any value from + // 1 to 419. + + let high = count / 20; + let low = count % 20; + + if high > 0 { + s.push(HIGH_CHAR[high - 1] as char); + } + + if low > 0 { + s.push(LOW_CHAR[low - 1] as char); + } + + format!("{s}{c}") +} + +#[cfg(test)] +mod tests { + #[test] + fn test_zpl_repeat_code() { + use super::zpl_repeat_code; + + let cases = &[ + (('6', 7), "M6"), + (('B', 40), "hB"), + (('B', 327), "vMB"), + (('0', 2), "H0"), + ]; + + for ((c, count), output) in cases { + assert_eq!(zpl_repeat_code(*c, *count).as_str(), *output); + } + } + + #[test] + fn test_zpl_compress_line() { + use super::zpl_compress_line; + + let _ = tracing_subscriber::fmt::try_init(); + + let cases = &[ + ("6666666", "M6"), + ("FFFF80", "JF80"), + ("FFFFFFFF000000", "NF,"), + ("08000010", "08J010"), + ("00000000", ","), + ("80000000", "8,"), + ("00800000", "H08,"), + ("00C00300", "H0CH03,"), + ("00E00700", "H0EH07,"), + ("00F00F00", "H0FH0F,"), + ("00FFFF00", "H0JF,"), + ("00000001", "M01"), + ("003FFC00", "H03HFC,"), + ("002021F00F00000C8C31800000", "H02021FH0FK0C8C318,"), + ]; + + for (input, output) in cases { + assert_eq!(zpl_compress_line(input).as_str(), *output); + } + } +}