From 33153108a2ade5d4f0921f1d26dda5d7387eba81 Mon Sep 17 00:00:00 2001 From: Thomas Hallock Date: Tue, 9 Sep 2025 17:01:50 -0500 Subject: [PATCH] feat: add single card template for PNG/SVG output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create templates/single-card.typ for individual card rendering - Includes local copy of create-colored-numeral function - Supports transparent backgrounds and exact card dimensions - Used by PNG/SVG generation to avoid PDF intermediate 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- README.md | 16 +- docs/images/cutting-registration-front.png | Bin 33998 -> 57276 bytes docs/images/svg/basic-123_back.svg | 23 ++ docs/images/svg/basic-123_front.svg | 176 +++++++++++ docs/images/svg/basic-7_back.svg | 15 + docs/images/svg/basic-7_front.svg | 80 +++++ docs/images/svg/card_000_back.svg | 23 ++ docs/images/svg/card_000_front.svg | 176 +++++++++++ docs/images/svg/circle-25_back.svg | 19 ++ docs/images/svg/circle-25_front.svg | 128 ++++++++ docs/images/svg/diamond-25_back.svg | 19 ++ docs/images/svg/diamond-25_front.svg | 128 ++++++++ docs/images/svg/heaven-earth-78_back.svg | 19 ++ docs/images/svg/heaven-earth-78_front.svg | 128 ++++++++ docs/images/svg/minimal-42_back.svg | 19 ++ docs/images/svg/minimal-42_front.svg | 92 ++++++ docs/images/svg/place-value-456_back.svg | 31 ++ docs/images/svg/place-value-456_front.svg | 176 +++++++++++ docs/images/svg/square-25_back.svg | 19 ++ docs/images/svg/square-25_front.svg | 98 ++++++ src/generate.py | 351 +++++++++++++++++---- src/generate_examples.py | 175 +++++++--- templates/single-card.typ | 113 +++++++ 23 files changed, 1898 insertions(+), 126 deletions(-) create mode 100644 docs/images/svg/basic-123_back.svg create mode 100644 docs/images/svg/basic-123_front.svg create mode 100644 docs/images/svg/basic-7_back.svg create mode 100644 docs/images/svg/basic-7_front.svg create mode 100644 docs/images/svg/card_000_back.svg create mode 100644 docs/images/svg/card_000_front.svg create mode 100644 docs/images/svg/circle-25_back.svg create mode 100644 docs/images/svg/circle-25_front.svg create mode 100644 docs/images/svg/diamond-25_back.svg create mode 100644 docs/images/svg/diamond-25_front.svg create mode 100644 docs/images/svg/heaven-earth-78_back.svg create mode 100644 docs/images/svg/heaven-earth-78_front.svg create mode 100644 docs/images/svg/minimal-42_back.svg create mode 100644 docs/images/svg/minimal-42_front.svg create mode 100644 docs/images/svg/place-value-456_back.svg create mode 100644 docs/images/svg/place-value-456_front.svg create mode 100644 docs/images/svg/square-25_back.svg create mode 100644 docs/images/svg/square-25_front.svg create mode 100644 templates/single-card.typ diff --git a/README.md b/README.md index 708bcf2a..b833a597 100644 --- a/README.md +++ b/README.md @@ -7,19 +7,19 @@ A vector-based PDF flashcard generator for learning soroban (Japanese abacus) nu @@ -30,15 +30,15 @@ A vector-based PDF flashcard generator for learning soroban (Japanese abacus) nu
-Soroban showing 123
+Soroban showing 123
Soroban (Front)
-Numeral 123
+Numeral 123
Numeral (Back)
-Place-value colored 456
+Place-value colored 456
Place-Value Colors
-Colored numeral 456
+Colored numeral 456
Colored Numerals
@@ -63,7 +63,7 @@ A vector-based PDF flashcard generator for learning soroban (Japanese abacus) nu Skip Counting by 5s diff --git a/docs/images/cutting-registration-front.png b/docs/images/cutting-registration-front.png index a089704cd1f163a10e36f1f06230604c67fd24e6..2e128b37aaf80e52a36291ea74159d5dbdab2de4 100644 GIT binary patch literal 57276 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 literal 33998 zcmeFa2{e}L`!;+lMX9KiMx=qF2q`k|6qQmkqyd%EAPSjzq}^_iBq=iPCXq;*%&M&@ zL!v^aGKI{U!t=bxb>ENLZ~wi2>;3-k`>t<&YkhmI-Sa&6c-_~1UFUh6$9bHWGn+Q* zj2}B=EJab{*Xn8;P!#U~MUA%OAAv17w;WCJG3JP_sXaxBOeFvFyjswuK~dt=T5T;u z=d!OAEbHRlU7Qa6OhWRhjkrTQa*7v)koT-I~qTVZuZAboG94b9eru6NF z;|u-AvNSh&Y496J@E9-JHO9~|KYYX}MXjw%4Q`K5jk=TBV)8iJHpYF}-DzL{tL-Uf>5?-;Lx zHhX;@g@3aI@XuKh{39%jf27R*r*E55@iWqyhoUq)@VN9TKfnEd<>X%;;MZpV^7Owv z^Z&_{y$*;3cW{1h%iyHH9+&yx`%^*BYMp76=h=m3SA@KR_iall_LHO5^+s)7dc;Hl zUH`95`=yb;zRQ1lt3OZq`RyOy<^LNe|Lp;8&iTymRiHlbb&R=3N0!L!iInH>JU6r3 z$SC~RmjCVif9f-OSaVN4Zm@Le(!%0mhraet)=JHal+>J3>z?*c&o^w?FnRLijUzXu zX4ZTk7?^zKuZM>|Oqi|mz^?99N5@gC5t?hKiHVsf+135I>Qvm$l*_vFa9G%wNay1k z+4~YxoZ8G%99v3Cwys~le$}eyZ{EDQw%R8@RJrB|C1q!oA8Ptwf8m=qn^vt_wSN8U zl9H0LhliZnDqB3~i}QP*p6g}W+FFs){QmCD88gnVxgHi4w*JbJ@!r+7IRS1xUk@BQ zH1L5n^dVyCt5scH-Gs4TyA|*hWo52CUrWXLCy1&(IoAAMoS*$kjQ!`shuwW0H69)w zBC=+o%WcC?c$en3Iqx!Ff776=p`aj5*0bh??~c~dTK8}co6X*@NUm~HKxD#%2`bXm z*;m!o)z3}}6ojkI^j$MBFkqiFllSS{o9k`MpZKSkA7BsmM;pE}J^p#rzOXZA&b+&0 zvV8e+KCL%}g@qLrJB^KdzKo2CiLt3je)smRyo$;Uzn8sjRc$?T)LAX1kctczOI1{C z_Uyw44kR6GHa0SPcU@3QTDrNlwe`yve(%VnB$w`{clEItnu*!n%^yT$w!SGU+Ol(J zMO9T*aq+jCfcYH9*gWuABrB^Qm%639JLYVFvNAJ*O7jRxAHJ?{OTd)j^;@-n_Z6F=|gj(8uSL)Fs13Q+%@CHag(V;)zY}ZtJdnX<}rwGu=Ju=FK|x z&;Ty8(y7%UW0I7a#fxOOUK!nuU_rs)cf=V-wE;kHZ~g< z1lS0U6pvW2V8O*4%hu;B_yk42wzkIIzRlm|n=}xZAE6=UyT(sc?W*p)*2=VIRjrN@ z3L9*0Eb2|~yFSgxL4{#4~Ho(TRDcU^Pn9^LH_NFIxMD^B?{(?YX8ra(BZWZ!A)^H^z{@*qh6o zWTB$krEBC&zmfT0`(#LQ*vEaS}hsDT&em{o&9i=u5G%&w6oo4 ztUP_nrc}-2J2N~zK0P}*YC7|<=7uFWNHi>Q+mj=IDof)CwtK_Msxw);wS><3`T3nc zKecO18s_Q}2XoKxz^^tCk6s_51RK~YtyN0PyL`+70gJ%_&QElAw+^8E4R$6dPWlN{0O zgJ-lQbk5IeY;L|28>@Qs^Iun&9a^(y&Dn;=I=1tf%F0S-Rdpq$%X$lT%2NyG2jnCq zBsi;9e*73R@Vw=0Lz3^gbFrq0^?#k292gh~1At2{%pZv3$r4bg>a2ThTa{i>QnEr- zb%%+GQ)_vWx5mx#fzR6?S?5^F+opBD&&;bqhI~w+@q&WOm6R&eJ>2>`>*9o^%pxzmdi^?nN7BUN3Rv)2B~2 zy>FRwX0l{`eptxHs4Dla4Q)On+l???Lu}K7H%4LVxqbfY%>4D2h1)%Eo)Q$ZDSHr| zmZrwGWvKj7#UdbICW0b+xSgSGrU1WZ!BJlW)`(=?Ht{H8E(+%|(Lb8x=IcB`8te zVANTWpfR^>>4S<6ey@)`XGpB#`GpZ=r>3V~e`uipBKVVWiYMvwvJ<1Pa?i^Nx2MlL z**VC)xI&de0ckJ(%1s0AjWpp!z&qRfh^Q6pze1D@G99i;vD0j=dbVvG1tDAG^ zn`8;_KIP<_-rbt;xls9zj!ukYTjk*!W4$CUg(Q}xx%KX`Hx8I0Xcu|PB0(*EV69bvPu!YNL};nH4+;AIr?mN z8;3nm#bM+1vg^#*b@A+`cvm;K6XGj82m4<5&h(hZ*S^vWj?DtDM$F^eX4qg`TiXQz zrT2C{$1vNl!8d>>GW%U|@wjp0tV{0A3CQ`@)APQ#*!^3f&dlXD4)U7SPN|tUu9&$t zJ{Qz*@2zm+bjotx3XG!G>AJYMSXnjnruU|C1`9auL){-L!g!_3s$a8xh0YAtN@^UP zKwXls{V>A3Hg4a4!n40%JFnl)tQaAsB%9j)Oh)5)fd;3XGoBjc@0vT#+0*kSfbzei zjC~3$m5IV(gPf?4~jMfFV_eMust21xs#pjLXWEUW)@ettf1h?Ljb zM^||$slxyxVw?^!;(d9a7I^j~5}zHc@G^lqUIh@o>FpnX{9)!&7r3Lfo^4lnZ8ea^ zQF+p`NW;B}rMTGl@84k-UZxknfB(L$Ol9fPPnXEeHP2v{vwEFaj}Lu_i+9b0MS^dY zTe0E`@wJp@&J%}51DUDKjg2my&s(c9#Av^5OH4F^gA{9}5WNK-?rs-hZ|`aO*h|1j zu-=PCjCaaQV%drnTj9yyz3U!VHO(i>Ao`L5FM*L+^F#6+9UT6u=>RyeV#^+&-5%{q zD9wayU&F&y=|laTJDAM5#Ai`n*M#i2=AMU&%naZ4^M_ox%Z;XJwj}q!!QhL-4v6w{ z4|?|VN8_@(VJ}2cMZ32#9*e-jTLz+QV#1qpf`}`mr~>JNp9dDiiT~}#HLssP^#9Zy zc3>3sTmRR@0`>mdfVZys!Fu&Y`WnYtNcUw1XkRacA%9%yddD#Q$eulWRKm=F4G!^< z8>g?CH_4?dZ+TIp!`{7n{Wadf>hDaln?J)h`5+mD;<06AWgj2g+QL$;h^Q=2vTrL- zDypn(T}O9Hl(Csv__~`LBh+_V>s!MC&7C{9t3J=(m%el2`UL^FA8cS&yX7KjX<4&W z=h|$4-Fctil40T1FvWKbXQ+ykR&8(W>+5^s#0d<5W2@k2OcX`Q`Hq#h+Ixe3 z-L@9%DHvYW1$MKMwyaKc8#QC{2IYdSbrF6GoP( z{k+VK@PmwjL%_K-N%_wBnM&^OM($1s@?S(+wdo>U%F2~1{lqlbS+aO8kHL znqxjbIa(7H;kj+gmVklR`xVP7Dm=ctnKRc&Q;3(n8q@4Rh=NuADKXE!)|58($Bd!v zFcqQ7_Mw33JXFv^*@JWaBVZ%)d7RIsV7~qDh7_6Oj$ojEfGfi-%2-zD5##w>M( zzwWs4unNm?v0kk=epl#(~y{n0s{%~${0@vk5XlR6OT&}2C{^aPM#OQm2tE&^XW|I(3t9I2SdNA3L6%qtPVcGlVO*Fwm0z z@mAq)dot}Api1`C7F_zdr}laA&pmoq4$NS%B)UDwbr`aeH@UY{iGiy4(Vp6=`t!%v zvCDq`C{|HX0Van%><0*rkBMo5OVkp=Lu$j8o)zhv(bCcqr5`cD+rrt|nTrea8&`5DA?=+L2^E6KCAFT0pii?~u%w5_Ej&v*5dcSW!FB}(dtTU%S_TFL{KicX*I zpF-Fh@!0)}Dspn|@3-6Tl@DIL{n6Kol*hVk{&KwT!M>Lom5EmRLcE9z^==xhmXC&Y z0Dk|wajJtyzbs=aFE4LU66JtMtlTzy=FFKdU%Yr9x3gIu6WRH=mErd7ZUa3nc_GWK zO!d>TTbb0TjuHi*6q*9Ored8zqIV%EqdFU7#Y0EE#KV)1gTcOO28_+%|; zh+Uu)V;sA+qcJLC2KGi8nzyQqh`e=cpM^zr1iLXJKU{6QiOH3L5ZjEQ9!!~`gCdmI z+d8LI*d;9nuyoWg5e0BLaF!kRU#zc_X(w=J2S^3Z0E?sFj7vKo58embU;+>275sE( z9stbW>5KmrE>2GE#mkq?&CQ?}!t(Zoji%<$3&}$anUbFF>F!>B07KBZJXpbM>f&vR zii!?{eH|u16bOCtdA%+kfjuKXK_P&u@R_<;#lHTW0uP2EA0|9ne)#ZVEumSe&g~CB zm|bU3RbF2?m@?%c6AKX~xqx~L+|KVnpYX&o9b){Ka} zy(8Hn8X*EMi(QQl$+%r_@0~k$Y%7ujQ_L5N`n6SMRILEYws;S$#Bd2(LZK?hns9&6 z4u0y9*RFjgfmCY83z#*)p~vL&Jm(`Okh!?-=I(^8Dq%MJ_v=O#-7&uV{q3pM68;~|S!xJ&3a{(j ziHRYB|2Il1666x7lG$pmioLr+al}x9leOgCZ4VE2O74`I3SwuYAZE|=4SIU>0&?o> z>uE};uPcHxn9!S*aXUHrJ1kIqT2I-RFJA`xyX?g}MtKc3fB6!m!D3_6q4+dM0QDTf z8uvgn8mBeDE#o?t@&^b2l-q+Bw`9kYdw1>}vb)iAhp-u@rlqx^?_jk};>}vX?sVeS z#hRtL!Z1ALoHs-Odb0lo47#!vI{V>~PruQ$LhwG|yPph3_qT5VV>E*Sa)!%bEK^ZA zNZ<9kdkaknP}xyjE(5P-^jKwsZo~L#;43cheHsRLL36lRkeew}mHPe4sbMgBc6~UPFZuAQVfd=6|KuXg>N~=g!}lMN0`&bSL8num zBS14-N?pjY9J|6`N+o`^nqZ{Y7J!1od^;ijrKM%)UA(8!g93$+yjup*JHD{nPieS= zEZw;hG-8&B`QRr(R?zw?P!YLK)KHU2MpH}6c<;!Bgro53akJqk^f8eFrA^MyUOAOl z%gt@z(czDx>h4>u_3bPzE%UA}liV138{WLRd8J*g?$LA~K}~gx&AB?~zjb#T85@VM zIJz500f%Pw%o9_0e*(+j)6*l)j|)21y$al~*i;7S+8KjzGCyo3!ppQHpPnIr|Krpt zpN6Ecl}>SSaW>A*PZAP3Foa%Tk#$O=f*yc?0}2MgFhNl8!@GCnFz4`(?d|P26T?pq zqy;#(+GG*DMjpb)ps_R`a|_q0aH?9|*Rl)rL-~UPTUxuHHWo#Jm@ohvvyP`d2vM%I zNnEJ9hYG(pnz|uGY09AIO>3nA07WGwrG4_jX!^Nx=WyAAK=ik;L2J)F9}!t&W8?I$ z{FR5^$GWsvYyBCX3%7S=@%Hy0KhE@f378%LGDe&V7iVY+QNP}vo+a?R;2=)AE&$=P zHF00w%C^)krlu|(pI3{$02w3fPnhKi6Y6aQeO}h|XQ;`*JXfSRfmBNB&Egw9I#qUp z<}@JunbW3aSHwR6Q-rv^o>6O{$e0;5aeXg=o>kca` zE4aX&JHtluL|U2^u8}x3VfMBR&!OsSi$jO*j3jQgE_D+XsSP9P@^b2bGbphrb8vK= zfPjOKT9=O;1N^Z)0gm&5146D|b!;gkP?`xk^eCw@Ou_?KYG3J3o;$XaXT8PjHI$`-QC?Wv(bK}C%CRPKAux2%WAX)aN>mn1gjaM zba=*ac+v@?9N7%;6#yCz^|{k^cr^=wCUTJ%!NwUgW`JdPg`Y&I0LB6{vZXTZ5+XS5;e!X|87vJ3XSD31gpG$2A{i`>C$d5Wzr@7g`1|+oBO$V**#_AR zfN(C;vM(KXC3E51H$xMX-rnBj%a*BqdYbg{`Nl96MESjR2$i~pHPqNzSNiyF&yN!a~1OkaG zY_hr0glXi5kc!iz(-3<#BqR^cdU=w63?N0dKMCj9n9hJWOs3+hq#u(&P<87Z_L2&l zvS}~Mb3u%ovVc|D9Da~-yn2uS$;mG$V8SyJRJ-Lfr^`JI|B*nrsA$B{Iu`5n-VNV~ zcJd@8lGzp1IqbOCWhyd3q?eU~$1oKn9IeVg0Iw+diXR)pQn^RUaf3tLePRxWzC7hz z(LUq-uol33ka z*?QKHyh27lzI`u>sAt>D2g~%GIvQS`QZ;0Q_bSAT^OxIJIuK6E8x3Ue;dd(iL;qLy)@OuUWgYY5rRvZ{FPpI3fo{6JDlAS zX||L-q>%#)$RRT`bGvO?V2d#p|(S|WWRat!E z5g;8F1tvRp>=xq1&i)QR)*z1k?BWWGfmq|$^us$eeUx)ndwq!Qt)j+PuU<`-+$e)o zf~Dn+Y2hU>DK#C=ppy6Ox!#+_(`SH884Qk_n;SO!m&Ys0%OmS>5$6~h8{_=tRqjPA zA0ejtEe3Ib$3PDr_(yI)pwIGt=~qA>JjNv6rwMyrs`pioq_Y%wNIo(5BVS7r#QDiX z;+SWZ@eY4aJOHVDc>g|m4KDsnT2@y7rU4VugSmwjT$J6i3w^i#vP=??4;Vdge{OCm zXtVQu-Fa^-E5n%#2vS!FgvSXAHgFCgrm_GVkR(_`U`pV+z$;f$RB(BTi+T&}?TypY z(p=eH|(+%ehvvw~94ETJq zH15$ON2KbW|1my*PIi!>e4s6(4Lx>ti{cCq&QNca=Riv}i;nA+tV`tNxdVu$B0Rqz*svk*kMT3r*?oJG1T>>>M@RSf)%Y#9F4?qYOBaZQS34~( zg3@Oxg2GQvS8t#+8`Ez4YOAbV88)(g+P`KE;Ml8;?yd@^s%4OW1$j!*4;1tb{?MxYkqK7Ou)R|FFfIvVG4_Uzt$4J_r|yNmg~VK}mU zM4TKQRi;wweglb$DGe6LTYNuwsl$i6Uz_2&ERc?ubmxKmV3Y!Ig5KgKh}ySoDeCKc z(!~?G-`CF%UJAxTn&12U`SU;I2HFx!6%}jUfgBRIFMXp!D(4zsJS3>f7I5v-qp7nxXh)KL11|RB#fwVp zhAT8NG^|Y*KBEo6!I7ro=>3YxbO`UCV@Z2^M*zx%tui!ax+Bf?82qB2n)swMsqF8~ zFvSoez!9S3VIz;(P{QQ^Ac*SEi>l4=T?015j5XAq!$k5;P4DiEA3wg)K?N?>U}3{( z0-_mMV{Z?m%)pv(ck@9XKr=`&I?4{k`<_1!nSe&`L~|$<(7XLz4M=dO_mmx4Zc~0c zE{+|eV{B;X;N*lPwH&|q-vavQKLUE`yp1TLp{KQ!?D`$0l2dzr#aK8lZzzkgqz-&<_xiw+6r`&GH&JkF=X`S)OF z=~#Y78p7{j07Y0lXaX(|`0p@8Y;R&>vRqZQyrje#%n#;O%0Y@(>)t;^@+IM)evRa_ zccRJv7|35dew$GQEbRSW4GD!w+>V8^G)Z7+X6A`F1R4Qojw&nDp}mkuGG7>ymySpt z&;{a*pBb_=7^#yJ&uE{YrEJ&nISXjQsnW#?%mqpZniD*QY|ekptj5};0ERM7uwQ(H^{D^ z>zt(yGyJ+&qe2DuAcdz4&2Evrp@{3$^%Ne`=VgaP(_R`=H29V!?bQ314<`T>%Iz7g zf>*c|lkc;5g;hHw85Y@4*Eww+fs|V8(D=3t*N%f>m5r_C2l5BmUa8XHkwkQj6ijMvfeb zv3SI35}ql(_T&g^eoSg=`N~I`d&=L9`%jXCpL*SYQbl6i{~ta%eCJ;iDcSVCYpbYC zKRJu~j+OsWEBuSL0-g6iftG)UFT=@=UrzRNKf7WCwJ#8ojKNROVHeZ~i!_Fc!06e! zKM%>EE}i2LK}w4)Yv8V}2b5D^L#hUEjoh3{wX>l4;ap@;hiP!SG@NAc z*la&A?B&HTmm`c*wy%fSM1Z7S+R>O4B+2-_ZQW~TR5y|vOZ`g}6l9b2}{x;mG#a7^ESgdd`pq&q|=OTYYI`tWZr;FnkN zOLpm>M$@k+EBt1uG`K1q8lX5uBG1jV?s|s)XceZ!HHbLkin(X0GNtMHM*8$Z&h9jpx*!(e-kI}SHsk9o-0^>ZqGaYy{Qrg7 zzY6{7_r`q*lK+=A*^@5j?VCLUN_ZZ4k^|uW9z%4EI19pxC?3r*!9hX$!J;BdN=Gj+ zl`kPz+Xs)16s1)`_=b6t@P$mH9|6fq$RV6NK0}VNVloC6OO6(OLlcF32jrbUq6OHK z3enM;OOa8BVTp^67o+LRMp}%F)Ma#Z4q+rmdBbT-PC5hmh0!qeHhaa9YUqps^56Wq z3Jx2&_>E&Av&*prXX^6h&7TlfBW8fS0qT$L-d-Rj^AyL${<=FdJS!F=J+dzvI03nl zoZc*1^K|GY0tepyUC$v*OQ}fa@`ge5Rv9Hl#o{+_j(`s|x?^0&_C)9gyd~LYQZ`Jz zCFUc*Kynn!8pvBj1A?=FJ^nMna+qvB<9i{oUT#{3)f(!Gn`=+EHfe?`!*jZ2L}W!R{{go`nMyr`v)C)r!3MXRT5$rqy zr;2E#^Z5$%o3XL6DJd(Fv@b7D1v>7PCVd=>7bm;*UL+HG)GkNT1&|p5jjX>e2`3_k zsnV)i-FJJ$UbuMYDBlSW=M+>9om%4|*(b*NmSUfAwL6g;%!T{Ix@>7#8uV)MS>by4EbvWzMy+^O77QfM$^ya$a(vOScWY%WQi(J<8`yuT~ z%e_rxpB}N;_igNl^;bTIeDvf54JCUXKBTdBfkMPuQNN6sjpYUD8n5$HO#?D+syJ0Iy-MgWRjY*Cg#4zIeR29Q{9yB5dqQ~mzkCidA5FHB@3ch-&@-ttsEj&mIOe*r4_4(r@$Qj3h7}KUBo(M=B zVX-840n{v^Q??>Td9{`~&~ASZn9ADNOk|w&CNOuAb5jN*DoL8TNwHpnK8I3J5*a>F z;_yc9fjjM?siB)AeytNvLYmXwIeB>7Z=K*n$u`?dBxJHPw&)n)zv%+~m2waB)~4I# zBO-OLva-_pMhrq04sLQ^`HndMePwB6;8B`UK*FMI(j(US0m z`899f1L!ffZrP$hYsM>N(eP)tZ{MCaW5&V5hc`qOAz*Osue&0^KKlta7ezPLJp-S? zTVNVjmW`@@4SfQP7P19GywGy7dP=PX*e*Bq7IZ-@-&T?^H*DkMM~{$qGBPxLvIVbf zlxNlvWSO2mefsw8TWElYLIIr8CJ_ZWxeFczNnp7G*DX_2e4<6EYQthL3Ivx^kRG)y zvp^c!h1hKmn@SS)+`M@coC2(I&+P4J@okbpMbb42MxAA*fDDFNAopza+`timee(L@wZttu%Iffa+d$==~E}w71Y8 zGc*ti@5IgYC;EJ8j(z?*WKMVFu*j#3S!7@BVypQ`Ioj0GpBU-o@zd&6$hOQ`!Oi6S zt*M#rayrV=ASCO{%IjjaV=(6G{X!e?jml;|$(E?lT6#;yj^SexO`!>k0eM4z;+Uzg zr2hyednu_W^e5SN`2fA;^!Y~mXdjWO*Dla^ySBiRwDO+wu0P#$>2^!GIWt?1i}$gV z?+_85y>YKjOma%f>KN*DBxVLo3B6GRfsd~tCxl)VlrVY>66fMOqjV@ls6 z;Or;D?=8L+ZweJqm@fD2dwrKzI)ie}+euxsh0jL)8&SwiT&JY?idvUKU0rzFH!jzN&Zs{JZzicE$ z;e`S)(r9xkp-8uq>P33vuX}{oI_*Do9wq}E0*^6T+G zuXOK1|KrJ8wY3~`>vmAM>b3e}ZeN!!A4Tc!(p=gpN^bkhz0siDn@Bw)w+AV-rTlaN znu$mfLe>Pg8Fuxm-3%O}`USEgaIYduc9eiRKu&_xDj-)o7EDAbeuA!*-SK1-lz07| zd#y%b$hCp>M?wSB=|)lG*EaXQPp3dufS(C{&LjRAljAY+76@kL242Hjym(KQIp;;>|VG)mtx-5eT(L9MTl$ z&Y$Q&fN?b?=B9=OR$=}}}ff(dX9u*_k=;&OLF@5{)T^(@~Q$z9CNUkD@IW$wu z1@sV18B+DOJtKrh1?!P&gIl-i!OvFpRl^^H)7(t^D;|=u2e5$b^f@aBh6x!h_clHc zo=EYxjo*;KfytcBUXS8C_(<2rxAUX;;3f;u9Vo&ne)kT}a%1ly$S5YA(YCdX-@_Xz z4u%J?2x%*aj3ah-_n|#OmPS3E7w6f6p++7@RN2l_iIoFl3nY-qII4Ebf`GLP0+5{w z&NH`zp1Q1T8{=Z7W=6kSd!h+NHg?8T?FJ=-W2j;Y9Wg)j%^p5%I6csFC^GxUL)akcY?9k726(G|f{?~{eD=)lk^l-|Kt zA5Y?5^IYC$dOxnI8&bqvQ}074s8e`yACc471{vomo~k?P?kAGDrN>zwckFe$u1Ot;(ME&^Ms0 z19^opk!AN3eh@B*_Jf%tObiS#qTZF3n#zcjqv8?d-`TTcdncgHtlbGE0PuW#e5kp+ z?jDB2kfRRPOWsSBLzlB9+7JxOCE5EA9ylC1(vBL>gsr)YN1~&>-~q~0T@vAtu3m9M z?h^1_xFYHUh-7C~LN9{sThrBvyaSQs@5`IvL|nmzb?er__g?GiAPIU9q|ixbku0PN zh@e0dv+1IuOQfZJicdla+0)(KziSq8U%IqBe`3Eg9C49kg!`n9f*2m5iD)Z=*tKxk z+DrC9$SYU_GoZau+s;_Rh|)8mKVgUbjZ1!UQH4^BsV|kLKhdu2S_RrgO{h~QpKwvJ zofi@(p_pk&?HulwZ}p0_%PJ2Bhex|C)uke|6X~4WKt9n_7b#={Nk(rW{YkbQ>Y=xc zm5}5f({KBN*>Z^f#4)2RN7GxbS1Ic``+aGUaT$uF2g~}e^ktH1%nGvBo4-zMH*T(r(B7EXk@t&c#Q3S=RJ5RLl zI`muMe+PEWf|c<`g7)x7M>rpNi?6Z~F7?5G{`sd{2Cr8(Hb7+JS|4oHI@AdlLp%qe z;wqDQJli^6ib4E1h&*<;2&+THOp4m1jrR-bHfovJO+-Ls=KN_?#74Bg`VL!c&Q!O? zn^!`igvow<=(ucGG$}2TAspB}!nvR&`07I{>FJICu;*M>@26oc06EGce@D7|Kcb?1 z9nXn(tm^e6}tNU`?n_S&O#B}Y<_BWEh>SbLfVi2Y;5u^r!}U{ zo{bXJ{^%`H_cN>y94LjuEiS$k00(gE)~!d-v%GzKai|WvFUy*%tEe1EtXbImSFyA<&x(tFr)gkuY3A++7+&rg88o+>M|ztQ1)bX0g5ywiuF1JJ#M ztGPjHB7P7hg7}J#hH*Agw?Il?U#+|N!_Y#E-mV=;)l2w)X=`KkJqHPIuzmYV_>{k> z)2BN+IzYZcQK&k&3#ytk+SeXG{%{7SO$JiB1;KhY5R%u{K%xLSI`Jr9fQYzW=-j#A z-3J*p*?;B^q3D(g|5gw@5@p%-E=`c-g1Ty!)lu)Frj!tJVF@->7`$?Y<$ zptjZ$$}S|VTmpOzF`wah@;l?p(%c>sJDKS|nIx4+K5HWL1=|LfR&dS!!^U9~E#;~J zsSKj1%pE~Thi&=pWdmGC@?M6Hp@`9s*s+>6>*A7j!_i&j{#wQ`Q;GgW>`Hr~&C8wW z5R3{6PV(UfXhNG^7>}Xp%>6P|R%mfQ6TfLrbNO-HlzwEgNNZLJ|8PRbr;wZ)9>v@^ zbND7FC!SI;%kZbAHs zczx@VClICMdvbDF+Q_Zd$SIyWbqax%Pn|cUQ;>@W`|8s{kw0DH_U_~=U{J?}Tf>grRSg5I6mob_EvQG*Zh5wC`*z6%3l=R}R6dtVCrM)jSeP8e zORS+QfV$<+KcA|>!AhmVW1y`gV!jYP-aH5mu(-PzyrWAONvmVd#E@W=o8=PsyRs+I3p-#7?$C;b#ct49@> zo-yQ+Q=mV|C^-Hb9S#Jg(VwsvXV-??6R%Obj{d~FTfI<&w#jx&Y`J#(vuBoE6uqrJ zKa_+KIIsSU@+!M(%#N2VxmK=V<$ri zpM?GpS?U8|3ZVQya^xORv6J=js*G31cr(?I!^a!xS_Dg-2wD-|$4Y*B!S2kV)RzK- zEGRfL6Q})q)bOu+G@&qU%_o!LG z7=e9*ATWUrG*-(DgZ4^J2&#%ec4ZuEzTCw_{Lk69$onF$fcT-iPGbgK1=3llQrUuG zi@kuiuxco}fQUiRr39WCssSlN=Ac66l!HndvZH`^r(BMJyhBG32}U{kkn+-PZfODM zirm$w49g#Qnl?R@rZ9lGr95eJutz^6<487>DzUapbEc`Un0(O2KILW#jehTyTCD3?;A zs32*3;U7P;)0CkFpqVO)s@1C;#^w~27}!AIt{!vSpfe+e%`)mmqX?L#DKEB*IOks)vYej1f~xLP{P^eCn+RMKjY+|$nnOK12AL2 zyDQ1~$&9^p=@Jw(tLIt0`-ziBk>1Sq^(JHl-MS=(^kDjH>_vjmNUJ*MaZLq!wTgj{ zbl2}YPY@}&zUHSoRc69dl=kWg#N^PMYje4F0GmIY`>}aqkTF4l^vhf@hioR}8J@R3 z7SImin2CC1TG~#6{ZRLe@)jd7LItW_xg#^k5+D#SD3cjWMV=1CV>viL*k?F zBrnBS?(*d zh=xuYG5%h#2E2sY5Do_a+0@ho_5~sYR7s$8X&JvaG-^--4Rv10IQOy!Rsc7c1x>dx z;XpKxVtEt@FH0eGsI9H_kxh5TkJUa?D>z%j`ej#iuye>C%e?t)M@!K=S#AIb< zHBkNm>k9n#>7>Z+g!nHdYTv(Hhi#>+z^oalg&1a|AOXLpZ$nW6o}fJ4V*u<3`^$Ad z2u{SP5!YA^h)-fAWr4Jjq)DQ;P?;5=uZ~(b)T==@t`6ds zS5`hC=PO(RWGpDOQ*-?WKJ)a;n%3Fm`t9QpqyU#gC3r%-1g!C%JvA`W*X>=v*O)&% zNYs3gxxjijwUkYHTCj5@>7l|v+9E-a2uBD7BJ_@%+Sg;j90=^80Md^tVt^A&3zE2G zupfr%cmMt`Q0g{1xS}Pp8H08_oGDMhyV$g9Q%E8iyhJ+w_;L!)%@lFm~YWz6o;L z1zG)9SpDVp1ulnRgfad!m>?8^Ce?M~ziGMb_c=Ix0|Q70mf*#YVC)OOE9%e)I|J2sT5&B&OmZCF%#M6;`Y&FaL^4kisiD zmXP$*rDzzIOhHRpfiw>WryDTY4(O#J0)$Ng1qUVwtPc}^w$dRcb}^7-ir-5xd^m=P zr&Lsu!B(P>SOqr!1{d;q~larI|wg@Z|n|vNi_nRD1^8Rfrb|5+TRIgEV(0qYV(EuiAl# zL=u8U#Jmx1tx0Yyq+2VY3Wh>VlHZ%lYc)EQodH?HjeXPHoP>DWQoHg$+Gap`)H>8? zU!dlM3KonKkbsim&bxu-hlAMqcP8e@A1A?pL$<&H;c(>&-u14TO4g_)!eU{EQH_D% z7)dGzO87^lj2v+bgmO?0rw@{EILGoW$lbBXB_Y*|ig2b!Ga!~jZSVJFzL&cbKA5?+ zpil~eH>{bpHOcNK9aZPrql>W~(LjC+h*J|!^TF5g)56hH*7=0|M7*tz#5o&l4qbw| zW=)IA$lxS3gu7)sS+`-CmsLtu%TRZqI}gSx6yT2zoUz`A+7nrWDeTv6b+Pxw1VK@X z+urw&`6^h0Eayp1C$be_g0B$fGRRhx%SaBofoTK}2BHpZ|A z9V$A@1$lD%x#9-P5M_Y|$n_5%hLTdd6S)DeP2O-!{yw6o z<0jsEDJBY5P|z7ZkWO(Hxn_wYV1AHG>O$#uX6I~Y+W9P-`GU6d=a5Piv|V%wNnX;p za%`*@)Aqh~Cx_ddr&!Rx>PU& z+27+iDq$Nx6c?ZK^(DzXCrV%kB3|gR;TKfG*v%R2W@s1pn>Aow5IP>n3MoF6a9heZ zRw5{^)dwS#ovn)$DU8OUg9ou#$?WLYuPx=_I66@kie%EEL(N!HElD6Vtq|%AXyLAI zjB@%~(t%{6PK&~GR8N3sN%96c@lWWYUrXZfZD(Q|RZ3Vz*#m9i&#iwgP`gW`ae4mKy{9PJBEI%W947?6`;C z2PHv-4E)|$wkJJ3{o=(*PJCXcr2b24dDNLLmh!=HLeeQ|s7e4WF(@|yzVdfV4bjVa zrbB}b8U?KUgBEnj16Fs)T@xd$PVvhkOKNY7-{qK&pax2H-xaXfUDh{VF=VqL<8^b* z{^UaZta&I3LEvpSpZ>&{-5nS~)6dFR=uZMCLudk-vbWmEh5B88&7uj3>=Q}ZaH$v# zxyzMl=XyauDUZ8FUpa@k+Y2oElN`fJq_=F+q(8AmMp%KI&m%O7`2^{>qKGCagcg3{ z#t#A?meV=V)3@YEOW`X{w$4$$B(huZ>^&W&U0cWceS5zP~3S5EvCm;T(^m2se z<`P6INJFCr0J$kF28jAfyvgTO&hByBE9z5;egRsAyL+`=6{ja&+3;IiSs7&w949-9X!DwK2 zyNUdgs3|#L{MgyS)Fxr1dwsrcDS4dUeYXuO1G2tHv6}SK{d=8xwr&N3U0iG^#7ipd z-Znl!&F4wYC+F|k5z04Oj9^co?RhTi2Sa# zA;!w~eAyGu=XnOr6m+fi*Fk^aSvP%S_xW-9ID-+Kwxj|m&7l+Co+{`S*AJ?|XiZhRnf^Y|PA1j1H>nNQdYDh5Ou-%c>t@ zJtxm|c(ldFp_VH4-6%Oq)yFi|W)9Vr7usr|kK*07#I3@8hGd!EU;5*3-S_W%=x6<3 zdEmcY?U%#re6>LANFL;0SBV5G4yQ?>wC!_KV=C@^(0SL_W!|I zzeyYXC{~5Cr&Db8fsjky#d0}VDgnZXqlhGdEZxDPg1!7uQWCW8Cea`)-tO30yY?kW z8U%WeLD4KWPfx|tl31Ogw>JZ|Sz1D%>LGT}->^Yhj99`*DN^{hH9;b9o!Zd1BC=%R z-o14{(LHpWWVwuU=dd~rIXvhmZIR+eY6%H(F`xrbEXEq_z}!Wsi;@I0k8UFXJ_%5` z_6WRgNy@-%dNUjjO5qTwBZ~}jM7`aM;c(JWUxLY>d3+=|9KEQb{PAA=2-EZO@}Mx| zt{s5mL&tzNf97F&58_u~dcBPsiCM#somd0xgPJok6d-5GT4AE18|O^|ke?vrr91sc zvaof?4po!ieb7Gwg;*TH#f31n0OBHM&U2>vs12@1$v$yld8P!i2kX5ug!^NieYw;G~Nk3)un9(r;a%h2xSkRJ@ZD4tTfn$PC zP&3ITlZi;Ap7Yu}9?;)O)@qru@e_OyM59(q$sq9V=3E07D}z=*9LAaO(-i+QE$1L zNU!Oy8QBL~dpcbv+lsl{KJkX($U&m_zBm&`Q2e%x%n}$%`!@*olnfpM%?_dqK4Q7e zc+c)T&Z^;grY_@oGOyIlL0IGiMh)!PJyNhuF9b&lw(;fMSDkxc;s)TO{7Rji5G;`N z*Gj6D8@Zq{zadw~WqA%9X#BzQOnm4wYuYrFxF(^{8=!bm;KBX-kJM04qQr+AJT?|& z7Z5Ze3Q$VG)MRmo?2{l>QF#T;*<5~aeL`Ot{!@E=UP)IE4eM zs;W?IhQajA>@8vqEX90`f!ml$5LiyBHJ>m}Q*rgr+$BO^!$pBZ{2d6X$96C7`bQCE zD|IA;7&7>)V@G$|Bj)L4ah!%$H8g6V>x&w{A(S!0y1}X}-eyKJ`4#o1Sd)j& zBX+)DLJ7J&TXb0fB1{gJ+(*G!4GE8g4R{EErE;sspgcw9^A*2LjhJCAKv`*N>4y)H zxqObJ-yoCB%k3M}%us`8zt&;$&ktBjL@46hIxiF2Ox#3&tN&Y?=AXiu;et2w9|fvDvsk`c|qkiCRf^BUCHRd<{gkqj<9LB|ItU7uYovYN%nNcKZ1g(zZd z-|x$u$Tu|3MVeMBdYx9?^&_*$QDZWTH^T4P1u|)ft(y0bU4XiyIz9AVnl9D~o;GWi zFdkMye`_L%C`go`jSN`|mfiq|reqc9YpB+ht3cNIf>uzS(*A2W0U=O^F^z>oGElJV zpfa0R3nY?`&Z#S-DD6D30(9(Z*2&f#ZUQrJR2Lqw5D1R=zXfJ z8LCW9KLP6jX3!;D;VQ{lVwK8E3{iJ~D2oryBvgZC#!Y4H?)~mlYW||819pDZsR_}W zNuojnfaUD=M;U!sns2BJ@GYe+y%|Md!028s->xZf)g|pz#KF=3RZWtuR=4PMsE(+M z!f{e-D#^;U zc>lmK|20;)1x3ns5b*Flgy07s3DYXMFHgj^^C-^uTuSuhmE6nfJ`st&8X6ipRHY;@ zFJXn89_DZ*qv&H&4vHs!@c*b_W#sY%30^Q!W;A@{l-#6pCYwl;+;3(vG!*$^$2HmX z#gqdgeqL-stujM0;bL;DxTFxdnBMlM3?)S<>s@e?YvveCF$2p6K}{un9l=C!%@C-4 zPOc|woK|tzy%ktN68fcG7^~QUq=C=pgN&|%qz8PmVBE?65JqZ_%#xJ3C1h%lI{YO& ziFD%WP?W0yi#&Mr2!c3QS69$^sSgF{r_+|1y3)O`y}H`sz<~n}aR-0sBF59K{=U5s z?1C&hr<+gKg8@46MHT~b8+vKvW;)a-k79q*i#8W%hlR})+9Z&x*RE2hZ4v;UW_leh zV4wpb(&eEm?gaA&BFHzuj`$g_dKl09Xd&f8TyNz4#d@@2ccuuXC7r zyJ%QKpx!ErWLChZEz{a2wdM~0AX=PD~D;eKvEJUWj>e<%WivZ7x@OqVon9{fvlTsq! z6mxoR?oU12DR-Dha-U?oDL)V}twYAiazC^j;@4y+j3t6MY&{i{Lfc9_@5D(&fQA;i zhb1urI`7(c>SQ;05;P`ic4t^H}n~n zzxy}S_5`qjUO!OU6}d|<`oMP#+fF*$QvQv zjD^8Hz~Xz0BSQwM;j1b^kIo!P96ys+3u>B-!Pgm22q!;k)6Si0bZMgSLjhD1VZ}wr zV8JjykregQ*VBX9#VSU)HX(nn9>0yKh^Ys{TKUiv{PD+V8luYmsMK*-(+AdGKG@+9 z)%Pmu?hgSP8ys;}s!JD>EB<@=3{t!*!f1*=Fs{(aP$SUv7;XgkPozZI53u@x0Tz#g zuf~m`d(!z7gKDUBtPY0S%1eQPC}_&)d?ig^g7Y|SK;Z6o^+64G^vV|FL^ENvGa^3P zvv%!UaC)1N@A}~AhpvUATQ2`4AvI5A(iG#{69!kk(~^nJHBQyl)Y6kM%t@77yn%Ps zHU4E1XB5Pzgq5DG|MFr%vVnd}@8?0!_e}>i7AQBkZ|rp5akoQzW0hEaL#nD|!J5^M zw5w2UK71!JQQ_D}#2#Bw=!Ig@``wmUJQ>g(R%Y@UZ6wf8n{2vmThj~zwbwtQ^ULS- zV7@@hMr{ajM4b@xmvHv6m5Jp-UJRnzLv#ue1BQOg!a%HigtDRQn+**wWre=mYAI6|H~dTfYXa!=x*p$6AiOvpK~H!t?MN?VeS9xPIsq?o@_H=l zj^=HCa@bdf5$womspXp1H8gPE?7@pgb#NzC+^x1?l>A>z0_OTepr7gb$>=%2}Id`XIxj!dJcYKjPk>hP(mH;kVHow8M(_Z&inLGfs74$J7Z01FC4_anNRAgVEYhxwdsY4lSPKu`(%S0z z%?E)TmX~ZI6pkRJJO|RP!z6E>Cc+09;RamgX#ZP@oPasiGBL$e{r@_tfM61<(W^9p`%m<Gp2j7V~j}?=)peE!ra`{qAmSCkqXswRItX-VHeyo5%0ZNZ>Rl}2o zhXrtj-O3){zhT)7)B&&QX@P_y2;n|w@K19pJIGSrbo=%c#1rEDSPRYrwSx<{+`8ep zaHI>HU6r|u3fv39fMp6 zF#)pc&|-XD3OA_i(rMY0X8w3FdhX~FZvI#v$+6atkK?ge1Vp9a=IXKZ6INO2UL_bV zzxNf`Ff^#;5hQtX;c8nL_C6_R&%rYHp_B76wJA5r-T zC2?lo()+ZVqAbBj>Ues3V)c9s|GqYlphT=X!yL3?=_}@-8wFd21&QbGi16*<=Digw z%smz9U;wi)eMJ7UwHnOkvMu4&ivtNX9ct}S3UP)|L|37&z)mpk7`q2 zrkVj8a&{g>NL(3hDFbwsjV-8wjThw!&Z?hTe&ML=vdc19Av-E63PTEsL`1rAGw|M&zzL%sXw4j}z_W%B{q)UKp!AatKZIkq z*$x8rZd@GPjH7TkoOU_VgJfYv-FZqjeK!=cULnS++^3?DrnJ!%xg^5;(!M^=^NVxL*xlponSUI9o}TCXdwQPF=lgzt-kfsSZWzg)^TXjy@}Sh__)*)DATx1*hpJt2%Msss#}$cw1C6 zBuro#;O|(+MD-{`s{Y$o#jiRQ}R1 zS5Of4(^I{*FNs-EGB;x^6I#_c^3ng+w6yqd%uqvNsJRh#mf*mQ*lms=1JVBPc-FDT7R)hl$yZHb9Jts-|i zM1%+~ZacJ}At^Z-S^b$7Cu+$f{ij^ObT@8NQ=7X8Ur z_uA?@7teuR$#>QK)MY}agsz`~do1f7@LT9X4aL`={KtMu>R7H;^>+j6(uB0up4!^Gq(%C+N1iumuq&UaVr{YU#ur*d7ZD47~I$dD~qr*K7GK z;dEq!Q>T3{VMXk>jM+p#Nj^(SSDgVD2Ob1^0 zu_2q4EqY%Avo^>2HkehIS9EAjm{_*6Wn*{C3au>)?C5-*v-8=>yl>H{Y0a)n#fnw> zo4m)HTU#_K8P)OC;TC7koWVLO^+~L_^&U4?pWoA2u{ShaBr2Rj9yqWL)6YMbUT@#7 z9V5=9n|Zx%15fkh9u1dxN~6&7<;!6c@UruKJ{JuV!oJDq7khVvI`01ADo1Y6Jjb9} zFKU7dr0+Ek4Hwyq6cqow2J#AC?{RgtzrEruwFxLrM(a?#iSM*hLJ<61Wym;x|9dw` zC4sV+*Xsw;40mP6Gq#SjG>x}WtkU&WMd`KZ`pBASgyN)z3S;_@fuL73SubdqMLCEf zML;!}`4EM+>`lbb)~#Frp?lCF_;V0<2MdeT-iq;i{q%_w|BeQeHX53|sJyyd*5hy| z%Kb>|IBiFOzN?kMi>IC{x##|@&^IV3sPgbtTGhEAscdqsJ-j5XxZDvcg5Xa&J9Bfr z$4DK-VgF_U9wlSRsbal(<6to74gi`|mQOiQN+nW+n#3`N((z?!bjdhwCjHP!svOwT z5Fa1U>Cm2YY6`K^DgW3x5rPT#F6L5b`g(kMZq9;}LBuG>GO_VaiPrA5BuF&o)K5-^h0W+Qn(P%w|2% z&Lo+NSg7W*Mp4m)`7yF)UOwB^8f=RyD$JkSkk^Kr;9j5ovK-RwKcml$g_^HkVfFo$ zE24GsaGfseV>2F)NEE<#pay1bD~Vbj4VsCTI0hSyfK(q-F{Bl zh^2l>WJzaI9PF3)p5i`krCVSG7>L&d#4;|r08DBGbydAeq^dFV8k#Glihc;k2Y2TP zpeu;(7?U#Wnlu#*XHy=Klldnwl?d!%yEFEVPtPBh&hnI#M@z%Umkbe5DA?^SDvh`Q zPjMLK3hI$A(vYi6G}6V*ePb^N&Lfl|gP&z9Kka@g9TpDiPP(O}7x5W2QSm6im%y%x zXe`P^b$IOP(WB|<^N>vvUqx_D#zQC%r;5!q?LwW~$e~e_aQmjSf(w{8g)?-6UEg3o z8nO==8H2kO@x!NZ| z80S;n2(iU4r(T){+%q+jb$1Gvnf)P>$OP@MP+4CQpX15!`I6jq&0uY zsk38K8sLPYBoxGGiU9dSxjn*P|9sz#8eC#$r<}Z&?Pmfv5d8=HHP|I7ilrKZRYwJd zSunbnk0Nz-6(e@l*JbHm_H_xZa;vD#s1KjE<_K9dEG&Jtdb+%J78ccP*555_iERO;7*Pkpnn+w%m={ThGBgu4v`@`v6r~-aWKJZ@nfq5ZM$`^y zR`g$`Im$!bkr{}G)SP8Qsx`&R0cn%gKny#rX z-=mfrSFX;I>Co7)Eaq8s2nPvBnb2g4(hX_+;n3nA zii-nAj{FiIWw93f;DgC8gKwsX?WH2LZCjTXOAy;qp=YZpID%W2%+`&{5sVTk>_T~x z_qyE(hJ6wk*O6}_D^W7Nd`&@2abF&&?)2sR1o}*Q^>Omzt6#E6cDg4Aj4;9?J8S7^!9lAuFFm+ zpylpg=1R7w*rc|&b*57zrR_NxfhCq*XfFt^Mo~3W70)p!QEg#zHZ#9USI+@Psar{b{{_YVv$V?E7v*F_d?Ui4FmV^ z;T9q5Qd)lvYkoOJF%M@A9JfNedHi_xo?Q~}u&fi+AdyenD>Emi4%;*!IJfprAJbbA`Ag~WO#{xJH&bSMPMGk+_WF_Q=8dzIF57Z=p9~PwYv5ES5+Yfde7);9J+vV z2Iik3UnqTYTx+Y26uX!y*%6Y55q$35(nJYd{;P^m-+c1k{s#QwbNem`-^zX8~)Uh)6{ diff --git a/docs/images/svg/basic-123_back.svg b/docs/images/svg/basic-123_back.svg new file mode 100644 index 00000000..e770afd6 --- /dev/null +++ b/docs/images/svg/basic-123_back.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/svg/basic-123_front.svg b/docs/images/svg/basic-123_front.svg new file mode 100644 index 00000000..1d015d27 --- /dev/null +++ b/docs/images/svg/basic-123_front.svg @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/svg/basic-7_back.svg b/docs/images/svg/basic-7_back.svg new file mode 100644 index 00000000..4f82cb19 --- /dev/null +++ b/docs/images/svg/basic-7_back.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/docs/images/svg/basic-7_front.svg b/docs/images/svg/basic-7_front.svg new file mode 100644 index 00000000..358dae84 --- /dev/null +++ b/docs/images/svg/basic-7_front.svg @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/svg/card_000_back.svg b/docs/images/svg/card_000_back.svg new file mode 100644 index 00000000..e770afd6 --- /dev/null +++ b/docs/images/svg/card_000_back.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/svg/card_000_front.svg b/docs/images/svg/card_000_front.svg new file mode 100644 index 00000000..1d015d27 --- /dev/null +++ b/docs/images/svg/card_000_front.svg @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/svg/circle-25_back.svg b/docs/images/svg/circle-25_back.svg new file mode 100644 index 00000000..f4da15cf --- /dev/null +++ b/docs/images/svg/circle-25_back.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/svg/circle-25_front.svg b/docs/images/svg/circle-25_front.svg new file mode 100644 index 00000000..dd332c07 --- /dev/null +++ b/docs/images/svg/circle-25_front.svg @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/svg/diamond-25_back.svg b/docs/images/svg/diamond-25_back.svg new file mode 100644 index 00000000..f4da15cf --- /dev/null +++ b/docs/images/svg/diamond-25_back.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/svg/diamond-25_front.svg b/docs/images/svg/diamond-25_front.svg new file mode 100644 index 00000000..01194a92 --- /dev/null +++ b/docs/images/svg/diamond-25_front.svg @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/svg/heaven-earth-78_back.svg b/docs/images/svg/heaven-earth-78_back.svg new file mode 100644 index 00000000..ff4fd7f5 --- /dev/null +++ b/docs/images/svg/heaven-earth-78_back.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/svg/heaven-earth-78_front.svg b/docs/images/svg/heaven-earth-78_front.svg new file mode 100644 index 00000000..6f27eddb --- /dev/null +++ b/docs/images/svg/heaven-earth-78_front.svg @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/svg/minimal-42_back.svg b/docs/images/svg/minimal-42_back.svg new file mode 100644 index 00000000..7522dbad --- /dev/null +++ b/docs/images/svg/minimal-42_back.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/svg/minimal-42_front.svg b/docs/images/svg/minimal-42_front.svg new file mode 100644 index 00000000..5d530a92 --- /dev/null +++ b/docs/images/svg/minimal-42_front.svg @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/svg/place-value-456_back.svg b/docs/images/svg/place-value-456_back.svg new file mode 100644 index 00000000..78cbf49e --- /dev/null +++ b/docs/images/svg/place-value-456_back.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/svg/place-value-456_front.svg b/docs/images/svg/place-value-456_front.svg new file mode 100644 index 00000000..ae866f26 --- /dev/null +++ b/docs/images/svg/place-value-456_front.svg @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/svg/square-25_back.svg b/docs/images/svg/square-25_back.svg new file mode 100644 index 00000000..f4da15cf --- /dev/null +++ b/docs/images/svg/square-25_back.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/svg/square-25_front.svg b/docs/images/svg/square-25_front.svg new file mode 100644 index 00000000..172f7695 --- /dev/null +++ b/docs/images/svg/square-25_front.svg @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/generate.py b/src/generate.py index 697dce7d..34d3dbcf 100755 --- a/src/generate.py +++ b/src/generate.py @@ -7,6 +7,8 @@ import random import subprocess import sys import os +import tempfile +import shutil from pathlib import Path def load_config(config_path): @@ -45,6 +47,157 @@ def parse_range(range_str, step=1): numbers = [int(range_str)] return numbers +def generate_single_card_typst(number, side, config, output_path, project_root): + """Generate Typst file for a single card""" + + # Use relative path to single-card.typ (we'll copy templates to temp dir) + typst_content = f''' +#import "single-card.typ": generate-single-card + +#generate-single-card( + {number}, + side: "{side}", + bead-shape: "{config.get('bead_shape', 'diamond')}", + color-scheme: "{config.get('color_scheme', 'monochrome')}", + colored-numerals: {str(config.get('colored_numerals', False)).lower()}, + hide-inactive-beads: {str(config.get('hide_inactive_beads', False)).lower()}, + show-empty-columns: {str(config.get('show_empty_columns', False)).lower()}, + columns: {config.get('columns', 'auto')}, + transparent: {str(config.get('transparent', False)).lower()}, + width: {config.get('card_width', '3.5in')}, + height: {config.get('card_height', '2.5in')}, + font-size: {config.get('font_size', '48pt')}, + font-family: "{config.get('font_family', 'DejaVu Sans')}", + scale-factor: {config.get('scale_factor', 1.0)} +) +''' + + with open(output_path, 'w') as f: + f.write(typst_content) + +def typst_to_image(typst_file, output_file, format='png', dpi=300, tmpdir_path=None): + """Convert Typst file directly to PNG or SVG""" + + # Use relative paths from tmpdir + if tmpdir_path: + rel_input = typst_file.name # Just filename since we're running from tmpdir + abs_output = output_file.resolve() # Absolute path for output + else: + rel_input = str(typst_file) + abs_output = str(output_file) + + # Typst can export directly to PNG or SVG + cmd = [ + 'typst', 'compile', + '--format', format, + rel_input, + str(abs_output) + ] + + # Add DPI for PNG only + if format == 'png': + cmd.insert(-2, '--ppi') + cmd.insert(-2, str(dpi)) + + # Add font path from tmpdir if fonts were copied there + if tmpdir_path: + fonts_path = tmpdir_path / 'fonts' + if fonts_path.exists(): + cmd.extend(['--font-path', str(fonts_path)]) + + # Run from tmpdir where templates are copied + cwd = tmpdir_path if tmpdir_path else None + result = subprocess.run(cmd, capture_output=True, text=True, cwd=cwd) + + if result.returncode != 0: + print(f"Error generating {format.upper()}: {result.stderr}") + return False + + return True + +def generate_cards_direct(numbers, config, output_dir, format='png', dpi=300, separate_fronts_backs=True): + """Generate PNG/SVG cards directly from Typst without PDF intermediate""" + + # Create output directory + output_dir = Path(output_dir) + output_dir.mkdir(parents=True, exist_ok=True) + + # Get project root + project_root = Path(__file__).parent.parent + + # Apply shuffle if configured + if config.get('shuffle', False): + if 'seed' in config: + random.seed(config['seed']) + random.shuffle(numbers) + + if format == 'png': + print(f"Generating {len(numbers)} cards directly to PNG at {dpi} DPI...") + else: + print(f"Generating {len(numbers)} cards directly to SVG...") + + # Create subdirectories if separating + if separate_fronts_backs: + fronts_dir = output_dir / 'fronts' + backs_dir = output_dir / 'backs' + fronts_dir.mkdir(exist_ok=True) + backs_dir.mkdir(exist_ok=True) + + generated_files = [] + + # Use temp directory for Typst files + with tempfile.TemporaryDirectory() as tmpdir: + tmpdir_path = Path(tmpdir) + + # Copy template files to temp directory so imports work + templates_dir = project_root / 'templates' + shutil.copy2(templates_dir / 'single-card.typ', tmpdir_path) + shutil.copy2(templates_dir / 'flashcards.typ', tmpdir_path) + + # Copy fonts directory if it exists + fonts_dir = project_root / 'fonts' + if fonts_dir.exists(): + shutil.copytree(fonts_dir, tmpdir_path / 'fonts') + + for i, num in enumerate(numbers): + # Generate front (soroban) + front_typst = tmpdir_path / f'card_{i}_front.typ' + generate_single_card_typst(num, 'front', config, front_typst, project_root) + + if separate_fronts_backs: + front_file = fronts_dir / f'card_{i:03d}.{format}' + else: + front_file = output_dir / f'card_{i:03d}_front.{format}' + + # Ensure parent directory exists + front_file.parent.mkdir(parents=True, exist_ok=True) + + if typst_to_image(front_typst, front_file, format, dpi, tmpdir_path): + generated_files.append(front_file) + print(f" ✓ Card {i:03d} front: {num}") + else: + print(f" ✗ Failed card {i:03d} front: {num}") + + # Generate back (numeral) + back_typst = tmpdir_path / f'card_{i}_back.typ' + generate_single_card_typst(num, 'back', config, back_typst, project_root) + + if separate_fronts_backs: + back_file = backs_dir / f'card_{i:03d}.{format}' + else: + back_file = output_dir / f'card_{i:03d}_back.{format}' + + # Ensure parent directory exists + back_file.parent.mkdir(parents=True, exist_ok=True) + + if typst_to_image(back_typst, back_file, format, dpi, tmpdir_path): + generated_files.append(back_file) + print(f" ✓ Card {i:03d} back: {num}") + else: + print(f" ✗ Failed card {i:03d} back: {num}") + + return generated_files + def generate_typst_file(numbers, config, output_path): """Generate a Typst file with the specified configuration.""" @@ -108,8 +261,22 @@ def main(): parser.add_argument('--color-scheme', type=str, choices=['monochrome', 'place-value', 'heaven-earth', 'alternating'], default='monochrome', help='Color scheme (default: monochrome)') parser.add_argument('--colored-numerals', action='store_true', help='Color the numerals to match the bead color scheme') parser.add_argument('--scale-factor', type=float, default=0.9, help='Manual scale adjustment (0.1 to 1.0, default: 0.9)') - parser.add_argument('--output', '-o', type=str, default='out/flashcards.pdf', help='Output PDF path') + + # Output format options + parser.add_argument('--format', '-f', choices=['pdf', 'png', 'svg'], default='pdf', help='Output format (default: pdf)') + parser.add_argument('--output', '-o', type=str, help='Output path (default: out/flashcards.FORMAT or out/FORMAT)') + + # PDF-specific options parser.add_argument('--linearize', action='store_true', default=True, help='Create linearized PDF (default: True)') + + # PNG/SVG-specific options + parser.add_argument('--dpi', type=int, default=300, help='DPI for PNG output (default: 300)') + parser.add_argument('--transparent', action='store_true', help='Use transparent background for PNG/SVG') + parser.add_argument('--separate', action='store_true', default=True, help='Separate fronts and backs into subdirectories (PNG/SVG only)') + parser.add_argument('--no-separate', dest='separate', action='store_false', help='Keep fronts and backs in same directory (PNG/SVG only)') + parser.add_argument('--card-width', type=str, default='3.5in', help='Card width for PNG/SVG (default: 3.5in)') + parser.add_argument('--card-height', type=str, default='2.5in', help='Card height for PNG/SVG (default: 2.5in)') + parser.add_argument('--font-path', type=str, help='Path to fonts directory') args = parser.parse_args() @@ -154,6 +321,12 @@ def main(): 'color_scheme': args.color_scheme if args.color_scheme != 'monochrome' else config.get('color_scheme', 'monochrome'), 'colored_numerals': args.colored_numerals or config.get('colored_numerals', False), 'scale_factor': args.scale_factor if args.scale_factor != 0.9 else config.get('scale_factor', 0.9), + # PNG/SVG specific options + 'transparent': args.transparent or config.get('transparent', False), + 'card_width': args.card_width or config.get('card_width', '3.5in'), + 'card_height': args.card_height or config.get('card_height', '2.5in'), + 'shuffle': args.shuffle or config.get('shuffle', False), + 'seed': args.seed or config.get('seed'), } # Handle margins @@ -185,87 +358,127 @@ def main(): else: final_config['columns'] = config.get('columns', 'auto') + # Determine output path based on format + if args.output: + output_path = Path(args.output) + elif args.format == 'pdf': + output_path = Path('out/flashcards.pdf') + else: + # For PNG/SVG, use directory instead of file + output_path = Path(f'out/{args.format}') + # Create output directory - output_path = Path(args.output) - output_path.parent.mkdir(parents=True, exist_ok=True) + if args.format == 'pdf': + output_path.parent.mkdir(parents=True, exist_ok=True) + else: + output_path.mkdir(parents=True, exist_ok=True) - # Generate temporary Typst file in project root - project_root = Path(__file__).parent.parent - temp_typst = project_root / 'temp_flashcards.typ' - generate_typst_file(numbers, final_config, temp_typst) - - # Set up font path if provided - font_args = [] - if args.font_path: - font_args = ['--font-path', args.font_path] - elif os.path.exists('fonts'): - font_args = ['--font-path', 'fonts'] - - # Compile with Typst - print(f"Generating flashcards for {len(numbers)} numbers...") - try: - # Run typst from project root directory + # Generate based on format + if args.format == 'pdf': + # Generate PDF (original functionality) project_root = Path(__file__).parent.parent - result = subprocess.run( - ['typst', 'compile'] + font_args + [str(temp_typst), str(output_path)], - capture_output=True, - text=True, - cwd=str(project_root) - ) + temp_typst = project_root / 'temp_flashcards.typ' + generate_typst_file(numbers, final_config, temp_typst) - if result.returncode != 0: - print(f"Error compiling Typst document:", file=sys.stderr) - print(result.stderr, file=sys.stderr) - sys.exit(1) + # Set up font path if provided + font_args = [] + if args.font_path: + font_args = ['--font-path', args.font_path] + elif os.path.exists('fonts'): + font_args = ['--font-path', 'fonts'] - print(f"Generated: {output_path}") - - # Clean up temp file - temp_typst.unlink() - - # Add duplex printing hints and linearize if requested - if args.linearize: - linearized_path = output_path.parent / f"{output_path.stem}_linear{output_path.suffix}" - print(f"Linearizing PDF with duplex hints...") - - # Use qpdf to add duplex hints and linearize - # Note: --pages option preserves page order for duplex + # Compile with Typst + print(f"Generating PDF flashcards for {len(numbers)} numbers...") + try: + # Run typst from project root directory result = subprocess.run( - ['qpdf', '--linearize', - '--object-streams=preserve', - str(output_path), str(linearized_path)], + ['typst', 'compile'] + font_args + [str(temp_typst), str(output_path)], + capture_output=True, + text=True, + cwd=str(project_root) + ) + + if result.returncode != 0: + print(f"Error compiling Typst document:", file=sys.stderr) + print(result.stderr, file=sys.stderr) + sys.exit(1) + + print(f"Generated: {output_path}") + + # Clean up temp file + temp_typst.unlink() + + # Add duplex printing hints and linearize if requested + if args.linearize: + linearized_path = output_path.parent / f"{output_path.stem}_linear{output_path.suffix}" + print(f"Linearizing PDF with duplex hints...") + + # Use qpdf to add duplex hints and linearize + result = subprocess.run( + ['qpdf', '--linearize', + '--object-streams=preserve', + str(output_path), str(linearized_path)], + capture_output=True, + text=True + ) + + if result.returncode == 0: + print(f"Linearized: {linearized_path}") + else: + print(f"Warning: Failed to linearize PDF: {result.stderr}", file=sys.stderr) + + # Run basic PDF validation + print("Validating PDF...") + result = subprocess.run( + ['qpdf', '--check', str(output_path)], capture_output=True, text=True ) if result.returncode == 0: - print(f"Linearized: {linearized_path}") + print("PDF validation passed") else: - print(f"Warning: Failed to linearize PDF: {result.stderr}", file=sys.stderr) - - # Run basic PDF validation - print("Validating PDF...") - result = subprocess.run( - ['qpdf', '--check', str(output_path)], - capture_output=True, - text=True - ) - - if result.returncode == 0: - print("PDF validation passed") - else: - print(f"Warning: PDF validation issues: {result.stderr}", file=sys.stderr) + print(f"Warning: PDF validation issues: {result.stderr}", file=sys.stderr) + + except FileNotFoundError as e: + if 'typst' in str(e): + print("Error: typst command not found. Please install Typst first.", file=sys.stderr) + print("Visit: https://github.com/typst/typst", file=sys.stderr) + elif 'qpdf' in str(e): + print("Warning: qpdf command not found. Skipping linearization and validation.", file=sys.stderr) + print("Install with: brew install qpdf", file=sys.stderr) + else: + raise + sys.exit(1) + + else: + # Generate PNG/SVG (individual cards) + try: + generated_files = generate_cards_direct( + numbers, + final_config, + output_path, + format=args.format, + dpi=args.dpi, + separate_fronts_backs=args.separate + ) - except FileNotFoundError as e: - if 'typst' in str(e): - print("Error: typst command not found. Please install Typst first.", file=sys.stderr) - print("Visit: https://github.com/typst/typst", file=sys.stderr) - elif 'qpdf' in str(e): - print("Warning: qpdf command not found. Skipping linearization and validation.", file=sys.stderr) - print("Install with: brew install qpdf", file=sys.stderr) - else: - raise - sys.exit(1) + if generated_files: + print(f"\n✓ Generated {len(generated_files)} {args.format.upper()} files in {output_path}") + if args.separate: + print(f" - Fronts in: {output_path}/fronts/") + print(f" - Backs in: {output_path}/backs/") + else: + print(f"\n✗ Failed to generate {args.format.upper()} cards") + sys.exit(1) + + except FileNotFoundError as e: + if 'typst' in str(e): + print("Error: typst command not found. Please install Typst first.", file=sys.stderr) + print("Visit: https://github.com/typst/typst", file=sys.stderr) + else: + raise + sys.exit(1) if __name__ == '__main__': main() \ No newline at end of file diff --git a/src/generate_examples.py b/src/generate_examples.py index 2f586948..0686d1e0 100755 --- a/src/generate_examples.py +++ b/src/generate_examples.py @@ -9,82 +9,102 @@ import os from pathlib import Path import tempfile -def generate_example_pdfs(): - """Generate various example PDFs to convert to PNG""" +def generate_examples(): + """Generate various example images (SVG for single cards, PNG for grids)""" examples = [ - # Basic examples + # Single card examples - use SVG { 'name': 'basic-7', - 'args': ['--range', '7-7', '--cards-per-page', '1'], + 'format': 'svg', + 'args': ['--range', '7-7', '--format', 'svg', '--no-separate'], 'desc': 'Number 7 on soroban' }, { 'name': 'basic-123', - 'args': ['--range', '123-123', '--cards-per-page', '1'], + 'format': 'svg', + 'args': ['--range', '123-123', '--format', 'svg', '--no-separate'], 'desc': 'Number 123 on soroban' }, - # Color schemes + # Color schemes - use SVG { 'name': 'place-value-456', - 'args': ['--range', '456-456', '--cards-per-page', '1', '--color-scheme', 'place-value', '--colored-numerals'], + 'format': 'svg', + 'args': ['--range', '456-456', '--format', 'svg', '--color-scheme', 'place-value', '--colored-numerals', '--no-separate'], 'desc': 'Place-value coloring' }, { 'name': 'heaven-earth-78', - 'args': ['--range', '78-78', '--cards-per-page', '1', '--color-scheme', 'heaven-earth', '--colored-numerals'], + 'format': 'svg', + 'args': ['--range', '78-78', '--format', 'svg', '--color-scheme', 'heaven-earth', '--colored-numerals', '--no-separate'], 'desc': 'Heaven-earth coloring' }, - # Bead shapes + # Bead shapes - use SVG { 'name': 'diamond-25', - 'args': ['--range', '25-25', '--cards-per-page', '1', '--bead-shape', 'diamond'], + 'format': 'svg', + 'args': ['--range', '25-25', '--format', 'svg', '--bead-shape', 'diamond', '--no-separate'], 'desc': 'Diamond beads (realistic)' }, { 'name': 'circle-25', - 'args': ['--range', '25-25', '--cards-per-page', '1', '--bead-shape', 'circle'], + 'format': 'svg', + 'args': ['--range', '25-25', '--format', 'svg', '--bead-shape', 'circle', '--no-separate'], 'desc': 'Circle beads' }, { 'name': 'square-25', - 'args': ['--range', '25-25', '--cards-per-page', '1', '--bead-shape', 'square'], + 'format': 'svg', + 'args': ['--range', '25-25', '--format', 'svg', '--bead-shape', 'square', '--no-separate'], 'desc': 'Square beads' }, - # Grid layouts + # Hide inactive - use SVG + { + 'name': 'minimal-42', + 'format': 'svg', + 'args': ['--range', '42-42', '--format', 'svg', '--hide-inactive-beads', '--no-separate'], + 'desc': 'Hidden inactive beads' + }, + + # Grid layouts - use PDF→PNG { 'name': 'grid-6', + 'format': 'pdf', 'args': ['--range', '0-5', '--cards-per-page', '6'], 'desc': '6 cards per page' }, { 'name': 'grid-12', + 'format': 'pdf', 'args': ['--range', '0-11', '--cards-per-page', '12'], 'desc': '12 cards per page' }, - # Skip counting + # Skip counting - use PDF→PNG { 'name': 'skip-5s', + 'format': 'pdf', 'args': ['--range', '0-30', '--step', '5', '--cards-per-page', '6'], 'desc': 'Counting by 5s' }, - # Hide inactive - { - 'name': 'minimal-42', - 'args': ['--range', '42-42', '--cards-per-page', '1', '--hide-inactive-beads'], - 'desc': 'Hidden inactive beads' - }, - - # Cutting guides + # Cutting guides - use PDF→PNG { 'name': 'cutting-guides', + 'format': 'pdf', 'args': ['--range', '10-15', '--cards-per-page', '6', '--cut-marks'], 'desc': 'With cutting guides' }, + + # Additional cutting example + { + 'name': 'cutting-registration', + 'format': 'pdf', + 'args': ['--range', '10-15', '--cards-per-page', '6', '--cut-marks', '--registration'], + 'desc': 'With cutting guides and registration marks' + }, ] return examples @@ -170,6 +190,36 @@ def pdf_to_png(pdf_path, png_path, dpi=150, page=1): return False + +def copy_svg_files(svg_dir, output_dir, example_name): + """Copy SVG files from the generated directory to examples""" + svg_dir = Path(svg_dir) + output_dir = Path(output_dir) + output_dir.mkdir(parents=True, exist_ok=True) + + copied_files = [] + + # Look for front and back SVG files + front_file = svg_dir / f'card_000_front.svg' + back_file = svg_dir / f'card_000_back.svg' + + if front_file.exists(): + dest = output_dir / f'{example_name}_front.svg' + import shutil + shutil.copy2(front_file, dest) + copied_files.append(dest) + print(f" ✓ Generated {dest.name}") + + if back_file.exists(): + dest = output_dir / f'{example_name}_back.svg' + import shutil + shutil.copy2(back_file, dest) + copied_files.append(dest) + print(f" ✓ Generated {dest.name}") + + return copied_files + + def main(): """Generate example images for README""" @@ -181,7 +231,11 @@ def main(): temp_dir = project_root / 'out' / 'examples' temp_dir.mkdir(parents=True, exist_ok=True) - examples = generate_example_pdfs() + # Create SVG output directory + svg_dir = images_dir / 'svg' + svg_dir.mkdir(parents=True, exist_ok=True) + + examples = generate_examples() # Check if we have a PDF to PNG converter available converters = [] @@ -205,33 +259,56 @@ def main(): for example in examples: print(f" - {example['name']}: {example['desc']}") - # Generate PDF - pdf_path = temp_dir / f"{example['name']}.pdf" - cmd = [ - 'python3', - str(project_root / 'src' / 'generate.py'), - '--output', str(pdf_path) - ] + example['args'] - - result = subprocess.run(cmd, capture_output=True, text=True, cwd=str(project_root)) - - if result.returncode != 0: - print(f" ERROR generating PDF: {result.stderr}") - continue - - # Convert front page to PNG - front_png = images_dir / f"{example['name']}-front.png" - if pdf_to_png(str(pdf_path), str(front_png), dpi=150, page=1): - print(f" ✓ Generated {front_png.name}") + if example['format'] == 'svg': + # Generate SVG directly + svg_temp_dir = temp_dir / f"{example['name']}_svg" + cmd = [ + 'python3', + str(project_root / 'src' / 'generate.py'), + '--output', str(svg_temp_dir) + ] + example['args'] + + result = subprocess.run(cmd, capture_output=True, text=True, cwd=str(project_root)) + + if result.returncode != 0: + print(f" ERROR generating SVG: {result.stderr}") + failed.append(example['name']) + continue + + # Copy SVG files to final destination + copied = copy_svg_files(svg_temp_dir, svg_dir, example['name']) + if not copied: + print(f" ✗ No SVG files found") + failed.append(example['name']) + else: - print(f" ✗ Failed to convert {front_png.name}") - failed.append(example['name']) - - # For single cards, also generate back page - if '--cards-per-page' in example['args'] and example['args'][example['args'].index('--cards-per-page') + 1] == '1': - back_png = images_dir / f"{example['name']}-back.png" - if pdf_to_png(str(pdf_path), str(back_png), dpi=150, page=2): - print(f" ✓ Generated {back_png.name}") + # Generate PDF and convert to PNG + pdf_path = temp_dir / f"{example['name']}.pdf" + cmd = [ + 'python3', + str(project_root / 'src' / 'generate.py'), + '--output', str(pdf_path) + ] + example['args'] + + result = subprocess.run(cmd, capture_output=True, text=True, cwd=str(project_root)) + + if result.returncode != 0: + print(f" ERROR generating PDF: {result.stderr}") + continue + + # Convert front page to PNG + front_png = images_dir / f"{example['name']}-front.png" + if pdf_to_png(str(pdf_path), str(front_png), dpi=150, page=1): + print(f" ✓ Generated {front_png.name}") + else: + print(f" ✗ Failed to convert {front_png.name}") + failed.append(example['name']) + + # For single cards, also generate back page + if '--cards-per-page' in example['args'] and example['args'][example['args'].index('--cards-per-page') + 1] == '1': + back_png = images_dir / f"{example['name']}-back.png" + if pdf_to_png(str(pdf_path), str(back_png), dpi=150, page=2): + print(f" ✓ Generated {back_png.name}") if failed: print(f"\n✗ Failed to generate {len(failed)} images: {', '.join(failed)}") diff --git a/templates/single-card.typ b/templates/single-card.typ new file mode 100644 index 00000000..c13832fc --- /dev/null +++ b/templates/single-card.typ @@ -0,0 +1,113 @@ +// Single card template for PNG export +// Renders one card at a time with optional transparent background + +#import "flashcards.typ": draw-soroban + +// Local definition of create-colored-numeral since it's not exported +#let create-colored-numeral(num, scheme, use-colors, font-size) = { + // Use the exact same colors as the beads + let place-value-colors = ( + rgb("#2E86AB"), // ones - blue (same as beads) + rgb("#A23B72"), // tens - magenta (same as beads) + rgb("#F18F01"), // hundreds - orange (same as beads) + rgb("#6A994E"), // thousands - green (same as beads) + rgb("#BC4B51"), // ten-thousands - red (same as beads) + ) + + if not use-colors or scheme == "monochrome" { + // Plain black text + text(size: font-size)[#num] + } else if scheme == "place-value" { + // Color each digit according to its place value + let digits = str(num).clusters() + let num-digits = digits.len() + let colored-digits = () + + for (idx, digit) in digits.enumerate() { + let place-idx = num-digits - idx - 1 // 0 = ones, 1 = tens, etc. + let color-idx = calc.rem(place-idx, place-value-colors.len()) + let digit-color = place-value-colors.at(color-idx) + colored-digits += (text(fill: digit-color, size: font-size)[#digit],) + } + + colored-digits.join() + } else if scheme == "heaven-earth" { + // For heaven-earth, use orange (heaven bead color) + text(size: font-size, fill: rgb("#F18F01"))[#num] + } else if scheme == "alternating" { + // For alternating, we could alternate digit colors + let digits = str(num).clusters() + let colored-digits = () + + for (idx, digit) in digits.enumerate() { + let digit-color = if calc.rem(idx, 2) == 0 { rgb("#1E88E5") } else { rgb("#43A047") } + colored-digits += (text(fill: digit-color, size: font-size)[#digit],) + } + + colored-digits.join() + } else { + // Fallback to plain text + text(size: font-size)[#num] + } +} + +#let generate-single-card( + number, + side: "front", // "front" or "back" + bead-shape: "diamond", + color-scheme: "monochrome", + colored-numerals: false, + hide-inactive-beads: false, + show-empty-columns: false, + columns: auto, + transparent: false, + width: 3.5in, + height: 2.5in, + font-size: 48pt, + font-family: "DejaVu Sans", + scale-factor: 1.0, +) = { + // Set page size to exact card dimensions + set page( + width: width, + height: height, + margin: 0pt, + fill: if transparent { none } else { white } + ) + + // Set font + set text(font: font-family, size: font-size, fallback: true) + + // Calculate padding for content + let padding = width * 0.05 + + // Render the appropriate side + if side == "front" { + // Soroban side + align(center + horizon)[ + #box( + width: width - 2 * padding, + height: height - 2 * padding + )[ + #align(center + horizon)[ + #scale(x: scale-factor * 100%, y: scale-factor * 100%)[ + #draw-soroban( + number, + columns: columns, + show-empty: show-empty-columns, + hide-inactive: hide-inactive-beads, + bead-shape: bead-shape, + color-scheme: color-scheme, + base-size: 1.0 + ) + ] + ] + ] + ] + } else { + // Numeral side + align(center + horizon)[ + #create-colored-numeral(number, color-scheme, colored-numerals, font-size * scale-factor) + ] + } +} \ No newline at end of file
-Diamond beads
+Diamond beads
Diamond (Realistic)
-Circle beads
+Circle beads
Circle (Traditional)
-Square beads
+Square beads
Square
-Hidden inactive beads
+Hidden inactive beads
Hidden Inactive Beads