From 478276da56d6bf2bbe2ea068f703884440880f29 Mon Sep 17 00:00:00 2001 From: fummicc1 Date: Sat, 19 Oct 2024 05:05:59 +0900 Subject: [PATCH] fix: coordinate gap / text rendering --- Fixtures/outputs/category.png | Bin 1456 -> 64360 bytes Sources/Csv2ImgCore/ImageMaker.swift | 177 +++++++++++-------- Sources/Csv2ImgCore/ImageRenderer.swift | 46 ++++- Tests/Csv2ImgCoreTests/ImageMakerTests.swift | 109 ++++++++++-- 4 files changed, 234 insertions(+), 98 deletions(-) diff --git a/Fixtures/outputs/category.png b/Fixtures/outputs/category.png index 42b3501c203806f5dd6aa094324c8a571fa959c9..ec0c22177f4e85c64ff19d59c4164b270f14cdc2 100644 GIT binary patch literal 64360 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ⅈfMXb}FjHw^oeN(fCF6G4@E{O}*}3mR;v zZ?K)q5GHW%6fCIPra&QwX~skACaNrdhC3r`S*MRS;h+e2>_aC9j{*#P?^RPTByK2q zhtiy7j%5ys@#~-V!KesEucDBUxLLm!f(77zySF1TeRz7!1pu=u4(ms@yl+|w;S6k2 zvl7d$^BXw(esOX=$&1rlOqs~-aM?R~Wse8;>Ir2D5&cpE$X2>oIiMBEqUnRZBFB>X+msV{knq8w752 zs^cU+{b1>4c|Et0Qzek-T;)h5`)pz+!iZh#I@?!TpF#$?dOnJ2KMcH>Y*YEV`;6t*4z_uhf4=93AAck-i@&D|X1XgaqQ>Iy|Hj z(!z@`oEh9agZ!8Pg2wY$PGr-xNKpbP^`XuftgH%8j{E9k{)SiecJMEWh1#!aoA7os z^k23@WKe2}{>0<-12Obl@?Be&)n=smb?Zo^kz4NFLP^nlD>u4ZDmZOOjV?YEvdK<= ztGB8viir1VIPVPo`dBL2*LE>04~RVN`Z;6lBK=-YNtIXqp7Lr(#c+g@e?+^LT(n_J zGDG?H!;MkFFJUSDe!?@4>t=d|4<&Ke<<39M@$+bh2bw2_76$I>KXq*dNU-Bj@vc7i z6V1vcdPfs?Yq2l>sey0Zi;u4ti*KoBzU-k3H@{c#wVUXpae!!`s3~Cgr%sfkZUaa7 z*haS|YBQl!sH$Q-Uh^@<5|4`L)=$mQ&`JDj>Q$}F!Ln@}GoNf7QvcOYyo{TPUPOt< zLBsDl)`ZDnS+rw@-1cF{T_k>)qne1GbA`?GW2eF1VgS|ugX7poM?||yyolh8)r|23 zD8B{>#qrmviz>D4-{av5HW~F4&5$Y^ywt`oKpkxERzL*czxYZhZ2aTRlY$3|E`VH7MGA=kZUu=lQR4 zFY8=|eN2x9<{$K&eloCuJ;|_9aZem(<`GVr&LrrERXXOlo5V8fLuQQ^=p2T5`}9~||tcJnR+9teQ8qC@WcRZFy1oWveX?h-q-o=?Zvl4Zl=}~N;?21U?b@6YGS7(@@ghi>A~1u zj?7ouv~rEdBvv~7jkBek3X*h!=w2|(>LywS=bx0un- z2KMuSe$04Wwnw^eHZp}5^skVK^|TTIDVf`B|QKJ)3TzluKWJPy^hUt20*YL@7DChB-X_TF-;eA+PB zt9@0>w^z#B!b%s;-jJ)7rI~obJd8O*7LoK@$7p%ls|!C@^mw5_59BXRetmFZqZ&)Q z40+N1zLtkR=S&bf6ph7p##NBdZtg9JL)b7qbMoqT<{CnQzmZ0;KG?hG`G!1=y8R`w zCHE!9iCm70Jd{<95KfbzlwdokGVH2Fc&dmVrw7V(G69(h{1N?Qx8$0ch-2)sEl}DY^#p_P&+dtDgPW{ zPQ6TSt4&;x_+^d%&_9(c3m%N-Vecson?3&Io`9L~&=|A{DGM*UMn5qhPAi#bUOI%O zT-zt?k{|!kxuc+8oj_{zWaetb_X7#yImS^pECY>B-IP;+N4R@R!KBx32o z5l~kJTjDv2c1l2v`g@LqK2#)UyLlFcMc-c`2AFN9cuiilY6-EIOvE$9spB)S%a*1W zK`1REysU}+?&r_H_+?xu9Hm{n|4I8}ChV5^wy18!o3T#iuw)(Qo%UVvdPEgmiT*)$ zS)gi0gH0Dpj!|7dVpKvylL^Y%LGX!6my-@OOSiC(ZLBpxseHV?J(nz#aE@f0Ry z(kQ`mHnUqokHH%kLIQktdUCim^}Ye%r;KZpq(qfpfd{qqM(WqHE03dvj50GWcJq%e z|L74O`^LQ)LLsh@0a;wn*Lpsr9WDJ3eY=Oe<=dD!OE2ZWB{V!#jAo&w9V+21T85t0!6uu3p2}neKXzu>5JC#= zPL&u>@rC)mL**i0wyGPnrSB7H(kWv>3YH{F{;N?D3|5P{iHSN5%2@<)(l7h2D`k(z zzwp~EH20;Cu8Mv}uP|+(MLLBkedc4@?jK^H`*~Docs`CMtSh?=R@k?g-qGc&&+$i;TED}nKVkwvXUt%^_jTr@|XYN9ckD)f-M zwPd7wRB>6}Adj^v9<(+Fn!Z!t<>zSL0Y{a-%RbJgYzy7;yMeRu5Od3fjh+2Sfl%W7@P_I$iAM^>wVlH4}bE1Br+cf z1M;S|_PYragaOps%BX4V7ZEAz+v+S0$TAd!aVs=qolH<~y!V)YmT2I<7wiyz|4-Wq*BAB0f&H^uVPz6Xfiv+HV zu~}OdGDWtcLk}AEk$#u;i9pfsq8OU78-4ozjxMk5Pq9Ko-pDLvaU?}~tB&g=bDCoz z(Ocwb_Z<%$kv_c8A;j*F>gHbK^c&-;`)$g6fKt1h|4aMo)<&)2SNdgTiA_A-53Tm5 zkcxJ^D+T*}Q}_pA&49B+?&r3Ib<&wcSmI8Hu&GB)MWuf}q!{QqTge48b^SwdV#W+15uplq^UT?hM z@-UUYO{4i`RINU{u{yx0if*;{3(NF*V+7)_*rR~F3l-F z`TUh1PD=SAQI(0!(elXwlUr*$)W`x?mb}R8a#&E)-u6nSeSW}QnN<9$UL|09{yjsuIS$4!l?X}q&3yEe^Q*(B1{)3Kccb}&7 zbWbuX|5#<)e98bZ=jvj~eiYS?wV<^oBl4F)G$(%U3eU>^0^r~2CGakdX^J!D@^wv^ z5AdBBpD!;V0I`}Q+4@JsPIqqyH$C?_(@io=f;YWySL($_0lr`%&GsYL809C= zuh~Wf4@d{&CgRsJxn_#J_&{Afs3@{)+DVb zgArz+3Ehl(C2w;a$D&j;4apd};CLK%ze}|Bt?gQxHyQ0H-v=gSwinh5J7O*h3qG3Y z8Ts+Ja=#JxAsjtfo!za>#+hrMWuGR*R=lbho-deCaalk5V@KhxwXXC+xVyL}NL7=x z^s2R(rwa&&vM+}Fh(1ys7rJ{E=K+|NX;a{%>^! zUXfeM!Wjbljy>oJfpq2}Y3q)zXIrYl+(ClTLx%0sOOq@XXJ-!*y z%ZNn_?Kvrp0p_-qic(gE8G$#)VR2YRH`_Cz{(cS4^87Vt?#ld`KW5q<>v^|dzdz!R zU}s!ST;yy-Uc5ovrWGu{drwBRz3*|ZiT=|Edb!R@{D874_**L)<=sWhMYVRCpm+|Y zeo_9zyEw98vb(YznvOQPtz%j<0Wduna;JgfJB2IyJ+82^PI37 zu~*obpIHBam{$N+oS(FUB1aJIEoYlw+5C40iQInrtL1R1$|{IA&=fagjV=*m>IvQd z;_4Z8%T!82IoKTG)z@MZ(>}z+tj5P&tW9RY-YcYY>BPf@Ktg8`7N5`y@z*Ofk;)InV%7P9~)EQ2HCH{Rd`P0N}^743M+C#!<3D|N$%fC#=2zS_SrwSB{nEiAR@yufKM6YcP)T62mp=U@6yxFu}jRNHBpB6u@nepxD&VNu*?sbtL zFRZYN_VMxsQ~9h`D))sC&yfB`p4@df{OaG_JD&s67Tv@P5-3fry5UN6!$;q>4^i#_ zmfh9^;~I2Rv65k+%iHa*_8dUnohDAr-aR*7$ONXr6>Rn%K(D|^J--8te~0#pVKfM1 z>AU^al`T9XCI3Y^kzt~%ZqB&)tsJ{?*vL#PxM+HkA88pY*HfY^xNBVF^3E zEr7^-j3Aw~%3d~Zyv7fi*0T?c16zDnKqw$TWZR5tI)JL1@ekBt!;`r{p9hjc0SF!u z?2%)l9slboSeKGrQB!?i4Va=0HdW$k*QWcw4E)+x{0>@Wb2EvU zVXRn8WO_crEM#&YtUWL|Yhj{h;%3@{Rq*9a$hJ!F^FAzR8Uf~4M1LkNO|D{aK15b< zcBx$7zSAb7d-vr}%Tw;6^$}mG$y1fdI;>gu+4vLV_FiwdR@7jQG4dsfz_WdZ5-Dc+ zP`-r=Ma$CgRzaEwuQsiWGr>4ATPo25t14yw*7_lwL6`4}ibx(j=5TWHlZ~Li(;Wrk z!>0GW>xBx+Z@snZ45xT!mjUvz{k2*s{TGXW+()i~{Y}=Rr!}s3tcI3pJ0fXZJFw|B zT^OH|e&nkoNwffWHX?Eq=)0>w`(*+_Qt;p;X7tl9#5h9-7iW$<2HZ~5h1E0WO_k5C z%&AVy72a#TPs9#-?)&GnA3AE@268LhnFNqArxJP9#@P4+Va{vk>*F!Q-qKj7P^9c} zQ?=&L;;cYQ%WLNVsplHyLBL$Z1|DSOm!aaudG=`3&Ij~Y!+f8r!y0*Bj6<-W9<9fT z(~vovp~tT!X_*ad1Gn$TwJFm7B}FYD`{+t@zxNAyB4iCdc zw@U9Ux4qUG?>LF+Ri8zp-(H)X?h;Vod6oSque2Eso?Pp@ zA$~Rp&-p4SaF28~HW$`0wr59^Iy)Yy!Vsz zo@%~Nb#h2`{ioTgRKK(VK9f|TdiQE0ExQr`%O!qc`r?CKs|2K%#7Cr(b0Rv*0?0H^ zJA9Zp?=W<1`6}zNJ4z(y+``79(~)So(L&Vk)Or1CCMxs=t35kj(ky_f5K$mSm1XQQJGKN`#{p0iqkX~wB1$mJmfOTW4?chdlgTcNObSJ4RQeA>>Ry{nK?f}7OC_@GU0#r$GyiJr3M52k6dyYZ2l*`Ug#XEKJQU zu1exB56b}{Q;`N5o@X_}Ie*1^u6F|way$Dv7P;+68bnod$4;ns4+VY6Sa%p)Y=ocY zQBN2Jc7m)XNmS3dZ8!{!G}OJ^j~*S=9*$N9{#DX*GOJoA&HqVYI@sg3)s$+hhb?GR zpv?j6AXgrzpO-(XYCbQ)vKKUprueyMM$6&uDn3n%{#Z{rD;L3CavC}oGzzMz0GHrs zA@&}4G?dpu3;m*oe%>Nm{p6znl?)YXHy(PM6`TiII+iEa7S>a^q<7Vz&!GQc%)s_v z7B0y6w*PGc0Ya!zt~I>5%Ww_sc?ueD&svNZx0V{~%8^T{Ba&^DZKPKqa{u(rSMv57 z{6c@9%VQ0jr5fR4>}-^7Owmv-YQHB6Jb|Q z>Vi)KbS1TjoHuSsAy=5cG)@BguWIY}(s#Y<6OM$ey?@$YFIh<|Q>uUI6|?i)+-1KF zrH80WK6h!6|Ned~U37tbep^`^i|lx9;P_jrGp9 z?9%PGow$OzeLFucYQEc|WX1*)4a2e*D!(bS$^@T@1&|#Bg zbhkz9Qh@)(-gkyIwQXxFMG&Nkh|)!cO-HKKfDM(V(z{5L8l<-bP$`NumEL>rO?vOW zcS3IgB7_h~fP5?Nz0W!Ko^#K)@4xSRp7SS9%F0@E&NarEqrC4}1O=P`sdaUG(5Bw4 z)D$0I;{6u<%jN}w#>6AIWiGdonqZsP!EX6d))rWHqb^Z6Z|?e=&9D^rT<$f(AS!(} zxF8K@2G#J}9TKTkouSxkVYYVfviqfZrS6ag)F z$I@pp-{-sbG^Dxo*5Bs%%7=h`=h816!yhk0>qIT780+X?NQsY|oQxW+fxJY^@XAjm z+#Jhsl)uBO{``krC*A!q8THA&Y+|Mp*syXZv2p(&+@jZ9Oq{5R1 zNY8psf6f1k{|J);( zS5#WPNv!u8Z>u@+`}^w3aTBZxs13i-$6wwzgk26Z^|Dd~{s}ljr0BxdGbBdH75FK+ zo-7PnKFQbXk&)&j!IC3?hq;R7-4i~h8pxl$0RE;3|1jEr{_YhuAaZ2dUq1hR2>cZu3~uiDT?`yZbW&6>(SkRs%lt zfxS{RllZsyeU=v30C~XQa6fSG(tPm@c6t;e*n1V4-U&0eU6I$~0oNY4hi*!xg8g_N z4fMrtnN?DoJfOGDAzkiV6bH8Dh^CE!jgVqLHyOA2&>oj?t@ObpWl`LbSd!=wgC3ne z{O@df?JUk+TSz~K_b2`FORd0#_IjB5#@Ye`1+DM9`X;3+?5v$_7=S*BaTU+$?2+=g z7! z)p|%YnIp;SI;wH*Yx@I&ThBvzWDMz|1yk~iZDvB8I)xy!s#rW-KL>eGxistFHpz6* z5h#9;$8_t`eZu&sSRI22qKiZ$+D-9UVE(%5O*7?FSE8Kn<%bx-k>Dqg)7?^Z~wX~a} zS>Dg@EE6(y%{>X5(A^7hUi#&}H}PK%v#YT2s8(eB^a=0GiSf!wK9C&ocU4XGm(#sG z25C5zTOj-(>~)JjBDlt=EN=-%ci#BEM=mgcM8+oXh)Mw9c^&w<>50YEN~&dAI}(hs zjdX}T`@yT8nOeS)E2EVoz6!7Gw^vI}-}Fr-(dfVJY6#gNLOp?1Lk4&CmM$BU@pl=1 zG`}`=d+PR!*)2`b-D#~?f@X5^ z)dFp46KR?elUK;COv=1EJv)PXQ2HKcBbTMf{M*|1Z}RNaG@m7CYTVc+f3`>=9~#!&BkhFyai>;_w!-o~W$agP#T zb^Q{yr~r_*de(?_Q}wl13Nc(>N+0zT|13HE}8B5;=e z0&Q9bjob6xGu3CPa~RW1ccjLus{Va0waNhWjRhI#?S}HLjO6Dk8`po-e2ii%4{QqF z3ohhlE4}00eXImPfftAbn993Sbw~-ApDdQtbz;QSsn<((Iz!&Rbqnx)^SA=vJ%E$o zq~omoL|E9=bFZ%&_hJ{QK-3*GJU;9?#Sc;U#cMnP(ZTQjk#}gn0M%;Nh!vR^qQoV% zq9rnk*XvFv`AO<@D{|RA#j4KJc$7R&W0pvb&2$O8it@kPX5@Gtn`XuDZqXymSQ?EotM6c^wR|mKMVHQ#3fH10f=-?)j1%Plg3`&rGJN@=n z)#OzZ{H;18Z;Rr4i?g1koDdZ5y7KWZh;QY{^MV`?^hINUTVbABd_HF&t3Cu*fvU*JJveVU zd4N^lnmz6=D#)ps^%CYi;c^8lmDN4J1+TdLHEB}QI?oXHs&?$U-NEQxDRV^xjKh#F z^?_7A06501QPm`mbvh@}k)T0ivPVWO5;f6nF^aOH9)O~61 zZ%a?>r({%a0Bun)Zc}f;HjyhS)n2hy4e(u*M?FAlv7K=GZTdL&thj)gwYwu}ADBFf zxbI}8bfot=H}9>6U9>Go<8}Yo*#1C5=>`8U>tQAVQ^wPc)vfbdb27T?V=f=di*@fZPf-gk=|r4ij+s2+}_d__U9ly_Y-?3RvcZw?p_K zj%_Fq5LQv@EzR3fWFLS`fiDqoC&mTSZ(nwy_W0GqtUMQpv2-K6(ey0W$K!sUZk||= z1?$qv#_M`%jnTges&CgMzH*gV?dKx>-Nqpp)8Nckl)DbwBSh)~vd8Au(TPn`s*uEf zjzzp=`U4l((!x?UW8->r#%ate?Nz=5^}2x@4o?jIZa>1aw)56d3Dd3~F*|!4HMSb_ z=EvT1xF0X4W#(fGK`HMRNk|$D?JmEY3+^;XS@@xJ?NTtKNNUT~mM;RZx8-^9k4zAA z-mjL@Tb_f&&VVk?K|Ku|Ka$XNTJ9=P_vDd72*1xbf!XhbE*2DA2GY7V^7D62r zp|7cLd3oO-=g%53AMA&&N29W2l)O9}N3kdey+DeiOJ^6*TJ4RoT3oe`_?8BkxHU7| zUfrytq@af;PUCCq@>N`JuPfmxs?3u0a}ij*E!n$Pr-_ecHgZt1Z!k1|xwu{8D#WYc z`~CIaoHHR{Pq4K>87l(M)3&O2Fke^F>Wq*Wcyh#?lV>VUoWA-S%0G=Pw5Dt>XgAb| z`D8qNzZ1phS9Pkgn`DlSK2q3Iz#QkeW|nRc9uc}k@U?|Li)bwIc+uUL}K_ z+zUrFZ7=)ze7LzrkBdptE9}&nws%c4FRZ+u;&aVxik)i>6RY!zGT;uShc=lZ1(3HB z^J7*95lX$^htGYngyN>%to=+%7Z*~opIHJiC4L5=T@0v){Z?uKtQDM{Y?qa+mOQdF z+W~V?YYS0s^jO_4(`axgsMn+y38l!v*WC*kscL>@)?Ac*obqN0sz-YbW+j#Ixs!6p z#gXuB+eeZkM`(>Vfq0uwgtz(M3V6X|Jz41`5joq}B4kzxMW#tX6R=YufNw6n+}E(} zJA0UxNB$b$-$(WIcul6qGn;{J{meSiXpQt;?{~dSEsqEKVG#LB4>sF{bb0`Cec|Vq zbBn_Q`;W&PE!zw#_|kbuk#Q<}9=oD*+!Cjf^wP$f!veV9(>l>Zunm6cnWzqC5$ zZ>OKf@M4U@O1Qz$$mkAp<*MB!lRjgd_}lwl0#2(>2`#00QOYmQ<~{YRM(9(M zi<9Ll7|Ho6yeH>F5VA&RK9H#GK8SmrKkJ|pjAipF+8s>~}BIlFawD$qYY8FmJZ5Pvt<>>o?IuPtS>J2-aIUf0c%dqpFcE7GX`)WKOc zTGC~EI-(RZN7osw22aY)*wAWxM*Gd+>p{8k^vPE#ZCt~|NF`YqFWMAtDzy^>G^%m%%4>*Kk$ndmCO+IA z0H~sR;w($R6`hv~ZHi1?R*HsPkt#1gzq#Hqq^X8ik)I3k}y8zmAiQDq>vzQR- zZ-p_d9gVk+XAUM0Ic+-VLzA?)0yxRmo9DX-pP1*7Br~ckyapiLLY1aAFNt;c7+xCq zp*m?n1Bg3VZ?R?J`Z-VJ-uOPXXtUHz8TZ=HBZfZ0<6m50hZBGbe2DRWaon7)Bvr{w z`Ht${LwTju{CxbK#cv z!>+HyURGqQ@llS63$jTyNfVwAjqZ>RYRGVoeOP?|iTK;tc?maod{G5zBV|~Akjl0b z$lRQt#r4+d4>(Aj&T%Hx(Z%{`B3`A`^F04#RESXLTmNW%62-|Ig&*FVXAd>xGajX6 zpb<6uzsSQsHu03y3T6ws!4WDSF*9(_%kz#6tuk*^od=s~OO5a(g$7kymQuWsip+5# z;~%8gk1>OWy=Yzjohj`qR2<$g+4m$(%2o#^w)1p3?~{%j-hY>}fIBGctCcqGB;&27s`k$RzQPEWuqq=`E9(Gho~EVueC7kFUoSI^ zr6y!N+BbFQGufIOPrXmJy~^@ZssTkATzx;w$*rJf-y9#1WuZFQUHk?cBq6X6<(ecb zuv2e?FtfsfnacMcRX|-;>pbK6;PIrwJCr+<+rRi2GhwV=!u}?X*01S4ef!h*^k_#}d@8A(VhcgGUBQ5Mw z6vz;#HqPJ2!xX>N%_`cQZd584#;TuPTdd&PcfEgB{N-y8VxvSDl8zPd>*Aaz@i;zvpS!X-~;!vTI?D2VLp4_xsUP z+e1Z?381?CemwYSW&86v!-;7}9ju?{d+3Yy@bhqJmtt{)W^&+qjUPKUXE4LcVnN^CQN4zM%e{$ zZXq6ocw+5bw#_eMZEjO%Lg}X;r3$x~a;LSn-cvR*pUy7PWSN^5W%B8bnx-A)iJLd> zdsXi=noltpppFysj`TB@Q9U|E`ro;*2slFO@s9_j3IOHydoXvHpoSRDG$hex%p+Fy z3omPlw9#(BuX@`msVJZ};4=MDG2$e{cShBmt@O)3)hJmDLs)Ni%2m|#JSj-gDI(V5 zk4~SkeG%_Rft$ZH^xBP{DO ztGt&yc^R`E!ow51#?&49R!jKtA9cna_nz+Wy9#>{Q>zRf^3VANr@HOny!vrXtbvKf zaFD?j;ryEq#`ROkg>)W7zo)_T-!OG46Pib$5t$K+ZWr7O96}wN<|#WOx+y(_9^TadB&Syh@Pvi!}M}p~x#Co_%9Fz;n)pDTq2hTUp zX6z^$WN{ACkf&ijjXkm}=jX5g@!O6%A}hiHn%R*oZc#6xmBIR9x7h`cNmQV6+tylc2l%(e59Jx_&H2ZbQ&b> z3cm-(4kjKYY64{5(s#XNsgb9qYEaDvS?|Dk@+}vyorM`8QNW!t^{pYkuXM3JF~M`M zXrQXwKP+2T5A9w$xN^%VG0H4$pjVNDKjuafW%NnKghT&<<*)rFJxk6ygSDVy?r`pT z8v{@wds|CT;4BNUxhm)R1&F-OJHFowgpIG`i&qqZPAIP7NyQ&0wJI;+U zy@RLgWo45DI)6C5=9|azBi4S?AYhy1`}RezDIo}LRp%fRNO-x^KTb9Jn)_ozkwBDq z<8?ZI?k|{3MN*{y5B?ihCqZIQc8%zX(jHAk5?^{kDJ`}0*@ZC#ggz!8|DcLYt}USx z6&2^LaUGrq95V}*=>6-w{}rSV}#R=+ym3X&{~1`n&KLt2g}c_DI^3u z3^h*=D=zg=A{Q66H?%ornz+0I$OCADiIv+z6r=)YyRedN2~3qAdC1~fgIq*}-A zoK9w$G{$;*E=X^!KiihJl@8E-SinsDkRfpS=jKn| zwb$>tn)dNAKH1KW-TfJfnKK-|1NpVJlcs$O@qpB~q}e`fO4Jd#5k8ex*2XXD@_Z%; z)rF=s&#dpJC%QHHXzpiU%ulWCd0n6JK$;rfyl6|uzUZ4OxOg#;%q?Yg3Wrs3o9_P_ z%^POYikKhdL=4Cv6Jip&>jvw2{}%SfM!8bLz9FnF1hi#MexZ?$J_mf57&24W4hg%Re+$SGe{Y&OtvIRidOba4X3??JKRD^E zGx<1*^{Z~B+?$(fy$d5E(~_e^y#Y~Bw`wSoUjk>^#kus!AcnbDWiGvu3gaoC$$9W- zi&oLeP_Mbbsx;lyWHlU^BI|s@5RB61ltiDV*7mQ$A*gbmw$Blt%OE3Ui_oZ?r(HrifnB zq-Wo>iAxMK2)b8^sN^xw`0(+}kI%}@Bm+H{n_0BJAdC)ayHM7gM^ZFUY;6=Ysv#G{ z!jcmvXtx(f*dQUtYH?To*$c~YSp(aL4A$O}I%biSP43g)5;j*Y;-v_k+Wr06vz?yzeS1K?7!C(df*xa;eO)6kn!)&s!Uok zb%iD#Pky@lhG493-_dZ(z0CO{WHPEox?+Lm=6CVmuDsB;695nSgnMtCNvYyk?q;RX zJ#6GAf8>%>hDCCxzxj3=MouN6NF^@H7_)_dI~y5hSk!DMi65TFZ}gK}?@W95o!{;q z!=M<4ijo!z^MZ;JU!~M=Cfx5Xv|n!QRfU|5nG@G^nP)aPFS1z$nL?xX6J%wTlAIjl-`QTG*gmGtL9I*8GqCCKI4IS*+w<6$ddUic+=f7oT2AV#g?Sp z7Utz-fO0H~b*M3k^7Zz)I=YynzD2SMUK?t;*NoSvX6I{leZcPU z&l6X(GcP2psk}*6FnE-?UNWe1a%jrjWHBO}hUN;14*6J@6B$=b=Jgm4k9hj~6Szph6a#x>OhP>S=TF>lgRuMPk0Tm@+Dii@wi8}0ARM;PuI z8aEHo?$O$1=8=6ABl7CIV(wB<{>U&EJz`@ZXjGcH8uy&$rRclziO$wrS?arW>M`!QEq5-ZeUV#|2+xR`D7Wt)FBv4e5<$L>Nt0WzmC} zFKI@{zHmXhk`tP3#}=medxIh1E!ZxsA3Luo$#y5dRC%p{zS8iwkgk_lmhs!U{%FK> zW6^p2r1FC;*p`@loE*?zvftSxcXsTQo#OkK(U5Z|wPw^}z`|JW+2gz(E)2%>)xn3M;Ip{50GDg^SLn3G)#S`0gJ%5Q zO3_~|yxni6S`1J-fg*DMGoK6L>*eCxCH%FXwVpLTPRHMuXob|FX&CkFE%Qdn*-2tc zRU%cQ8aXr(oG7p30_{c<4UOo6a9ZNcTfSDG*HM+=Es`1qCP*8KMo)}WXX%k$e|zi6 z?E_{R7>*K$AeC8ozfV4=P9WOio(Dp9YKh9cu7UTIO_-i#6KW}8N?}oFZSzBB4#LI$ zUM6gc6s@rjixTZr#oySvl^GR;xg&+eyC@D_SW;wB?!`qM$|{-;p-$Vf#o?g{r_z&Z z=iN=$D})^W-NnT|3#%}6KAEXK!Z}NehgjL)46R(6txBI<_Mrp*_UC9?KZ&9C) zOWU_j7EjpB^L!#`qo~F%5^yMQi{^mM*&L!F(ai6ZVKYdLquk<4@vS6GEE8SE9bQcy zO(UY;zPw0^g|ha$Q6vl=Se$rZTbiiF8_ENq9&c-T_1p_(B`b&3oWV+(<3%&Id$nIT z%ig&WLL~AI^Y6Y?AP!vZ3w#S29qW4(qfN(FdNyXI+#8RAjvwa~h7`V3;MB_q)QYgk z-W%RdLrRbgZsmnaX?#<*t7ZhL?47mOGxJE(v8BpVlo-YJo}`dI1#VxHCJiYr>E~mY zV=QGF3P%WX!luMgZpJah)M%2NGH1$ST&oHXv!%J|&iza3j;CbmSPb1*w*zSrc~+lC zaGl4&jdN|zi&B6e8~=^Kp_#obICpsF_+$sZ+BBvO!@ zE8}Jykn7kkuG_5~)ckzaPM-^_O+&;vZMbi_*POmTEq-!s6t}X(8lXIII*X~QDDl4l z$iwN@;+7meX~W@8&>;LmG$s==4-p8}-YbsZ zQI}>H-zj!%$P6O4O)hK}JHJr$3MHV%de+ofc=$t_<9K`5V&0>TB}jaW zS#1jP-Q7bMb{7glu|pxzd}#)Ekpo9;!I6Z`LKtlUEgz&T4Cyj3i$M>IhzfTnn!;g> zqvc9@W(AR3u#9ix8yj}~vQD5YRWPOiZ$@O*J;@8*L`P5go)P=_$#RxUq$n?+YB7~9 zqdn>?MMTj^6UC~~EgHGkvowN@W$QKJb!(aHKV;9aOf0D7hlm|di$*d9(~`0&8lDBv z$*h&9^Nm0WyTc!;ZP<>pqA5@v&Tge@-x_0Jm%GMM1e*WdWGMQ;HIXx{`4sphj2(f# z!ia5XNyG2I$+MMw$&kpWg1=DpP56*Nc(&-gs(hQ%?2eKN*>=l&esSX_{~`r#1#OJ{ zPpH-oy=+rKUx*o8K5NJA`Qw;vTrox+!;XKhL1E4#@&43NsXZyc&BMr{t3_w+BN)NM$bB+gjBf!-D=?Bz-*Yc{N{Z>{n94$9*5x!3AKx z`njC#G;+BB%`0cQN^AGVk3OY`De38%f2B8a8=kz#fwMoSx&Br&&?BBXIwiOJ3VPli ztAc5N#$3_rD$@W@nM|wBYU%bH6?%WHI}j_yGdGY@{qXi$E>0S3c;kl2(Tb#XLI!N^ z3b_k&_$|19`N#?#6`uDS$=t$H+QxuvJ0@m|-~`?Qt( zmHfBGC1`W+lK~9BG$O0_u=p9HaN1~t^loQ19hji|um}vrY>%3}l#)F<)yuVF*Il$T zRDQBAVLM$V=+i#y#3K1q1=mnmtTlx}&!=Y$yi!Pt`rt^SaCk1XQGIh^$lWE3dGidi zEuQtBDVPXlzC7X_^N;pe63%uDN816-8u6sajwp{MX5RlqP5fFM% zkRnFWJPY724M{RT{RtH!Pjb2VQ8`=&FTL6~+ZcBa;t!Z0to92d$jC^&+{axMzHH`z z46?RhyU5Lr1odE@Mg2x`Np6i^Yi3%8Nhz6mJfhHQIS=V-@6^yR=zyy^rAd;Mq6 z;9;t0WRW&JIiLJN%?x?0kP41$-G2E|a5!pEaa+z3>%1g*t zOC?ZRnA!D1Mds3&InQQzRDW>HC3`S=Xy_OQ{dpR<0u>hA&b^3IB9@mATl&1S-^4Pm z|2dP+k2qFJW2amfthIMAM|F5`5QJ>q_3ZUTE<|%WG#OFdpeY&iF;Dcr^zcLWZ3om; z-h3O$ff`R9fej_M^xJ64;(OS88@&ni@Ld)|NolWQoLWGVe6RO>;~t=m;LFSq_bP*K}-^hT~IS_*e#2IE)S#hF`fcV#{s`G{`ib6VNt4> z3ngWmTEy2X42F*AAhM?pEkIa6tXSL72xr99)ou8A9`NXkzJ6Hqb9iac^q9T&xb}EC zwjnMlKr|-1yOl%h^w{fC=Or!jpMg|4uqt06!TjzwSk)?=`p&Tk`lzN#D}x?kw*$`1 z4Ex~EwE8IicqEHS@a~mp_`w8=Kz>yOuCsPv7@LMI-&32&+h@u1;=K|tb(sE84I}eK zz__=8f{$4@*Bfl5Xu)^0MzS@>_HCUvN#lni@5#Xg$X@=Hh`7TMat%>&ozZ-I&stm- z_PdaR0nFT$Yq+yQrin7{imvl4K3Bz^kQS-CnXuP?T3Jj z9S|!euZ;$od*u^8QTEuVaqVa)2>07-5u~0u6hBEhN>OQ}t9Dy%H++*Ec==JErwGCh za<(Ot>3ZYsh?!>8RS3kxX+xnE9v(Azvv+_Lq0q{Z&O2Il1Vk8mScZE5SPIC`-6Q^C zm%~6`&~eQ|2QTNnR~IE)oYT#ihi&k-dPszTC{QFA+mZ2PLc#e2)^TmTJ!Ya~K(aAf zyteatNWDlUPEXim_a0-=sNz*P%KsB@%aBNtr$6xSVLL z%$m74b#3ZN3%B?qO4+T&QD@(EHDsdh*NjZ5Nzi1GYID5h)i)TD(I|_Yt#5>aU#&^+ zT@*mCol8Yp=7>*5CDlkqcqg}tosVJqy&Tus>meLXcRV$YxOep?D)S47_G2F?$*-Kj zS-N`4A7f@9z_S-$Vag&%uwTP*sIavFkA_G$ORimDgFE&y?(YxRD%?)4N(kU`KVc$J<5xsuQ2~M(kYV6(X zG@ht1sgBF}bjNv8%*+;9WDDu@Vt^lO!b_g8s&e?vXQr{ZAGiQdWq{v{`NDUD@zR05 z(EitAIg2XX-cdaNZ>lQYqH8;Aduva|o+A09U3DV)gnpC!%_g^%iHgJCdMVp4s&mYZ zuAo;Bj%*{Mo^L!lkCp}5G1!m#T+s?Hju=U+&h5Sik}YE~@%%ChpB~-XdbHz)Q*Vs% zNrAbZKsMe|Ct-$nSqn4?{cetEV@K6Xa4n+(eIpz5i)J4tAK8X8gJ$+`zpR!}RrZOW z22E1b6tcC_+*A|H?2s$8Z-}({`ZZKo`29BP9hSySZ`+ew1GkLj6c-HKT_3$a)@Ei! z+_+@LWXEhb?!?s5nSXBU1=}YSk`C{#{#I+AE5DARHDJA56r?z4xV_nwwZ_TMnr+=- ziGEWX+1vhv<66$0W09aB@jT7#{jQM6K?q*FIAD&(sXs~d>+v7?o1Sd1h#PX3g0 z&$O0qwqR(IhgSQbDBjz-|H`YP{X9VWd58#q`KDrcw`81Li^92nEF^u1! zY1;K_N>&^MnV-GqR~40ZqY4eR1X2$)<@4k~)C;8d;g1K{6<7}?2-L}=vk_769=!P;K^aDfm^YUBC581ctRdVQm?l$t7$n9ShF`-<_=cSkth(w^JJ zG&S-i;MP?JPfDsCf~AZFizaZXqC)6Mow-9jB%2?9PHAGnF)LhV+gEr3MuW-TS24Y@G+&DO#wYc$e< z`B_$ddyQQlfj+2mTxQTkT;Zal>{YfACNw3y{8Q5e8TGVj$Kfw(Kz3A9g-Ic^8cjBX zlHJV}&0}^QBMV3Yq2~3ocQ0R7GE$Y(UL{ce?X{hKKR30H&7H1a2$V{*i-n=H2MZRn zcmP)cXA{a4t|P<=7|hptR~IoQe7m-l71iX?$a^z$THjH4z#l%p*Fh^N%6H~`WTeQ= zMB`Zuv8lf6(Ep=EYqN>^n}5Bq4b)%pb>Kdm!taOw`p-YT#Mb)c7A5}ep5eYZC0OKzKIg2IDG3hYueWScCYF?YBsxo%-FOFo{eL>wpWpeK^UL`>6^6;@ z8%8u)I>Z8cZ=`%YzREjpja{1+pw@!WNJyOa;=4Kkvt=)SSp1N!vWvNXZ!>P|Q}mwE zk4F33R+mZclia5fmClv@zg+B)hE47cS1z{ns=7%a0)4<|CIzM$I;oJ;!_H|90ZvXYtnSBEchu|IOl4pQIO^ zx4j9jQNQm0*H)Bm=q(=g{C13&^e+yI!S|_-k~c%+rEt2+^EUCC4@IDTyN170df9+S z%%Y=m-rnQKFM3lYdlTxN-t?#qC*GYYCeT-9n24&zJNSGWK5zxa`7#?VNwR2UI0Ha? zLzE2s#ib+z?$rN^_D0S2HN-R6`7hJn039Oceex>}{BJb-k2M>42?&DIoOQnNUlX*j z_kbL8y#^Z8{iW%Kh2UqqVrY`^@3r+$;rG2pL`wbaV$jRKc(~IwV7AO?2Kbl1CN$}6 zfR_KSIE@$Cw|-1&|8-6y!J}7AZ`A*q5dj`Y2+X##zC-d?*!WLs_fK=;A_atj;Ck9$ zv}s$v0kf6d6P>vE*EHm*JRl6$if_>U#lsZzuju0_0n9q^l<#p7r4-G~;gR zl+JR8`ng%7fafQkQrzYl`R!cnMLwC#rvk~7HaRvK112*SzoTCPS+ zMz~yYIYgZdPs4ZMNK2q_EVomWIdkMs)x-E*Ywxr43F@1C#({)wPY!LWZx^Hl>nOx` z>-XKgScd?l4bol?QqoF#E*gODtTRe4T3LIw@MN&KbCV_QHaF{QisD0N8^*?9*frnJ z<}&ovL37cDB@~umSFl!*QC{-6`8=p^f8Fh#(du2?UC??UFJq<6i@xjAkb}L^Lr6Ce z->Qt!N%YC~g=EM7a>9px5UisctM#*>rBJe>IP!VF@j`{Typdt}=40vb`P2CpT!2~v z)nti!`wgJ03w8EY?Hyj1wyP%-=My3B>{3{%fI&`Voph1xpm5b#Ne;9z&Z=5(`&Wj? zi|A{wca4WZ{nO&L!Jx?5UfZ*}E%FU=1yIs-H;n7lU~k>o`a5iG{q|hxcXLn{t(U9^p(zrx81F64h#GVG&_8 z19wHw)HxQ_z^!d%Fzn%Yp0a%^w-LZ@g%NI$HhDaDUBY5QBI9P_Q~IJHtt6z9uC@>r z+bogodXd(wW};eRnt`i!UF#d>r)8P*v2`|zcT(}u!&`=ndjfGC2^m1hINChwOKEeC zwD3Mp)?ecZ)pP%HPEuDnW^NZ(bqLw{c$*_WA7K(e^zeN)A4OsxVV^L&MLr+;u`J8^ zK2R^^kdZ-b>F6Xr_0DT7K(KB<2kCu7yxk#}d_ryyu{pBH&@ zGGvg)_0i{m>r$9YBSikD`9X!FG@T?A5?!CNmpWafCO%)vmBTLZqwmUQpS!@o=y5Q6 zVui=sbbX4(^wqCl$+X7HlVwwDo{`!p`@&97#Iv?YH0y&rL%EQT_bT=(xa$k5KaQ=N z4bXm5m{f48YvtG)y_J;6Quf&H2DFvb+5G7s+%rTU5p=2m*;ex@>%KBt*9nx=mKcZA z=3Z8rYO{N)%sG19p}y%j9DOfF%=gxV2l#;I>9a)*oCaw9bNtBpcTLQB6EOCo9>WZT zh3eLR zckiM*p+m1sIBLQp2%z`}OmR(sI^ytV<_0 zx3-__cWz&Ob02UPUX=Od`2MW=;O&`z-nbl@QuT2VrTqG7znX1*gE9&`uiiWF;m~&( z?}#Pe#Rp$k(N^ROnLJtg67rKbgsk^?8%I8YH*aw=JJe8;x^H}T9qiRSvHoTl7oTwn zWQaNbs-z{;#qx9ircPp4U8;Lpnay}xpFhc9>8)T^NoX>qNX)8IDeZ=}|F_#iSBaE} zwnE|FGqSVS@14{*gUq*(>jjs=el*GLA3kHvJ=i~_ewf(|`shA%WDtt**d)jqbyNx6 z6c4Z<5^fPs9RF=ronb4)$=Ro?<-MPuPGJ`*_#-_;mlvyYqi&beR`$l)A?!OTOSU}I=h(}koW z`M3@4V_UACnFr)-Jx9-eHO;*$lL@!xBHn3b3Qv3UokiZ>3CfE`eLT#d(H;v z8|62zgD#WCcuvm^dth8ru!j zFt+Sr=J7$uR?Fz zUUBeS=(gAPCgZVx?z{H9>@NQhXOr1;%$HEgM2@K}T$E99XeObZvY`Lp&XwIQ{ z4DT9?N#>WB7b)u|1f|NsCR(BOl2^jKN)NlyWpm#xYTvz&!y(q2=@@I(8CQ_5iA4w_ zFz=5Nwo$sv){&K_-<(jJc|p$21`ho`;t9@bFPBfnV#0n0s&HKvLBqdKN7H@KJRF?_ zeUnr{+&93n!iGM5Akyb3XT5F~l*~Ima8m6w02J2@r zv38*&GUi2E?%2@T!C_n1(_08CW4fuT*{a!yTyF>=&!r7YN^7er$iUuUln#3u#}_iz zsklm)&|O!XA_s-^d9B^Z%{8Zl3Fps-cE48>bFB{QzO}5y`cR5Kf4(ITmshN1y}#a+ zDil$5?$Fr|*GLrS1NV3++;O$tbX8qdOXa6vlPqybnIiNk01VI; z1P~V$Jj~&Cs?N2IN^7;>_k{EWz$*t)m3qmw%4(0CiSu_r#yG|vX__+W(e&`x$I*p} z_9)RcsMn91VBgxNU}B9Gk*vl{|1UwDhC60=@~7w(Aj3wWFi&*|_lJ)coxLDU9&FNu z2jDK&oY1_4#yQJ<4^c?e2>;T#7W1}yxc=tE{hI`gl3(%|*F)fJ=oPOP z`y<{Zslw9;q4j&F_TV$_x%RlX0Xb%61mxeDGYzJX&{t0-?A?CO5oVn#Je*VhI0Wvj z?{>a-aLy0T?%B^UvFw~Smd#q(5gLaicqZu408QHw_cA&^3W-DKl)hPtiUCjh-G^Fs zXQ*+Hc^%a|!Y*U(@4lG1rEeEEZX0QpB%^qAW$=UNj%SOw^v7TBX0ZnGx|?I&a{<}j zUnrU7TV+J)CMON?_eQ4TF9|RO4iE z9+5o?bRlH0{DJREG(4!7I4o?$`}+cm8d7*`q&w=|0A^HjC)CVn_&|WsSjBwA+r;TIXuX*H8Ya z2Y<>g*s(@eRIOfYdiH9(5{u(F6|m7=3BJ62Y5b~0lSEVSRE6+@CG6&D1R)J3hqu z`BHMScq|L_XrLV(lgMXZi_=V*SHo-a;p`28IW^cY5UbD+C5n8oT>Jddyh}zbZfN{r zR3r&I5*!0EB;mhPb=fi!x&1|}mAfGbsF%;WYOeMPzU~;ipL*TEL% z#FyfB)|V@Byfw*(@27!1mGhyjgmC7;>S6yy7*jIgOC_~O9AS6erAO-z&^)b8mjNY# zt(s%|?7vN}nPmb!F=yUelRNo-$svH{ccz6aOm43<7MkXM=hZf#X3;_zqV0~?oetia z9ks3!a%{lm{8x5YJfkGGmCe01>j@tFKN%nk1vxhK*IU?WJDc&=3h5QCs33L^6SGfZ zni90k>x9%;5eiJs$G6NX11;P^j5ksUf(}soua9P9Peo5qTw!V(XVQ=bBaz)yE{yBq zbo-$HMvel=1G~9AY{KmoUKT|emla~KbBh@GgdC-;>L&Trm(e zqh{G?e;AZNrf52x5%i>O%lz-Y4?F%U??#X&{AKkG1a zvCcgDrRdt&XGB<=-LWC zSk)i^igw4i{zLcCDAQ{8DpXwo*k7O&QVcm>HaC$F9Y?sT@l}=R>rno>jt4oAC|JN2 z0sZ$a3EsS1N=oIX^ zc6j%4U(i5MU(bWvjoZuKp*e43P-(6K#)OXZt+~)LH{?AH?``nFn)k((-04;G1u6$M z99F#@heHEY0T+dFmFLWtylj|?mx3;D3SMME7@!Tc9sMmm4=N3-Q2mFGBd_Sb3fuiu zr**in$*VvYjdL!KGw$s$C7UAOpqt=XG++71*f0lanm4}CQWT(0*Y`UFJ|u9uVM8%7 zDPk=zRy$S0_mMe9!3BCJ|K7?IbO!_ZKom>K;i`>j!nA-C?}Bm3l(`jZ-FkRx*h31? zqqr3TS#u=DJlAWGH5??JI{`~~?WlJ|4$^f+Gsq244J0MG=`+voZWjA=-Y?p&;Fc~- zj7OWg>7N}GX>7Q=tGI_yUXaXyb5)xq2D%SfM1mdE2zx(Bfv&N5XW|~y3LFR#go}FS z&#SUl898IN=_Ql&L|n`HkGGwnMaaVwqv71r2*VK1EUYWUFm^?~QgD8I&Pr>B9k+uV z^nxtJIVn|{5Qx;eMR-0gj@6Rcatmb%Ap6><=c;Wa4Qwwm z8Pt!>Fd-IQf40+h8%ESWhf&3;&z||-RQZ)p0&+z_93`ZeVnuy9$uZh)G(9`#mP6z6 zS;r=hl?26fts(3nt_605N9{JE(y59V&WFO8?Qt0g=jH8aRQ|A%V+Oo5X1Oc)g2`7R z7g85eLiv!YkScchk*5E{-g|~M)opv<(nO^x2q;}pPe0J zB2ohcrFRhNgdiX&y@!r;NPy5n4|x`N@8|62-DjV3KE5B$7cO$K60+8sYtAvp`2YUn z>AK^esElLq>-~$UOq8zQTJtV(YDqJLpZZ~J{R6g#JoWf++;q5gJ%a#Z4)1&`m+YCk zj`;ajw6WL94!fp&As)87sTzoWpWq=)q`#E+2LKO)x4%U)& z(Ehbc)~cZfMAh9@DGu1TTefDtxw@p!nQWQq`5}4cd9QtP3(~g(I;ehbDeUz1v3@3N zQolBvyseJRJ@ODY5&tS3PX7t|a3+rrRxLTCB_`om`?+u1t%ZFCnRP@md4Iv z8F09m^wS&5<;m!~W(* zly*{Y^Maie=KlIHw2a}NnmBpG96KDZ#x@i>^YV;9_~hO~$9q;9GQqvqaPaw45`0et zww%hk%Cvfy#Q;1}bi9f)nuHo=<-KpJoYEnnXC#!Nly0y;Rx$uF40R;9JS5RlGFA#B z(b44fdFhi_lxm+k5STkd<*!MsG@+E-1PBzQ~~DF7ck-08O9iR4AV5Z`HgZ zSF&m_Z`fkhStus#nF`M#6ifs)@^{hI0h_%A5e2KCk#}Yl^$x`M#jE*)CkFRo1M#=g zyjhZWcj883+k~C;+#)>wV%}cpcmiqwdr!&S@)J~|!noD?KADQ=(|D3dah0g98`CN< z1^#O#q2Me2^~u)jx`kL){N($;mI9Cq4J;FL0S5sy-81v>X7cljRvcC7@$W%H!oTz! z)#*T=m1GbvR6=Mu*$x@eP@CKRNleWagm1mhK>=A*-T2GW1yh!FS42}_>bG(`i>g$h zSow$k873ZFlB}9frtJu4oUK;PH5JY=@qjY0d!5>K#O~qW=Bhjd^f?80+(0-s^-S%e zcMx@TzS|AP?^3A>tjSitKBp$^(P#5--hkBm`l-!Wy3P~LsdtRk2XrdHZK=b%p@Nv) zWL8+sHN*S8it-~&5c87CCJ&JhSNrXUWc6@d` zg2W{Ea!d_ZQ-Iri;DgkMFI_`ZoAmm*$;ZO`3XW?u%<=@~Ru^~TYc!?#mt$b)dn|c2 zh12lq0A}TvgR$vB>%~ahk0QTH-R;Y6-0kZE=IH?S?_zUD1^aHdo@*mrAc;E0rKw-8 zvI_FEG}TV{i0}A%Ocg|+CIa22qWOvoU2?0JhHLPziZqSWEs!#vPI|baSt>65*fq2n zq2+mja57hJj5wJUGT(jw8`;Fs4W#=B2(p5q9u}Pjkk2e4aj5Z&0f-_ZnGsy%Syt^Q zGvC&rgR720bfF_i-kuYRP&-u*+4r&M@%8h2ADoc|2_41K(%ZgC=UVXj{8UCa{J5CN zbt5<$B~!_#$FWw2p|vSE12Z+EemahSvl;X%_2Y6p-h2P^wuMfP{U-(*WCerK>V*%j zA3&v@NAM3%m|Ly^CKYe5Ceu5r)9gT@W$3S^sxKup-U zN-Ed36fJe*MWFl6<+2!GOlSxx+RGGC#IOADRgid9s#=TqK408>j{%Pe0upZXFYSbo zN~ZV&rU5sF;fJOkD;s%Lu*AdPjm7Fl&^D~N8(_DQ*S>*X+jr-9z}rXsA~{tjaOt$O zVC_3T?W)_eml;O&KQHLWlS*?Z`9rzf(#HJk{OqX+O-R$bn2fbTDNwUabF`sHtZn<1 zjv93XX=KQQ2m*oaJAAv7CyE&Qdzbx_Q=L5bz4oz067g!$G@Q>{_gI70|FWR<#2p-L zaLqWQx7AbKX459Cyj*+u>YqaMQL}T~l$uYaMXHqtzI_=#_8zzFWvT31KXk>k}~+^Na@lcdS6jkYy*3q3P-=%kgp ze=o(Zx>$q`U-L;+mi|7HfTL~&&((@ zq1R%ptO#w-i7g%tVMF1f*-)MB40+xAsi}|Ed$Q>@8^^i?iS#xRkI>e*m*1&Isr1#U z+UjENnQxz0m6bXE9w4~`r51VsE5P$F*y1m~JB~{uR-QcyKr!0pD~DlpDv8<r`F9ONgP+jCBM1kW4(wHma*a+ z4T&kpeq*Ewb%p({55wpu2X9Nh2B=h)RPDlDFs5elD@3L&c3EU+XegI}`F8n_R{O#` z=liB~)R~BSX0mvEw?AE7RGMxg(E1hWUn+h;35TiK- z%1Sr4Ldx`zhTC%k*a7S||KGcAzL0>@=@lE(OzS~y+k*H`TzU%s{cDBO2kS0DNu(aZ ze?9muvLKe^57e~8T^hQK4Ze+rgmnQ-Ave-gNAPg`0W2f!7F%WCYCt5-%B)E>6nicp z8C1`#1Lq0f)7ir-LT)029;hxs%;F@~*qe|l<^xjX>LI5srvo=s7roKms!7Jq&ZYJytgvTwE2C4Fmq)w_ zQ3!5e)d6R&fc;{^4cYNJtjvajL8qq<4&2_(`F!I;8=LPy;qOM0GFeyAob>w!uw&^D z57p~G9>8LTGaH9QcC}yz#AOF0Z0K% zOrw-LL3133Q=exbp#ZPId1yQ`{;Q+qAV(CQye{=jILi3jM(f5@@5BYdtK^*ChH8Nd~>XUZCWb$)fn z-T^HEEj{iF1gs)&Q%rgI;*yr(qQ*!j?Y|jCb&{&lU7sB^mm*b=*HlANQo&h{enG-g zR5nBZXHE(oNL~$1RcqNt!!7I7$C58%N|7$M_JZ6zyo|YIgXb(4aA)|+k%#d1HS};7 zX`{3bM8zBQvOu76nCo3Ow07t~OZb^d0P|$W^mT7xxn5<9htUsFGcz+{GxW(DtGwxG z57X1`BVO|R1$KH8_qLByPcKyuJbX`bzJcXaGdHf&@bGE~!;9O1<8bs93Tn|6xt!e` zjU4^~@P`rTiHIQ7aZ4Co*Fe#4*%@@6EeTz|XJvR+falU0(%7hu9<%}l#&<4=2^J)#4h>+pOeFKc!HNn+dj#zKCWwvKBh zqcp|7|7%m1#?O7lr&CfdeV=CfIx4I78o6drzV{su8jr2Zg`nz6=fVDQrenRw@e$^-$xch8jO_(%BUYPWz)kyhpQ6g8yiX?v7K4ka}Y^=s`?=x@_FXmcY}HKqJETFKup~=1+`~3j90&3U9rG zhT2*Ml+Jd(hSOq#7RsTK+`4%MMt5Yhmm!v%?{DkeXbW?~ukFK78{^3F8Wq-6Tb0CB zQVtn1KeLawR$t?i(NTNHqu9)Q)t~3XL5pX1v^NVFca>)_B(XBN+0 zHaH$NAxp#NqCOx-Lwx{y@LLeVGEzOI?2|3+GDfP%dhclrf^{i-L=iQaqcg_K5An>nZ8)F9BfDd1TTTGE zzT&d^NC{K3xTkpn^UUE8z90~PuYyp!ZX_Ci_(MMZ&z#Wgg#Bm6q#vAKF(gZrtAFVW z;w~CRcRn!C=dE%b_l)9Av>?y3 zipr+4+cGxp-ndArf}_dVs54nLaFPLYQ(;hdbnH+b1V(T}U?%;v#{{y6RZm;|OflvPB2Z=|P{+XG6O=<$Zh3E67*wwpNi^|7~+~!@~%#@%U;GZ~r%T?=_;=SYV2bKx3dOYQ_ z_;twDln!EKdMumEGyhso|U zT`TbX1F&vDAiqcI%vwboA@nwnjemB!e}em^#{oq50-bMTZ&y2xeU%q7ZhMsKpCK+F z?TmNAO%6f?@jh#?B-EQc79C%;`)-P7BP-ye2&!JS%u)CTW}06oI*HbbM6x-*TK7@i z>r~lNwNx>$#4^_36g|ug52zHncX|RJuAuMweJtr<{OYU>_&{&>zf>wiewFWvY)e!h zJZB$QcehiukhPK@Y?X_?TOZd3{PY!+&gd)_R@DuP?{+Osb=A9-Z*gpUE{2$NOY=yh zrBIvg>Ej|#7L>y~Gx-jFCdzX5mldZ(xcntMAlWz(xa_NHRTg%T`UUQF;*We+KMDWg z!E<`(^L09MLx1?&^xvWdo)EAE!j(Nn89#&D%?k7%!E6Xwdj-G))PRZzfzg2`HEnn#KbAkh1jJR_)x>iO5JTOQ%>53X z6aNbzd%McAo~aVi#E-<{`H(^!PtOx~q9x72W<)J3ttYe?@uwh7**|0@G?6U1>QC0K zblmwOp+P`wPyMsFfD3osu9T3Y@kU1ArA)GV5vI6FjYg+TN^(K<-ZbvK45Q5-&}ZXI zIi_Y2wd_3l{NQY|c^|4|>S+9Vaf7ZZz+Z~a&A^>f;6LAs& zy^+s38>RFP;+qImdq#_&Ya0ryf%;?5z>DO#-!DjH-`|LL+*Mp0hG-<@-xGFju6;w! zHG?QX;N;J)mm;}>4i|wc_sE#whV}rJp_+MH<-0!0B3+&onY$+k^F@V*QAo`JDjVHY z>a*#sJPhP`Vq(B&*65=ln{)dsV0X3`=zVS*_EjJr@QdG3@rmuUrG2)@NS?{k z05%TG9ltT^jE~?S0~<6tzc+JEPNS&nY@b}v?A}w5JW0nzw=TSOzQtet1Z=h`ues)Vk2Ua0uHnQ-O>qb+kl@Fnyc6;>t_DBw@TwJR`zVZ-GSY` z?h0qrxTsaa@|9&-dKRz3%%X%d_?6zPcXN+bc|1Ic3kw&Wm5hD;HofsNODTm72VZC8 zQ#ymRS>dLCVxK*_H2c*z%6z`||NI!h6EAb(`rf$CBl*r&~7Mo63x7mCM4`{uZ7UmMou}f}gK| zWTMNJ&8f)XrYqTO=JxitX1z`?;pC2Hru-UgUKD2fJ{cLb(a)tA@6$Z?&kXE$w$B$7 zBT7!?$?#tJWeup1sz{J@Hu>B3MG^`h2bI0Dp%xKS6X?;VlqT-g+3SMlsZ2s$T_(^v zeW2yB7^#Wae&K7$vLgCYFDI&eS&EnP+n;ykhmzyu6 zDh$F-tUF>oPnFBcj7n2r63&-{+>TT88P4Q-i6m0Esqpyn>|W7obH`i#uYa|vK88)7 zHB7o46X>~K<8i+F@x(dM%PK&Wyu1Lz)VWx3Ut|-{Z z$e^ou!P)7vrR~kp@F}~%6|3rqWUZ;{4n)De8hF)G> zD6iIS^|^PWsJdbS=e1o#sKN3A_k6WZ7-HSGF1MI-RY1L0J1e1^$LQlyr2nJU);Ep` zAfw~Ok7}AEvQGK(Yk8)D6MV~E?KjT?4H>1x169mGB5dp6)LJ3%MgFCwihTW@I2NgN?Ji1I+cY#XCjS9sKpXC(fSLrx^ zuGxX*F~4DEu=0V{ zr9RNKN&RFrSNyu|#?j!};Go#8^|_5Xv>d6<4~I^XQp-1eX?;x0<=f|34zhMVlW2M^ zpTZN2HQpLr20;+ujuVW&3YDx^eT8_k4UR$>FEb@yivF?r(g0z!mvy{Ks(k!CekQg+ zR`BDCkK3X*FgD-LjX5?NO70Eqb%phY#WdYox?C3$XpRJA&=ED*Yk{&ZvR7ldHcDgj z0JgLhXK#P_;qtTP!xJC@MqOnxee+|j2mNZwh!Da7!Fy(HXq~$TexWNV*)?+-%q+s{ zO8nv@0B!NTf;cx!?v$RC7R`E#An;FHOlo{3Wm?uz5GyT$RI^;!I@%z?p;=Cd^kX3Hjdvq8cpS?_)y<_tRpaGdMyc z1uw8t@79V|`Ar;ihMyS@el>Gn6D#?Go&s4>|JSV{e6Qc zcVaG8-)APS4a{yed&yiwdqJFjEm?P|rU`K}Y`)o#1Ppp8Xe!ny?r#B-sTVIwmqAOx z5A<{L?71hNSN4+!wNLSmO!sZta*f`%Bn+R~o7r;@HXdM9tn6Zr6KBmw&EU=r#sD|% z3U|3g0brR^zAh+Un@dmd^XVI=3-N<7a3-~Un1E_#;N_Svz_L|m+;ck@(^lUDM8%VX zGd9N1hF}R+J(=c2NzW8<=%LrS|$}Oy^smO)YnWO7%bX`IjU>DLPVD-3ILjLs!(9q*o-l&gqnDX zmG;T)4O|0iOO={z;ao2%u)$aW$^Yljv-atG+ezN|rIk9hdfx1dz;x|P{vj65g z{Nql@i2(>#p-X?rzr92M&mRB*JE6jh8CmP7e{*5~KUX}&9>D)}7c&2cEfej0fWyBv z26WQ>!8ZQ=xcU2AjR3S@LA(6_uqS5!4siG@pOa5~7b^1qd}#dh85Ew6lIXXT|85xl z^Ra+VdkHvvaA*I4@qaVu|8YrmX~O*n4T4@)*axir_h07E6}J;QhhATL-eY|8kB>PI z&i-*f|Mjvg#O4>Jmp9$0QwO8wKXXnto_FI4~l=)w>@IQMH{uiqK*KznCqZDWu z_rFl>zkS}7{}-zL_mdgh|3bC@G;#mS)bsx%sP+)Qze%hSIdAs+Fvx~VF^+^-V$n{J z`=u_Mz4Ubkg}1WS1TglyeNLj6Evd~HZVcRy){Il&;I)_LfXi;j1S>V$Wo|zmCJ}gk zok5pUi$H4tzO09J7HVzY?4AGGK!ix88r~BM@Ne)c%tOtNT#T5i>wskjoniO?yk9Xe z4S5fb+5A_R^gm+AOgbPq877P}x=>pD%eDWH1ONOM^2eGWmSOULXiYHu7|=VsFc;zh zGO5lXe4})#(sAd0g4>J-)n7e4xXRm&7Q%JBx-3=xi|6A!6j+ZSO!1r7sL;GJoY0)K zpHyAT4wHmQ3N-i(NSe5Q{0+UUJU?I_B#66Oe(X@FL|en25Bxc$KSjVYG@mBfTd(!;0G8=H=8nD^x%d9wRt!G-4ai6Zn>}*7Ke<*dsdDoE ziX~ys(Vj5DDA#OHx1)JFO?Z%lT4z1D<-jMz%BdSB%lvMZzOCFt7Mm8OGfkZG@p6tL z53#3w7(RjJsK!OaVh2;)2!{dH0CM(R2=uuuPxH0L>o29skcJv&Er~x6T_8#KL6GHx zyuk9nD_uP~n}ZvjS*CigR;qT{Gk(gS$y>;1I3+!i&^H!ZK6lySCf0w%!(<9;`Z4pe zLpshTPy{p|?!H~`P5skZlLCvIevY2r$Sl6N*KB;msIPpYe4feC3YiZH55^A0BtQJF z7eYq!?&_RcJ(qy-ebh^=(mJpjz>l55zhs2#EEW&bPOjt|4BMZEa4>E}(-H{p=T(>H z_h}0Re4pvrpnG^pnM0*dATY3r^7>e6LltcQ;j7ILbv{cPiowdEw+nX4O3M6JS(zAP zXYAHa(TQMjB(3~c?T4e*Pr7CT&+ZRQe^gWaoo3!EbzG|&<4eM>8MNJPP%qENS;3TW zHN@ye$VHIaKSp9hTKN{J9c}MLVfgiqjzy4E!$?!%7wv_3FXbfs%1#gpoj_RB+_|Ut zl$ZlRX&Yoxu62Vo4y^+V1a`JpW0g=F4`1!ikM9DB8UhsUcF$PhE~W(28VV;fFs&`r z?b-LP?8uSMPn*>{B7acKM9s|hC9I1#Zm2$ElmnG7H=7n2+)o!s1~w5(zxYn&#>5N` z5_*etb&?-;J_+T$=Ph_EgC80OssAchbbemecKo)GPF+GjfqdbDy(4*6Os$Gsz}5Zk zqv-@3#5AXTgHno6yEge{V6n5d&s>zF1lt0I$W6OO`+N*0^hMd1yMJThZ-e=K%0YARhH6OUW=>uCGl$uN=k-DVk}Mb*i;wPsK~9EYx#w@4kmjkY=@ zKob-J0#>?sKlS@slL3Q2xG0LEB9rQ8;AD~fhADHpXTh&W(sOWrRKp>UR*I5Jf4_2k zaMUQ&`fD;}b47FKYCo8)Dh*@Kwq9*|M@ z2cNb!jqk@Ct;m2dQ0DMcw_T8djU>Az>n$mfClp?Fd0nRU1e4k0JT|0Di9g3pdz}sD z!wY{r$KDD_d1huD0+_35p1>b%{J0I62a8+IIHr4TNqWz#W~rosJpAsr*DaVZvwanX ztR?dr;NU)%aJ%OG)XlB4piX7%I~A*Pfm|xw9)p;J z-~k4Gwe`9w9AkG%iv2D_Eh`Jy$j@+KZiRE0v z4&4uqRn?)Uo=8s#tPGycAu0zGj2k=?_Wz_FP==@|fj}gJ9*!}#EI4IYOdH0uO8S`^ zez&WyFUD*L2a(Rcn9cn$3kf(Wmj#w}bIluvURxM8o2W)VbVIy5@{n`M??}@oe~x3C zV>W=g)H!MKBvaO_Ys- zGU=v%xGD?tWf8?b-hq&mhE(UH4Zc=AyS?h+^>iD|YVaq-GEPijNDr+eP+=A3zH#Wl zg$`}dpmO&WtROsE#@9)JTzsVbKis$&P*v1+>r0=;Q#_uGVpZ4Wv!}Z~MLcjVn;M@i z8;p~eID(rq*v$$1f-1Mgi=OetVd?{sdZg`879#pnMUpo~=>WkAGA2FT_I~i1TguHm!WM$V`W*Yl({@!{*j!%^eBX^4 zaiu&xZ2s~RJ4awkeg$)ywy5Fi{-f@}qv6SPPMU$~vyy%Mg9?RGzS{jDRor2*aXn9Rt|@Fo65e*>Rb9mrH$2@bYNFR5{jKT>-aSL_ zWfA{K135(lIe3%b%X3sNQ}(Ori+O$*OZ)fZEb&lQ_Q{^KP@*;0v%5p3p88?*3M*BQ z1f(;VSG@f7S!eeZH(D2_PovUkj~itgx3(HoP2zZLOofWnmI}<|OuwPNY@LdCoP0G( z>fv_FZr`gusLsRa9^&R_`vfW#DgE}!U*njgBtwHhJ+czN1K_5(usu-Lu(sDjZ^gOO zgYkp$CFfIVw9MCo*VFfG^w!id>H^C@RzTk0zvBl^5Cb!}%$}P{pgSMK5!0AWv*%xy zMS@6s#29-pR^$2Pc}|krTG3PIhC$7F2R=){I*SB9ocswIZC-nxxhk3q~XJ# zo*r}H@_AfW!BpwgD#<<;-&>68rwWRO=RQ-$uFAcqKf}S0mAZk`$>O(kjglH<7^0|Kwi-4bXwxS0!~>=iGXj603J?Tv)!*U*XYGAk3UpI- z=BbqA1>Ay7rFsmglZMZu#>hZkst~x5xp&lDoHob|5oG$^V~#?MRf;uiLmxlAglP4Q zwfWK0)Q>$H1)9r@-H`Ylm21(^@e3g%;Rs4z>D#3tg6fD(PjA@UNIlCD^>6tObUM$P ze9*VJq3?C-RkXg-~uyxQpgtnD5tBJ69FIE1jYwB z80?vpf5wLr2zhlZr5*tz9r9Hi+aJR5>ld*z6Rsw!Y=q)B9=V2Z*}z$~aUmkdf4N27 zA{|=t#@vAm@M~4BT`O!0{cBM(HJGqch7*#X!S=DHtTdGI- z0KvEqyWx2U;oM(ex%civoz5fe+zjfH!N>s~{s<#>9sXp1P+jW4?;pk;*CK~$Vy=C= z(kJECD2^Q)=2$ME(vLe=JP}1`VElahsErplWxXb?V}5qd7Ly|(vicL>iVMz8d|qv; zx^Y**l@$99a%Fzw9$*?O55YP&4msj_{K0$pmeGyS1+G}w^W`X$TKmO$Z84V(BL~toPpXBR z`%IXEt9sef)4fJN>~E*vy{^t+bDe+~C7+oz)Dcj`^b`GMYd?0@#UMf1E-mnI5EF)T#^P1eSO+hQ2smhtD^Imi)!$Se zuNPG&XPP;1==x^pr5%YSONCo+Pl10ekk3xss&H5`dA-Q=ox}0u-AUl~`K*xpc zhEynvRIlv(x+kz1U1ou{Hh&BNO&{n4WT=PqM~ z0?X#!>s?oHpu2DJxTQ8r_~~ZBY|k+0{p_zGu2Vl;^^cnLN~gFL8yRr{)o9G&^Nx-B ze#CYzW5*ieEu9#aDzxf(kHz!9DKs86}yv>W=OC-x5R3eaLZf! zk06(@QTBe@qhhIX4WjVKZbTkKXUx7* z{YboVVMv;Agg37+&9#{OcKdk=Ns4|lbtD zCR-+{pXVUodS0h8l&a>Cg%9t;_E8W?b$h2P4I@BB2BG(~2OG2NO66eQ(`^-!<_k0N z7zxFoIeg9txR_iZu%ob!0~`E1Sg^$pWDDYYsiNpQA~J{QJ2VsMP%BH*w)U)@@q1RE-oVs{kz0}tkXB)9Sbq4>~Bj*3K}#@Lp7feE*k(zLue z#=*Lty-SH7cD+);b;or_fdW4!DwWKziG#!;=9w(_*t@ z|Mgq$2?_!Q<(JRvXgaXhsH!uaC|TKxw&~1fhJZwHD={VO z@M7k}`Rt~BW$QKM+MLFa2V0jbb`VLSx|`&T6j4Q~T4|i8-a-^Y+#lqPQkaN8$GxHU z(PkMyWR)hnD1L6b(|p%$LFI&N5|q^y72Cm&&`m;sS#ciK{9Lw$26YT(f$cjqs*Z8^ zF?>bkR3WR+Q;FR2^Y1Rla7}dh!0SngI0e-W=zF<|tMBpgewLwp!hrDz%CmUmBbRHO z@0#bkw{PCQQA>d7Jp5Lyy9A3l6y>mqQezdC(L6Ab)rWuK*3>v3%K%&(mNag^A!5}u z6Uvgbe~VG8HCE95voD?9T+J4izQ|`d)Cj&d-b@ZPk$L$;eZ8((+U?9l!r)6H zkiziqelH{@j_0r2B!;ujM$XoR$ z?JhQc>ctc$+O`YuniyW+?f&4~zBZy{Mq2@N9EzCGwiI4*nvLI}@$&qxzd-oW=2o@K z*wX+4Bb~y+u7HhvfxCYal56*_%ldZHj}Hc@dQA&j4~BdX32YO4Qm{gLT=FJ8z&X9L zH$tL7Ag^iJ1PL)kI1wQ1b4Z(ff1~SPS{Uf?AV$y@ii%5wqf}<}o*wl@T?=l&jclIb z|KPkdd>n#4;Y#$9h;pTi$Z`&Up8)-%5Q?*+aZ0C}8EoSJ?LJw!M zlT-0YabRbDJs-V@|2yeL8)trQLNMCHqfhM0J+5Te$Q9LZdzFNdyMA$zNPtKFpx zU^>Sc4A^-QUT#Ct&(h&kT!={^^vvzR(q^V&BCbHIisFt$ZVmAC4XnR)0R!}CGKW@(28?9`~h-OHZEYc)1j zFNq8aPbx>v9Jm7cwK

o_S*9#0pBi3|4ev+fwMZ7hq?ndsw194~yDyU_=*UM#;_G z68X8*QR_x#^E*A>{}+1BNfS2JVVz?U68hrlG?^Y##4?#rB%juN6(TJt11yE&GKQ(w z4!ZKpHm#E*xDUW-2d;I;{)gS}AdAOK#+?<+>bK6L%9E65F| zfq7+@op0$Bd45tWz55T+JFIhe+bcJKkCeNWJXcf#dRl+?>3r4hgpWlZS?M(zmj%Cr z@7Yw`84rSTOlVnocAn>jwb=UKiz(_kq2(}mulD|WagVJ|idRL=BVM~+=;%6c4foI9rYB+hI#lsZlI(S>u2VqsKo_?|0^>@%! zT;O$P0Q4iX@W()4<((cjsyd}KRsBhj_!VZE$llMbNs&?o`*$?_)S-OFrwO>NUs zD?skv+3?-RBEZG0;M*%B9r(0Edet1qlB73tqi(155I`T+!7^wr{}hb_uPrgjo^Ab_ z+u5~xZzlUk5R1IXWU)jynD4xis{H6Rpvt0!-Fn9(U~=0-!C9+J7%QQ}4_`(+6vz$L z)Dti*O;wPvx$xu<0eGT|v@gV$nYo${c)2Q>Q$&B&9?@f(=aHUvSQpEW&z~P?)#J7I zswrCA|L8=(ZsyNX?SK!DE!{B%8WV-z`>2&zr`4xB^Hsqer>6G+c*Kh)VD7B2lufDT zeGZ^13F-E=9Ia-dcyq|Ygki$0Ufx+8UMl7HT8oT`9p(Yr)|`%g*IbBdV6! zmiyTJS%)(Hrq+U)?D3L>??=k(SBdOael=zr6*23ID@)Z+-DGB)FSvBIhJ8!Hbt!2i zusnp5Wtq8pDDq|{D|7Z+yQI`Xpt)d7QwFbtHVc)gKI?=&f$A2Pyyf1*yy&w#7|5{7mKC;7E_)@kkJwJi-BG`s-BTr^cY$aURiZl0aU zMnkq8GVHTP%9y?iT*CqUp!G<#{nPtlMZ4kg^QGkv~EcLlKmA^%YH(9 z5^nhT8MQKUmx}>7%Q0@iS9aU7J+`l0(G0#j^6EII3`u4NOSF#o=2tkaCNrH)8snXr z-V~4S$X(cp>57uv&bdeoF|Xb*+sbdu{nl+tr+>ZI&u6Acviw{DdA?_FYs($7>KnwO^UwfV@KkpRcP&gkK*-8JH+|jG$Rlv(; zDV>=*dD7>@58>iVGUD5AG6HKiX8Kbb(~j*8hGIEKu_7hQ)jJ-3X(~g$8ffiALC1E3 zgxTrc&5Wh0WYF{k@MvRXtfhg{LuZ^7h@q;Pbd%JAKrnwHGwazDGZ9y5ia+*{ya;6q z&|S#naAw%qS@uCGlB9F@`>y;gpIfkRLtdOauTAD^A5Z@dTq@=v!_wobl@iwXbQ1cs zE9Yry+-C<(#g&UIk>iqbIfdV*Oj#c0b7>;uA|g)Wq~Ru=ty-~4EV0A6pV@pQ{3c!l zk27l>>$?uz@f$SPYABSEYwFQw-sh&PlZ=iY5Vr&s(_4S7{21@)bIvflU&2_sS&Ztf zhSg_LyRbtS3y6ZPfqtgmTuaAJpBUd5bj>X5+7SG`oX;s7=ZMSw&dLbw~G& z007@qn_So7r${g8x_Wg}j4`CFY+sW9$jw8{F=JrTRt|QiRmIFXJ$#H({q3(a6?T)qez%o&e7TF)>D*ES$6=R%NdK0 zOou`-Gstvy!~U%ZDFn%~+qV-G>73M%yOl~f9b<=z>rPA!b{yJtgTVa!R*%zkb)HnY z<<sRU)nozxW+i4f+ODO)zz|H-diEMQk-#v`|>I*4D)@{dG#i&#mP! zIqWc0QU7u#dkzdkJg|=zAb`K+hX9FFAJPNofdFD;6h7f{fSm(MssJtz#{e zglP#;7R{(ps!l3km?B{0@vBfo55zKGtA?qYIMQp2?>X(a=3eFH{2aD z?nLaV(oF2V_bH>k(4s@sh|}?eeUFDriW`${X8%Z=(*oB@>t*5tExm=1#T=)#U2_$) z&ntM=Ad8kpTLB{hOp3Rbu#WwUjpNotYvr^{AsfGeM7XO2L#uGvBaY zl&e0y%u)Sql!o`jS$##;39GkOBc*&02HQIP2Dip|O~`@ueV zwBCzjxA%1^nqNMaHib5Lmd7+w#PbZE{o0?)vf)qhQqdZFJ!;^}lXsTy_<{WE7yGLR z-|CQq?`EA^A&f;enDj|HJl}GZzsI1#Z6O2od^NrqyBlWZ(~nT?AnP%EzoeUG!zTy1 zz6p{EB5{9f7NQ8fJezit)xg;Y6mYIJxgS{D(!SB9x$-m%?ZS4}a}~i2G1+4{H=H+a zDT(joNw>a?skL%w6N|&SOdue@@X%z=!B=9LQcUHUCM3`|p)~oqOv2s}EIyZ3qz+_;|aX-jwkQKRm39K z{CRL;smGX*jQf8NUjOd% z+4f_rh9qzV z(6C)xl;3c^M`@wq5pFzwsLa5f+y-i%OUg{n!VJkmu_9d=`8!WXchwef6oOP-mmW{k zR37_QS4oVhLWR2#ts@DKPpQ3kuHvVgRv1{ayvzCn&9yl%mMyuHM_7Zc*VCAun(eW{ zbA%fxU=u%H|xe#mKUs$QK}dP?vOUY?+PpzYdo zWf#azLiZd5*%+SQlA_CXQ<@{}`DU~^!w}wpF}n5;0$EIayfMF?5{-2_N$<9;tPz1K zyrZf*6W+uDz>{iboW2_}Qo}IUx3qI-4mzi@IrjD*&QPQ0j}T~a`<;4JM;MXM7)ASF z&<#3_@o)ih-0*;YwM<&)S?BZnqKXTt1&_yK(DDrbK(m&fqq5ZfV(j484PcD!NirE1 zoblEA#a05D9TZSj3g)fQ?dSb$j2URMCj2=lJ4XEdFXK%woRXjC*M)bQ^vbV{jZzqm zs3E*&@D=CesBN4tBF|k>l3$g-L4gmr47fxT5QoD> zVvE5p!=+;xp&AVi)pn8yNla4YMFmK7k5b_nGyG^u$tY{`YBtD`c-Vdtg*K3rXb(zB z;=egKiaxW{7e_G*UOz92W3Rs`F=^$N&Gz$2r+@VlHEY}lQ+@PxIfD%!V>hQA?VxHs zxVR%=v(nELGOIYn0b9XvGvL=(7d1W?Z@;Cc(egN3wMEDUN%K%PAt{luW>>}tS#NFelcsi z)xCeUS!FkAEwWKi!C#6BG29;cHHH26rDuv2vExYSV7EN3ejeR2Yez~y6$?;e(1=#g0-~l^Nz%jiS2)AHG_BnFw_HcreY%CE zs3nYaRG2%(eNudFeSMvbjZetKv6Sx939Mz8L58pN*poAA>Q>qo*JV%(EA-@sWfr&# zQ0O{(738&~BsCvLAb!%5Ocw)Vc)Q*_U8=d=m)xj)OD;6ph%836R_Rsg1ta>bu-#^{ zyYXmaC2gg!Z7+wKASv^v1gk)+XS0S1+B;*k&gs5Uf2| zbgcvRoH5zvAKtMR6gL#{T=!JdC}?P3DFC41 z7V$fw)@y}4pDt3{vx_p@e`pU>PUv39OX4{qY>idJ+EaGp6Y<{~H^^ym2Z&+xl^Ih0 z@gT#+_Lar~Wgz;R024Wzp~x{2?0(vvDwa{;GPV}C)?BQ^%5byT8n2>f3~mF|qc*w^ zkej*s{TJOP-C#>-`Pvg}exwI7oNKF$jLW=^%bNIM{4k;;3;+f$1d9Yvy_nx`At)dH zePdW3bkcaH36V+O^u+8U=5uG%orEH;NBTQ*y;kBqBdl1E27D2pry8X8%U2NWg>l3v zfz_ATwvwjp3gqbJnU&cqGKD1?V3!8($N0-!rKH|{JVdodK9$mf;ayM}RnM3zY?4Ml_rF2j08f1(ghF@Q&a6 zY#_|TZrvD8LW!hq|5>6t&O^$3}E zf5}BYTzM$})-*k8+1|+>WDgRZl;m+CgI)GXaQz5qFs&5+|ETDcUp7nLy_>B0lhT+TW3t6I)AA2Ld#9>)0A4IONjsnEQm05!RzOw z!Dw~ZX>D8l>(uk3X%KT(Szk3MH|JFjYaEnE4nUbk<@Ily$P4(C&Tq<3^z!?PoFbA* zS89nCyxPIY6X|85Ax-O>D!_%Fw4a8OYNVaAa890_t51Km^h1~3EUW35KA35cX`tm| zZdBjfR?@y07{WTG`)Y1Dh9iZy@(>oNOotrS+e~Nux^9Ppl!6pc3cVbldBzRh9olzy zHcr7veiJU;q$U6Jw78vW)@Rn|GyR{K9``=GtOcWQEe`CNal#Z8vl z{adJ>Ksxvb$v19(tb6?Po+)3L99vKqXY{vM{HIm^Y+YmW@w}4yFH45za<%{h1a+1bVw4ZR=CkoXnBpU& s;d9Ubh*AC=x}T?=PfbAo|Fp9qDPFrK?N1Ia+yWfd7Ix;0W3E^K3lnR;umAu6 literal 1456 zcmYjR3s6#N6uy92nr>=t;Vavq2hlc)EnDvTDpD^gmX;5SBvDL7w*f61H6_P%l`=86 z@Rc)ZZ5!&|Od7Rx%{N22YgssC+TO6%$P)Ia1w#-<2JBs{Ou;^3>a-pxj3C%=FI4x$Q4L;FW5Qu>U?8*|*sGuw7#j$O zLcjxI?4cD#8-nNsMT2xtvssBVBeYxqlywoNs)qOZObv>Q&hD=3!-V+e}zn zpe%NJ*z6cbT8Jzz>02KjuV3ivj}?@Ey4dzH@vcYOG-K4BlOd{AVTSDL3{zb;iotvrGIT`{GCr%7?J7#ap>K30P4%@ z^yT4y=WZ-|T4MM0#+&y`OTDlyiO`V8){gixR)25mLwzLS|(kd_bBBGr?Rz+ zy*M~HoPMi5S$u5kW45%=HHy&4yj?pR0wTD<%swW7;t^{H)X`!j?08J;mo#8Pwgf&F?1POE==2z^>HrDkl zzD6bw0ui1N<2EO>8~Ir#!)6al@*OqGp~)v}XeF0PyX*0z>WXk!06NZ#(XE6agF$+h z+yI8@7&=Va3fD`Iysb?BHQ<&oHhS;Tr;eL8CPe#IGQ-#`3RcBZHL|N)Jg)a=jK7&l z)uc$%FNQs&Hn7sPj8SHHs^|#!zS;Za%53)RUjb)bR4IU_x0S`N8#wuiGeLKT3-p>7 z_mfj=1XQ4QpzNi!sB7nLtuwFQtQRh%B? zg_t%^MvHpFVT|#_;3X zmVriin-rss;I^Qo4+|*W94TeauIwjWk%PGU&mB(3)x;)7X%U)p;O!GNq2sY2M6CFp zSg`CvNK`v-l%`%$R0xl-J`b(6pqKXouwIqMFgC%HBk1wu?-SephE|47I0zGfkbaOp z`{NTD0!%o6=dzF5wFP(5)Mk&9&Ai`tG!9A48{osIDwg?!8bBdVNQc-aTC&y-eQlO@qc_nS{hh)48ML~+?DPKt2;+G# diff --git a/Sources/Csv2ImgCore/ImageMaker.swift b/Sources/Csv2ImgCore/ImageMaker.swift index 618c4dc..7a56799 100644 --- a/Sources/Csv2ImgCore/ImageMaker.swift +++ b/Sources/Csv2ImgCore/ImageMaker.swift @@ -57,6 +57,45 @@ final class ImageMaker: ImageMakerType { self.fontSize = size } + private func createContext( + width: Int, + height: Int + ) throws -> CGContext { + + #if os(macOS) + let canvas = NSImage( + size: NSSize( + width: width, + height: height + ) + ) + canvas.lockFocus() + guard let context = NSGraphicsContext.current?.cgContext else { + throw ImageMakingError.noContextAvailable + } + #elseif os(iOS) + UIGraphicsBeginImageContext( + CGSize( + width: width, + height: height + ) + ) + guard let context = UIGraphicsGetCurrentContext() else { + throw ImageMakingError.noContextAvailable + } + #endif + + defer { + #if os(macOS) + canvas.unlockFocus() + #elseif os(iOS) + UIGraphicsEndImageContext() + #endif + } + + return context + } + /// generate png-image data from ``Csv``. internal func make( columns: [Csv.Column], @@ -66,22 +105,15 @@ final class ImageMaker: ImageMakerType { ) -> Void ) throws -> CGImage { let representation = try build(columns: columns, rows: rows, progress: progress) - guard - let context = CGContext( - data: nil, - width: representation.width, - height: representation.height, - bitsPerComponent: 8, - bytesPerRow: 0, - space: CGColorSpaceCreateDeviceRGB(), - bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue - ) - else { - throw ImageMakingError.noContextAvailable - } - guard let image = ImageRenderer().render(context: context, representation) else { + let context = try createContext(width: representation.width, height: representation.height) + + let renderer = ImageRenderer() + let image = renderer.render(context: context, representation) + guard let image = image else { throw ImageMakingError.failedCreateImage(context) } + + self.latestOutput = image return image } @@ -103,46 +135,15 @@ final class ImageMaker: ImageMakerType { let horizontalSpace = 8 let verticalSpace = 12 - let textSizeList = - rows - .flatMap({ - $0.values - }) - .map({ - $0.getSize( - fontSize: fontSize - ) - }) - + columns - .map({ - $0.name - }) - .map({ - $0.getSize( - fontSize: fontSize - ) - }) - - let longestHeight = textSizeList.map({ - $0.height - }).sorted().reversed()[0] - let longestWidth = textSizeList.map({ - $0.width - }).sorted().reversed()[0] - let width = - (Int( - longestWidth - ) + horizontalSpace) * columns.count - let height = - (rows.count + 1) - * (Int( - longestHeight - ) + verticalSpace) let backgroundColor = CGColor(red: 250 / 255, green: 250 / 255, blue: 250 / 255, alpha: 1) - let columnWidth = width / columns.count - let rowHeight = height / (rows.count + 1) + let columnWidths = calculateColumnWidths(columns: columns, rows: rows) + let width = columnWidths.reduce(0, +) + (columns.count + 1) * horizontalSpace + + let rowHeights = calculateRowHeights( + columns: columns, rows: rows, columnWidths: columnWidths) + let height = rowHeights.reduce(0, +) + (rows.count + 2) * verticalSpace var columnRepresentations: [CsvImageRepresentation.ColumnRepresentation] = [] var rowRepresentations: [CsvImageRepresentation.RowRepresentation] = [] @@ -150,53 +151,39 @@ final class ImageMaker: ImageMakerType { let completeCount: Double = Double(rows.count + columns.count) var completeFraction: Double = 0 + var yOffset = verticalSpace + // ヘッダー行の描画 for (i, column) in columns.enumerated() { - let size = column.name.getSize(fontSize: fontSize) - let originX = i * columnWidth + columnWidth / 2 - Int(size.width) / 2 - let originY = height - Int(size.height) / 2 - rowHeight / 2 - + let xOffset = columnWidths[0.. [Int] { + return columns.enumerated().map { (index, column) in + let headerWidth = column.name.getSize(fontSize: fontSize).width + let maxContentWidth = + rows.map { row in + row.values.count > index + ? row.values[index].getSize(fontSize: fontSize).width : 0 + }.max() ?? 0 + return Int(max(headerWidth, maxContentWidth)) + 20 // 20はパディング + } + } + + private func calculateRowHeights(columns: [Csv.Column], rows: [Csv.Row], columnWidths: [Int]) + -> [Int] + { + let headerHeight = + Int(columns.map { $0.name.getSize(fontSize: fontSize).height }.max() ?? 0) + 10 + + let contentHeights = rows.map { row in + let maxHeight = + row.values.enumerated().map { (index, value) in + if index < columnWidths.count { + let size = value.getSize(fontSize: fontSize) + let lines = ceil(size.width / CGFloat(columnWidths[index])) + return size.height * lines + } + return 0 + }.max() ?? 0 + return Int(maxHeight) + 10 // 10はパディング + } + + return [headerHeight] + contentHeights + } } diff --git a/Sources/Csv2ImgCore/ImageRenderer.swift b/Sources/Csv2ImgCore/ImageRenderer.swift index 7465018..b5638ed 100644 --- a/Sources/Csv2ImgCore/ImageRenderer.swift +++ b/Sources/Csv2ImgCore/ImageRenderer.swift @@ -2,6 +2,12 @@ import CoreGraphics import CoreText import Foundation +#if canImport(UIKit) + import UIKit +#elseif canImport(AppKit) + import AppKit +#endif + public class ImageRenderer { public func render(context: CGContext, _ representation: CsvImageRepresentation) -> CGImage? { let width = representation.width @@ -10,9 +16,16 @@ public class ImageRenderer { context.setFillColor(representation.backgroundColor) context.fill(CGRect(x: 0, y: 0, width: width, height: height)) - // Draw grid lines context.setLineWidth(1) - context.setStrokeColor(CGColor(gray: 0.8, alpha: 1)) + #if os(macOS) + context.setStrokeColor( + Color.separatorColor.cgColor + ) + #elseif os(iOS) + context.setStrokeColor( + Color.separator.cgColor + ) + #endif for column in representation.columns { context.move(to: CGPoint(x: column.frame.minX, y: 0)) @@ -31,14 +44,17 @@ public class ImageRenderer { context.strokePath() - // Draw columns + #if os(macOS) + context.translateBy(x: 0, y: CGFloat(height)) + context.scaleBy(x: 1.0, y: -1.0) + #endif + for column in representation.columns { drawText( context: context, text: column.name, frame: column.frame, style: column.style, fontSize: representation.fontSize) } - // Draw rows for row in representation.rows { for (index, (value, frame)) in zip(row.values, row.frames).enumerated() { let column = representation.columns[index] @@ -60,9 +76,10 @@ public class ImageRenderer { ] let attributedString = NSAttributedString(string: text, attributes: attributes) - let textSize = (text as NSString).size(withAttributes: attributes) - let rect = CGRect( + let textSize = attributedString.size() + + let textRect = CGRect( x: frame.origin.x + (frame.width - textSize.width) / 2, y: frame.origin.y + (frame.height - textSize.height) / 2, width: textSize.width, @@ -71,12 +88,23 @@ public class ImageRenderer { context.saveGState() + // テキストの描画位置を設定 + let textPath = CGPath(rect: textRect, transform: nil) + context.addPath(textPath) + context.clip() + + // テキストを描画 #if os(macOS) - context.translateBy(x: 0, y: CGFloat(context.height)) - context.scaleBy(x: 1.0, y: -1.0) + NSGraphicsContext.saveGraphicsState() + NSGraphicsContext.current = NSGraphicsContext(cgContext: context, flipped: true) + attributedString.draw(in: textRect) + NSGraphicsContext.restoreGraphicsState() + #else + UIGraphicsPushContext(context) + attributedString.draw(in: textRect) + UIGraphicsPopContext() #endif - attributedString.draw(in: rect) context.restoreGState() } } diff --git a/Tests/Csv2ImgCoreTests/ImageMakerTests.swift b/Tests/Csv2ImgCoreTests/ImageMakerTests.swift index 71e9fae..89438af 100644 --- a/Tests/Csv2ImgCoreTests/ImageMakerTests.swift +++ b/Tests/Csv2ImgCoreTests/ImageMakerTests.swift @@ -5,18 +5,76 @@ import XCTest class ImageMakerTests: XCTestCase { func testBuildImage() async throws { // Given - let fileURL = getRelativeFilePathFromPackageSource( - path: "/Fixtures/outputs/category.png" - ) let csv = Csv.loadFromString( """ - name,beginnerValue,middleValue - Requirements Analysis,1.00,1 + name,beginnerValue,middleValue,expertValue,unit + Requirements Analysis,1.00,1.00,1.00,H + Concept Design,0.10,0.50,1.00,H + Detail Design,0.10,0.50,1.00,page """, styles: [ Csv.Column.Style(color: Color.blue.cgColor), Csv.Column.Style(color: Color.blue.cgColor), Csv.Column.Style(color: Color.blue.cgColor), + Csv.Column.Style(color: Color.blue.cgColor), + Csv.Column.Style(color: Color.blue.cgColor), + ] + ) + let expectedImageRepresentation = CsvImageRepresentation( + width: 546, + height: 160, + backgroundColor: CGColor(red: 0.980392, green: 0.980392, blue: 0.980392, alpha: 1), + fontSize: 12.0, + columns: [ + CsvImageRepresentation.ColumnRepresentation( + name: "name", + style: Csv.Column.Style(color: Color.blue.cgColor, applyOnlyColumn: false), + frame: CGRect(x: 8.0, y: 12.0, width: 158.0, height: 25.0)), + CsvImageRepresentation.ColumnRepresentation( + name: "beginnerValue", + style: Csv.Column.Style(color: Color.blue.cgColor, applyOnlyColumn: false), + frame: CGRect(x: 174.0, y: 12.0, width: 106.0, height: 25.0)), + CsvImageRepresentation.ColumnRepresentation( + name: "middleValue", + style: Csv.Column.Style(color: Color.blue.cgColor, applyOnlyColumn: false), + frame: CGRect(x: 288.0, y: 12.0, width: 93.0, height: 25.0)), + CsvImageRepresentation.ColumnRepresentation( + name: "expertValue", + style: Csv.Column.Style(color: Color.blue.cgColor, applyOnlyColumn: false), + frame: CGRect(x: 389.0, y: 12.0, width: 92.0, height: 25.0)), + CsvImageRepresentation.ColumnRepresentation( + name: "unit", + style: Csv.Column.Style(color: Color.blue.cgColor, applyOnlyColumn: false), + frame: CGRect(x: 489.0, y: 12.0, width: 49.0, height: 25.0)), + ], + rows: [ + CsvImageRepresentation.RowRepresentation( + values: ["Requirements Analysis", "1.00", "1.00", "1.00", "H"], + frames: [ + CGRect(x: 8.0, y: 49.0, width: 158.0, height: 25.0), + CGRect(x: 174.0, y: 49.0, width: 106.0, height: 25.0), + CGRect(x: 288.0, y: 49.0, width: 93.0, height: 25.0), + CGRect(x: 389.0, y: 49.0, width: 92.0, height: 25.0), + CGRect(x: 489.0, y: 49.0, width: 49.0, height: 25.0), + ]), + CsvImageRepresentation.RowRepresentation( + values: ["Concept Design", "0.10", "0.50", "1.00", "H"], + frames: [ + CGRect(x: 8.0, y: 86.0, width: 158.0, height: 25.0), + CGRect(x: 174.0, y: 86.0, width: 106.0, height: 25.0), + CGRect(x: 288.0, y: 86.0, width: 93.0, height: 25.0), + CGRect(x: 389.0, y: 86.0, width: 92.0, height: 25.0), + CGRect(x: 489.0, y: 86.0, width: 49.0, height: 25.0), + ]), + CsvImageRepresentation.RowRepresentation( + values: ["Detail Design", "0.10", "0.50", "1.00", "page"], + frames: [ + CGRect(x: 8.0, y: 123.0, width: 158.0, height: 25.0), + CGRect(x: 174.0, y: 123.0, width: 106.0, height: 25.0), + CGRect(x: 288.0, y: 123.0, width: 93.0, height: 25.0), + CGRect(x: 389.0, y: 123.0, width: 92.0, height: 25.0), + CGRect(x: 489.0, y: 123.0, width: 49.0, height: 25.0), + ]), ] ) let imageMaker = ImageMaker(maximumRowCount: nil, fontSize: 12) @@ -28,13 +86,42 @@ class ImageMakerTests: XCTestCase { rows: rows ) { double in } + // Then - print(imageRepresentation) - let cgImage = try imageMaker.make( - columns: columns, - rows: rows - ) { _ in + XCTAssertEqual(imageRepresentation.width, expectedImageRepresentation.width) + XCTAssertEqual(imageRepresentation.height, expectedImageRepresentation.height) + XCTAssertEqual(imageRepresentation.fontSize, expectedImageRepresentation.fontSize) + + // background colors + XCTAssertEqual( + imageRepresentation.backgroundColor.components?.count, + expectedImageRepresentation.backgroundColor.components?.count + ) + + if let actualComponents = imageRepresentation.backgroundColor.components, + let expectedComponents = expectedImageRepresentation.backgroundColor.components + { + for (actual, expected) in zip(actualComponents, expectedComponents) { + // allow 0.0001 difference + XCTAssertEqual(actual, expected, accuracy: 0.0001) + } + } + + // columns + XCTAssertEqual(imageRepresentation.columns.count, expectedImageRepresentation.columns.count) + for (actual, expected) in zip( + imageRepresentation.columns, expectedImageRepresentation.columns) + { + XCTAssertEqual(actual.name, expected.name) + XCTAssertEqual(actual.frame, expected.frame) + XCTAssertEqual(actual.style.color.components, expected.style.color.components) + } + + // rows + XCTAssertEqual(imageRepresentation.rows.count, expectedImageRepresentation.rows.count) + for (actual, expected) in zip(imageRepresentation.rows, expectedImageRepresentation.rows) { + XCTAssertEqual(actual.values, expected.values) + XCTAssertEqual(actual.frames, expected.frames) } - try cgImage.convertToData()?.write(to: fileURL) } }