From e569afec1785aabb9d57196d7c48c0a71850354e Mon Sep 17 00:00:00 2001 From: Artemis Tosini Date: Sun, 24 Sep 2023 00:28:46 +0000 Subject: [PATCH] First attempt at labels --- .envrc | 1 + .gitignore | 15 + common.typ | 54 ++ flake.lock | 61 ++ flake.nix | 53 ++ fonts/OCR-B-Regular.otf | Bin 0 -> 24656 bytes fonts/USPSIMBCompact.ttf | Bin 0 -> 2708 bytes format.py | 149 ++++ imb.py | 138 ++++ imb_table.py | 1500 ++++++++++++++++++++++++++++++++++++++ labels.typ | 34 + 11 files changed, 2005 insertions(+) create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 common.typ create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 fonts/OCR-B-Regular.otf create mode 100644 fonts/USPSIMBCompact.ttf create mode 100755 format.py create mode 100644 imb.py create mode 100644 imb_table.py create mode 100644 labels.typ diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1e92b0e --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +result +.direnv + +*.pdf +addresses.csv +addresses.*.csv +/content +cache/ + +*.pyc +__pycache__/ + +options.json +next_serial.txt +mailer_id.txt diff --git a/common.typ b/common.typ new file mode 100644 index 0000000..33945af --- /dev/null +++ b/common.typ @@ -0,0 +1,54 @@ +#let address_content(width, height, card) = { + set par(leading: 0.5em) + let text_height = if card.imb == "" { + height + } else { + height - 1in/8 + } + place( + top + left, + dx: 0.5in, + block( + width: width - 0.5in, + height: text_height, + fill: luma(230), + align( + start + horizon, + text(font: ("OCR-B", "Noto Emoji"), size: 8pt, card.address) + ) + ) + ) + if card.imb != "" { + place( + top + left, + dy: height - 1in/8, + block( + width: 100%, + height: 1in/8, + fill: luma(220), + align( + top + center, + text(font: "USPSIMBCompact", size: 12pt, card.imb) + ) + ) + ) + } + if card.avatar != "" { + place( + top + left, + dx: 0.1in, + dy: 0.1in, + image(card.avatar, width: 0.3in) + ) + } +} + +#let address_block(width, height, card) = { + block( + width: width, + height: height, + breakable: false, + fill: luma(240), + address_content(width, height, card) + ) +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..a1128ea --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1695318763, + "narHash": "sha256-FHVPDRP2AfvsxAdc+AsgFJevMz5VBmnZglFUMlxBkcY=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "e12483116b3b51a185a33a272bf351e357ba9a99", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs", + "utils": "utils" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1694529238, + "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..f6ee314 --- /dev/null +++ b/flake.nix @@ -0,0 +1,53 @@ +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + utils.url = "github:numtide/flake-utils"; + }; + + outputs = { + self, + nixpkgs, + utils, + }: let + supportedSystems = ["x86_64-linux" "aarch64-linux"]; + in + utils.lib.eachSystem supportedSystems (system: let + pkgs = import nixpkgs {inherit system;}; + in rec { + formatter = pkgs.alejandra; + + packages.included-fonts = pkgs.runCommand "included-fonts" {} '' + mkdir -p $out/share/fonts/{truetype,opentype} + cp ${./fonts}/*.ttf $out/share/fonts/truetype + cp ${./fonts}/*.otf $out/share/fonts/opentype + ''; + + devShell = with pkgs; + mkShell { + packages = [ + typst + python3 + python3Packages.autopep8 + python3Packages.requests + ]; + + # We need CLDR main, not just the annotations + CLDR_ROOT = pkgs.cldr-annotations.overrideAttrs (final: prev: { + installPhase = '' + runHook preInstall + mkdir -p $out/share/unicode/cldr + mv common $out/share/unicode/cldr + runHook postInstall + ''; + }); + + TYPST_FONT_PATHS = with pkgs; symlinkJoin { + name = "typst-fonts"; + paths = [ + packages.included-fonts + noto-fonts-emoji + ]; + }; + }; + }); +} diff --git a/fonts/OCR-B-Regular.otf b/fonts/OCR-B-Regular.otf new file mode 100644 index 0000000000000000000000000000000000000000..5fba217d72e2a4d4a71325d056e77fef40d701a0 GIT binary patch literal 24656 zcmd43cYG5^*FU-|S#9MAun;j4yK5Uv$JnO#-n;1?V{DqcCEIe5d+%ND-KO^%dVtVN z=tdAAKqx7Y1Trf|Ccm>X$@9GLy`TI0_exkh<;O`kG<;rM-72X+(0>Zb%j_|KW&-+f@eUhN2iY!C4@UVff|Cugx& z3BqO#L5L1*@b+9Mo-}$s+fA%ARb5%f?opzLbQJmC1yf<>>=NQAmLX)B*=bDEneMj+=o4Lz@{87XoeD2g(VzecnSX6QupU>+juyhgMEsGWiEN8s3*6 z`KAa3$z(1ef{M}wOCUa@?Mbd8W{_j~>P0e0ZTmpa5F7r9IKYP(OE~;>R_Fk&UMAdT z<;S;~5OflpfJfUK*bzVcTYo1=wNk*Zi0JesEFdI==1W*e*b`1)!Z!a!BO-m5={3UEds7cM2 zu!!&{2Ym^PA-(=fSVlOIC%%Mj33u}GEKjX=gLjz2IRACt!65ly6s;_B)?g72#l*5rO)(iZ{!M!tYGf9`w;xR2Nnp!Evy4|Q;JadjEuKEls) zvv+{j$HjN!T6dR0E`x@-y8aIy|GV}LT5aI){{8*h8s+(=O)g$u#4N&-(86~E;Z20W zu#6-8iFFVTCPIh+2s;q&&=qdPAVNmWAtn$WL_cCY{CX4X2_4}JrGtUO1!!F1IRfb0 zz9asNy5Ikz{|_IB|CO`oOU)bqt@}Tm=MwX+t#Pr|vjHfygqM{r6w=+SZEyim;9KDb z?b-~ycp4wb@g+7AYpv}Z1hGNHFl&4N(~ke$!v9;ZY_RrOAc$Rm`0};=rw={C zE4@JI;3I7N6LkC{m0fs=fjQBU5R)9_$q4i%=byR2m-_$ukfZ=K#wT(VVIjq&gzQ9q zO?D%DkOavPt)vt2p7@>kgZPv9Kzt6-?uHFi%u4Ke`g# zh@M1mVgNCa7)*>LM!`VOhGCpbEFhK=t3X6H!mB}-lJ;a*vNP#SI+AWgIFUpo6B$Gf zQA(5%RYVQ3mDmPaZy&LrI6|Bw&J*7emx&w19hmD+K>NKU-jZsv3#lQy!;6Vw{wWDH zh}~ouAUzRHL=sU%JZRxGB9|y83W!3Yj%Xn2iALfuag;bld_x>3P7rsA`@}us0Xdbx z=5%{pUkYCZ|9wfjC2oNPbMde8>)@ z9q}0=62g|40#oBG$&wBvMcR@wQcm`PoSz8L$K)8Y9ofFE5UC`;A{7u85j2b@1ESam z^ot8jlL-X0g_uOFCf0y})ROMxAaXD{kQ_=5BS(;~Ax(De-4k4$K%Smt2Kt3k_7H9+)1do{{=1+%?4r&JmREU#9cZcB) zI~)s~?EX6Y_us(N0YrQ_QA_LsUWcI9PQiOHWOuSRv|$)IlUzk^0$!g4_JZ#O513fy z4+o`#y+fBTyoLd<+<$qU27de}vhDHz{QbKFpZ3|eE$5jn|9sZ-=^sxYJiYg{fFPc9 zqQXG3r$e$2@f!)N5lAcFuNCA;@*;Ved_X=VIanLI!EBf=fOS!@M6gx>`~DWn}K0;v;%6xdql z#8)sY9AIA1AW{9kNR&Oyl1WynnnFw_dxLa)T4if3tavMlcQAMKFhcP#e_}zl(_r@G zTIWv#%piXLWWx+P26N~em_Nrsn$N%-y35ZZ;uWlcKM_)pJsTndv``Gufk+`b66u5; zkqT>ZCh;|qMK}^AuomYLEKvmOu#xCOSvL_CG%;YV2R-dJb(S5~=HzzmsA z&LZ2aC#%r?J&4yQ|LW&|L=qBg1O%v5#s64=`IG@%5y2IcT!}5g83kOakSnvvv?Xz( zt?-_$%`kl4?EmSBs7rf~c3td#?{J{Y#PN1SEbszJP=H2ep;!AtPmTmVJQXzY0?@(U z(AHoW=L8s^JQ$BE7*}4`9)&SI3w?G2#`6*J2J}9L21rN+=*2FeLwbR}8A6VNcbZ2o zAy<-Kq(2!(Mw6*zHd#nkkqzW_avyn|JWF08Z;&nI6Y?ebGxR4_&`Nib6|SFlL10_NyOL4Y7ikRZqw6bi}&wSs2BZowhJalu)^Wx-9sW5El- z&w}@Yzl5Za61Ep=gpR@Mry5K*Y2fMeWjaz7~VBvpPu&}Lyg>6k<$amplE4#&B z8-u-cem=h5;fvR8^bYn8*%-2TeXwV!_tO95EVU*qvldwPZ)bQ~tMP2(?AgZI^WVYo z^tO_Fwf$OK>Gf~Xb=LSg>)?1>*?3!1y#J-}Ze#1+#&*4x#@c`G1J?gbv;hWZqcv+| z8~Min9+Dxx*1D~w22AjUQohzo{H@9U*042m;NSo&lYsy3v4A#?0sk42U~7rs|1cdi zB*YpC`5!|vO#3fqcenA@3I-3*wY|W9SDLjJ|3tpQ(eAFVYI zym39a24Mj%Av$m*-2b=R@Lx`g!x!(ws^n$|1p9gV5=VGl)f04aAn4c}>$+ls)m;oa zXEoVO{sy{Zj9{@K2>iywf^P-)1#bm^2`s|C!okAnLcK6Sm?x|j?iQXCzO-?$DYw~a zv(M&`%`uzXHb2<>YV()PC(#hm2+=svbkS^)hiH*#naEERB8n8niq45{ihdCNEc#1K zi=D*%#6!g+#bd>j#52T;#a`m|;>}{MI9yyJE*IB}kBCo*&xtRJTf{$z-%5lMl|(IZ zlFX7UkSvpUOME54l2S>NWVhsy6_TQqr!{9@2hN zH|cQc80kc*hjfv2xpcEMSQ;fwkY-5prA^YU(w)+y(r={~q*tUjrT3(drO%{qq#vYz z%fvFdtevc*Of6$%PO?5SH`!>}c-a)$EZKb7QrRlmTG@J8fGkWFDT|XO$ueX)GNY_Q zRx4|i?UWssosgZCU6Ngw-I2A(9?PD|UWQcbYm3VY3v!v8zld`4vkD5-b>Y?8*ysd9 zJTu)zl$@BF5U-Z2N)1I(35jXR$&AaOe$Pa?Nr_nrnkYl8Avr5KJEeif{Y1IRg=q!( z`B{0k-H;UZ6J_UQ<>sp8gXj@)s#WxYAi5{5P~lNOVOun!<-?h8deiKWr)E-$SyVvj zRcasIi&Cgw-oA0KGO9dWA04evV>hI%_gtu1xZ&WgWVSLrx-?u99u*Zn_Rf?StX$QN zRw2ia$Qf-KN3nP`YE`YI@c3!Y-JPfWj;1gQbXoG}gxA<{Eye6AKmSZKmX_mPC=D4# zQ`mquqg?}Od^L^M1+elGnsKAC9TwuRa4)%P!|udwCmMF_s$$S}?!3rYmseY;u864$ z)W(E`#WL6h@4*AmHc@3zQb`|s5a(RD#VQQANz2@graVF9?$WRz-u%n(x>qzQ@&S{cVK#3`YudmxHRLPI+@ zrpML?Xo8|c1Lf~Ve1k=0j7!O;El1Rc_cq&9SFO3N}$U|d+3{D?t+aHVEt_}Uc)77ws|D;hz|y{ndP zJ*U2S^1w9)bqsettEnnCHeVXrf+cBekF=hX9MstU=go+j#2g4czj?NLq{ph^nm$?J2ksY>%L*RcY`TQsRU

T-;Pw(%h^ofw5EF|Ecm6HD0oK^WsHAH5;=Q)?8G}VI(e#@TvJU zIwe74(7Ex@)B_YVnASyvhp~!zbkj^o(N_jFGw3zQ!&WhBMsGz0MKw*e>e|p+e+FMH zq*00(Z^Q3JafY}Ety-~$9z_S}41ug-g`py(NmEx+QJu%0LeoVkTrBse(NoDc+rK^Z zjpj`KX73_4BBxl_plLGJHM7^#r`#H#Svt*Uy+3;_rY7J#Ek_O(H}M2&Jv0N)`KvEd zCQ{5KidCq*-i-Z#hMm0oq94u5UmRPD1t}~}Nbj=(E7fv5)Xd}kc#rCCd{ z);Xd4VLScTo#SVBG0KzE4&CzlS%b%*H!~^a53Mphc@WIk6ngizxbOxBN8)Xw=xvF+ z_o>l9yj->3ued6hMUiN$$mpBDey#d8&0s&QfZ-7N?mMPGqY0p|MttY@4g2t|D8Deb zv_xGMSs4)>pBx{@jB*ie^Il%HP!m8+ub8uSB|F+hln|d36{8NXiYPZ0pyknVhNy(B#N1@|8E7<7a#C7if;y_$ zV9d?U%Fba>KO_}FurQ+_BR?%a&(<09DcLEY?=|V8p}%6bx~!ZhUmojcm{jpNg}Qt zFjO^_*B^>u@euBUTvbLfXrQIVhuX|{QEg>uU5&nCGdor+@AZ1&)5|xu9cyIdp-3d! zndDcuNfY)x9a0=t8N{9+BGQ(ZMKoyYi>r$YQ_GXL@KX}rBQ82E?kZgoQN_q5BNvTz z>A&#S4~*OxOUaE*P4X-{f|mQz=Oj&XZiM8Ru68+tdyD0D6_KGZt##VFv|Ja!$m`b8 z@(px&g}!ol8{DCQ&CNAHd~JLnVDJYI$iB1zd&tZ7GzJLV1~Ry?U1V+ z%|~NF%|xL9+zC10Xm6c%h1{qeLCJj>t=LB^CsUyhJnFQ20%^u%IiljWOM)mpRZYpc zFtf{I+T!T3n3fBXQ=_VYLOaak!)d&&b%ZE2DK#QdEf;0yRjtkmc8{5n(BC{!ZCRGOFWXgag z)P5_zxC^_;>my3S^$Qn$^ z%c3?kw=A7@_Y0_3i{XxYrvT7ED0p7Mhnrhv{{Zn{ee<(Twv>n2C9fg&OzOwKJc$uqMv;fk}jo3}519Xy3X7DgAnMr05d&z9VnaqJLnlb+yhWI4cYl{~0KRspz_Nmi3JWGz_-pk;ru9-x3m zFy&nVDmo6J%n1Mvo4}agLS&I!0YY*EKw<#74d8+u027}kcapov-NYbr54o4z2SCtZ zfR(-l*!L{CpFBVwBoC2?0q_}09wCpC-;l=u2pSH6@Ogk5P5>Zu0pPz=0B>9*PXl;y znYcopA-^TAl4k+($tBP6U>A9w{EoOzULYm`PchK0u8vJ_ZnM zEmn8Ksy=$uDbv%zXkxnNPvPi04z8Tpm!<2S~`HYHUk8-31IhN z*mH0Mu#N(NXf!}us{jP70pKVApue#I>5l^VYYYH{wgAUf0325hP$CVWQZ&HzQ2_O& z0yq|9g$m<|MA8f}ehR>kYspUls(Ava76LGSqZQZ>Br$0rTglG=Jrl%waxU-p37}aN zG`00mr6F|T&0K)$n{68hBCu7Mh5VZ3o-#M*NkOUmb9uTBqf+>Raf*8Pyss+~t zW}#5nN$4ybEA$fX6dnX$@RsnIjl{;qW`fOPn|zy#HrH()+B_F^6S<4Vh!%*vM0-WY zMAt-5MDN53u?oCDSMexuj<^~8yz}Dw;wR!?C95Qr5+wOss*uvsUf{KjmClxCNlT>_ z;G_K}{Yy4ZHdVGr<|PY~otE8}y_6xU8#SIvrShqIYCm8 zzn4D&t^K!cN89eU18nEouCfiZZL-~Kd(QT`?OR*!t8rgV`)cu5-d`1cwePFrUtLky zC@K^Uiv5c7iaUxQ+Y#+F?RvKx+0M7!&UOdeU2AXCo@qa+eQx_*?GLp-*Z%wVPdbP@ zbneingLjAR9rkuO-r>BR*si0Uqum(0*>=TtRdze=j@wU`Pxk@GLz?Ya-? zeyWF0Pon3Lo+oCwjeHhEaS>)0RY19} zzP|h$Fcdsg0TsHsx{7Zo&Z9NRS`3>{ccsv{*2`ADu`R&Y-h9cO=DM}s?ndFw_t5Vq zSe#;n+*Z7KF_o2-m7SU0xu7JgGO>=g{_I`o6L>uS8qZuud6=(z@vUpRMxs^M{<

  • =h!!7UYQ=3>Ncd@bV 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 W9E7$Z=8=To{&Gr)nTYsZh2lTTnQ!3$ literal 0 HcmV?d00001 diff --git a/fonts/USPSIMBCompact.ttf b/fonts/USPSIMBCompact.ttf new file mode 100644 index 0000000000000000000000000000000000000000..da40626d3deb0bb059bc9b10936fdf0c511a1938 GIT binary patch literal 2708 zcmd5;TZmgl82;y+Bzx)7qP0?+ZJm&9?IqppMcNug)^0X+x0c3DT63`4J)4}($u`Le z$?2y1pe%Ty5E}zO@v>R6oT>9y zi2P^a^Kr$gxm2X1h(8YdNTq63nnZhq4YH3!uI%WEZ7;q0-N_A4{YsDepdebgnfhsU zj@`aQ7uhuAZqkGn4eKa2P@2YwQ@S04)(%Vg$VfS{&JuPEtInZ+O)>Qd8eR2%#eD)A zv7{dnk74eU+4``{GZd!;rCTj%?~q#Gv{qVITFb2u6a&od#>D&UU~yudQ&T!&6x-jk z;YIifM0d2VJMjM>qRt|Ro79ucTxRT?(PXVRniSvqA>OKf^5wlvViC?K3UQCkK+qyk z><$6x5uWh$?TP73*x(J`n9MbJg4<@!^KF+i3|7O4a*tA(0>*SE=#9mVU?{{VyyaxZTaE`qh5@g@YNUwx zmBMBfa~EO`?1sI&&63J^V?mFMMniC>GU1T7)Mx}7$V16ZT3(}Iv{o^u6gUZR;3OuR zY$*vcOyN*aD8iv|2yq&5?A;SNoXSif(vY#2mNW=D(8hZ(&cv(r!aEt1!dud|6?Qy66jqb?ARn*hlMGSYbc)vF8*H zKwnq5i?*{b74D`-w1~nzv_YFwxR-+3C56|~Rz09_AMgzFWj=97&7ik2!~OBL!W!aP zR#=Drn!-NnXMTnKw3&@49Dv?XxQnLPn+kW+2>V^(9@?fQ6z-*c+JeICD5ia`a396= z0U9TVDzr#dDo~zm{DyGaNm<&3d4z`W8~~by2kucy(jhrp@UW?jwMF#2vEv8Ra~Xxhr3E}8)MMRH%x$FXA- zc@|{8oQ9F*C`LMNRci&O%!da@A>9|}y_`9As^Qd4bQe3DltTn1neQp+9Gb(}VOVoO zyinsNcdKU3Dw)+&+?nIz1nFPAOZvxmCfIEGnoJpK8n`ZN!NQn>*>*~1ji;S@xn`B2 zh$DjqbWuGO%sH7y8RIM@8@Ux_R%>#LTMJgvsaQFVW1h8avpDx>Mxu6Iu*E2fr!JpC zsmWvfd&Lf-phRg!iEP)c#G=u9y*}tx^6qL~4rZN_F#l)FYk%=^LHwn4Ly~tM4bWrw ZCh-qZYA~$qbY2s}%Jm(e$oXdK(jR7N*jNAn literal 0 HcmV?d00001 diff --git a/format.py b/format.py new file mode 100755 index 0000000..b7560c1 --- /dev/null +++ b/format.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 +import argparse +import csv +import json +import os +import string +import typing +import xml.etree.ElementTree as ET +import requests +import imb + + +def iso_code(s: str) -> str: + if len(s) != 2: + raise ValueError("must be 2 characters long") + s = s.lower() + if not (s[0] in string.ascii_lowercase and s[1] in string.ascii_lowercase): + raise ValueError("must be ascii letters") + return s + + +def get_avatar(url: str) -> str: + if not os.path.exists("cache"): + os.mkdir("cache") + name = url.split("?")[0].split("/")[-1] + if os.path.exists("cache/" + name): + return "cache/" + name + result = requests.get(url) + if result.ok: + with open("cache/" + name, "wb") as outfile: + outfile.write(result.content) + return "cache/" + name + return "" + + +def get_country_name( + root: ET.ElementTree, destination: str, alt=None +) -> typing.Optional[str]: + elements = root.findall( + f"./localeDisplayNames/territories/territory[@type='{destination.upper()}']" + ) + normal = None + for element in elements: + if element.attrib.get("alt") == alt: + return element.text + elif element.attrib.get("alt") is None: + normal = element.text + return normal + + +parser = argparse.ArgumentParser( + prog="format", description="format postcards with latex" +) +parser.add_argument("template", help="template to use", + nargs="?", default="2card") +parser.add_argument( + "-o", "--origin", help="origin country code", default="us", type=iso_code +) +parser.add_argument( + "-l", + "--language", + help="language to use for countries ", + default="en", + type=iso_code, +) +parser.add_argument( + "-c", + "--count", + default=1, + type=int, + help="Number of sets of labels to print, default 1 (labels only)", +) +parser.add_argument( + "-s", + "--skip", + default=0, + type=int, + help="Number of labels to skip (label sheets only)", +) + +parser.add_argument( + "-a", + "--address-file", + default="addresses.csv", + type=str, + help="CSV file containing addresses", +) + +args = parser.parse_args() + +root = ET.parse( + f"{os.getenv('CLDR_ROOT')}/share/unicode/cldr/common/main/{args.language}.xml" +) + +csvfile = open(args.address_file) +rows = csv.DictReader(csvfile) + +current_serial = imb.get_first_serial() +mid = int(open("mailer_id.txt").read().strip()) + + +cards = [] +for row in rows: + if row["Address"] == "": + continue + + country = ( + [] + if row["Country"].lower() == args.origin + else [get_country_name(root, row["Country"]).upper()] + ) + + address = row["Address"].split("\n") + country + + if row.get("Avatar", "") != "": + avatar = get_avatar(row["Avatar"]) + else: + avatar = None + + cards += [ + { + "address": "\n".join(address), + "avatar": avatar, + "row": row, + "imb": "", + } + ] + +cards = cards * args.count + +serial = imb.get_first_serial() +if args.origin == "us": + for card in cards: + dpc = card["row"].get("DPC", "") + if dpc != "": + card["imb"] = imb.generate( + 0, 310, mid, serial, dpc.replace(" ", "").replace("-", "") + ) + serial += 1 +imb.write_current_serial(serial) + +with open("options.json", "w") as options: + json.dump( + fp=options, + obj={ + "args": args.__dict__, + "cards": cards, + }, + ) diff --git a/imb.py b/imb.py new file mode 100644 index 0000000..3e03eb3 --- /dev/null +++ b/imb.py @@ -0,0 +1,138 @@ +import datetime +import typing +import imb_table + + +def get_first_serial() -> int: + """ + Generate a 6-digit serial number for an intelligent mail barcode. + The first 2 digits are the last 2 digits of the julian date, and the next 4 + are serial and the next number to use is stored in a temporary file. + This will break if over 10000 serials are requested in a day + """ + # Last 2 digits of the ordinal day + date = datetime.datetime.utcnow().timetuple().tm_yday % 100 + try: + serial_file = open("next_serial.txt") + next_serial = int(serial_file.read().strip()) + if next_serial // 10000 == date: + first_idx = next_serial % 10000 + else: + first_idx = 0 + except (ValueError, IndexError, FileNotFoundError): + first_idx = 0 + + return date * 10000 + first_idx + + +def write_current_serial(current_serial: int): + serial_file = open("next_serial.txt", "w") + serial_file.write(format(current_serial, "06d")) + + +def _format_routing(routing: str) -> int: + if len(routing) == 0: + return 0 + elif len(routing) == 5: + return int(routing) + 1 + elif len(routing) == 9: + return int(routing) + 100000 + 1 + elif len(routing) == 11: + return int(routing) + 1000000000 + 100000 + 1 + else: + raise ValueError("Routing code must be 0, 5, 9, or 11 characters") + + +def _generate_crc(data_int: int) -> int: + """ + Do the weird USPS CRC11 which requires precisely 102 bits in + This is done by copying USPS code which is not very optimal + """ + data = data_int.to_bytes(13, "big") + poly = 0xF35 + fcs = 0x7FF + + current = data[0] << 5 + for _ in range(6): + if (fcs ^ current) & 0x400: + fcs = (fcs << 1) ^ poly + else: + fcs = fcs << 1 + fcs &= 0x7FF + current <<= 1 + + for current in data[1:]: + current <<= 3 + for _ in range(8): + if (fcs ^ current) & 0x400: + fcs = (fcs << 1) ^ poly + else: + fcs = fcs << 1 + fcs &= 0x7FF + current <<= 1 + + return fcs + + +def _generate_codewords(data: int, crc: int) -> typing.List[int]: + codewords = [] + codewords.append(data % 636) + data //= 636 + for i in range(8): + codewords.append(data % 1365) + data //= 1365 + codewords.append(data) + + codewords.reverse() + codewords[0] += ((crc >> 10) & 1) * 659 + codewords[9] *= 2 + return codewords + + +def _generate_characters(codewords: typing.List[int], crc: int) -> typing.List[int]: + characters = [] + for idx, codeword in enumerate(codewords): + xor = ((crc >> idx) & 1) * 0x1FFF + characters.append(imb_table.CHARACTER_TABLE[codeword] ^ xor) + + return characters + + +def _get_bit(characters: typing.List[int], character: int, bit: int) -> int: + return (characters[character] >> bit) & 1 + + +def _generate_bars(characters: typing.List[int]) -> str: + s = "" + for bar in range(65): + descender = _get_bit(characters, *imb_table.BAR_TABLE[bar * 2]) + ascender = _get_bit(characters, *imb_table.BAR_TABLE[bar * 2 + 1]) + s = s + "TADF"[descender * 2 + ascender] + return s + + +def from_payload(data: int) -> str: + crc = _generate_crc(data) + codewords = _generate_codewords(data, crc) + characters = _generate_characters(codewords, crc) + return _generate_bars(characters) + + +def _generate_payload(bi: int, stid: int, mailer: int, serial: int, raw_routing: str): + if mailer >= 1000000: + decimal_tracking = int(f"{stid:03d}{mailer:09d}{serial:06d}") + else: + # Why are you using this program? + decimal_tracking = int(f"{stid:03d}{mailer:06d}{serial:09d}") + + payload = _format_routing(raw_routing) + + # ... what the fuck usps + payload = payload * 10 + (bi // 10) + payload = payload * 5 + (bi % 10) + payload = payload * 10**18 + decimal_tracking + return payload + + +def generate(bi: int, stid: int, mailer: int, serial: int, raw_routing: str): + return from_payload(_generate_payload(bi, stid, mailer, serial, raw_routing)) diff --git a/imb_table.py b/imb_table.py new file mode 100644 index 0000000..26d4925 --- /dev/null +++ b/imb_table.py @@ -0,0 +1,1500 @@ +CHARACTER_TABLE = [ + 0x001F, + 0x1F00, + 0x002F, + 0x1E80, + 0x0037, + 0x1D80, + 0x003B, + 0x1B80, + 0x003D, + 0x1780, + 0x003E, + 0x0F80, + 0x004F, + 0x1E40, + 0x0057, + 0x1D40, + 0x005B, + 0x1B40, + 0x005D, + 0x1740, + 0x005E, + 0x0F40, + 0x0067, + 0x1CC0, + 0x006B, + 0x1AC0, + 0x006D, + 0x16C0, + 0x006E, + 0x0EC0, + 0x0073, + 0x19C0, + 0x0075, + 0x15C0, + 0x0076, + 0x0DC0, + 0x0079, + 0x13C0, + 0x007A, + 0x0BC0, + 0x007C, + 0x07C0, + 0x008F, + 0x1E20, + 0x0097, + 0x1D20, + 0x009B, + 0x1B20, + 0x009D, + 0x1720, + 0x009E, + 0x0F20, + 0x00A7, + 0x1CA0, + 0x00AB, + 0x1AA0, + 0x00AD, + 0x16A0, + 0x00AE, + 0x0EA0, + 0x00B3, + 0x19A0, + 0x00B5, + 0x15A0, + 0x00B6, + 0x0DA0, + 0x00B9, + 0x13A0, + 0x00BA, + 0x0BA0, + 0x00BC, + 0x07A0, + 0x00C7, + 0x1C60, + 0x00CB, + 0x1A60, + 0x00CD, + 0x1660, + 0x00CE, + 0x0E60, + 0x00D3, + 0x1960, + 0x00D5, + 0x1560, + 0x00D6, + 0x0D60, + 0x00D9, + 0x1360, + 0x00DA, + 0x0B60, + 0x00DC, + 0x0760, + 0x00E3, + 0x18E0, + 0x00E5, + 0x14E0, + 0x00E6, + 0x0CE0, + 0x00E9, + 0x12E0, + 0x00EA, + 0x0AE0, + 0x00EC, + 0x06E0, + 0x00F1, + 0x11E0, + 0x00F2, + 0x09E0, + 0x00F4, + 0x05E0, + 0x00F8, + 0x03E0, + 0x010F, + 0x1E10, + 0x0117, + 0x1D10, + 0x011B, + 0x1B10, + 0x011D, + 0x1710, + 0x011E, + 0x0F10, + 0x0127, + 0x1C90, + 0x012B, + 0x1A90, + 0x012D, + 0x1690, + 0x012E, + 0x0E90, + 0x0133, + 0x1990, + 0x0135, + 0x1590, + 0x0136, + 0x0D90, + 0x0139, + 0x1390, + 0x013A, + 0x0B90, + 0x013C, + 0x0790, + 0x0147, + 0x1C50, + 0x014B, + 0x1A50, + 0x014D, + 0x1650, + 0x014E, + 0x0E50, + 0x0153, + 0x1950, + 0x0155, + 0x1550, + 0x0156, + 0x0D50, + 0x0159, + 0x1350, + 0x015A, + 0x0B50, + 0x015C, + 0x0750, + 0x0163, + 0x18D0, + 0x0165, + 0x14D0, + 0x0166, + 0x0CD0, + 0x0169, + 0x12D0, + 0x016A, + 0x0AD0, + 0x016C, + 0x06D0, + 0x0171, + 0x11D0, + 0x0172, + 0x09D0, + 0x0174, + 0x05D0, + 0x0178, + 0x03D0, + 0x0187, + 0x1C30, + 0x018B, + 0x1A30, + 0x018D, + 0x1630, + 0x018E, + 0x0E30, + 0x0193, + 0x1930, + 0x0195, + 0x1530, + 0x0196, + 0x0D30, + 0x0199, + 0x1330, + 0x019A, + 0x0B30, + 0x019C, + 0x0730, + 0x01A3, + 0x18B0, + 0x01A5, + 0x14B0, + 0x01A6, + 0x0CB0, + 0x01A9, + 0x12B0, + 0x01AA, + 0x0AB0, + 0x01AC, + 0x06B0, + 0x01B1, + 0x11B0, + 0x01B2, + 0x09B0, + 0x01B4, + 0x05B0, + 0x01B8, + 0x03B0, + 0x01C3, + 0x1870, + 0x01C5, + 0x1470, + 0x01C6, + 0x0C70, + 0x01C9, + 0x1270, + 0x01CA, + 0x0A70, + 0x01CC, + 0x0670, + 0x01D1, + 0x1170, + 0x01D2, + 0x0970, + 0x01D4, + 0x0570, + 0x01D8, + 0x0370, + 0x01E1, + 0x10F0, + 0x01E2, + 0x08F0, + 0x01E4, + 0x04F0, + 0x01E8, + 0x02F0, + 0x020F, + 0x1E08, + 0x0217, + 0x1D08, + 0x021B, + 0x1B08, + 0x021D, + 0x1708, + 0x021E, + 0x0F08, + 0x0227, + 0x1C88, + 0x022B, + 0x1A88, + 0x022D, + 0x1688, + 0x022E, + 0x0E88, + 0x0233, + 0x1988, + 0x0235, + 0x1588, + 0x0236, + 0x0D88, + 0x0239, + 0x1388, + 0x023A, + 0x0B88, + 0x023C, + 0x0788, + 0x0247, + 0x1C48, + 0x024B, + 0x1A48, + 0x024D, + 0x1648, + 0x024E, + 0x0E48, + 0x0253, + 0x1948, + 0x0255, + 0x1548, + 0x0256, + 0x0D48, + 0x0259, + 0x1348, + 0x025A, + 0x0B48, + 0x025C, + 0x0748, + 0x0263, + 0x18C8, + 0x0265, + 0x14C8, + 0x0266, + 0x0CC8, + 0x0269, + 0x12C8, + 0x026A, + 0x0AC8, + 0x026C, + 0x06C8, + 0x0271, + 0x11C8, + 0x0272, + 0x09C8, + 0x0274, + 0x05C8, + 0x0278, + 0x03C8, + 0x0287, + 0x1C28, + 0x028B, + 0x1A28, + 0x028D, + 0x1628, + 0x028E, + 0x0E28, + 0x0293, + 0x1928, + 0x0295, + 0x1528, + 0x0296, + 0x0D28, + 0x0299, + 0x1328, + 0x029A, + 0x0B28, + 0x029C, + 0x0728, + 0x02A3, + 0x18A8, + 0x02A5, + 0x14A8, + 0x02A6, + 0x0CA8, + 0x02A9, + 0x12A8, + 0x02AA, + 0x0AA8, + 0x02AC, + 0x06A8, + 0x02B1, + 0x11A8, + 0x02B2, + 0x09A8, + 0x02B4, + 0x05A8, + 0x02B8, + 0x03A8, + 0x02C3, + 0x1868, + 0x02C5, + 0x1468, + 0x02C6, + 0x0C68, + 0x02C9, + 0x1268, + 0x02CA, + 0x0A68, + 0x02CC, + 0x0668, + 0x02D1, + 0x1168, + 0x02D2, + 0x0968, + 0x02D4, + 0x0568, + 0x02D8, + 0x0368, + 0x02E1, + 0x10E8, + 0x02E2, + 0x08E8, + 0x02E4, + 0x04E8, + 0x0307, + 0x1C18, + 0x030B, + 0x1A18, + 0x030D, + 0x1618, + 0x030E, + 0x0E18, + 0x0313, + 0x1918, + 0x0315, + 0x1518, + 0x0316, + 0x0D18, + 0x0319, + 0x1318, + 0x031A, + 0x0B18, + 0x031C, + 0x0718, + 0x0323, + 0x1898, + 0x0325, + 0x1498, + 0x0326, + 0x0C98, + 0x0329, + 0x1298, + 0x032A, + 0x0A98, + 0x032C, + 0x0698, + 0x0331, + 0x1198, + 0x0332, + 0x0998, + 0x0334, + 0x0598, + 0x0338, + 0x0398, + 0x0343, + 0x1858, + 0x0345, + 0x1458, + 0x0346, + 0x0C58, + 0x0349, + 0x1258, + 0x034A, + 0x0A58, + 0x034C, + 0x0658, + 0x0351, + 0x1158, + 0x0352, + 0x0958, + 0x0354, + 0x0558, + 0x0361, + 0x10D8, + 0x0362, + 0x08D8, + 0x0364, + 0x04D8, + 0x0383, + 0x1838, + 0x0385, + 0x1438, + 0x0386, + 0x0C38, + 0x0389, + 0x1238, + 0x038A, + 0x0A38, + 0x038C, + 0x0638, + 0x0391, + 0x1138, + 0x0392, + 0x0938, + 0x0394, + 0x0538, + 0x03A1, + 0x10B8, + 0x03A2, + 0x08B8, + 0x03A4, + 0x04B8, + 0x03C1, + 0x1078, + 0x03C2, + 0x0878, + 0x03C4, + 0x0478, + 0x040F, + 0x1E04, + 0x0417, + 0x1D04, + 0x041B, + 0x1B04, + 0x041D, + 0x1704, + 0x041E, + 0x0F04, + 0x0427, + 0x1C84, + 0x042B, + 0x1A84, + 0x042D, + 0x1684, + 0x042E, + 0x0E84, + 0x0433, + 0x1984, + 0x0435, + 0x1584, + 0x0436, + 0x0D84, + 0x0439, + 0x1384, + 0x043A, + 0x0B84, + 0x043C, + 0x0784, + 0x0447, + 0x1C44, + 0x044B, + 0x1A44, + 0x044D, + 0x1644, + 0x044E, + 0x0E44, + 0x0453, + 0x1944, + 0x0455, + 0x1544, + 0x0456, + 0x0D44, + 0x0459, + 0x1344, + 0x045A, + 0x0B44, + 0x045C, + 0x0744, + 0x0463, + 0x18C4, + 0x0465, + 0x14C4, + 0x0466, + 0x0CC4, + 0x0469, + 0x12C4, + 0x046A, + 0x0AC4, + 0x046C, + 0x06C4, + 0x0471, + 0x11C4, + 0x0472, + 0x09C4, + 0x0474, + 0x05C4, + 0x0487, + 0x1C24, + 0x048B, + 0x1A24, + 0x048D, + 0x1624, + 0x048E, + 0x0E24, + 0x0493, + 0x1924, + 0x0495, + 0x1524, + 0x0496, + 0x0D24, + 0x0499, + 0x1324, + 0x049A, + 0x0B24, + 0x049C, + 0x0724, + 0x04A3, + 0x18A4, + 0x04A5, + 0x14A4, + 0x04A6, + 0x0CA4, + 0x04A9, + 0x12A4, + 0x04AA, + 0x0AA4, + 0x04AC, + 0x06A4, + 0x04B1, + 0x11A4, + 0x04B2, + 0x09A4, + 0x04B4, + 0x05A4, + 0x04C3, + 0x1864, + 0x04C5, + 0x1464, + 0x04C6, + 0x0C64, + 0x04C9, + 0x1264, + 0x04CA, + 0x0A64, + 0x04CC, + 0x0664, + 0x04D1, + 0x1164, + 0x04D2, + 0x0964, + 0x04D4, + 0x0564, + 0x04E1, + 0x10E4, + 0x04E2, + 0x08E4, + 0x0507, + 0x1C14, + 0x050B, + 0x1A14, + 0x050D, + 0x1614, + 0x050E, + 0x0E14, + 0x0513, + 0x1914, + 0x0515, + 0x1514, + 0x0516, + 0x0D14, + 0x0519, + 0x1314, + 0x051A, + 0x0B14, + 0x051C, + 0x0714, + 0x0523, + 0x1894, + 0x0525, + 0x1494, + 0x0526, + 0x0C94, + 0x0529, + 0x1294, + 0x052A, + 0x0A94, + 0x052C, + 0x0694, + 0x0531, + 0x1194, + 0x0532, + 0x0994, + 0x0534, + 0x0594, + 0x0543, + 0x1854, + 0x0545, + 0x1454, + 0x0546, + 0x0C54, + 0x0549, + 0x1254, + 0x054A, + 0x0A54, + 0x054C, + 0x0654, + 0x0551, + 0x1154, + 0x0552, + 0x0954, + 0x0561, + 0x10D4, + 0x0562, + 0x08D4, + 0x0583, + 0x1834, + 0x0585, + 0x1434, + 0x0586, + 0x0C34, + 0x0589, + 0x1234, + 0x058A, + 0x0A34, + 0x058C, + 0x0634, + 0x0591, + 0x1134, + 0x0592, + 0x0934, + 0x05A1, + 0x10B4, + 0x05A2, + 0x08B4, + 0x05C1, + 0x1074, + 0x05C2, + 0x0874, + 0x0607, + 0x1C0C, + 0x060B, + 0x1A0C, + 0x060D, + 0x160C, + 0x060E, + 0x0E0C, + 0x0613, + 0x190C, + 0x0615, + 0x150C, + 0x0616, + 0x0D0C, + 0x0619, + 0x130C, + 0x061A, + 0x0B0C, + 0x061C, + 0x070C, + 0x0623, + 0x188C, + 0x0625, + 0x148C, + 0x0626, + 0x0C8C, + 0x0629, + 0x128C, + 0x062A, + 0x0A8C, + 0x062C, + 0x068C, + 0x0631, + 0x118C, + 0x0632, + 0x098C, + 0x0643, + 0x184C, + 0x0645, + 0x144C, + 0x0646, + 0x0C4C, + 0x0649, + 0x124C, + 0x064A, + 0x0A4C, + 0x0651, + 0x114C, + 0x0652, + 0x094C, + 0x0661, + 0x10CC, + 0x0662, + 0x08CC, + 0x0683, + 0x182C, + 0x0685, + 0x142C, + 0x0686, + 0x0C2C, + 0x0689, + 0x122C, + 0x068A, + 0x0A2C, + 0x0691, + 0x112C, + 0x0692, + 0x092C, + 0x06A1, + 0x10AC, + 0x06A2, + 0x08AC, + 0x06C1, + 0x106C, + 0x06C2, + 0x086C, + 0x0703, + 0x181C, + 0x0705, + 0x141C, + 0x0706, + 0x0C1C, + 0x0709, + 0x121C, + 0x070A, + 0x0A1C, + 0x0711, + 0x111C, + 0x0712, + 0x091C, + 0x0721, + 0x109C, + 0x0722, + 0x089C, + 0x0741, + 0x105C, + 0x0742, + 0x085C, + 0x0781, + 0x103C, + 0x0782, + 0x083C, + 0x080F, + 0x1E02, + 0x0817, + 0x1D02, + 0x081B, + 0x1B02, + 0x081D, + 0x1702, + 0x081E, + 0x0F02, + 0x0827, + 0x1C82, + 0x082B, + 0x1A82, + 0x082D, + 0x1682, + 0x082E, + 0x0E82, + 0x0833, + 0x1982, + 0x0835, + 0x1582, + 0x0836, + 0x0D82, + 0x0839, + 0x1382, + 0x083A, + 0x0B82, + 0x0847, + 0x1C42, + 0x084B, + 0x1A42, + 0x084D, + 0x1642, + 0x084E, + 0x0E42, + 0x0853, + 0x1942, + 0x0855, + 0x1542, + 0x0856, + 0x0D42, + 0x0859, + 0x1342, + 0x085A, + 0x0B42, + 0x0863, + 0x18C2, + 0x0865, + 0x14C2, + 0x0866, + 0x0CC2, + 0x0869, + 0x12C2, + 0x086A, + 0x0AC2, + 0x0871, + 0x11C2, + 0x0872, + 0x09C2, + 0x0887, + 0x1C22, + 0x088B, + 0x1A22, + 0x088D, + 0x1622, + 0x088E, + 0x0E22, + 0x0893, + 0x1922, + 0x0895, + 0x1522, + 0x0896, + 0x0D22, + 0x0899, + 0x1322, + 0x089A, + 0x0B22, + 0x08A3, + 0x18A2, + 0x08A5, + 0x14A2, + 0x08A6, + 0x0CA2, + 0x08A9, + 0x12A2, + 0x08AA, + 0x0AA2, + 0x08B1, + 0x11A2, + 0x08B2, + 0x09A2, + 0x08C3, + 0x1862, + 0x08C5, + 0x1462, + 0x08C6, + 0x0C62, + 0x08C9, + 0x1262, + 0x08CA, + 0x0A62, + 0x08D1, + 0x1162, + 0x08D2, + 0x0962, + 0x08E1, + 0x10E2, + 0x0907, + 0x1C12, + 0x090B, + 0x1A12, + 0x090D, + 0x1612, + 0x090E, + 0x0E12, + 0x0913, + 0x1912, + 0x0915, + 0x1512, + 0x0916, + 0x0D12, + 0x0919, + 0x1312, + 0x091A, + 0x0B12, + 0x0923, + 0x1892, + 0x0925, + 0x1492, + 0x0926, + 0x0C92, + 0x0929, + 0x1292, + 0x092A, + 0x0A92, + 0x0931, + 0x1192, + 0x0932, + 0x0992, + 0x0943, + 0x1852, + 0x0945, + 0x1452, + 0x0946, + 0x0C52, + 0x0949, + 0x1252, + 0x094A, + 0x0A52, + 0x0951, + 0x1152, + 0x0961, + 0x10D2, + 0x0983, + 0x1832, + 0x0985, + 0x1432, + 0x0986, + 0x0C32, + 0x0989, + 0x1232, + 0x098A, + 0x0A32, + 0x0991, + 0x1132, + 0x09A1, + 0x10B2, + 0x09C1, + 0x1072, + 0x0A07, + 0x1C0A, + 0x0A0B, + 0x1A0A, + 0x0A0D, + 0x160A, + 0x0A0E, + 0x0E0A, + 0x0A13, + 0x190A, + 0x0A15, + 0x150A, + 0x0A16, + 0x0D0A, + 0x0A19, + 0x130A, + 0x0A1A, + 0x0B0A, + 0x0A23, + 0x188A, + 0x0A25, + 0x148A, + 0x0A26, + 0x0C8A, + 0x0A29, + 0x128A, + 0x0A2A, + 0x0A8A, + 0x0A31, + 0x118A, + 0x0A43, + 0x184A, + 0x0A45, + 0x144A, + 0x0A46, + 0x0C4A, + 0x0A49, + 0x124A, + 0x0A51, + 0x114A, + 0x0A61, + 0x10CA, + 0x0A83, + 0x182A, + 0x0A85, + 0x142A, + 0x0A86, + 0x0C2A, + 0x0A89, + 0x122A, + 0x0A91, + 0x112A, + 0x0AA1, + 0x10AA, + 0x0AC1, + 0x106A, + 0x0B03, + 0x181A, + 0x0B05, + 0x141A, + 0x0B06, + 0x0C1A, + 0x0B09, + 0x121A, + 0x0B11, + 0x111A, + 0x0B21, + 0x109A, + 0x0B41, + 0x105A, + 0x0B81, + 0x103A, + 0x0C07, + 0x1C06, + 0x0C0B, + 0x1A06, + 0x0C0D, + 0x1606, + 0x0C0E, + 0x0E06, + 0x0C13, + 0x1906, + 0x0C15, + 0x1506, + 0x0C16, + 0x0D06, + 0x0C19, + 0x1306, + 0x0C23, + 0x1886, + 0x0C25, + 0x1486, + 0x0C26, + 0x0C86, + 0x0C29, + 0x1286, + 0x0C31, + 0x1186, + 0x0C43, + 0x1846, + 0x0C45, + 0x1446, + 0x0C49, + 0x1246, + 0x0C51, + 0x1146, + 0x0C61, + 0x10C6, + 0x0C83, + 0x1826, + 0x0C85, + 0x1426, + 0x0C89, + 0x1226, + 0x0C91, + 0x1126, + 0x0CA1, + 0x10A6, + 0x0CC1, + 0x1066, + 0x0D03, + 0x1816, + 0x0D05, + 0x1416, + 0x0D09, + 0x1216, + 0x0D11, + 0x1116, + 0x0D21, + 0x1096, + 0x0D41, + 0x1056, + 0x0D81, + 0x1036, + 0x0E03, + 0x180E, + 0x0E05, + 0x140E, + 0x0E09, + 0x120E, + 0x0E11, + 0x110E, + 0x0E21, + 0x108E, + 0x0E41, + 0x104E, + 0x0E81, + 0x102E, + 0x0F01, + 0x101E, + 0x100F, + 0x1E01, + 0x1017, + 0x1D01, + 0x101B, + 0x1B01, + 0x101D, + 0x1701, + 0x1027, + 0x1C81, + 0x102B, + 0x1A81, + 0x102D, + 0x1681, + 0x1033, + 0x1981, + 0x1035, + 0x1581, + 0x1039, + 0x1381, + 0x1047, + 0x1C41, + 0x104B, + 0x1A41, + 0x104D, + 0x1641, + 0x1053, + 0x1941, + 0x1055, + 0x1541, + 0x1059, + 0x1341, + 0x1063, + 0x18C1, + 0x1065, + 0x14C1, + 0x1069, + 0x12C1, + 0x1071, + 0x11C1, + 0x1087, + 0x1C21, + 0x108B, + 0x1A21, + 0x108D, + 0x1621, + 0x1093, + 0x1921, + 0x1095, + 0x1521, + 0x1099, + 0x1321, + 0x10A3, + 0x18A1, + 0x10A5, + 0x14A1, + 0x10A9, + 0x12A1, + 0x10B1, + 0x11A1, + 0x10C3, + 0x1861, + 0x10C5, + 0x1461, + 0x10C9, + 0x1261, + 0x10D1, + 0x1161, + 0x1107, + 0x1C11, + 0x110B, + 0x1A11, + 0x110D, + 0x1611, + 0x1113, + 0x1911, + 0x1115, + 0x1511, + 0x1119, + 0x1311, + 0x1123, + 0x1891, + 0x1125, + 0x1491, + 0x1129, + 0x1291, + 0x1131, + 0x1191, + 0x1143, + 0x1851, + 0x1145, + 0x1451, + 0x1149, + 0x1251, + 0x1183, + 0x1831, + 0x1185, + 0x1431, + 0x1189, + 0x1231, + 0x1207, + 0x1C09, + 0x120B, + 0x1A09, + 0x120D, + 0x1609, + 0x1213, + 0x1909, + 0x1215, + 0x1509, + 0x1219, + 0x1309, + 0x1223, + 0x1889, + 0x1225, + 0x1489, + 0x1229, + 0x1289, + 0x1243, + 0x1849, + 0x1245, + 0x1449, + 0x1283, + 0x1829, + 0x1285, + 0x1429, + 0x1303, + 0x1819, + 0x1305, + 0x1419, + 0x1407, + 0x1C05, + 0x140B, + 0x1A05, + 0x140D, + 0x1605, + 0x1413, + 0x1905, + 0x1415, + 0x1505, + 0x1423, + 0x1885, + 0x1425, + 0x1485, + 0x1443, + 0x1845, + 0x1483, + 0x1825, + 0x1503, + 0x1815, + 0x1603, + 0x180D, + 0x1807, + 0x1C03, + 0x180B, + 0x1A03, + 0x1813, + 0x1903, + 0x1823, + 0x1883, + 0x1843, + 0x1445, + 0x1249, + 0x1151, + 0x10E1, + 0x0C46, + 0x0A4A, + 0x0952, + 0x08E2, + 0x064C, + 0x0554, + 0x04E4, + 0x0358, + 0x02E8, + 0x01F0, + 0x0003, + 0x1800, + 0x0005, + 0x1400, + 0x0006, + 0x0C00, + 0x0009, + 0x1200, + 0x000A, + 0x0A00, + 0x000C, + 0x0600, + 0x0011, + 0x1100, + 0x0012, + 0x0900, + 0x0014, + 0x0500, + 0x0018, + 0x0300, + 0x0021, + 0x1080, + 0x0022, + 0x0880, + 0x0024, + 0x0480, + 0x0028, + 0x0280, + 0x0030, + 0x0180, + 0x0041, + 0x1040, + 0x0042, + 0x0840, + 0x0044, + 0x0440, + 0x0048, + 0x0240, + 0x0050, + 0x0140, + 0x0060, + 0x00C0, + 0x0081, + 0x1020, + 0x0082, + 0x0820, + 0x0084, + 0x0420, + 0x0088, + 0x0220, + 0x0090, + 0x0120, + 0x0101, + 0x1010, + 0x0102, + 0x0810, + 0x0104, + 0x0410, + 0x0108, + 0x0210, + 0x0201, + 0x1008, + 0x0202, + 0x0808, + 0x0204, + 0x0408, + 0x0401, + 0x1004, + 0x0402, + 0x0804, + 0x0801, + 0x1002, + 0x1001, + 0x0802, + 0x0404, + 0x0208, + 0x0110, + 0x00A0, +] + +BAR_TABLE = [ + (7, 2), + (4, 3), + (1, 10), + (0, 0), + (9, 12), + (2, 8), + (5, 5), + (6, 11), + (8, 9), + (3, 1), + (0, 1), + (5, 12), + (2, 5), + (1, 8), + (4, 4), + (9, 11), + (6, 3), + (8, 10), + (3, 9), + (7, 6), + (5, 11), + (1, 4), + (8, 5), + (2, 12), + (9, 10), + (0, 2), + (7, 1), + (6, 7), + (3, 6), + (4, 9), + (0, 3), + (8, 6), + (6, 4), + (2, 7), + (1, 1), + (9, 9), + (7, 10), + (5, 2), + (4, 0), + (3, 8), + (6, 2), + (0, 4), + (8, 11), + (1, 0), + (9, 8), + (3, 12), + (2, 6), + (7, 7), + (5, 1), + (4, 10), + (1, 12), + (6, 9), + (7, 3), + (8, 0), + (5, 8), + (9, 7), + (4, 6), + (2, 10), + (3, 4), + (0, 5), + (8, 4), + (5, 7), + (7, 11), + (1, 9), + (6, 0), + (9, 6), + (0, 6), + (4, 8), + (2, 1), + (3, 2), + (5, 9), + (8, 12), + (4, 11), + (6, 1), + (9, 5), + (7, 4), + (3, 3), + (1, 2), + (0, 7), + (2, 0), + (1, 3), + (4, 1), + (6, 10), + (3, 5), + (8, 7), + (9, 4), + (2, 11), + (5, 6), + (0, 8), + (7, 12), + (4, 2), + (8, 1), + (5, 10), + (3, 0), + (9, 3), + (0, 9), + (6, 5), + (2, 4), + (7, 8), + (1, 7), + (5, 0), + (4, 5), + (2, 3), + (0, 10), + (6, 12), + (9, 2), + (3, 11), + (1, 6), + (8, 8), + (7, 9), + (5, 4), + (0, 11), + (1, 5), + (2, 2), + (9, 1), + (4, 12), + (8, 3), + (6, 6), + (7, 0), + (3, 7), + (4, 7), + (7, 5), + (0, 12), + (1, 11), + (2, 9), + (9, 0), + (6, 8), + (5, 3), + (3, 10), + (8, 2), +] diff --git a/labels.typ b/labels.typ new file mode 100644 index 0000000..92168bf --- /dev/null +++ b/labels.typ @@ -0,0 +1,34 @@ +#{ + set page("us-letter", margin: 0em) + + import "common.typ" + + let options = json("options.json") + let cards = options.cards + let args = options.args + + let printer_offset = 1in/16 + + let label_position(idx) = { + let offset_idx = idx + args.skip + let col = calc.rem(offset_idx, 3) + let row = calc.rem(calc.floor(offset_idx / 3), 10) + let x_pos = 3in/16 + 2.75in * col + let y_pos = printer_offset + 1in/2 + 1in * row + (x_pos, y_pos) + } + + + for (idx, card) in cards.enumerate() { + if idx != 0 and calc.rem(idx + args.skip, 30) == 0 { + pagebreak() + } + let (dx, dy) = label_position(idx) + place( + top + left, + dx: dx, + dy: dy, + common.address_block(2in + 5in/8, 7in/8, card) + ) + } +}