Add label printing

This commit is contained in:
Simon Bruder 2024-07-08 19:35:22 +02:00
parent 21c99a1677
commit af2ce1e9b1
Signed by: simon
GPG key ID: 347FF8699CDA0776
8 changed files with 855 additions and 18 deletions

432
Cargo.lock generated
View file

@ -88,7 +88,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb"
dependencies = [
"quote",
"syn",
"syn 2.0.68",
]
[[package]]
@ -204,7 +204,7 @@ dependencies = [
"actix-router",
"proc-macro2",
"quote",
"syn",
"syn 2.0.68",
]
[[package]]
@ -244,6 +244,12 @@ dependencies = [
"memchr",
]
[[package]]
name = "aliasable"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
[[package]]
name = "alloc-no-stdlib"
version = "2.0.4"
@ -259,6 +265,33 @@ dependencies = [
"alloc-no-stdlib",
]
[[package]]
name = "allsorts"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afb67debdbc7e8b0716e7b10ce247ff6cd2811b0b478969158b16ac58995d16b"
dependencies = [
"bitflags 1.3.2",
"bitreader",
"brotli-decompressor 2.5.1",
"byteorder",
"encoding_rs",
"flate2",
"glyph-names",
"itertools",
"lazy_static",
"libc",
"log",
"num-traits",
"ouroboros",
"rustc-hash",
"tinyvec",
"ucd-trie",
"unicode-canonical-combining-class",
"unicode-general-category",
"unicode-joining-type",
]
[[package]]
name = "anstream"
version = "0.6.14"
@ -308,6 +341,12 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "arrayvec"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "askama"
version = "0.12.1"
@ -344,7 +383,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde",
"syn",
"syn 2.0.68",
]
[[package]]
@ -370,7 +409,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.68",
]
[[package]]
@ -394,6 +433,12 @@ dependencies = [
"rustc-demangle",
]
[[package]]
name = "barcoders"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3826fb6e98ec72c0c0db8c9a40af4932d16793021027469857e84c1b50a1e8f"
[[package]]
name = "base64"
version = "0.21.7"
@ -427,6 +472,15 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "bitreader"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdd859c9d97f7c468252795b35aeccc412bdbb1e90ee6969c4fa6328272eaeff"
dependencies = [
"cfg-if",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
@ -444,7 +498,17 @@ checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
"brotli-decompressor",
"brotli-decompressor 4.0.1",
]
[[package]]
name = "brotli-decompressor"
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
]
[[package]]
@ -457,6 +521,17 @@ dependencies = [
"alloc-stdlib",
]
[[package]]
name = "bstr"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
dependencies = [
"memchr",
"regex-automata",
"serde",
]
[[package]]
name = "bumpalo"
version = "3.16.0"
@ -542,6 +617,31 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
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.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]]
name = "crypto-common"
version = "0.1.6"
@ -573,7 +673,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
"syn",
"syn 2.0.68",
]
[[package]]
@ -584,7 +684,17 @@ checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178"
dependencies = [
"darling_core",
"quote",
"syn",
"syn 2.0.68",
]
[[package]]
name = "datamatrix"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9133f6cb9217da268046cfa9b5916b37d7c77c11707a8b7e95474a816154c42"
dependencies = [
"arrayvec",
"flagset",
]
[[package]]
@ -623,7 +733,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustc_version",
"syn",
"syn 2.0.68",
]
[[package]]
@ -662,7 +772,7 @@ dependencies = [
"heck 0.4.1",
"proc-macro2",
"quote",
"syn",
"syn 2.0.68",
]
[[package]]
@ -675,7 +785,7 @@ dependencies = [
"dsl_auto_type",
"proc-macro2",
"quote",
"syn",
"syn 2.0.68",
]
[[package]]
@ -695,7 +805,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25"
dependencies = [
"syn",
"syn 2.0.68",
]
[[package]]
@ -720,7 +830,7 @@ dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"syn",
"syn 2.0.68",
]
[[package]]
@ -773,6 +883,12 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
[[package]]
name = "flagset"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdeb3aa5e95cf9aabc17f060cfa0ced7b83f042390760ca53bf09df9968acaa1"
[[package]]
name = "flate2"
version = "1.0.30"
@ -822,7 +938,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.68",
]
[[package]]
@ -879,6 +995,12 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
[[package]]
name = "glyph-names"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3531d702d6c1a3ba92a5fb55a404c7b8c476c8e7ca249951077afcbe4bc807f"
[[package]]
name = "h2"
version = "0.3.26"
@ -1007,6 +1129,15 @@ version = "1.70.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.11"
@ -1037,6 +1168,12 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "li7y"
version = "0.0.0"
@ -1045,13 +1182,19 @@ dependencies = [
"actix-web",
"askama",
"askama_actix",
"barcoders",
"datamatrix",
"diesel",
"diesel-async",
"diesel-derive-enum",
"diesel_migrations",
"env_logger",
"log",
"mime",
"printpdf",
"rust-fontconfig",
"serde",
"thiserror",
"uuid",
]
@ -1067,6 +1210,12 @@ version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
[[package]]
name = "linked-hash-map"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "local-channel"
version = "0.1.5"
@ -1100,6 +1249,23 @@ version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "lopdf"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07c8e1b6184b1b32ea5f72f572ebdc40e5da1d2921fa469947ff7c480ad1f85a"
dependencies = [
"encoding_rs",
"flate2",
"itoa",
"linked-hash-map",
"log",
"md5",
"pom",
"time",
"weezl",
]
[[package]]
name = "md-5"
version = "0.10.6"
@ -1110,6 +1276,12 @@ dependencies = [
"digest",
]
[[package]]
name = "md5"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
[[package]]
name = "memchr"
version = "2.7.4"
@ -1180,6 +1352,16 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "mmapio"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0204e2cac68f5b2e35b7ec8cb5d906f6e58e78dad8066a30b6ee54da99bb03dd"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "nom"
version = "7.1.3"
@ -1230,6 +1412,39 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "ouroboros"
version = "0.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2ba07320d39dfea882faa70554b4bd342a5f273ed59ba7c1c6b4c840492c954"
dependencies = [
"aliasable",
"ouroboros_macro",
"static_assertions",
]
[[package]]
name = "ouroboros_macro"
version = "0.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec4c6225c69b4ca778c0aea097321a64c421cf4577b331c61b229267edabb6f8"
dependencies = [
"heck 0.4.1",
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.68",
]
[[package]]
name = "owned_ttf_parser"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "706de7e2214113d63a8238d1910463cfce781129a6f263d13fdb09ff64355ba4"
dependencies = [
"ttf-parser",
]
[[package]]
name = "parking_lot"
version = "0.12.3"
@ -1301,6 +1516,15 @@ version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
[[package]]
name = "pom"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c972d8f86e943ad532d0b04e8965a749ad1d18bb981a9c7b3ae72fe7fd7744b"
dependencies = [
"bstr",
]
[[package]]
name = "postgres-protocol"
version = "0.6.6"
@ -1342,6 +1566,42 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "printpdf"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c30a4cc87c3ca9a98f4970db158a7153f8d1ec8076e005751173c57836380b1d"
dependencies = [
"js-sys",
"lopdf",
"owned_ttf_parser",
"time",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn 1.0.109",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.86"
@ -1390,6 +1650,26 @@ dependencies = [
"getrandom",
]
[[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.4.1"
@ -1443,12 +1723,30 @@ version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]]
name = "rust-fontconfig"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6002602a06c22f5f4e5ea2ed68854bfce071b48e1092c03874e34d0b59eb4b"
dependencies = [
"allsorts",
"mmapio",
"rayon",
"xmlparser",
]
[[package]]
name = "rustc-demangle"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc_version"
version = "0.4.0"
@ -1503,7 +1801,7 @@ checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.68",
]
[[package]]
@ -1600,6 +1898,12 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "stringprep"
version = "0.1.5"
@ -1623,6 +1927,16 @@ version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.68"
@ -1634,6 +1948,26 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.68",
]
[[package]]
name = "time"
version = "0.3.36"
@ -1790,12 +2124,24 @@ dependencies = [
"once_cell",
]
[[package]]
name = "ttf-parser"
version = "0.19.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49d64318d8311fc2668e48b63969f4343e0a85c4a109aa8460d6672e364b8bd1"
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "ucd-trie"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
[[package]]
name = "unicase"
version = "2.7.0"
@ -1811,12 +2157,30 @@ version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
[[package]]
name = "unicode-canonical-combining-class"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6925586af9268182c711e47c0853ed84131049efaca41776d0ca97f983865c32"
[[package]]
name = "unicode-general-category"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2281c8c1d221438e373249e065ca4989c4c36952c211ff21a0ee91c44a3869e7"
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-joining-type"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22f8cb47ccb8bc750808755af3071da4a10dcd147b68fc874b7ae4b12543f6f5"
[[package]]
name = "unicode-normalization"
version = "0.1.23"
@ -1904,7 +2268,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn",
"syn 2.0.68",
"wasm-bindgen-shared",
]
@ -1926,7 +2290,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.68",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -1947,6 +2311,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "weezl"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
[[package]]
name = "whoami"
version = "1.5.1"
@ -1958,6 +2328,28 @@ dependencies = [
"web-sys",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.48.0"
@ -2106,6 +2498,12 @@ dependencies = [
"memchr",
]
[[package]]
name = "xmlparser"
version = "0.13.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4"
[[package]]
name = "zerocopy"
version = "0.7.34"
@ -2123,7 +2521,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.68",
]
[[package]]

View file

@ -14,11 +14,17 @@ actix-files = "0.6.6"
actix-web = "4.8.0"
askama = { version = "0.12.1", features = ["with-actix-web"] }
askama_actix = "0.14.0"
barcoders = { version = "2.0.0", default-features = false, features = ["std"] }
datamatrix = "0.3.1"
diesel = { version = "2.2.1", features = ["uuid"] }
diesel-async = { git = "https://github.com/weiznich/diesel_async.git", version = "0.4.1", features = ["postgres", "deadpool", "async-connection-wrapper"] }
diesel-derive-enum = { version = "2.1.0", features = ["postgres"] }
diesel_migrations = { version = "2.2.0", features = ["postgres"] }
env_logger = "0.11.3"
log = "0.4.21"
mime = "0.3.17"
printpdf = "0.7.0"
rust-fontconfig = "0.1.7"
serde = { version = "1.0.203", features = ["serde_derive"] }
thiserror = "1.0.61"
uuid = { version = "1.9.0", features = ["serde", "v4"] }

55
src/api/v1/label.rs Normal file
View file

@ -0,0 +1,55 @@
// SPDX-FileCopyrightText: 2024 Simon Bruder <simon@sbruder.de>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
use actix_web::http::header::{ContentDisposition, ContentType, DispositionParam, DispositionType};
use actix_web::{error, get, web, HttpResponse, Responder};
use serde::Deserialize;
use uuid::Uuid;
use crate::label::{Label, LabelPage, LabelPreset};
pub fn config(cfg: &mut web::ServiceConfig) {
cfg.service(items);
}
#[derive(Debug, Deserialize)]
struct QueryParams {
// FIXME: serde_urlencoded does not support sequences
ids: String,
preset: LabelPreset,
}
#[get("/label/items")]
async fn items(params: web::Query<QueryParams>) -> actix_web::Result<impl Responder> {
let ids = params
.ids
.split(',')
.skip_while(|s| s.is_empty()) // to make the empty string parse as an empty iterator
.map(Uuid::try_parse)
.collect::<Result<Vec<Uuid>, uuid::Error>>()
.map_err(error::ErrorInternalServerError)?;
let label_config = params.preset.clone().into();
let label = Label {
pages: ids
.into_iter()
.map(|id| LabelPage {
id: Some(id),
short_id: None,
})
.collect(),
config: label_config,
};
Ok(HttpResponse::Ok()
.insert_header(ContentType(mime::APPLICATION_PDF))
.insert_header(ContentDisposition {
disposition: DispositionType::Inline,
parameters: vec![DispositionParam::Filename(
"li7y-item-labels.pdf".to_string(),
)],
})
.body(label.generate().map_err(error::ErrorInternalServerError)?))
}

View file

@ -4,9 +4,12 @@
mod item;
mod item_class;
mod label;
use actix_web::web;
pub fn config(cfg: &mut web::ServiceConfig) {
cfg.configure(item::config).configure(item_class::config);
cfg.configure(item::config)
.configure(item_class::config)
.configure(label::config);
}

60
src/label/barcode.rs Normal file
View file

@ -0,0 +1,60 @@
// SPDX-FileCopyrightText: 2024 Simon Bruder <simon@sbruder.de>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
use barcoders::sym::code128::Code128;
use datamatrix::{DataMatrix, SymbolSize};
use printpdf::{ColorBits, ColorSpace, Image, ImageXObject, Px};
use uuid::Uuid;
fn create_image(width: usize, height: usize, data: Vec<bool>) -> Image {
Image::from(ImageXObject {
width: Px(width),
height: Px(height),
color_space: ColorSpace::Greyscale,
bits_per_component: ColorBits::Bit8, // TODO: why does Bit1 not work?
image_data: data
.iter()
.map(|x| match x {
true => 0,
false => u8::MAX,
})
.collect(),
interpolate: false,
image_filter: None,
smask: None,
clipping_bbox: None,
})
}
pub fn encode_data_matrix(id: Uuid) -> Result<Image, datamatrix::data::DataEncodingError> {
let code = DataMatrix::encode(
&id.to_string().bytes().collect::<Vec<u8>>(),
SymbolSize::Square22,
)?;
let bitmap = code.bitmap();
assert_eq!(bitmap.width(), 22);
assert_eq!(bitmap.height(), 22);
let mut buf = vec![false; bitmap.width() * bitmap.height()];
for (x, y) in bitmap.pixels() {
buf[x + y * bitmap.width()] = true;
}
Ok(create_image(22, 22, buf))
}
pub fn encode_code128(text: String) -> Result<Image, barcoders::error::Error> {
let mut text = text;
// barcoders uses special characters for setting the Code128 type (A, B, C)
text.insert(0, 'À');
let buf: Vec<bool> = Code128::new(text)?
.encode()
.iter()
.map(|x| !matches!(x, 0))
.collect();
Ok(create_image(buf.len(), 1, buf))
}

239
src/label/mod.rs Normal file
View file

@ -0,0 +1,239 @@
// SPDX-FileCopyrightText: 2024 Simon Bruder <simon@sbruder.de>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
mod barcode;
mod preset;
use std::fs::File;
use std::sync::OnceLock;
use pdf::{IndirectFontRef, PdfLayerReference};
use printpdf as pdf;
use printpdf::{ImageTransform, Mm, PdfDocument, PdfDocumentReference, Pt, Px};
use rust_fontconfig::{FcFontCache, FcPattern};
use thiserror::Error;
use uuid::Uuid;
use barcode::{encode_code128, encode_data_matrix};
pub use preset::LabelPreset;
#[derive(Error, Debug)]
pub enum Error {
#[error("could not encode data matrix")]
DataMatrixEncoding(datamatrix::data::DataEncodingError),
#[error("could not encode barcode: {0}]")]
Barcoders(#[from] barcoders::error::Error),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("PDF error: {0}")]
PrintPdf(#[from] printpdf::Error),
#[error("data is incomplete ({0} not given in data, but required in config)")]
DataIncomplete(String),
#[error("cant create labels without any pages")]
NoPages,
}
// The datamatrix crate does not implement Into<std::error::Error> for its error type
impl From<datamatrix::data::DataEncodingError> for Error {
fn from(value: datamatrix::data::DataEncodingError) -> Self {
Self::DataMatrixEncoding(value)
}
}
type Result<T> = std::result::Result<T, Error>;
// this is implemented based on lazy_statics recommendation
fn fc_cache() -> &'static FcFontCache {
static FC_CACHE: OnceLock<FcFontCache> = OnceLock::new();
FC_CACHE.get_or_init(FcFontCache::build)
}
fn get_font_path(name: &str) -> String {
fc_cache()
.query(&FcPattern {
name: Some(name.to_string()),
..Default::default()
})
.expect("Could not find font")
.path
.clone()
}
fn mm_to_scale_factor(mm: Mm, size: Px) -> f32 {
mm / Mm::from(Pt(0.24 * (size.0 as f32)))
}
#[derive(Debug)]
pub struct LabelConfig {
width: Mm,
height: Mm,
data_matrix: Option<DataMatrixConfig>,
code128: Option<Code128Config>,
id_text: Option<IdTextConfig>,
short_id_text: Option<IdTextConfig>,
}
#[derive(Debug)]
pub struct DataMatrixConfig {
scale: Mm,
position: (Mm, Mm),
}
impl DataMatrixConfig {
fn draw(&self, layer: &PdfLayerReference, id: Option<Uuid>) -> Result<()> {
let image = encode_data_matrix(id.ok_or(Error::DataIncomplete("UUID".to_string()))?)?;
let size = image.image.width;
image.add_to_layer(
layer.clone(),
ImageTransform {
translate_x: Some(self.position.0),
translate_y: Some(self.position.1),
scale_x: Some(mm_to_scale_factor(self.scale, size)),
scale_y: Some(mm_to_scale_factor(self.scale, size)),
..Default::default()
},
);
Ok(())
}
}
#[derive(Debug)]
pub struct Code128Config {
scale: (Mm, Mm),
position: (Mm, Mm),
}
impl Code128Config {
fn draw<S: Into<String>>(&self, layer: &PdfLayerReference, id: Option<S>) -> Result<()> {
let image = encode_code128(
id.ok_or(Error::DataIncomplete("short ID".to_string()))?
.into(),
)?;
let width = image.image.width;
image.add_to_layer(
layer.clone(),
ImageTransform {
translate_x: Some(self.position.0),
translate_y: Some(self.position.1),
scale_x: Some(mm_to_scale_factor(self.scale.0, width)),
scale_y: Some(mm_to_scale_factor(self.scale.1, Px(1))),
..Default::default()
},
);
Ok(())
}
}
#[derive(Debug)]
pub struct IdTextConfig {
font_size: f32,
position: (Mm, Mm),
}
impl IdTextConfig {
fn draw<S: Into<String>>(
&self,
layer: &PdfLayerReference,
font: &IndirectFontRef,
text: Option<S>,
) -> Result<()> {
layer.use_text(
text.ok_or(Error::DataIncomplete("ID text".to_string()))?,
self.font_size,
self.position.0,
self.position.1,
font,
);
Ok(())
}
}
#[derive(Debug)]
pub struct LabelPage {
pub id: Option<Uuid>,
pub short_id: Option<String>,
}
impl LabelPage {
fn draw(
&self,
config: &LabelConfig,
layer: &PdfLayerReference,
font: &IndirectFontRef,
) -> Result<()> {
if let Some(cfg) = &config.data_matrix {
cfg.draw(layer, self.id)?;
}
if let Some(cfg) = &config.code128 {
cfg.draw(layer, self.short_id.as_ref())?;
}
if let Some(cfg) = &config.id_text {
cfg.draw(layer, font, self.id)?;
}
if let Some(cfg) = &config.short_id_text {
cfg.draw(layer, font, self.short_id.as_ref())?;
}
Ok(())
}
}
#[derive(Debug)]
pub struct Label {
pub pages: Vec<LabelPage>,
pub config: LabelConfig,
}
impl Label {
pub fn generate(&self) -> Result<Vec<u8>> {
let mut doc: Option<PdfDocumentReference> = None;
let mut font: Option<IndirectFontRef> = None;
for label in &self.pages {
let page;
let layer;
let name: String = label
.id
.map(String::from)
.or(label.short_id.clone())
.unwrap_or("empty".into());
if let Some(ref doc) = &doc {
(page, layer) = doc.add_page(self.config.width, self.config.height, name);
} else {
let (new_doc, page1, layer1) =
PdfDocument::new("li7y labels", self.config.width, self.config.height, name);
// TODO: do not hardcode font
// counter-argument: the presets rely on its spacing
// but at least it should be somehow included (or at least documented)
font =
Some(new_doc.add_external_font(File::open(get_font_path(
"IosevkaSbruder Nerd Font",
))?)?);
doc = Some(new_doc);
page = page1;
layer = layer1;
}
let doc = doc.as_ref().unwrap();
let font = font.as_ref().unwrap();
let layer_ref = doc.get_page(page).get_layer(layer);
label.draw(&self.config, &layer_ref, font)?
}
Ok(doc.ok_or(Error::NoPages)?.save_to_bytes()?)
}
}

75
src/label/preset.rs Normal file
View file

@ -0,0 +1,75 @@
// SPDX-FileCopyrightText: 2024 Simon Bruder <simon@sbruder.de>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
use printpdf::Mm;
use serde::Deserialize;
use super::{Code128Config, DataMatrixConfig, IdTextConfig, LabelConfig};
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum LabelPreset {
SeikoSlpMrlDataMatrix,
SeikoSlpMrlCode128,
SeikoSlpMrl,
}
#[allow(clippy::from_over_into)]
impl Into<LabelConfig> for LabelPreset {
fn into(self) -> LabelConfig {
match self {
Self::SeikoSlpMrlDataMatrix => LabelConfig {
width: Mm(44.45),
height: Mm(24.13),
data_matrix: Some(DataMatrixConfig {
scale: Mm(20.0),
position: (Mm(0.0), Mm(0.0)),
}),
code128: None,
id_text: Some(IdTextConfig {
font_size: 7.0,
position: (Mm(0.0), Mm(22.0)),
}),
short_id_text: None,
},
Self::SeikoSlpMrlCode128 => LabelConfig {
width: Mm(44.45),
height: Mm(24.13),
data_matrix: None,
code128: Some(Code128Config {
scale: (Mm(44.45), Mm(19.0)),
position: (Mm(0.0), Mm(2.5)),
}),
id_text: Some(IdTextConfig {
font_size: 7.0,
position: (Mm(0.0), Mm(22.0)),
}),
short_id_text: Some(IdTextConfig {
font_size: 7.0,
position: (Mm(0.0), Mm(0.0)),
}),
},
Self::SeikoSlpMrl => LabelConfig {
width: Mm(44.45),
height: Mm(24.13),
data_matrix: Some(DataMatrixConfig {
scale: Mm(16.0),
position: (Mm(0.0), Mm(4.0)),
}),
code128: Some(Code128Config {
scale: (Mm(27.45), Mm(16.0)),
position: (Mm(17.0), Mm(4.0)),
}),
id_text: Some(IdTextConfig {
font_size: 7.0,
position: (Mm(0.0), Mm(22.0)),
}),
short_id_text: Some(IdTextConfig {
font_size: 7.0,
position: (Mm(17.0), Mm(0.0)),
}),
},
}
}
}

View file

@ -4,6 +4,7 @@
pub mod api;
pub mod frontend;
pub mod label;
pub mod manage;
pub mod models;
pub mod schema;