From f44e8162d4668e27bfed12efd1a568ad7111079e Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Mon, 6 Oct 2025 23:01:41 -0500 Subject: [PATCH 1/6] Add test image and new CJK test --- ...6\343\201\241\343\201\213\343\202\211.png" | Bin 0 -> 61930 bytes Tests/OcrTests.cs | 33 ++++++- Tests/Tests.csproj | 3 + Text-Grab/Utilities/OcrUtilities.cs | 92 +++++++++++++++++- 4 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 "Tests/Images/ja-\351\273\222\343\201\217\343\202\215\343\201\224\343\201\276\343\201\256\343\201\241\343\201\213\343\202\211.png" diff --git "a/Tests/Images/ja-\351\273\222\343\201\217\343\202\215\343\201\224\343\201\276\343\201\256\343\201\241\343\201\213\343\202\211.png" "b/Tests/Images/ja-\351\273\222\343\201\217\343\202\215\343\201\224\343\201\276\343\201\256\343\201\241\343\201\213\343\202\211.png" new file mode 100644 index 0000000000000000000000000000000000000000..3d1c6ae9653280e777c2b8f5df1316643e0af669 GIT binary patch literal 61930 zcmdSAV|1iV*nk;36MKS5CPv4Z*fuA&ZCexDwr$&-*tTtL&-;D5`)hyi?m17NK3%8# z={i;QRNYngb%n~wh$6z_!hwK*Ac~6#DS&{0`GbIfI>A5zOFW~)DS>aG4ho_IAXQV} zPkL+}Hy6b&BCFJB@Uf8a&W z7Q!d@U$I^ks$b53g?-;i!TA4u5jl`u0>b~_*N9*o5?~-8|0_JCK!Jh#uW&-17#aP4 zZkaDcMDag26b=3V)D780h)Sp^@^6sf>ZS1(cL`Dd)pv)?`2)On2AP{va%J^cByZ&tL?3rE z@4X=SpT%CW#eA3npEDI^Wt(C-#yZg$f$UdEQyJ_IEC2xD^Cs10RC;gA5m{YXus_Lj zKjWEPO5kBmc}{G58rM|ShK$Y=rHa{4b|L z*oWcy)G%x$SC@z__8O*YHep+dNkvdN&#B2g)mUFE$YsWS>L8~Wi2eBAsJ@C2)^?wN z!}`ZeIKe_B#%s-c4~M^`LX|Dz?zzZb#-$G(dC>E$@r`tf4uPzQO!WX zzNl~?D_|scm}nPVg5rEhYM4%y>Q6H*E+>Qka4$1&ecyVLv$t8VQVJG)mVs*J;_B0${~WmKwaf1-i(2R{0Hca}9BzrN}vc~6(e-=*$l;ipMNFS1o6HazrOAqU#X>=j0JCagF1KjlqX_67wTlGj49+T~~34 z9gID5k5aT~IHPEGr9M1q#3*6{;5w(1ceP{nd?{fZ*3S=NwC&~^0U;0Nh>9>^EdDn0 ziZg$Q527VakNP7J{@}Qp02)aP=esGPA`Hig@M4W%1!Z8eW&4u|(d<+086wTy<(f_L z&v-8Kj~-eItc8DM7zho?+JwhP;$;uvjP9*#e=_5pX+kIzVVU255&Is@Q4?>x!ZQGx z>kh*YEZ5`(5Ax4yXxZxNKaKo})_>lA-PLU?+U>EE_;?W zQSL-1_P=V8=dl>Z~>uwsrDyu5TYvK$6S zpNXSObhyJn-s#F?O~O0tkgtV8@(IBol21`^t|Z3KS)WXhjq*q9XCjOMnK3&uL`)6W z%s_Y|W=DI*d^ZY{>JQ3P|L)6z`dVyeuRZ%jrFjSKRQqVG{5<8Wa=V^bhYmAg9XH_w1L1UACZTOfBlN+~GL`Ig@*6YuV18~qg6jE+ zW%i>Q^Lx4QZ@#94X3Z8GfUcHhE<5gkgJ!RJYJrGV3_YC$!W~FMxHYazdn|!1GVfxF z9zfIwMZ*Be!3{=)C&D9(2!@S;MZZ$Vu?bg)q9)A_Q@ED|o#x~b zlDH}`@u*eQYiTf_K=?-%ACMqG>F(veNJtW=SIKb@i?%n8`doy+k~`+Z9yUF1lf+Nj z>4VUj6C9EmXqbpWSLHyyQt3}UNfr;{BPZ~RGaPp~a*sT4BQS3-8RLDTQ}^qKDIm5u z0$pZ|!>-bwAubd^e2BisqZtm|s=*H_v4H`{|7}CKw)@uoUmtViq(eRJwCT1#Rw)As zy8iUk))D_32=J#kEzyepPn5ym(j`QclH~vOV4SvvJU2T4*C`)eUj;5ZmQ5<9BS=|@ z=!JHKj~*Bu<`}RPsFssU1=p)cux{gOaL6oOPzpCT2-0O>?H&cO>jK{=8oitU_TuhN zH*l4LQ@_gc-Mc<(b*b2->1kaQx5UHLSOk{3SgWBMZm@B1!XqE|2z0rjJhYDzK`8ui z8i%(%H1GXc4-E%*;V->ZMlk?Udf{lFX(BnchsyC%+0Di6!A-MS>Av&Gp*j%qWHlW|Ldw& zi!5X-f@!xbN};4+LG**^uaO zH%@JPweCF<))3oYfmepePJWPU_6A`Lh;W;anU3|TN%rufc+G!rY=j#+S~EHzI(h?-KWdSJ`^8+;i%DVTjjLMH@<6nO0vb%LUU-e`$#f&KJ;rTez9Bq-_ujc+rEKjYx_x(Dq!`rC6%;P}Cu3fKKw=M2xt z62eK09|za3BHE5j;NEgeth7EU%3y9inPuPT>QY6$`$~LHlB{085a&UR+Cx_ImxN@^ zd(ofAX5&xjtOY*$u}4cooVq|O=rbGW8u_FeKpnS5xKH~iVmU<^$BVs4KUG*>jw!1I zs|HN>jPO=WXkIZK{f9@&w(UDSF`35}IfFq?}PJ^@B4?@(1 z>5^tVaj%&4X9C{u%W7Y~DBw^N(O7!j=tS75u|G(PwH)6-M)^mRt2!rYxs*iI+;9ew z)@rMp47AA3EON=YYIXbW&c`Q5?4dddC6J%8)H6KZ(Fl|8D9y}r_q9W}&l;7^AB(b= z9!dCSq3j6l_P7bL7IBrJqFU^}PI0G&C#QG1h;|g|PRAvgRI&9raAHk%51BZ`t}}vB z6=J04))~KR)h$@ctO**oR0m4GPsn%2bI7%S^W@@K`| z7H>p@SYMC8#P}pl#JOUCcE^!Y#M5d_B4}TKtjZ*HtYCXvSim&tN0EU*^)EXD1&Za` zKXD#=Ph_}nndL|dZaVLq7AWjI-KiCCMrm!qfJX(b6=_>#4CqX|?%3+^Ss4I)a#hF# zC$=c;tj{N7)2xid?VgLV?!4q3+N7(v1Kkkbw;i81`mE^6{PSFdu7?|A)&^H54(Pmw znCyNVx0I(BMmUAaV=#)VC;R8(ALx7^7a-utN^%$pN{OinQs~VMatZrqrdS`Y(`1V( zfb1u%in3^MSTS4!hJo2F51xuu)lO8k6|b!qz;TYB=BosW{|^ds>qEFs-cM zXw!yNx%~y)CB@|0rdwdqWZbx_Eu96vGj0jv+t$QlXou@}E)w*NJ zkL6_)v@UX&Tx7XD7iU_ZIx0aHf?iQh8(FQ9VraQm+D;}q*zT&V&_tdcQC5wi#Cv{>1jVF`5coG?W=BYB}bT91Rw6Yn8N{cQYA59ng%ESSjGh-N03>XDK-ZeHJSJm0*%G5vI8F&OHKUxWkPB&@L1$@}%OH~r zNa=I;f~So(xyO==W>cKm749VQx(v~FOL48lF~kuM%ka31RHYF)^pYCLr z@<+niyU;*YYw#(np0w#QcE`<8lz|qIV#_@f;UeH$mHVyK6u6OFJZ@j3umSu~`>;8G zZLN;`Sa?jC&d<%l>+$NqnwI^5*c-F$$7c=us*C7rXL|EOMJT7}AH9PA03^=Z%?jy# ze1lUw=*0QCA3--3vH}A^(hyFt+Px8WL~MIRwu%d{sPxE)OnE12h(noc8UEb@Ayy>$ zSL|C`hbJ1#8XQBhWr-MFOLfBO5=MlMb>2m&)YWI|%$9~E-Z{0+qCy)v$T;iFt$?2K zQ-+zn78w6{e`3X(;k&l)xa*VC9)6wN^LpF9-*Pq5+^Y!D4MaD;^d=lfs@U1;6l|jLuLNuIOt=sZx-WI@RS6 zt>ZIMwd7S}! zmRQU=iMhsqF7RqFni_@M$hL;cHu&h7m5$}S~De^suzhc64H0&iF ztq2w}_svUnZ7TC-`6o4>~?<7psRV= zbED=^(CFDgoK|I-4ZB1t`qx$VXETYOhTjY3Jq9W2qi~Z4BQ>%OK>&g_$mIwroA*)) zk1d&AxbxS6*hsjPstUU;mr?}D9G(heuJ*Ug2(KO~{?@EHrY;O!&=!*H_h|6uqxf!^ z1mUKZqZ5rbB4k~9bTFy?qOe$d1hKk^4E^!@A=b=-#zAyiC{OPDtne*P{GZ=t(e)ra zs~^cAcI9uqAknL)hUc@1Iw~sp2eV5 z&nK(eoG(NrdH|1RHsg$9cI3asvf^zr&vgP}x1bA=XSVw#xCQ4`iSXR>&8~8>HJsFx zV$SRNYo;-Nl6+D48e`~*M50@;eujf_ryS5kLRUO&RG603@k*+UWQxx%;Gy)~P8!QC zgW`A8+5#yx+}h%TZ{CkCjeoak@(nw|hhI_4PS=hr06l?W#hrLx=(o^_k}BJ(XJU22 z;NT>SfnKl18f2ijo++xSSz15VJroeWjmC~{#qlHm!X-`_O`xD2M;(TiY7!nsTGu0E z+jvNq8WH_Mt{DloihEP|cL-y-T;qp`*DR-AhO}DY+J*51NleN3+C$`Z*5BBkR@vAY zdlLK8GCjO@-`6JFLgHveNh_I8t~2j=^b|Hd?DdClSu}x6lUC{mgBDKe7${!kM7$Gg zLY*)?i13Uk4o;+908<3|`SNsc05lqFXS#gfjV5{Kg?p|_Qz6V*LVFNPN41FTBJ-5H ztq#~+_9`*I1p)PC{I(WbR)xbHb6>I&5td@vWgK2ys*_I5F>RBVo%qR|(FzCLB^ceq z4BKGrPA=hPpVK#*bPI|w%8o`REhWqKs5AdSuW@6VE0@}{NkdW}_c1j#trxu~EHJ^8N4$>lOp=LhO+7n(?1r z_^$KFoi;{{yxK86I3VzD;vVIs-;vHL=l!z3%czBl33Ja$KR9Fc%EO)|1&eD8`P)38 zfD}x#D>O}&h-%(zg6#!FF}H@|`dzWjdVMQ+TdKE?+oE9sF2QX0TfAk_2m~s6p#gO^ zPIW#u@XBWws+gqFA%*Rd6xxs_AJ0W{y-Zb`334clY@B2m^edF&%f&W>OuXEJB7HUu zvp2?uwj)2j5}@V-y_Y2(ww|QC0jd7B6t`9J2qR)-_m}blz8$MsA1wHStcS#aIEEum z+X~v8=M@R6B@G6U*$?IO6<+V`%`I)w9mc;?&JM>y`-qqra__znzh8%_E$X-1ApEbw)08!bPPR4vA&#_GMlZy@mMW&4a^K1j9TMYF}=`>#>)w%t|ViTudQUw>m~SrW8#?5)$*Wq<4E~<#>Eh-ih6%5;dR)X%&9^6U_I;ZY4i8&On(+L zR)mwLKi9h||DmcWHX49t!x-EizS1C(d*t(os;pgPWr3ujubGJgpL}B?b)pTeYVibeq5IWN_wdrtUvK zE+n{TBIIkHz@B#VW=v4H5>%FmroWFXkHam`pY2m%tknv0D9%Y56SUL z(kN~{GY zx%3lLr*~B}>q7w|{tPVw&MN)=ew$P)Ujqk8(jSsLUQ)~S8d|U#xs-o9!VG@DU*i!^ z`v%(R($))de*_?+&L+Dz9bWO}-iYkUGsu4c7~{Jafvtd&p4Ct&Tk&C)B9|1@(}0V( z^J)#)`OcJ1&^UaB&o&=^W`%KSza8>X^29-*%9V&2LLbl#iaBy}cWxXJ8x(%OxXC=H zO~$r0lqL(FPtbwZHn&HkUebIUW&D9fU!f`J{q4GnEnk-@$@!h4OP-$ndkUKWx!BV{ zko99%zTT?>yE5>28?fU>iSRJn1qf-X2r24(d!S3_&Qz+310npJy8*Pwp!0HbQkQtq zh8WW;t}pi#wUhpRcl4|e?lSS{Ch1hH(#*3m`&03cX#`JGCA`luJty^0cD_yIca8?E?EpFc;)oCN0X z83$V^lp}TF>A<_t|rMM}K%S7I8vcYO< zhI?sF5rW;;(nW6-7qgM7D)HSM{gm6d?_%O) zIaWi2kVazryRsra`8#g^(H)v>T4m!65o!`s|X3=tDS7x`3{F=4s&*7&alDWct^RPo-$KPK~aXCRS4_ z%eVMVFuR}W@$N!b_G~zF&|^%-1hcfqt?X)0>bgBmjP~{1iA5f+T?NpsghN*&J)U?? z2R$W7U#ZWTCoi&Z_oI@$nY*~^NHkEy8JR8aZ&JucgP^;(U=BNU1Xv| zUFtTy_upmjJSNCSLuhd5Q{YP6YD4pkVv#Ir6dv{Q5-p^$(Rr&f3zZt&U@USn&-3VAe0j)hCFSwke}tpPwXEZ>1$f% zR~!sZsBFq@G+e=hh`?os;kRat+>0R^QIldTw{jKu7RF4990Q3*D1JQhc0Rlr_^Q<-^qDBDKi^#5a!K3V>pIf{AF?Ggdt{`5m82MHDQSvs` zddVVL@gp6Lrn%l&?T7M7@Z2BwV|U4*L1FZ*Y*mUB0&Q9EZ_kfTJK+FBCKmiHQfe}( zR$#8L=x?Onh>jnmob0QD&WNOxJKce8Vz+(ZeNmG_;YK%dw7uHaFV`z7{lEKy0^@1{MT0M`8tEHcS}?gR=F+B z=)R}ijkEI}9^}a0!jmSFSWO9QrD{{6gih0IkzO^mUn*oR5p8J*X^EnxCym>Z3LV4Q zivBpelJuZXZyqeMNlyfC!_DDFGRgkN%4~6JGR--iEevXBPIS{awHgdBrMCb3KEjd zH4KdG<+ubtjW^FrB^_>pzcm8m-In&0Noqx%wnI_L8UV;crpl_Up(iPS!-+O)d^^!C z--z_vrOLN2j?2)w3D07l?!%sypOs`L+~-!+g)9dy3RI=I#cniJQ;l+@MeKOE zd!?>x8O(%=dTI=uSsh85o*$l@>T{7_aAHqMhL{jCL8pZu?XjAxlj(KVrAS?SWDwX_ zvYWJALrOxU0jLqL$2>85?&Gcf_nk=loVEqOob^7oiAhtL$sf`uSn1*bo6_+j_`y9j$4^>xqYkA2pSEnFAX+Ry?d+|7#g5P95FS9TqpQ43p@# z(t#?aSyk@#H}J7HFUPCP9wz}k?Vl7QIy@_t;)82ViD4U}uz-5SXG+*F>V;o;_h23> zX8i8v&2vubL8OpM3)X#YAT7(HdyB85M<5fv&NG64?#!sPXP25;cvSnt5rtEs{d~Vz zu>9n=yPVXdD)onq*%%LEkL(0&7S9x~iS%4VM08~R+`Dvid^pQ#V;)968OAS`Nw@dq zqm1nkwbD~A@1Vmer938xpXlQm>d!$=L|#JZC#9j*FGCz!KZxBcMCbl?rNw}krl z4nwm!ITT;DoZ;-jTBeWrQ+xMwZY_+KF^75BLyvDI*?TS+$~A?ObtId(Rv@va$_VD$ zE5%Gc0-0kjH-t8p;Og6Ye};HszBL6XZ7wpg7N>Y=;3bM6;oFD=q>ilCrPNk3M;`>H z5K{HKGuF8aMnzOM{E)tFjb0?jeMqxfzctudDlc8e8hAY?I3*Z)+?^J2!~la$ua7KK zj_qcd7qt?`@_TVETcuX4szByh4Bu)AW;rP;#s)Ga9qGP?`DK;n6&R##0)Y)}VN*(y z4k$x+ZG?NLEGq|hk*_CImBm+Zqth!Vj1{NU>l9w|OD!;)3Vd27;;-s&sdP=}2y)K? zbFTC#YM{bi=DaN8_HrivzG zRfdG&NiAhn(vFWC{DV#De)3hZ@x?^8jsf*DO^xE6KIseP7!o#MTyS(zLss9?D1R`B zxsgo#BveC9hm_t%QK@EjlK zlH?xS1EwfkK(@!`ycnJ zTs#}kE}pCL$@{E1KKNbpQ(}6!B0!Za4g?tXZ!8%e(Um->igsBfXAb8F)OkM6qdYqs zzUuM%frxp^}!2xMQ!$1W*jT(jZGH)S~oMOQ9)^VQmnFuRK z`U4TY=;GaU*HWApa)`i|B;AL)PG{)D3WVSBMt_qqj+8?}La=?=pV!J*KXhQWl7%p~PDog^pty_qIuNV|t3N&ut0t_=#L6nhL94@yOyqfbmC@@0}R6{LP;p z4+;F}#v+IEtL@{_L@Z3WoDbl0Gs3ZoRD<%%$K83_a9mKXv8#_?B>BONkg}*U``B5O z3Jdl}aBsPS}|LTWrJjlHO$kOo+MFK9W>k|w$$DQej6*H6w+{d}6;IIch zyrN^s97+fQT{TRPSyPJQD8`z-$ZXxLm=!#-t%P7B{~8B>@ftL6PKS~1I1WVv4Q!|P==-xNvSov9lE9`f|{ zrG=Ca9TNT4xNI)lB-UDUrjyqEJVrXzhLeoku8YB34K_A|4y4XrvKU|E3yu@jd9>ai zGnNsJFqgf~>*Bv*R3Ggzvs4`;Aw>LWdzEOU1 zrHH6wUzKY)el3fthEJqE8X~eFk(aI;{qU?v&vJRc|DyWUbx-p8ra?ecv_BqIdmn2V z+4#A?ulQ&$E#iPU1b<0g`0517XeEue0F!w>=(LTz8RPvhIv!qHK_S5%>O@v$5Kfe@ zt)6W;#AKdZQ19n&@j_5)%I}_zd%}ZZ#x7lFTzjiR5=o8~m5tONaGwp66Jh*!)#D$0 zdCXokdrGyMCCR|yQUS@{r`snlFSt^s#La|e229J!zTUWV-**!dWTz4j`Q;M0 zL?_BN`NQgy)K9sjQXXy z*s%&I|CylXVXVGybM2k}m++VptGGkS8ev%#b|jGv@s$azZgRlm21aC_O@4#)bg|aN z+Ca#5Mc4<0&dT&{3hX3RRXbTaRNI#&(HpJ5_ut#Jq|t{u6M@S`XSX`P_^R4#ez+dF zv&|pvYU8)A{oa_JDd~>C*lZ0x%pX0`;)BkJTgjK50{2Snb|`a++>kRw9LmX&&NI_H zBn0opudqYE4~&?oljwxB%?&2OU-mGfOH<@R@{eXZD%2yf#v znYow;kvxF;V`&~Kq=cG_(syRJE~-!NNaio*9#t>dCWUuRQsKFoTQthML?c$q*WGsd zygcF`qy;-s^o)$rZU)hRZU}GCvdoD>f{V>aD#?564sgB46O=pw%5%@o*!B_V^UU&f zT%(iJX;A2*UESs#eVGxM1IoW8r`4nZPk3;ONBfy4MUEx_I4G&7WW_tFqyXJKt%X=0 z+$2?7boBa5(6h$G+ENPdwk|E%t!3Rdi*}v>8>gM|(S8cN+I2HS?WqAG0Le@O9F3*9 zP009-k{z3MZa?%8fNcgd)A>Rlu#mU`+j>Z|CNvHoFV)KgZwx^Ge#R6%Ue2-5n*_x+ zAiUJ;g`w08PV1kh{)tEVwg&l26t#GD8( zoFKPXdB49rDOzHyJ+x`*7rD_%l6Q3(8+TRcN|rGD8ibQjAFmnAtY4M$&9`ZV&48cv z@rKAB91B}Gqv)7Ucrlq)vLtF@^fswDfrVh7i@YlYRpDxllvZ;|m*$mLmSX{r*y(sN0MClwMuFEWAUk>=+6gO93a}j)qOTcNvmSZQ zQ`5edldo+Na2+c_UyCfuyjDZM@D37A3-fcacofC-ENyn3^0!wULSo;R_F9-OMZYh= ze4WX=>mVAe5v0TAZ*y<=x!A?{@nK^g=`~{1cx$oJrZJf;g7umTR2`H8)2vY+H^lt? zK`n<@dVtD#b$I!kCZ)fgn$ojI6cZ&Y-ynjbi}bitI*W2leo1XNcr!6?u?bcp z_C2svpdviObn*ILC40Y{2pUvI216QGWH&3ejPYy#yfE55>v)!Nzae-+imb7f&1nOt6_%o2niK2+kt}zLJZS~KG{74OpkVEQ84odSXB^IJo5f_xgn@{K^Ar!S<-#l-{=A4 zP6@yA#Ic2tDJHb)7l~nTS8G7~9WhSRpn?O51%T4TbBG*W5CaW;k=7->Mp~I(N+g{m ze~b8pOgIt995zJW()K^GBAU+a<{rw=7X}RShh#nW{|Xf_Y^eW#ax4B{wLe-?Bh5|K z-vihvh4~|Wq>yJ*C71pX6Ne*8!`o*bNK^<5cR%SEUpvB()>#<(;SAf) z1s21z)yM-TWt$j@Bx(N5TnPV`);g3kb9npryZF@EhaWI(SilbKn>@E?{1yAZyj8I#Z4Lm6ODhkl`#sDO&#W?I{ zBk$=GK5!y2z=(z4jkHUH8TsaSAuD*}AhLXmxT!`l<_p=(B51n7f6Wguz(!iYThYZ_nywg0FrOXCuO4w;}c~NXnHp|}{ zZ@6l#BB^%t9zqy$=OX~UUMCZlTG~rK13s|%J?H<~Kh{(~+%1~syqvjiM*rbY&=F-6 z<^)ESPQMI!@u!&$mYaYS9C-(S22oYz1I6@ZuBF?4ZZ62D9+S%4#8!#o98A}Y*awml zT$i@!&4yx&mX7`0vnSG!2_8i&AU{fIM}!eLrMTW?BG4lrlb3+h;S6I*s&=oY5SL_M zo(UrJy#BM1*f0Frp~;Y0HY+N~~eyzVBDDLG@q~kLJkYla&84}s7)1-01g~`63y$c}le_?S{*(ZmED!zF| z4onZkW}yMr4g6Lg$ITB^famp()xM~;JxFUSnFw+J&jaYiSf8>#n+|(#pb2x_%+?zt zLzNhoRyXu{t)*W2;CR#)C+_{Sa`Jkyo|*o9+Y#YWE{F2ZjinHNxV>zrrKZPwx5K0j zcX}eh{RkcP$*XY+r&|bv-Fz)O*s6C@@R@H<^~JTpaSz|lpVa8bj4P1P6`vl63tYbK zL`8w6ZLd}%@wcq}lftYOa$TbHJQssm4r_aRQ}|4}@Q2MYzN-5SkSJ}I>K_XE7d<~b z&cED2;jz%==DPlTKtImi@_ygG$ayp(yycP;8QwBi*=UT);{?pzZCHcB!Wx@J01>@a ziHqo*!*hwSe3(R;QQG0n+G`sU15ELsH+~62}#Ynkx~Gra(gLs)&@CaE(t3m?#?4dSPJx4 zXO+Jw&VR4<@q@eW5}4!9Y{j3&s73e8WMZ45_IxL9sL)KPLLxpvjy;%7;CU^L^WH7~&a=4LMZT|T7o5OTRpF9rk;gxR55-VEpUH6Gg);#{E zuW3kDvir=X&TyLJOO|3Q5724*XYI9|P~3N(4&4Vip&co3p7oZi*3yJ~(GQ`*FK#{6 z+`Xu0_{{Zpkh2;@ccyCF&%M@VzZb(X0KF1|CY^3Beh`F|CAJ&=LdYvR@&_9T5;Wmr zqVdur?C;9gR~7|tI<&u3!ZCAFjhS(8zH%_02GK<=RY_g#>b7~3f^WOhRf^HDg61v7 zS>=A3hyB+u10{O=Eu$sF>7~caa*Nub(lg|8Dp+{((@@*FWUEJO07)y+Vwwv_a%q2+%qFdh-Gn zC>ptsl_>v7YH{lOrT%Zg!~QpIv8D;wj`+HTqMK3P>>-tliBK=L?=#bqQc`$pUUW`) z6lCOiRNLU`asWZbw1A*_F7u%nGGq|QD;}&aG4K5U%d=SdV(n|hQIV&Ey7_az$UAD< z+u=$O{KBZvRR*)EPzaG3oh1f~k0AUAa2f^t&;8GTossxnN3wLDIo?hdvwn{-!SaB79o!}yq_F<;MRDzLcGO2 ze=w1X>39(KMypY{E;87ihL|~Yc4+`7_p*!@x&dB-qo{vQLM5A94rwPHJSO4)Lz?P3G|4woDYJiP|0C`lMTYjD+X1~tIHf>$<}xoIbHY;bY>R)L z=eaI&v-eWZ)fh)nTo0_@TI30oDT)$o*U^xdA&3(u`1b=>>c(o<6ARl}JE}oV6-9j^ zf`?u1y|C7jSY{_y=_@#SdWGT5MX12M7d1bbUrjd=8qYNA74Q(f!QK7vIJ;hoMh!u!Hva? ziM>Zzn(}LMrk`-m1UbxNR-F{kUq|6R#S$emanr*xCrHADp>Fg+$hcpB58qh7KR?OI z@5EUOW#%&7j#q17!R432L>M)ZC zv1exDHDq+jSo`u_GbZ~$@28BmlX!1Uzz~`ZtnBg+`o7%YN+ijyC4&1pCE~+gL;K&f zSBYOY5_sn492^>?SK>~Tlx+zmA2RBtX-VU^>)jI^=h(RrrUI}rjIY^x@ZI)>i$dh4 z@8xvbVrdf_nJ=viz27idtxE@;$i~yBlmMslh{~x^0+S&O@nqpE&Upc|x3dWv$w;o< z>9dm>CDzA12e3a5uoPb^Ss$-91mo*rG?M1Z5g@>PLpxFqVJ{@4a&ANjyG}wmBmH~BO&)YmXRLUe8ggI8gSssAfpa&yx$EK? zUJ>f8zCcq1bf0rVO^bX^AYo3ejeq>)Z2#2-k=koM$r+{PAN~whK&|Hw%KNGKBLc}P z?T}0(aywTYh)(7mj4V^@;u_$PW=k^}Y1>_J=36-YVYD%#%ILxY#|718{*2%2d8ve#^c7|dy8aml zD=?jEvKIurB;mBbx@w}haOko*0?))?Xo1ikyfnIzrug-2TP}|5{+-0X0Fzdo7io`o zmz*ZGR!w*N-6*61<^(c4bTub&bs)9m*LZtot1AOXSM2LquHd?V>EQ^MLPH^@)&PP# z+n)Tt)FR@8uvt>gI8Y{zezMe0@OucHq-{0nH%%{Zl(8)tV@Acxo7&IM8 zF^8IBeddVv!lWoPIGUQelQTsve!gi*I2tWKHK-T{+G!)b2`0MitKqk(KRw9UhB#G1 zvbvdO|AF~;gUlW6K%1sWW~VVVJ?1ys?-*>%5wSacI@OgxrxSq><61ER?U+Fux2E*j zH0=Ul`?TyKW3+RLAuax`A@%7C0|72bRL*X4SXeXn`gXys#Qd+goWBy}Tn9!Oh(wcs zehhmOlmX8fL6e>YYpm7(7!aQ~lyx?o{|sSK6M=m)4$DD3X#%pCyq@rBqygQ5TV2?- ztgvyPTPOKdoa9n0b|2I!JOpf7H|(SFluMTQcD^x7)a?Rdw)p2i#XsNpveX7M(_~#6(a%u3DubXU&U+^J;hvd+Y(PbvnQ(60WiO8F);gZyeN%L6ls@SXwc3-o z?>IshTrNVo?JxM+f$mZCQ2SS7n&RqeASNaFPqvY;3ppGgnf)i{q~tSwY}wJWg<_Xw zSEaeX(t-W2tgyd3E%hI_&Vxk_jOB+{paQyBJs`mF@_&4r);6xsz%e1 z=n(O+Lu{n9>?)bk(9ruUeWeXV%x^3GyD`J*mIxOew$0pg;AzfTNLZkgB)AjOAmOnE zGkT8$8G;!06|+Ia7otNR+k?0|GfU^VT$)5r<-a6e!4XYLcSk@QIgNPL45dN@P9V5TfDoi{ch_b%Ip_QT znX0Lpnftk2WqBBVmKl6ZW)O=N}C98Y#O+(UvdNyQL%R1=OL)`iyx*t4Yn5WWVsln6YH+Vxm-CA~x4;|jzPz440rA=0 z+~|Ri=6#96J*RqIYrCJ0TH&^yJ-5?%UlYeJIuxZG-xsCyURQBCdc<}5OAby-~-=A_kA@R^kb9T+Hd;T30fe;7a2{{UDZ z#u&BI)}Q>0d+{@oI55JZl#d4H*93?D>;lOVxT$&p(Zb69jg-a489a5ufH-=Io~a^XCPrG zT25T=HFq0x71JYZr2dBS! z9GI8KJb#^UE%9u+$0*-`d@dCdA0ycIwM$!At!|CEzsQZS0k&4I#lXIP=XKH>i+7E_ zuY?&E5!Bi?qD+1zuor|DCGEzfc-a#yKW<7+Jruf}j&FJiM3Vl94T|PIuBj#b$S^bL z49&2&G5pfH?e9f3orY>S7r00)j%q0<8;DGx19Y4c8YC*adXh6 zEb|RQmnf-Hca7GfPoynv>lCJOrhshpU}Uvy=Q>(nwIc-mn*&;sIMZR8#FY_+ ztp(9h`v{{_YhjGcV*bzXEaI*vtX)LX&5_dop_>&GC|=V`Q64y(I?{9Y!|EQG8b-z= zijhdeTcUPcM#mzTcE9u`^iJVV)P63^N-(A1^!-q&#^&iauQkxN95*5;3WT2~LgFho3)nis+6)w`(?XT=Q?87ML_6NZc-v?@Kyjsrj8 zky%Z(C^-AVQckC7jDLudppQSLEi`YZN!CCF;_0{Ef=&CJJSn7XBYRWR>TR5$pc61p z5p5tcbYIz*{!SjEqeE7WB6zY71lMdJQ57xBmOsCEaSKWZ!;i9p-eMTEh1^Qcm?3tY z+1FlQ071&s2kGs?n{ZJcSF3`P;698%m+v7|rIdFHRHe10N4W5!R~(mPcS? zWPvSL#fD8+^^gT6RYKZO*z;@XSeaGLFcFd^0Io&pMbm6h4D~x%hF(UAyopu6UR-yG zs&)@zz9=p#vecKVhejbpC34dtc!G+-2T3#reN8Rt5#jf|Q));X?&a|dMg~!pk;AAR z2cCMi>Dh_ej_O{buzu1EO0^;j4C{ZU-W zoMv>e4J&V(3QH9`vs+olf?kG(()v%ITypz3Aw(lO6>8CK%RP^>)l(AqI8r0`y0nSd zs@?{w)pVxx;VeTVD9VI1r$=mW8)0WBo^YP@dI39oNEBZ^CK+0GW6Ea_R?Me>@aoU; zXH>lELpi2~q0+|UNV88T9_@0K`V0oNG$kAw>--fTJbpy#fSl21k<)TL{7XU;UrR=y zyY(bb@FMuGdJ8GKl4(L*Ix}lfk*IVrM7zegfEyIKfdUCyN$%A_=BW_V$xsvpggfX- z0#6%BtwE0d{u``TK2w=y`HHF^V@U&9bw$BMy4AW!2=L3&7AwT*ojqX_qhFQp^GFZj zTJ@7_RZPG|zF{c>`d-&T@l}+;-DkF$Z4;1upD+Jm7X!PWyMUinptCz1%Myuk2!yOmuk+;P zF=<$22N|cDEboL>Hnf5-qu3}#QTT~iWX4g}CN`7mM$^kp#!@^gvZ(@xKW8(s+@h$& zhhPT?v|9~zzd2x3rt~x!!tP8V-}Mj}@oddXB#TJ?L?ZTeybcuowFXpI;7FGh%^i^L0Gov>c#}?RNSF#@@3lamG}Hh!TzRJ6kP8 zNBp>h^->O}G+qEW0RS#bd?lxB5q%hRR^kMtczEKsIn7(bl?2SAyxt%yKQF$` zc-osjEt=Qy7%%QAFlR968a=a9@SwFVl`N4g=6FnIw$j_+i92&bimOUyWKwm{L0~a$~X#ES$bzJB{ z{>m0PE~A^IQedk#V2{q^K3{DPpR~pN{=2A3N1>I+@MkMkAiqybW<)%2%KWH?FGPjt?w^r|96QghRnhZF6 z)7E~x^FYf{NDchA25jqj@f!MJITy43)N$85Jx#r1{g)?SO$|3!*wYT-GtzOAm&rg< zdr46-+vQmp_a|U$_hM*}B0bqUXfZXY9qHd%n*=k^S7Ff!W#4FRxBoRor zDfDTVJUZxjcc}w|;nN%CQjjVfA2^ZaXTC{f18Uo%w?9Lc*vQ_>JPGc zG8XM(;h%|GRQ@Ipe0H8-#rOhwVujkA91U2t_-Um~1EC>CtYm#J z`x(B(F(bX3Iq&={g+1(R;b@4K=ASntj9s^4Ir03B_o&w!VY@j7!FSG#2@>(;>9E}U zjR-GyC#koCgn6yS`1$RWj6CxqU(#Zfl2+RfDb$WYNw8r{J^6xN5a0pL-B}52VP-yQ zX*2>HTQyWDYpOdD6#Vp+7YrV|ntaEQ@XDyL2Cx<4fu4C8jdr=|QJ#Ft9%%MsKX?9` z`iN{VG~$OfEoMjTN8RtG=Sm;JY8Ccut);~!7Oj97i=3xB9g_%bFznkD6wvCOZi665 z?6JZ7{g1F^q?nwfZF%1ZW^;yg*?EV;(fASHlHiikD)B-A3dh)l<#*)*b ztUiNMXCYz-a_=Jdeh0*CAUSY!=nFzMVoIW;Rr`*D)p#Rm3?I@O5u3gRrk0w{CV_W= z4(_KHZKiIRug%J>^x+OpV@jazSGnQ=siZk2yXc*3AbS3{TDfOa>{0pit%*U*Y0b~J zA^sO?)5Q^I^M8V=$7Z{P8S(N&-&zeTq3dQ@YIF&{9Wd^DRc$Rzs{pfimhGRP4m3Fj zOHA$L?;JD+{fjQY(-nKZ3upcNhum|vYP2BRs<>$+FhhjaTr(ndq2fD_+OUoCVqNGU zUG=?9IKgK&SvkLy5nqPbb8cOsUyl>3kNa#D3wpms5i%kQEU-o4_W!O$?FAykUS%B^ zj!i-Y*U$X`C^*_og@^BguTkFvE>66|atV51%P&1k~JGhetj+rPz@z32jLW@vsa z$&{ktYNct1Ju;R)GB2A)f1)hRIxcWgBifibVkjQ3tEi32zgE!OB5{+7SPT4OMyW}? zy5m2e+~hSM6D4{64Qs-hu+RSN%-0;q49n%iQ0b2Uj4T>66&35+OReu2WW30VPLWA$ zTIlV+1U-vGk^BCZJ)xD?)*D|3v2J~SATE^_(Sl~iRsZlJt7P^8W9Rxfb#8R|CJ8K% z!!i3PggU>xy3O9Az@w4({DI+#E}+ooaSahl6#w>~!8C_2;g}r?S=tqIS-+&NCAi|r z1RG^mOHFVq{>!D7;c9y69(T5rDc?P*Fqr5l@kX<|?yEZ*w(v`AtMOleIg-(V7VT}w z=cvpKVA13M1?6uBjt7FRsk=I%*L^1b*%ivcV{L>MJzIhzeY#Dj8Aw<=O_7Jy9vg2z zgA{XpF#1%|ZWz*f>bdf*0!<>4Bxm_Fnzv4~*Hr|hh+s7fYMHr9M(V&IEBLv&B43-i z)X}+5Um^16e10d3pzob0T4B^AUu0hn@o=Ds3SU3VVF^a5pJs(}73D>JA+vozR4w{* zX^a9SSQU0ZlV`efbThdz)!B@OGlAuYt@~@B#nQW&D&0ht0rHeLz^8iNDVMPK*$o{t- zl9h6cs=?I7oK9o%qcFJ2u`(PdY~6P;v)kE?<=d32Cp^?(a!XY$jDqZx4=3;Xosoy# zOaFn(DOHiaJ2t4CdJv60Eh{1-!q#gOJvYqPYtSs`YeuY2P)CGa&6Lrme(7>tQttVc zUhZC3?lW@_;mmy?NN@8w_-bpp=b4$`y(9I?w>8AmCi8rA#M_Fmjh4Xc@Rc{%lX!gK z&Zp1w^{n)MZYMG<{i~(J)vN6%a4sKD&wGdGy`Js6AKM9@Pb5YBQND)#brDS1hGt+P z8<`vYYfZ&P8&s@X&HLE}`tyjJ=PwaKYV`Dqdsx??v_5EpYzXbJwoz7K2Qr0twcQQh2V5E<-Xq58Eu-*_PL?5 z&7JG8W4)q}$My)ED3>12Cd+2~t*|dDJ0azd0`NhXETy9NId;<$f!@hFR8Z|``$nbYxi@el3Kd6GY z$1<1h$zMRhdS8mpuV~d<*Oho)_$AeeypJP;s~Y>J3n>3wsNvlVoxU2Y?rFt%q5Ar~ zlK6OE^L`D1YF+L5K9gG`!6k9gG=uTX@8$n@H`mSvdp!0y?{H(ef%aFzePO=d>{Fy; z+3OCo*JQihvD^5oQ8);E&e82lWT0?A^lbfP9}-u`uQvAh>KWmyg0xvtSxS$*JEY}y zaS(jJc8mUuh1vt|&utj|B3KqW=t87M23L&97Im>Wm#jClxVN{EjHhne#cETk%jeEz zS^fftip5F_vz)mE_VcmUdr%?rF70i%A-MZ8IQCJEN&n4m&I$Q2ByzW^O&XE|s z&l+z#LA}2@(w)rpja7P0AIg1ZC%rP_KPwac^NP-etCwdhkNStEa=~7!{Ymg+K~~O- zgXYC+5b10DR4O037NK7M-7mgAS8s$Nouw6XTj|CXR(uJkF6fj}E^{}Ikr58u!Q~}! zOQ!*UjE@^=pB^H;GreUy1`7tGiRq^R67=vFc z#5#xQPLB6F_i~dTPv(?W;e3%Bh!KrkNYjQrkz;vNzCb5mSRn z|CKcN5}VgzsbU*vV&DmT?Se2sDSEQpuL{Ha#{55Vd&+DxR!PpI-j@Y~6~Y(qUcZBU z>*GIJxl8)7|IHF>4W?YoPPUa*U+7z#Wio_Wj5ZPTIybjYTBH%#=2KH76MJk6`0ufc4ma^iQ1*Wm*uUEq_j6Z9MyE`Q;os%t?5PJ;8LuWZR3sSKM0nsq15U zE7vaxvn>=)bGle*Lp+T(%D*=QLH_77#x9!_Oz&qpRweE@0i_1)A)%zBy(~pZWE705 zWVeEt;Gkp3ccK3#pRaQwlA<}87AP&t+|`LVJthoem%VVVcxf5N^1usnuTL8nm+C-V zc~J#JhP9c(B{^kYrHQw6s+67WCp>9zGzxZ4ZK^$W2fkAAuo)tFart2X;={tVU;yj< z0D1|v6qnpOKskn1H+AEhV`ka*-C`Y4cY;#9eoALu%+p?%yA+_e28Q4gz(WECuHP(V zw#{QfRHdeES$wMlDIpLr(I^;Vif~C=)f`$XY+c%cq`#)@Ni$ak?36#>*JJ{DP^(Fq z?NX#7R!nk?exe9Vs2tmVhtdxc?F+29(f55PDDMFcklUV8(l)7qTj`e1XN?F!D5yuIgtZEW@k!@vAvqF)YqWGm43Dv`et~De0TeU@+ob zuVbiS;g?gA-8$Ky(<*u_mn`qrgXvfioOzOJP|(d3+awuQ)}Gl zW1oc5=S`S975Ntrj<0B8lj3M<7mBMad~^P1pSW4+Rbx_leMAScR^@dvRNO+f><(sv2*Ev;C6CGLLCM-ItSw$Bj`z#W*I7n9a`qfSPXgRE z`)2Zi2t#U?R2tdp6ci}Xg@6d+gaH3CcxWs}S~Nv;>>P^sx*JQn6)Ww`UjBTaYhi_) z(ksWDa`eD{4iL0cj*F$li>f3UkiyKPFt@fZ9s`Em^N_-i9M$@5?A>H&v$zY>yLct# z40F>VDA=-8Po1l)d=O)gl5wq%0lU3nMUf^FlO?(JAXb> z;Jw_#{_Bl?(Eyi2W8HF^>ss`ao0AIkpyqXyl$w5uiS(GGM(fW2Yxm6wXVqdH3`~OS zyfw8Eu*K7#y(o%nO6f+2-r&ZH&}pZt1;<{w^{oZV+ya_p1I!6x8BVm0oPb)+a$Yo{ zUw#i`+-0655(CCUW9$9%Y1ig*o4BwP()Gogt2%;L9k5lgN%FU4bUU44=1{%xZ`bSM z-Ru@~L%IDW`H~GsK^EcL6z@-yB#=5x)w<^So7s<#3JtSC!G_=OIw=lbUAI{g5_4MH zz=yj0$m~vQ&B15%t6@A%Ucf>v06FV&iy!kRQJD2f3PrZB6&v(><+`h8hJ{Ps-&N5) z!f^JSnHTh+{ua8H3)L(GdIHe3!HWgLZR&G?bPyjQHzSp}^m@qbC@ z%OqfBk^ZBiXQ%!DL(*>=|NjT)zOxZ@01yqqeOHh zRA!!MEg$bo7JsBYPTvil9#sd1*F~)@y^HvdT*@+6!BgjZ?uySfO~27~SbA zZC-xecbOx1n0r;^J#ADGu1RV_ z;`71m7TO=LxC8H?s#|FEcivMX^*KG9#Fo|g#1`x6`hP&#c~RkeKXrxpK;c;8oH+^ly%cHRQu}}Rcc6^^FhJyJD9aXqpQ1n{p>)wNjSRg zz55>M`6i%SQsV!sEKQ?kNf9TonOfSFo^XInr z!x2#h!NoZ2hw_u#4MbT-r#o%*MN__o>w0h%TAem{_@?CAzR;0)J;UeRfx};KVppHn z1=#n_+>kS=!OdG>0dVa$FbA%$4xLMTP|k^eC`8<#Exf;brlU-G#<_deyQqB!o*p+P zdd85#iu(Azb4bqj9`VWd9O_-}{YT63gZKSTh*xX=giTW^`W?JJL|MM2nRnp*DUCbO z$H0C@Ebr^ihtIx`px308SaY*qoZHWzx3$5XzE7*42|W*E?|UnMd7U=4jW7Jj5GS=O zg1hOlPQUN4Fz77BaIlpEuZDmhh|HXB9C!Fb6~J<7{)PM-rT<3$O3YWbQ7&E^-><>^ z7qLArMr<|DaYUzOyPiInK8sVY9!nzU@8Q_ykX3~FQpR$5t|4-@Zm1n>lhyBEaWJjby+o3FC0pvO`WlN|RHf7i;$hhL@-SDo97sl@PG;Pvrz zX0@x!%T1zO_tX^@unb(j3M-4%h}h)q*XlO`3)#6T3b4Im6bADBr38}03`jgq_bW))XpkyBm=@mCbapXT4s*dN>8pdJFiBB)vv?~N_qm)MmVDq zST)m#l7#hSV4Fe)&ku>YHFT@kc$rk%?~q{-%#avGyB%MF_0nA+h#?tn=k_^AlIQnl%&jfDrm96!(^uN+px3G_)&@x7lAf(5q*5D=aB@am&+#CEQzj=%&Br)Xd?ox>I@`VK#87Y!^0pYzxn`GRNdtt`8q1F z2+(5C{?$Lvp&|0`nHwYjoWT5FY77kEi{t|ZUM#^%o<^eB^Hl&yUbEr7@3o(}QkRp; zt$vkl0hNrUe$}+)JWCE;-YF-XC{&QHZGTr)mQZ3)r`9PytiSd3&MikJ22x|ew(Rd2CUS0Vp<^WGPPC?Em*svG((f(vH@a#{@=dos zH3y%KK(cBs*!`iq)@}}-z@cHTy1p(iq(saRDVKP4=O#+|IDdOBEmZ04f05Qk$qf=H zmC#VSUX)PC(2oc-epZ7Q10NS^*(g-i3*TxQo*>8XlBVV3iLQ81T(?A5a!-pk@}1Ox z3$T$8KIDyNq?NzF;fgq$>}FQuofmPIihHJ9jA!Sc^>C>*slq%UmAIHbwiqKVZg;tfgd)7dupgjpbe z>j@FE4jngF+n>qYYkjHWZ(I7{=BIKEOTV{-)soz~a@?3-6GtqM=LZwij4eHmM&;l- z*aR}>skz}nSL@?i`~4ovGPu1KK{lc?GT*nYWdiZ$a?M8;Ws|t>o=TIvbrFlx*tKck zT+cf^^q9IReF~CxceTxXzY&ByNlDDpNH{)>!SZ5qQB%87I8}x z{N_p4{X5j~ydQot_NE!FkVoKB;RV zC0XvvjPV)}8+0E3U6OYBK&?8-3Fw0`rA(&XGgz7x8cY1xrP6$@9p*dFaT5@`R^p+( zXBaXOO+yu*GxrT~6b&28|26u1RwnyplOy*sP|f1)NiAnvGvF*5?K?9LkMePpl^o?A zWK<+Y`3UOWobO;?#Pcwnp+C;iB`XGBV|2)&U>>> z&lKO83g<0{C*?etyb)z_b3rgJ(4P2~*YN-l8M zflPa;GVA%|rB@dtg%%)|hU%`5cpqT{bCzeF`(!TyMYlcK6|OL$YKR^-^|(aaJ<*-@er$UB z@aQYYG;euzXX?*V6MseTCfo2`l2~-dTjsi1Z*1%WHl1EN)oqGwP2Y_0wV9uZpcGfM zcVA}6l7p3eyj-iWiRe0q1H7Acdg8w{9=`f6I!C35cDaF?^|f%WaD+YwFv>B4&VG9N zK=@u$V&BX)H-Gw3AB1)16Pw_PqMl=w?|3G7>$;%O#4?998MXzK!bW}=ZIfS%%7BhJ2@*Q#vA9lZSyOWQD05b z!aL3b%_HJ}Xf)04JB8FtV1U4{%2mPt6%l6f^s*=XwB7KjG)j*{5Dc%yz6=|v z>OtG!koL=TLH;?9P#l+xx#dQs^IV$m3;c}M1Iy%M0=|)8TEU8(riPnT0+WlApMU_x z6w?@HWh3M>c6?&1R6nf>q#EQ@`P2YJI+bH?&(faj395h|2)5_odUTjudRp;Ift9eq9jjtYZuky1Hzw3-6 ziL;1}V&P{G-9wQMUjj?T9Y2to=w3=czQBze7H?1emio<18zp?-jRV0u!R*e81S~ux z1z-8-D-*rPWD#S|n`@>*M6h?X(`zNn&|&|(W6|RGm5+JvL_WK1GOWcRj?q#=n~jcq z%>X4)c~B+jFayhNr^ZJ=y3{L{2TLU(h8c}?5qpv1TZEN9AMwBS_S}CWg_`}y`H&T< ziG5e|edPy--3mdEBkUK=FfZz;gGNgR2q=FDchpY?IwYiHOoK2VSM)^2uvBhUIS+h% zpNs#77vA%o{v_;TM9~p_IENjDs0<~fDJv0ile@4Ji zaGdJqfiSRrwH)s4O*w0S@OLH5sm?mgow@84#3xd=p+pjJRnCbjhL+d_eB2RQlp%|V z;f?;kHoR=%B?GWyO?)@V@8T=mK(4|PUc*cSdFuLzPbdFD@mbb4!JB(+-8?`q8hS18 zK^3P7o#1yj%6A-jSPl;h!hwzqs;HEOGtY~ahIbv*7p^mt*R#&XoxdI~AAFwoFj~9( zG9EuxNstbhvjNRoj`Z$ybQtN^StAjvkkYyJ31h@BltSu1oE6=3A#b$NtkXGX+duU; zH{-ryy)B;3uCW~#7B<8e76Jq{;}QDwdsL>pn*5R$V)OSiQ8*|Ye^2|LN*}{afvMDA zIUGixFXHR$s-wC!z44cK^YFQsA6rR`G|UIEd7UD1b6Qm2Xa7)KQVgftIRZwJ<_6Da zZs$215l_nSHuU<-i&DdKi?m)q2%7yDhHoYGG6{-pW}>PvIUMaJ0+D%%ORRrFjign$ zh(gf+EnFq)$B5Vq+}qtp16P0y-GOi5b*d$H;Y9$grtpXe{+;%IK3GhYCbzj%78h2a zw__^!ECNjLUXFRx^{`DRl^ymR&wTuAer>8V+0w2m%Q%OFHS7%Q^H)85Vjh0fBX72< zgOZH!;(r(rT6(~cVej(+&v+x(hg_j`b+m)-=Xz}s85E}TNHj!ZQ2OQ*mEfc9m7!Nb z+VdaW*D}$2y%*>{#+QxAFMrN*<{8i+=rA@fKcWxf>vIIX8l%DBTzM}9b&iji2Z3x# zS?dBuO^>&F^NGjf)-wrO`Rf{XeehBS(zES0wse{ZuRr1{GN$1xFT|)(sW@Nyg(X!<&%8V(sp)D<&kUalHwtBh-T4LfxpHGAP&~~5x6IawQzHbx7Sk6u zZHVHo(iH))lngLvi#{K)z%5E6ZMnClgFTPRv~lvBElImsr0|Q;y($B{8};4GkcS%XyV4cpnl`O2 z@>aXT+iNjP(uGX3fMIGzY&um8a^G-HND0n0LJc!xRROZ4eq3B>hLKxMYz+IB7{}2# zYO(^_!%NZ~@4b@f2hplZI`hR78HtOnM_IW1W@ja>S1vmGYP^h#etvvgXH2-<#sS{4 zf1dw-5l}p!K49R@v03Um+WuG1LGfS}5841AG{rBpZlsE`?RMGn3_dRCA`b^wD7hRPh3WP0Hyqp4i~JI~>Wds#^LSO{u95FQ znoNl{m+8~PiLX(qg&uhehI`7RJ&FOyC2lnN!qZlM1~g-4H23(t&8(-*3|%+12?v8r);MPz9CYO6nr$|LlAB0`UfoVhtQ zH)?6rGzAvnPw)P0C%iECW&hl!*>>)Emo)!BcjFNu6+ss*YVuw50?*fxbG_GYK$weA zx=SQL0N{%_mLe4n@w0cTlr1$FspaiyOT?(^+WEx`F{3iOi{dA z5t;l?*H3!e4Fm%Q=WAeUts`1lekT!GmLV7YiBMc#Hv=ccA+_A!l+y8Unh{DiI``Ob zT$>M(+G%Bkk6ik>d-JfP(u$dvPEty$;)S>Gb543bkg>Mk0xym+V|4aoAC|tpJJ-+} zB|aymedK;0GroSt@gKI1e}~nM8DMVyRpv-E{$DC{GOb?}=K{yl`e@sRuP|V?Qpc!T z2gL3m`9qR3soNI?IFd-nGB!sFbQI*74>l74k%<-1EQ5eSPvEPby z6)S6E138qG19SVr_m%RmSyO6r&R>>XXF1wyGY;vv50tK(w=(gx%vTP_?UKvJ_jl|f zm~2dzf}VO~dKe%n7S6)DI{d4T4iDOpgxEW^MNnv6(#N^)@&wrv=ay~~8f z;`#Q(42~1E@oyMR+Fv$p*Z{iq$6gAHd+JYF6_xlh!4Y6!+I2-4Zh*du-qsjYdxHNj zJf^vB6*uhZF}sU}-+IERKmi*Z++tgbf08?$`fQVs6#(#Y*O2?|vonpsr@yA{g^C#8(IlG!c{h_l6aqA-^VpJ^_e3)L+{a(#!ezIY*^u z&4jRLm4%n!doKI5wG&LI+(w}Mfu@F)c($4%Q={I=;27w`g9ciCG9&CW-#7SuCz{N{<#jKD_nqj(MBSrOJJx&ySg0KPV&RO z-sCMc>|6|~LPjHuxt7)rUvWurIrw#-6?YK}*e85O{AT`o*s~-o&#Vu=e1DizM{J@P zQ;OkEcX0p}@4$2VLw3zM`?*b_y9I@%+o+(dEqIgL@Gj`UvTSF1y5GSoYgAv~@4VoK zr6?^^`HhAf3^x(EejIM%$!-|7z2k;+rr+E0A?bM14QR!PnWmM^3Kh~=`N5KO*VXFF z1u(+)-yL&9JnXo1g0KLJO@Ct+^O2`bx>M9D@+-5QM068a6zV6-UgpPsPp%|1su*p2 zVziw5{d@1_5Y7S97#X#89PLyAJ1%1%CfI{&%i_CPHxy=ED#$o=gv|nD<`}Ut>Tdc* zB({51Qi57?Soe#;fQJ1Ex9<4oDJ;Vd(#(A!5d|`1_f9*5Zt3G8(*yx|6^y1C7Oe3< zFtze^MyTZJRoWkj`lBt%)L;?xMh3DD?jnJ-vTWt5WMQy;KXfSB-Fl*y6anvu3BPGrVWjk^(+m6DbD&_L|~$>7TahLzk+&c|S% zyk_B%!83mm`B&mrnoVj?!x=D>N21}EDP$~kmV@+>%@j|iQt@SXD=BgH_^&Gq<8Y)= z*h=0Ytv6H2RBhpT&Gt^PkM1Bpmx#uTjndvf3!(%@*YsdU;+B8P7FKhT4}Vah3#!SO zk#3%w`RZDf7~~cyN)s>j>QDbXAw3;x9n%Ewmi;!ijoxqOF~Zkwc*Z#YH#FTK?YmzT z_>)3kE^L`p_Oz9)&inz>)9ts%V#>ti>csHw!CfBl`HK6l5s^SKa}`YMNn{!mz>IZv zykjO5hc!g}8Ds%L7L~%|uou|*ySF`5X**2p-S|N`-zIG(k@q4w8h#J)rp{nTE#6sN zaNpiS{IxRp#y}GUZ~OsXm%lt9#R?#xFAK;@T{2Q;e*3aFX0(GnkjS%9F#4a({|#pQ z`It!@wphNzt&iRZXC5~evqbt6Q(g1vN!~q)M(COqx=FyF;eFNSzl@0r8?nL#Sx72{(Qn+zf)Y%)l~)rO)q=6Pc`55O2=CF zFWJyocp%V!Eh=|L-<5thjFFHXnvRRN!t!r1`D*hPZ+A@m%E(5UIwrS-ri@5(7^%cA zs=@gm(dkwcPT>0+7omv;=fQ=Q7vl}$G+e5JIdET!t!_e7n#733ktT$Xfr0oCKYG_M zX-noO{-{+_$#B5JT9Qw!EBk1amvy)?z5H?y^|`M+AdOM!!CUdFrz!aH#|{}?c6f<{ ztlSQ36zoAwRmojKeBU4&Td_u^hhm~OsX0pMc{XyvyM_@-n?AaEKe8kA_|oqbHL9kBEfQhc^@59O7Qv_)2dfY4R(-otFUghO*m_Uacm3PiVCxg*M6DZw zLEo(#Im*e?5@=p1urUfyli>f0Di=0(L!i^-+}b?j5Tf$dQpEK-QPcD$LMFuqUmQEm z+|gntcBPHlHrM-}5oTnG{5eZt%sc26@tS925r=2k*&RH=HdW87-Nuh=C95h!nq%XXG7VBzDT&;c&^-?QZX`c#U1hc0C%@(l8 z30Z6q>8h@O?Z5n}KV3^3oy3H3?uH!0QdFKs`3@BB&e4D1A}|IlbhX&amnq_Y-)c;r z>oFNu9}$w!1TobuBypiXzEmgT(`w{AV-Jr-zR6Bz-)t$U4tBPbzykO~(!e&0tBXs6 z>FYI&Kl07SWBS?=tL?F7l&bJxuAiHpVv zEBe=HVThmZf;80X)eY1sJhIu@Q*_sL=Xnp^#U7FdhN`G<<{fKgAA^>K<~_JmL`L|3 zIJqJWR6;Zpfr21W%5~m9UgRo{=vWS6@0U2cVs%kI-^+=x)34S0!nI`qd0TIOxVx4a zdEKCwuioW!K;I}(3rNFeTXg-PimP`cOQqawhmASh#XEmu4GVlv9^500v)A!2<(Dss zGd{I>$U3sChUu(Ox1sprezj-k{a&5MC6}&ZH;~_Z8^hbg5^+`FyH`c3m3l6?dbV<& znJ00X2bxtdc7~E5kS?js*Z8|z*!>Pm*-tv-nSUw@Xt*!^QMF1WHOJhFO-{xtI<+A{ zM2D4@QdCPTng3rD9xdcZWIFXl&n`)v-FwTjRLb;V(lxW~zcQ?w9*D@5NH@*%a)i6* z^3Q7=2%yi*e=%WpwCyAvt1Pg(uA7v_0@f|G{qIdzo@=Ct_dUy#W@nAH=(;{b5D_3e zkt|EiDJ!La`s~qStN$w`+6t#qMr970;01d+*U`+>7_@XWK~Cc zE>h)>$@Ju{yq1O>$oPSF(L#CNcDetN3EPr{S&bAZ02f8VRkLm!DC2N>d2jEg9M-e+@a+Xy-Yl*!o~W;zbWB# zbHMWiXvI0Y50AE@rYnBzSVPwIGKvb>uU?hBk@AKn*;BY@_aOG|0)#age;hEBLXuNs zWH3%Zi{f`u^E^6ReZDdZRieYCvEn!QN0j%F=Ynxyqxc~&CHN*KoLRART+rKVMV z;B1?@wWLTaNON1@Rw|WwzA~?IQ0;EF;ORytKC%ABVucS#$-O6 zbpCTzeuKi~0#m&Gm7d&|$Alt5%ljXn^$q%d=HocFWXAkZE}FmnT{u-5{NVu|g4E1B zU~+r>&#@?R|BUM#$nQTLHWSQq|LL$)zMSTYa4H z8kTZ(@|pJ5fciFfm`{nYnBP#(3p1w*NH0=N#%EhTVAIy?Y4plRLzG=0~2KQ*@6Q zq72*v&M+Af2l?yYyW-Dr`3n52Cxl5_$I8y9rgEN?**c)YgE0!?Z?EO$Ia5Tujkxd` zgf@9V?QBLZ*~rTni`Y3WtCNe%?D_e4&UHG#98~zHm|0giz6O0&@+Ra#AD}KXm+lHMUzsmsK5bcV?^%N#HDDdfetm4Pil0fRdKP7I zt??H^*QY7^oFR7?-FI=dqu#h?h@llv5;VpM{u9DNy3J}0%@EIxz9=y)b99AiX9$q#gzpNlD|JZeSuM% z>BwYQ`}C@A0ZZ*r{A&7~gZ!oOK=5xc__}&%46)1{pMUZ8I95iuqA!N8qZd4RvA16? zZi2y|6-)gqOAEqhW3W2l0O_bcdNl$i?MyEa0UkfLNnjEZ@Icbs>mCg{{}j0!lc1=z zI?F3u4Zd@Q@c*4?(2egNyGSAH;!c`5it2JO4{M=2_aO;Cv@YihouiqW zI~E^2Sv@1EUA979dAVZ0v;=qT1{ulR|F)w1rwHt=7{xLi=U*{O{Aq|8R{tO(m+Zd3 z=7`=WvfKjVWD!+-)P59P?2{o+o^Sep{IY;~Vt(|f3X}d%)fovT8OGqT38gX8jEhg? zP%X(1X}MIT#4;#6_U}%H_2cgfbhx*)#2RMyKeS0*<9gixxbS4MGws%O=bDrA^|H$? zKGq^joVmV>$1;@mcwbqjA@Yt{jMGJ#2p&2r>!|oBdi%le(wn@gOMb8Cc<*P6K50{-D9}y1MR=gbqR8HUA^0m8X)%@eu3u>`}_<3r|D0 zgRqXZ0v&J{DC^$sU{bhz>{XnTsGTF(#BgF8L|$ofD!uVM*i zhsXdpbrsMcDQ!Nh&E0u1rw@aH`AZnn7>D`*SQIA^sCYc z=r&X5KXsmey@oz4-RxK>)H%c9+ADBzz0`~^iTS}~5ih}5qADPb!5u-z)nFZfn!c7& z`{07ePw9U+=&k5WWoyj{^YERFBz#VoW|-m&s>t6+ zO}tkl*_1?Ma|v=5hR0FMv=@)Zpx9OIJBipWWt`L6{DXLm6OTcjmyk{x7qUG>)o-P8 z8Z7qPdl5T~iGvg$CiW0v6SGjw?qiB9MMi#ELBgYD^)bcQ!}=y3pP7!8?b*L~zv%bL zQQ+^^>wkXoz`JoQqlDmupy?*MnZA3mus|)oM>(~VZ{_K>Ru}2Q{^>d%_#}^|b5`VL zEqbYbr%j&htCHTUA206CE^!$BxEKFU(`B% z=k0sSYrkkV8{R}(00lndtX1~J9Z!MzpaSVY4bs0 zCribJr>GbH$snB#pBpSJYp9Y5XNOi@Z2j5pls&|WqOa7cc+uk533I z`e#mps#l(3l~nd0hVmJfhX@@7u?SPe{6WX=tEsQcuk_bgij{iCkpzFpn+Lznd+Tx% zDdEZ1NzjY_(nIGda@jINd#S?ePciL!>+yBee*W*CN_}s z5ryZeQum(ROjAB{+$l5g@npAhQzdDCO|inGvC#SXqQX$qeJgoG9_;yNGxdF?0qWK6 z$5f=hQdDzZ{vl|7BwLpY=QtB7>{1k+f;s3}?nwBE^< zv=ZvAnUd(W^ z^NsPBmT%MA;k!E$Zw%|!K`E50CcDqrj0|h~#xEtN1I>BE73X$jn!fi4C!QmEyAwGcKSK*h5h4L08455>67wi@Jj7IE0&TH|1qm{uT-^s#hh>2;b(ODW9xpMDHQKeVKA*44^47VHb=wol)%wxreo8`D6KK7@q^L?fo zP}n`hF)>d&DudGXBWCMC77iJK$1Td_1ZrIdx0-_6w}B!X1$E%Z2u9}MWWdCYxlHCt z^!D0@wB?QVd3eRJa@yFJ9$#LYe*OjWn)#jeO^N>Mcw0T5!mBG=eZEKZlA3D84J|7B zI;;w4=r1$nDc^)9)Te+CZh82xMIafTaXuV97_O`h}|tA0NuyM)=*b5TPL* zjqbc1*ivwnOTdCU-k`G}>#@yP9yWNIK(HmpWAD>2#t#{5xwiTTpx5$6RvKrhI?c%! zEJdGI(6gIsVJL9n!Ze&3BWtlC8pw^<@`wzdwXk*?|9R87EO#83iC>afe$^E9%_|2U z?OEiDOpMY7i^_eKbvc3dLz~xDj^~noJ7!o7Ju!eEpt2yH=_mMDH@?ydfAr{cjm%q% za;4&@Rg?QN;+1ZLs`wY`9)cX*Z1kmF8$!biOJPmtSV27tmv5+5u1EE5IU}w{N)5LeO<*)42g9lwUSE7lylG0bwj z>}ENBdwEauZx1!uArf#eO}ks}F5$<~eK1$q*5i@w4!w6o=5Y5}=1$kF74tX1RrFVU zg5csW8GGwEM(ZppN>_;F`Ga?*xY+S}MGgT@YGq&hUGUq~NaXu3hEXw^SRD$ zqx?<82*MlGUQs(>zWU3tA60~_2h+uf4onrR$$MbHbUfEvWR)h|A7~Kw&YKzQgTlcH z^SKq}uRAlWc6mxDMxt)a{#FdZmKdM#;tFKp5Uf}H@Gc7PFB+~}_YX(&i9{`%s`=aa zL+*UMDK9}$*mWQ2QW5Om!l&pU;P-?Pilr0l5hBV_5KvC<^afuyiz7~UtVb2+w}m#P z=78?nO9uV^Vvx3)E-THBm<*51Kkhs6dhV=)fYN)p-&uXb!`(nd?T!Nz4JvI-Q4bzw zLa2Rk+94XZT*4Xk00U#`KWl`TI6>Dr4|5G0v1{rQ4_oSnO6(pU0=IexjaPI_9zMN2 z3ls|T7bp}P1{{-I-el`juW6tks<8JT#k|lOxIa-2~ zCXPE-|J36*t4Aon&pQ@HC>R(PD1fjld`B~yhy zT`@7tE}B)2x_k4KOc1iZ^VmX!-wnJK*TP7t_p5v|>AjoKt66rmb;C zT|Vi52jEa^a8@WjBS74a5+12i*Ht%D5Z$0Zt%#C?Qjjb@(1Q;Y4CwI*bg41N`yU^) zN4s*U*1UQZ#^j6|i;5aQ^1j_2W3c+^YB>tP=jVoAcP|Hhk|<#QTu^iJ=*oo!AF9f> zB*Tk=rT8JsylsD*b{J}FFKgJkDq$eYSl33Mkk-)hc? z$dLpIvrr3fGy0YtpP4Kf@+2@|qq?X^8!?aaeP&1pPD+2<#sPg zr;n-Ciuw;^&fLjMoVZ8-!!(Yx!=J!yVgsCpw%(M|q$MOxy|`=K`Swpq=l+hmczbI> zYK!!T&FFn{ae5^!(6)*0464AA<0}uX0uq#rwv0og+Gb-UNxL;&r?^<8r&WdfY?2Y0 z0-P%EKXLHBAOW|rtZI=ANz^bHK z*EtmIG#32b^a7?L%ql0t_KCFD$#1PMKZwc-PR{q8J9Z^btDcQ841tAK=O%i36G?@x zSy716U4QpykZbEtUAz^#Qp~wO5X;ua2c{Ak$e@MP|2R$o4qQ%-OE7I zof43Hr;Gu!&_60Fto-IHQ_IS&fCmc@hK0kd0TnVpuA?G-?OlF-I-1>~sQo-f8%`8N z$=HfY+Po`iHjW48$Jlc~t`Xd_@@#2J}bikApQX zc{o)4sMfFg!IQdifi#~qq`_m3l{@8ff;;rRkT`(;HH~%aR?RmHZQ5LN3Ij;XgX{dU5xsb?l+!ViaX`;9L~EY;|fi{pFp;cv z$M>XKbx|+K6P2I#bmm_qwoVK4y%7l#af<&%lJo!N-0|L=s8exaqmijsd-uRvKC}*c z6)(lD-QsUUb)QU0QMvgIT}KP3QsfdiEWBv>3Ih(1=I!lHmq@rLW8S){6ZQi>rVAJd z$08wKBni$9R6DH1tH5{7kL(cg3yd@dkakWemh(ZC!kEjR(hsJD%2LVv(51?F;ex9M zVk!#Z&TYm5v6o9r7d_0_cv2}>m#840+wbZnd`9NohUtMoVi@9 zI?OI_Z#B{qn`&taS)=De4ka!un}IEIXPYNRQldu?8+VUn+`cePJx;&c+v9lmZ`jmf z{r2vYoa^G~Ph*X?><(?tz+cDt-HJTgEffG@M_Bw2%^_?HOZDSAp{vEhjyQ5tDvj>d zofREba5nI+4yiwq)h=_a?r3$>96dJ`hv$;uRZovEL^y$ibJ+0ThbDzy_1AMY+IK-$ z;*kpvJgQVTTT$QCGPZ}Bza$+kluH(sOcb2Ih?5zA3&=3*TmMWaas z2Z{OqM(z`C-S3J%Y7emVcYX8%v@Rq1#L>&}^-K4~&=G(r_UNZK%12P{+dj?PezL}m zr+}vbvEN4u$!8hq5i<&l=fpq3t=iZ`AoKp#){DZHQ@BsL zogFR%{{&+JFlXJ-TYxe0N;$tN=B>+;&(sNeK{7MVE+A{*mjr ztGergbUkSU=-@w6?0Q__cgehS8S573--`SMeJRa-VUzr;&@3qjd~&k0>jwn{6*MP1 zd<+oMrVU*fXb)xo4SG5tsJUwp7e3HP{Rr~P{l4GFV8`)!O>*;I^Na~72`*-Oyor5? z?Rz^NZvUp;9pzHxH9!2Tm!``1d578gbzSVMJ>G6PY;Z2Ed>bHRt-Cg&yO!7U_>%wm zzFVQw+Z?0!GZ8~q*BNE&N$eD@2o3Q1)B7DQA->WkeTc5YM<9Dx(Dy- zjX~&j$Lg({XdWk)U_9yF?u&tpK`0gTpua{Bzp#JkHUf&U;3F8*g`fGHmwtiwi>u0(aV|^EhyXvLkouojOXrNL;B*K z?w`&UyL@RSm0Q8+ySe&a*&a26HZ{Jeg;&!xxGfLtjL&z^Kc3~$*{gSJgzg|qlyWST zh0$1rSH=1@qP()FtnfaGU_QHrHW6NK2w$kWTOH%4YqoZ>bFYftixR=Na`=$!YqBp< zxIHz#-f($``7zdgWOH^!7=trdkOjJV`S_mW^GI_1{xhvN_FEcpWkJy`@!`?6Xw>Hr zM9n4d==9O&*NVz+5#AdUUE$oRrLH@(-I8gA9i_<}!PlCx)Yw#`bg1&w6dHa6Ouo{K zQsNWddyo2}6+|2B6WLWRhBs9#w;D-S;Z31(dC7f4BF@+T+D68`(?ZwA0My>UN_$r* zjxWa6m?)U|TJ~B&wvDxKT0H`%e2NHn-C)wITQ9-}lIp|qmB z^S|!~P%Fz0dgf%r@ot_&F%khk-S?F-*8JV0-Tdd|t*mRDXahf|ji7M)vk)cI8%(j9 z7bGj&xqW)~Y|(R7{92N$JzHUZPutTVIneSnUz%OW&@efa;dXD1Ru2t6rhRGWn!Zg5 zx|nfZ+;on*1VF0m!1$a+I!`9hI&f^S6G?$-Th*f@7U>Z44V{he+}ypQ2s&mA51?qG z`9(}&qP5j)Dhsc#OiAkY#hyiYCW=EwVL2JdlK^{mo!nZ5v8d(yw-&C(9XTN(6a08J zxD&s84Q65;!$MCRin9>!mGq9?4&^~#AG8>sD$Og+AeB+4&jm6pvmaZR4)TK*Y)Pa! z8-kutJ3CnY5FrEZqaRaWWq-*l#4vg{2DimPSmz_W791qiG@y6K>`Q@9^H%*OvcrGz zm9bz>Vw`izm?v8W$A9h{B5}UvGnTYABRv1|ho4hh`K{5cdQRK>p)r>D%iKl+$`7#C z%2A!OE!s|97UOssyONHS0u8`K#A^6Csw^IVShZKy0On zT?Wyg+xW}Bv#?@>{L`W!6+ouYNsI!A-^_#L~--jg?7K&CewN6#Y@yh1!qd>o^@2`*M zgmiA^Ll|CFF?P(sqABsl^rLa{yKUy9LrJP_brYJTxV}b+3exI~O0LnHExs)?EUIl3 zR)Gg`BpPM}U1W#{?HP%KXFuOAel}(+#}iwcf)d$TbiFr%oP>W$U%hTGd;dP&f7jd# z$ZMRB5!9QAZdC81c@J99B{;@o!R&cZN9+=P2N{6uzf5k(wEN*`mX&ht*@N=N-IPCd zLo&21+MKvR(cfq=hcWE{F%j*zJawE%7ufxnusCP}Yu3lDrr_yjKVU$jEtkFb4I9Im zM0-+*bHO3^o>npJ@m$l6=(5~hl_3H279vUoKx%OrH!@np+x|0VC1+|hHb{9KZPF+cwN7L7L(w-pNz8;#7ZvG#BksQSO0_F&QH=0xsyZ5A3UdRrISYisAb=k{qAU@-LicBQ_|Q$i^}XV*iQlq@i@5`@ z&;@Z)rx>gZ+LvE~2R2f&nU-J3Vmg~cH*;wwmoVkHXSyh?G*V+J6eJ}_tG;9e6o>GF z&fsP9(auK0X(Xyk(h;EH0F8nNeZqf(el1at)6sN1{vw#v$Uk3Oz+@(P#-^y@oT*Ay zeXn)9Ppx=y`q>`ldxD2gk_p`GMWWMRvNgcfkzFo>;}CJ@TeGiK5f7F4aJ;8_Qgf8VP5Six-q)}$wi@D zDwl=uic8uQw}3HE`}_?yq4rzyhW)$9-XMN&W>l_s^{$s(JVn1K^G607S$QXrxXp7ZfyNjeYd8#wZU zt1lwGuYU=gvIzk-GnVAeAA}LWm=81<@B|U`aL%KcrRbP8_R{7zpw0Y3#M>vpp`>Fp zwy~f%7{G4v{02ffbF|+S8JoF@Z9cxDNK46Jh4^Oma-#*2YT&T9oy8`*d6n;xd7Tm)@6&W8>n#)&wEoLLIF}J)Z)I-nw5}MT`;G$HCnDk0_;?N&vtiMT0PAyz zFZz62n*WNW$H(b7qTbh*Mka{hb52e76q&UdKRyw^ftky{9|Hn+VSmSJtqco z8fs3eCObn-y|M2iyt9Z%uKHSXl0WGd<06rmGgq1lg1m498sC2$UMU8h{%PD4rls`ne z3aEK1s@x;`gKNciV0{6h&M9K>iV>TWXx8Tnk|(1chj3|VSVo4XgtbUQ(Rq3Y`-bfb zXXs#zLYfNvxyv2^M0m8R5#R+51L49Noh&3^_+Y_q)b)eKL^ND zkF&PGDjODo={Fr&9A0WaRd}OckDX5|c11~h;$&|zO}zrpzDu~?nH*2ZN=7L zvz=mBYbzvZWgIcN4^nvDtpD5ZAg5p~pBwM;vy7D&Qv@50??aktFXd-vhse+v#(r-O zI<+;5U+XC1pFBG!eI3vaBah&zKX<>N9b%;#6Mx+m#_%`XrXMXajk+nZEdLrQpO&Cc z4IV~axGyEVfRPY4v$szi+&EJ_trQm2&#O#BTE#R-jA+Cb*?U0vHX^61L1ROc4YN;s z+wti!u)>CU>L>om+NRi+$gK^!Djz*A%L*aSB2VpiPUF_G0F!7Wu=hzxj{AD#6}$W5 zq*B{mfenlQrlUw`S<>RsXdTsRSTHrVQD2)_AOobg-*SLKz;QDCEL6efALVz~4$Qtf z^M&hNEb#KHMmew(CdXvmE+z$Q8L@iLkc?jYR-cp&P?aiD z$ADhggK37k5X!X?Uo~)|&)B2B3|0ea(PyuH>(O@pc1ZkMt;2z`By{0Dxl{ryMjsub zKu-}Cf;=RXDrM}E)MGk{dQ7!)%v4L#cT1K`qNQI4U#-2W?Xgxvr4F&mYYW37hldTX=qV`SfHfdGgOi}Aj@8AR@Gtb~9tBr&Ug z09c~7a0Z&GXH>$0ZvpWOW8AfwJ`0YFfj&A&A3m#A1`g!4YckT^XnRaXV%-g@H)4X4 zR{j$ln8MYoT;!j1*^8m<_t5{9x-*gfr8YDTql?n7$no46%Uv{Kd>T^p)43(c!<7yW zCF&e|4Re?SFsoW98`9D#;rgAP8QU;0A025g#m<#<{^C9r!O`tUGf!eLi=NrN!})y8 zov!I#f2;altlnWXm~#eO@*{5vEG6f_FD@u;%D#nQ>L{2V<)G~{malAw?Ug2jkL+sj zuC_;cx-#!3Dffbq;KH-RH>1p)MXhc))Xeih#%Z>-Ln!MDPhz8njzbH%{`y7>5iG@s zjd(c4G{jiMU;iJL>IrL?v{h4sL+mGkYWJ225E!cw~y-cX+@a{8sA_Z7flR}G}md#Bhb24>xEkEm|wqY7P;J8;yQ zfz6hEYnZJ*lH>T3r}GupX(ag*Qq{6~&cM)=tyuiv1h$1PNJN=`CA?u)W+Ae7KGeAc z+R?*xHXYnq!JQM)eH9VKQ+{AF4+%bz1K5QuSrnrI8luakxbq24J0fP6I^vlw{N`bh zBd%0$-fBayT^X%NBDRH(sW^!IeaJbmBvq6#In5CBGcy;rMw%Ciy_=V|Gh(FP#B%-K zNWMj_+B@+D#~vSLfy7QJ)M#&7^$`J;F`>RETW;K&h}>6yQN0SnLpDU;pL}aFQ&Pp) z-76J250Ww`V-(wBrPW9>po6zn;hRayTNHiS%Au+^Ld`X5$a~&qs1UJSyTQvurN%ogz~^ zFk*N#hlXHtLsE^l&MzT%3wfNzMR+%-hMZz|T$Kp%Ne))VjVTf&(Y|=2WgQp3?h~FX z+4nt4qg3abuLwTNk|-oAW=#N0tuyY4V-qJ6@wM|Z%MXQ;#KPXn69q$EP;yItHpX5k z(kNPRm#C3s#?b;M6tj}kn{hJmLXxo>9o$F@wrw1MAk|ZLYC3O4;SXkm%sQ<58IMlS z*c|bZ^=8FQkvEs%2F@K4{}+!78uWI&ZkXihF23dqWZ`4&MqyZwt6zjOwA zv`6`a&V46D*It(j_f@YwA3vYf+R@Zm8v-hzA-H^kpv;SR!tEsl{hz-s%t*j{%>0nO zK6q@$W#q^7lV;RqUjx1n;&wHuap@WxqWNM{zhzE?qft39DKX*E2Nc}~mr7>Ca?XpY zn(3%;Sq%M3XAgD@O)b@9ccXk^K`_{q_M zE~TR>Ur>Q=vqLVMu~w$ZRM4?7sHW47P{?&ZC*m`uuz{)2!1_HahosNC0rKmS*w=p? z>QwUA6Z(9gT*Ku+wanxh8>av0Ogwn+PRmI_I z1A!&~W!|F*mGzhu-)Ek%#eEsi%R^a-m8m}xCmh8OaF@w@RfxJhFXtlvB-Dt|Uu~)l zhQ1sY^M_gko_8L>Q2=d4%6^1pj!7}Wdr=Okag+Y4!-oSVEhDvX|8C64-ew7t~ z7=L@_0KWT){V4+(960+sUsOL0_l~ffLNQ1#?rca&CxY<~bAl$Og+^TX9o}6kAm!v7 zt+l+Q%5D4ago}1O)i7)HL6r-|4!c(q*68*zhm6Q{=VF{ClnrP&QGGp8m*`{>6Qm7| z^Lb&7=qhC@*V^cmchPw$yw}7D^(~SucE1SA)DG8Aad;g)n8!Z&&Iitx1$1q_OqN(H zHLO0GKnWq)Rv+5YR%KULIDRk}ojT&bhKZU}tJ#3M085*Y-tJ$4(%~O2=%9)Ij@j|7 zt=zr!WXq7fk#TAb80(72V~Pj$ni0qElq!m?PN9^QBTLz>#XFz=7af_!znEvBCAD%V*lG1y-Y}QTA65Mw9t{+E_jX*35G2RIArkr@7TnYB>pxCn^pcnq0IoqF0&q=m zv85>sOnJnL>lo4vGH5PgKo4zH^>CD$t*J{n1YUho^knTysHzFI2{rkaT+Nq zdOssL&zw@whqGPlj4aY-o88AdZ%8b2i9Yx2ph>KY&Tj5$G>b)8R9ZVgsu^F5e_Rdt zymk=!F!S*xTCo^E&r+9juRVyRa+f1O2WFiV-E{=lhgQw`qfM+}!UbBit<-m-@NNWi z6AMIJJiTVni|cE1d7#tQ9_X_ACq1eEslfRo0b7HH6da~M&n!RA1ng=M+lmt`Mip3B z0Qm{W@0@2r&i#4da2T;87`%?h<}<(}*e_xmzR@E$ZP?tQ<1l_zMR*)IVMg{Y$n?H~ zR#>QG%aOCUw)VkG9%D#~r$Q(&ijA-Q$NsScPc*SANk~O%=4c#u4!cPq zo+lOw{kBSKoMB=-vBv3{lZ0$#vUuoX{7bbSuQY8rSrwz18nS3_lxpDprSs0EMqW=? zL3#Wrr}ys?Vml)_X^4{mDR<$r(8kjh{Z+qX>eAmv0nraz<%On2Sw14TYVh3`R4k@N z$NGrQw}?N5?S9%p3*#t3WL@Q#Z~nRWY}+W^U5YEqXs$EAkD=#-xtg?V!O$8>av3EZ z8&|IlR(&A*@%9XfVOj@yWk&XnV4^K9hM%$#tE3R1)jlGTr1v!CYA%*k-@7)o0DYmaD4NHXdwL6}v#$6-Jw8 z2Fi_DI?!dee&Jz2vK#(Q#@oOiFTz3wyN;S0NrwWR%#H7Oi5&etj=+lK#&YM_CYrGd ziz}6@HiGjnEBmTf>k5zE4oXB@ZLGTFBF@)I&(`F^;;47pOfmOc{y)lyxg7E5nLoT3 zW*E!(@?KNYEci7a-Q%T7a6j0xTIGE;teDI5%D$dTt0lFPD0QgWHfpjk$FZFUu_oMb z3a|foH(9J(d9yuQQ-;1`Abxu=8=33Mq{P?&%WgESn%uU-)n!gq&r z7E(@Tx7Yq?POUXJPpn8zc)B(*ZgEfAfgp|gj0<#%jy{gO5xH)4lkg`Ve9>e*EbO6v zKbupz_MMWM@_{XE%|o{S4Y?OV9zmit)W$E5t4eUJiFp;n$M=Okg{IBY?^uclez#Zk zbOXbkqNu-w3-w!9ZE!KgZBStHNdmm08#b34hg!nan7AkabhK?Rc`$-xV_mNyqr8$E5ixI0(=11$ z&Nq{_7vCrhc*W7*p&_Hh@QiY!G!aLH8yl_moV56>Dl_RKfGv#~RO4Um z+D*i z1{nClY(ICC^pyc}7}&z1UxYS3le?)#CUo~_J4Jbo@b|Jrf&*QEpk=5+hmwV0WJh!GVF*VB| zMa}E^F|#+2_FMV$Br3gt)i``)Pt8e94SRE3G=rIfW($3f3?^g-M6V#cdxKtHbh~T%$ zVzOk657!Ggx1$A$K>Wem(}lUf#*o~yE~+qLkj$klvzt6HAAmP(RGVo|?YvmP+%g9~ zwWkM)gb~`#{^@B&wqDD$+I%K#vY}M=ZM3^0(u(?NSiIjJ-T0aMG+kr_C7Opl;9ncv z_5{pr1BA!u=5Sz{IcCAyeegxt=6&>uU(o?(RCG*`CMO7%HJd^ObP%)g`!Z>mHOA}<0mUJmzxTpDI#*32NYKT zD!OYZ&b&9{D46OKzd8I(X$LRQ`(74dSHDw}g9?{usAGmWfSk-xMPFk>VHX` z6r(Tt4W>y`5R)`N)dnrbDD)GBozZWup~wtybQunTOffdWa1^ga*sP3c8zg0=PKK2) z%LZB;7!@=RH_F|2QOaEwa`3GCG5gjt&l~h?ev=UT{MF19L0xiYYR&g^F-lH8i(7TJ zK2m`WV|M|=PQ4YFbZJ`@q;G94koMPIrPKSZf&4|A=qGcq(>zh$OZB|81L{pwiJ$!; zi-JYelUMH9m2X*Uo1J}M8r>Q=(FJHM!PrH*-#5$=Zu=aNRqCh9Y4?QLiNQ26qYQU{ zLYdC4fVa=X%uDpgwhs(bW>(17M)@yCwaiP;f!Yy}@mV(X{@scNkL9~agc@!p{9OLU zr|Hv;rPfULS^o&8nu(%%tRgI*@OY?q9NRtOT2lT(Hbb|`fx*@18bq0W5XyiBnlEt3liT5x6gl|+(L0jYuG4zd+}} zXEa1q;MEU%7`6ExD=`RjmzZenn9iLA^UipDQ0ty4e~BdY6e!y(xl_i+$Cg5eS`}kj zUF>3!E#en0>pu>l#q05#J{CCaW~O#c&XRPkE*bUU_Ud=*?LITC8l`VxEUKYz9=uIJ z@-AEyDmZ74;;#uS(Lr=}zi3`nJ*NvQ{LMI78g>j0G}MnY4Q&>xz@D@SHdJcVniagL zrHy%XIvp7)F0Ff$=Uz7Yt^@;%pS3cVjc*eL@Q~4Im zi!lpF>H&p^tn|P`{UI<1{H04@cSU862OSy;GmREdj6I~Q9Q${M17=J5x=7JCBB-D^ zvU5Yv)$ROni2ojf`!>@sCVhivjy?k8;$;I{zCg-cUh`7X`tArS*u9+Jy}@G(^3lC% zPB+m2wKVIr*jfh+o8Rx95s{RjxCmO=OT!T`zAra5{4$tc;3KF9irT); z`i&Vr{yV34?rLrf7PCmpiJ;I&4x@{6Pd=r48~pFgGpu*Z`H=YyobC*0Z%*nWV4>SB2&lN_y(lN8NfY`IY>noE;F ztzHRpO0LZ}O+DjDQ!Ab3w#?QU_VK9Ps!18lsb+fb@4TgeGJGFZpL~+PbB-ShMQY!< z8eif_6g6f@F9n75QyRF6qVe*lm%vRR#<)gnO+@g)(V@7dJm}z0DuRAgD6uSVf7`Fl z#`x2rpBo_#(;QUY7yI(S3*0MRg~_K49VLxqrgslsU;Q6o0tC?eL83hnQa83~`}J<# zh2aK|DG9tk5I|BJ!f**^(gAN&ShoB{D11wUi*f#x;<9XX=F}rmEzdP3YyXo5O(J)o zA@$cQoLi2d3lu3UwUw@(h-W(@WLU9m=U3N<)>Z{>WMbi4ug6hSq&@jX8#tIPnH;Jd znEc9i-B~{o_ijnnOP@SCJ8v_CZekGPv*AXD3z)43w_EJZH9-2vggiZ)-#c--eXbxM zg_eG-5TTEbD4z1o{uEi#IL3wTFQ~}w=5F)HI+t6eGx;I+!k-#0C9?QE)(=41B}O;d z(UlYNs&;yfWMK?N=*RwXCHd~^J2f&m#C57QAxi_UL5}uURFwfY0otR*FXZX0Tq{UjJcPbu=huKy z6qwEkaTl`?QdC1Hx@b;hN_F3h#FfRHj7@t}+k%p-mLw9|5>v<^WY?p=*}9Ao9!F~P zowyRo7wRp3@;(YytE8wshR(`$4cQ6|BK%w8Lj=6Uf~nqC)}VZBaM7qc8-#~~Jggb0 z>ywgnQ#p*!BNO&P4$AA1T3H%`^$uB}!est^oUbU5s6?+6d4>)zwQ0FqhL5wkC8K#Y zO@Xks#6YWOB17j_&dyk?x;ia?VywKi%KkXkfldWQOXDr2!SDaRKI{Vf;`kZ(Bsm#d z;Y)!OuKpP(l*DrQ!2HZ^UzaleCc=tMy8m!`_jI^M_uNJ_CMH8NiL0z^jDE|;pP6%F zyp-6wpf@&l5T-9?+AA+#7>(KmZzwUzdS;qd&TYmbzjDN$MwrBXVeFv6fCbW`^`jjd z$+7Jb|I>iv4-C6e4id?T8EV8kE_hYOWKn%7HJJw)eY{W;qL&W&NS#JT%TY|vh+I*z z2&;_-Wan_j2M(HpH|Yf)h*&rs~eT=rT#tAr8*cv#8$QQ@>|;-fJQ+DF=7? z53>mPZ;T`fBvuJkNP$T_0L~kV%!fOhZ|qdD%lBAEQ{Kgv)t~bjHzF*~wJ{kRqVq*X z9PX|&Put7zd{s~wHX50~;ZxPRM}nyuV@3%Vc7J%uUSo0RD`Bg>@%}zo){qMVaUXZK z_mQ7UcM47I#68>8Z4C~e=80iQ#xBgR6FdAyA>8iS+;DB0S526kqRS(x0R!$p1QZ*RI*@ghScNP?f^!r=rz9#PUO({GQ`#qDFYwi>7b z<2>$dt?_n$X`f$R9MBBV?_Ro-{qfI5C$BpbOZnJKGIdPcUR~n z1$|0Qb!5$BQk@2;TC@7V(q__g5V5`h8*zFQ^84=DL$uGbQm1BI7dKWQmzq;9wQ$iq>}ZuC+XUw%^XL(O6q;Lo(1~9sIdn-tG?egQG9vs zwXg*Ag~Y>uyl1!QpHmPjG4f5Q8Br`&3t>OnZ z;vRq@VXdR_pXAqfH$mZh)NCF3l`k?DTE>#gG(c$aGU3sm)gP?g4$@sdOxPm4mjvB# z90TOS7c=ZcBX|WZ%i;GSEYW*w`G)7RptTf10zPgwY!UyPE+j$mT|=I|C5wO^JMxUn0deM#a+>t0Hz=T zZss6+?QUZUn8MdcDJD`phM-q3k~`uRQ6B&v6$os1_{ZH^Bjm()B-I_>UKWdww$&g| zGwkvg_fFXd9L60O#f1O-<$#0p?j`$&ANSz)clpwCgJym51UI@Fg0Mic#$o*QMn1JJ=u4I@j;cmvVx+{UV0%e1K55O}|| zJa$R6z;h^lY*Us0DnEPn^2NIqD>B__sC3`U+bt&Y<-Rym%vUto0Oqb>LY)~gW>gat zJY5k^xp5SOJ#ds^9b~X7wb7+)FMiKX8mnw^Tc~ubQGNYfmp_}PZ^6bCRnm$u_1Y8R zh8gWah+lKv_i~GKu{;=Uf%9L=jne+lGRhFw=%hk)$<$2;TC~)V*bNkTOaug zwd|+&y>A^IPULjEp72vqJGV*#ALDbKx#>*q(5DMn*(nnmER)m^ zRmYSh1FK`ESQsS2+zQ$d=(ino*Xm zpQ3wdovJRbtc1+X75Vp|S@k)Qqj1+F=OjxGp*I(gj7Dks&q>NLKI^r|*#*@A6EfO& zd;BadHe_Cw@#NOWn8@OHBd}UdfJ+gM8l95Ivcz>>IX39MLUOoUNU)B)!>e9MKicHG zV}5E|Nyf1t`^}Va-*CtCf3ypyK%56txQ&wg^D;rVgJ9c&j;iE(Gg#bj)6cn?#{@@L z3Psan-@PCIs{A#hAh20W_&Q)id*yZVvB{Nz{1T3A{R5E-z~f7gl}2l4MS}IMSh%~M zm2?CaSkrhMT#1vanx0iUJd*_l5qvCf8Z-6u(CTQQ6>HKZ`uRO(G}P-jD+W?<|}8z0b#(`a71l zUP)Vu5Rerdmi+gSyM|S16^o=AzjGeSg-D&JB(LSvRV|5-n*L}s$SVB9VD=(qMi5kV zR;K4Lh#I-;(-i=AmGKVG{JZVSS7zfr)B1_KBcSAWA;#?X$*aF;W`#HLvlRdW%WBk@ z?<12feDq!nt)1wqbX;^P6K(SmTmp#m7HysC&Gw|SChP+vf*u}Sj}&}A=88-R3D{@! z2R@tqCUJ%FYM|RH!%iQ+<9FK+=1CsE#)H`pW6pU<&?k28staf$Oe|`u8mu2#EHYxd z^fU7{Hrw>FMj>7eYE{!xO%?IG!7o|5Aa@#qTh=S|NaE|TEH`tSpk8ftMkYgBAbw1KeW*(W_9)9cm zXUp|o6gpfE|DHP)Up}-3TtU?M{ExE_f1JLB0NKKhOpr5 zpcv!n_J~1$)mr;sH1>;1JNo{$3Q6zBoCyy0u=y{h^w5rfldG?Gyc(Oa#C8SwV>MR| zt&K^GBuUZotCNzdRU9maZ8ZrmG!5C(H{RiQLtCPff+xV@jgoHep4yhM(a)>n_y4E7 zvy5t^ZPYbIiWP_A#fufU;ts{#9g1s9DH9xbonLqS%S%xMo0aBTk0%7Tp-hREfe{yg4-wXNCWN^y7n z$6$N^9{hlB%{vdWqA~Fe!`ih;H{Pz-LUOV0hN0F9`9F8F{wx`yy)Q}2ByKm76KUHN zD0EXg?_oI|p-v^CyAwoWeMPE8I9bslp|)c+y<$&QX|P$NCW-iH)0wJ3Z-~0?T^-fE zY(WE?(j1z$9;n<%T_TdCyOJPRl3g=emLNVoD3)<9Kr3t)?Q6W$(zJYKc~IIojH5_X z`N)Ix{7=3%Fnq|8NvO|bUwRVs>r%|gA9yJklPhj0vXP4lyrV2?+iUn(=q_NnHWRlh z5KuW3nwP#7LE1lr9DuE|O0j+fZMkCWI{C(2^e(j?TVz~r#WOldSI?=^bl94?B00cM z2g9)sBfjR-_c<`*1Xdn~EfE?9_5=#WtjrQ~3fommPvC5FN)-jIqZW5hyv7pu{yvRY zc*=NtQGr9MHgyyetv;m%C@*!l+z849^K2TIowx`z>{Yguy}qjL>2gIPpb4u1rlm_q4DUQPi+~y8oYmTjS0xW_JZS|sD~1~_Nr7w^Yr{2Rd?W1j z$)!vD=`(r^)a*ZUDNlX@=DDZ*($5vJ)e7TITj%2 zWRMy)DaIbI_@Vdvt^iVi2FtQ8Ei_KIDfEKeqBzz1#{cdBz&c_+$p-KL^aw^6*nvo? z(XO9edicNVz^Yd{{PXf8X&XdJQCQ#^&eAUS8lZtE<|S0xd4Db8IK6s`kA|eFAvpJ2 zl-Y%0Et8Sw!6>htRKo(8RPHar2hx<@W>N@F^|I?&jaloM`8A}E3#$7jWurXkT#TMp z!>M72b*arrpBwRY!3wf}P%3RUkbQ$h?)N4DL`F3m15PhJJaE~z{av|*aP3jHPW|TH z&1$)#X!OS_}bTZs%tI zNIQZ@w{O=CS})T|R%*1&47HV7UY6{W<1(-h`v^8*MX~)iTyM3U$JHD_`JN}YI2fR1 z5veGsQSgIy{#Rr6|I=mx6&8>d=hzjw14|4p#{UHuqpMNCFu}R&#MGNF0Klpw_qVjb z-yD3CJ@hd=f|rQy6K_C223mj%*KJ(2HS}TcWMfbRMhIY^aAjv>HTRTB&qh^|HHELZ z-u2fh?a%55rQeVMOlVrr-_k=B5KF+mp=PuP3;oGHbmJ=Z(+Qr7zL9#`BI@rJAMt&E z$xW#8G-uXeTBAW3(ez(1uu~37YkrQmPyl5E2Ziyk^W_g6H8AMjki}AM6#z4mX^yc| z{&H=A6xtx+o*2e3;UlhYQoRfZSmi(Fzqt$8<|@h}9v`3X1!x9wpRYZ#1c-@aJ8}N4 zUG)2()MZ8wkIYNJB^(vO#bi%fqXI)>_;*Oy%TSD+Q5FcNbGGgRJoH-)U!p$k`SI_b zM9{Am9)8vq2TQ+VRxG=cqzC{I)XB-bGK3OELYt&*`g;SKJwHI7UoH2ODmkp+No6q8 z5Nty=kl#<&*K63x`?dmS^j+caFzW%4jWQlng@r--O>}A%tm2tm;#zkfP#NJ9`KKsi z9{Gg!0(<@KmNfVyeHQ$nEwF=`CvtL}CEdkmiJJKIR&#(Mb7fCb4g_>kR$o)Yi^Jhd z^ntD-p{hpgfIVSEgt~GP`J@0Djpq~c&)uHmuRD9&dDY#L+pn*i%0O9JFBl?HgL$b! zEf!^MD9HSn!1cZ^^xnnFHT41K232 z?%SO?S7|an0d!|;^vyfp8ii**zuJ$Lh0lX~_-=8tYjTcq;cY|*TPBxtY)%vHqJf>RW9F;(g4 zj_pe* z-yof7iaA^uf)H@Z)B0Fg$NhKE&xZ6)^3vG~-UnAi-;WwWS!D@WGar+!bN4}^z<2W@ zE=jK@KXvWv_JV0^LDe?z`jzl`a%NrM{83z=J3Btr)ux$GeM#^!P~cWud=*&zJxu_G0jI7SJXsYB&sVSG5Rr={^W4_ zOS~{{9{~*~yI!$%rX;A8DvIls9#;1CXI^0^Yva4IL3&KQTKr182eOV3x9^r+898IH z?l0UrUC|Tnv82F-Ul~OcDrP`SQ@|z(vwts1Q~7L>Um1x(w*0m!Da&nOArj{WeOghQ z=r>nZta&=NrAO>8Z5{tsb_r(?hEwg|uArVsG0?!ow$^CoS9K( z7J#eHe*<|&G;ubny&NCa(wX=X10+fGv&|$hzIaV5P52%B&4s*D>t~~@xtA@ZckG>h zXW3az;ohrzov+z;sqqY_C&q|L2#?8VHB)HSsb75J$#;L>JbClM(Wcv)DZjNff-Kcf z^}a@{c2!3yzOGgyQQv}PWTnl&4)50`QS_z)(VwfW7$x1Lp>FK(@yndF-&xNlK$h{M zx<5~P_zfprg`7TG5RWU6awabM5GsK>aqZ1sekc%#11 z0~Y59NWbg4YJ7QT$YP|j!0{h|Pp}lrT12;7R)5>a(jhu&fU+;#oPOk&1Wg@Vf~iS* zcXL#xA7HD~5EID1HUHyYDbLASY1q}2pVAwVQtf~XV>|afa~J?=zfmaBg;kjI(t$)JaeSKH z$B(rUS)M=Fz)5i1AZCp`7IXPPeBhYhh!h}4DCIz~U&clV6W2 zd>;JH`tm?wSqTP?e?zNolcW*9Z4W8EOnB1PJE?vWcDIa5^r?YXgUtND=8O-=msCaw zv&QvMdyv$Pxr!{G`~nc3;!67aLaznlPzoB72kVTQ^MxWI`A zAChDSwBNUDZdrCnN?KxNKcSFY{b09uBMy{ipq!YSZ$pgbDY}#>W{WCQW4906RKqQ_ z0VHITO)@5T+)CZ14H3;vk!=Yyb4j5o^QJaGXa!`8=ESOy0%(z-9PFa=%J$Rf=7{b} z7_BiW5BqpRWs~gwCRfKdpmbk00o*$RlFCJ+pG|E16@IzYMXhoeb?PJ^#E^4w}7a< zJu?bl1~GWK7zbpM@gPl%x0!X(@$4zz_HS+8+1(CX(Jv|=qCwG=Y#;>#pw-u&NzoaB zT5(EjpT;C?Txl!_Iy?`xe;$ETRl;~MYZI8U$0541L=gj?zI&*tT=#5sug!Efkbm^3 zeKgcr6=1d)iKA(hd?t3f%2OF{wK>Raa|NMO`2(J8OW%2Ja^g}mJRXy8BKF?A{PTN| zN-XDUOo$MRg*Csj*B=pxNnC4S(ZCK7;ckJXY8POr9=C2&rQgJVdSMmeN>F2U1=G(n8l@R%q~4A1mar;I{+{9i1VbTdtF3 zF=EGKb*=7sLz@06u`u582NU_T^`Fs_*vTI-tzQDS-%_YXM1nBhjDDEYmkm4)3@x=_ zLhyzrwH*3tD4YC3)dvL9!95!%z+i8d5}V3W^%U^sjQ6CBPI_Nw_FT*jTRc`}RwzbfhIfv%vNDPY2CZRW+a zcl6?YX19R6q9hHDX%Cd<#1nQ5^HgF%29K;7D8F};yFO6g^p6xh%T#V+WIJUq=jQr{ zHskjV{>dSfFxU9uI~UvAg=KQYH7+ce^j0o7S6>o@uQI2k3ZDs-(og|pBlPw2v@d4J^--ogh`qT2s(|mZD9VfS6a@J(1=lz6 zdvt!eKh|e@RcoP$!8uNRG!S>V2`Bva|$vJz1VT3-GjhS@3n&5;}+j@*mjb z&J>je=7hbTGrsJ-?>4S_{qT~_Tx}PmK{u|T@Qngs$kAq=fD^G9F1ni^)1oqsse`b@ zpLeP(8<#5FD2VJ9m2gra4M5X|6Zfil%D05~QRJ;uL(A14j<715RH(K%7T zP9}t^Vkb-K+fj`4CW@#joli_@iY~p=Qx?|4yLbdP!fKjrcIPWS--s`(i~v@%eZe^m zh@;B+o~a3mrWsN(i#Nm!xlnp2QT$*h=88XTFn;ka^IfZRY_R5JlsYUO9s%@;BCIO_ zci?asUm%&CMXuFZ$aQVSpVxnt7U5#bYmjssYVGk>zQP1>7wSMnUb^>r*|pb()}s4g zYlms4^%@sWguJ@MfhUWK{~o`?82zCn6pIktzXei8{>x?&y~B!Ev58Fd%E7R1P0OM- zE4QSJNd%O zj+wb|R$YF~F^-rd#R?*w3^!*iBW;O9kU$VG{HFu>MYH&xV7RxJb~q(!9jVfH)B5b;U5}1!1=~@Ei@&^uRyzmS^d=&-_2tX+wfUF zmm&kAY1h@xWZ#J?Q+8unWGq(=Egm8b9cV>{^)-)f?x_T&9 zgZo+>cfUo)yx~|H&%)J_AfgZck$WitF@JkKrrZseCxjalWv( zhJFw^zi|+Hr<`AKk0)%j_Oe+5qaWs1pWQI*uwt%?oB2V{dI%qJ3m zI&SvR-f2@v%{Bin1^ccy`ix4aJlB*;UzAVbJN(Cjv2MTl1WGU{qlRbt1uSZgP{R4o zY5!qL(vEAuNQO$>gdog9MYe9WDCSx>$+hh;jh*c|3f`}4o<@!m#%vE(DU;&v>sBYB zzSgt4nb8x-!vh`T-iCfb(D9*tZZ;$`ajEsHTyZU#sOQVZ@n_%t`JQRyQ&L|IWjBrb zCCRrCHfkH6PfR*r3OMjz$JhC8=+@na#i(xIIx97(1gpg}bOy3~>iZV7^*%jQ0Au;G z>eQ2h2`^nCZ9nQ^)e)<&{TxRg1gf$p3Hz~I{8K~wm$ild%DdBUtB|idrOsJ=eJhA( z>e{P(&9;Sd4pYE7g`~Vy2U_=e;b1JB3<#zkZ%lRe;+BS0mSM(Ri{Vus1yZzA{rmKQ zY@_3e=H{_f+8VYJ|Y)as2gu%1Fhp5Tu=l3$7L~Ta@SqjIqV#q9hs`I@*=L<} zR*5M)&`-AelwS3LAOeef{NX?g5Lo`ArvGq>M0{7TJx#C@={x;hw&&51Ty)kihR&p_ z+NK3zrXj5TuG;-IXNMkJdHacm@3km_Q>*af#o?0Q4MlnbqQc>|%!Mn{T)wgI=Q2Jiv}LCh z%t}_~mDcb1O_o0kKYluK-9xSDW)Hf?c$EA4dYrBl$y zA8ElEH>%4)3IjiZobKMmk7HWuY;@&Uq7~bsmiV4~xQtTtD^>;dr*fA8Wdkf;EA3hXKSr)r{(wliXYOv4q$U0SWe*Uif6YiaaLJZPLS$SpGthhAyPJ_UrQL z@e=1NM~Ei~oYL{rc%0vvsu0&FI}$>p212PWCGjp39sZYa0Jm2KamVHuTQ9n3atxn1 z3|HXLOr<<&^ww)rdxg*gervvhd&6Rq65P!+?N-cQu(bA$s-Zn?gr=Y|EMqqwekU$b zQ~R}dBPAS$$)1d10cX0wG47kH(&P=lqqQi)_C%e;%328b)-@#b;Q0X$a$&3B`4~6r z`PkXLQ$XO&Wbmc~ph6|rdEq*&D_Wk^CeGhi`K8Sp{fk*;M%AOHvfQ*IH~lJzjpea1 zu7fhSj)06s%H0zoyPTHLJ&F*Wf{CL2sGZV~zUY3+_CHL?yjrX5x;&{2C6Hy7@kvT) zT#k~ftKoljX~%+fF1HYx6;kX5VsBdRuu0E6(22YQ1Y9*u5xr%t`!|W?jbbz>GUy|z zp=@_gyilvON$U^rZr+c!Z>nt;)JUAu-(myAC>Q-m2=IFRgauF#fBWH?;LJ*|=W5de zV?QRh)aKC5!MPW9u2uU$IDcd~dX{}K?Igj$4srwa0$6PWgtnZ(18#^7a>ButwPx=Z zmWoL+Rz%{j#1J3NBU<+OBWbrnR4 zU66tOa`CxD#n=#LTUuF=nGhEp6UVU4M${1gqi>p1lW(-rN&m#LMS`nSMz$Ho7@qLK zB5_JFMbWYULfi0s${Z-|jB}cT#+qUhAONAQrsG6lharU5rtfmp)wuiQC+o%MdUXmD zDnm6B$V``^n#48e^;JeD_y8~+v|iVj&eS4rHMMrZbD zhjUV{!u3#3gEG_i0y%)jQmJ>SQnv{_e{S`nlA&#jw2*0qD02|E-Elc!VkfU`PI~LX zBIs0T?z{)#w_iRzct;@zuq#g0n~#u>hXcduorKzDl_o_TVQ=H3+G$u>%PU zV~WLN-L2)d-AJbV&06FNq@`Rp$bz1xgY3oejZSl_JO?Rv4uajZ8E1EELVP*g^T0EQ zh5q30ZfC}DFXOi@#UTDWtJ?GRx-p|6WNvD;o-{lxPC^9|QUsPC(dE@iKAv`;YtnLN z5X-%b4Tu%Ew-KWX^XJzKSY^xxN(cLSR@HHk`_9#Sr?h5$#x$4S?y!4wTN&v*>_1fRU@kWkT;`KufR2 z-AY+es#%UYGxcsKsJ&qSjfzm;A$e{5QYkXUVTPN-9N^$c<6FBkrUyNim^XRjeJA(c zxe@2Tcu_n}Pp4*~I&I*>zwR^R;r$tq7WckkS37Pm{r0+7W!Evc<-Z~|&8y3PK-xULn#3{h!zDghlr|*_tceiRW3~S8QydPdP)&m{~G7)6Dk|^Hly_B_~aBo`F&|)c#ayA4VH8fe685;^;Eb%Ln zSHv!Q;XFeVq|v|YzOnrrkc?{^9K6oWBY)+NW+=pK_jdj- zJF+Ra9~_I!BXtz@vP5ql>mVN8x)_T9VVslsg6{N;m)b+$59&((nR_m2n7i)*bNFn{TkI(E;@zAGb(+Imt^Eq`ko74LB$(5*GabOGi=_{Kct zm53o)s0a4ux6Eo`Pu*&AAednO{`=6e!Dmcu>)G)E#vtETRbPdKNiNM;v?$9Zg=j?==(9^GJ|30R{Jd z>kZnPquV_5DSWKkFpwWtJ{(~f`_KSa^(GsX7TDKXAUPFY3JW`Uy=6cP0T`R%*AS93<@x;V2ZfFjsfZR< zu$HuVQ*OHTZuNUyq*Z~mYcbVPBQ)HbJU4a!c?O`s4x9-_tm3@qe)Vv4m{s&i+fl?Q zpfIArR*R0%F1a4@8JdOv$5d6DDu1;E z7t?8PNSfMBHW%V%2^QDKh8%E@QM&BKDs%1}?YW~zWdZ$Fbm znc9W`VkjhuZ)|FIKp5__Sc_K8*!P7Wg5&9pMNU8LP|8S>0;U2qRe?BwOUV$6Ly6)u z)EZpOzRs=H9j)<)9(c+%bycf7fbxsT3qXBFnu!9BB!1O^_iy#mzasGewR**WxR1$4 ZbXoMl5L+X>2sq#&C#@`1CSe-ze*n_1{wV+e literal 0 HcmV?d00001 diff --git a/Tests/OcrTests.cs b/Tests/OcrTests.cs index d0f94949..3d53d064 100644 --- a/Tests/OcrTests.cs +++ b/Tests/OcrTests.cs @@ -1,4 +1,4 @@ -using System.Drawing; +using System.Drawing; using System.IO; using System.Text; using System.Text.Json; @@ -90,6 +90,20 @@ BANK CHARGES 344 670 326 49% TOTAL EXPENDITURES 1,016,684 2,065,620 1,048,936 51% REVENUES OVERY(UNDER) EXPENDITURES $9,749 $0 $9,749 N/A"; + private const string jaExpectedResult = """ + くろ からだ しつ + 黒ごまは体にいいです。タンバク質やカルシウムが + かみ くろ こうか + たくさんあります。髪を黒くする効果もあります。 + くろ あぶら はだ かみ りようり + 黒ごま油は肌や髪に使います。料理にも使います。 + かゆ た からだ + お粥やデサートに入れます。でも、食べすきると体 た + によくないです。少しすつ食べましよう。 + """; + + private const string jaTestImagePath = @".\Images\ja-黒くろごまのちから.png"; + [WpfFact] public async Task OcrFontSampleImage() { @@ -362,4 +376,21 @@ public async Task GetTesseractGitHubLanguage() File.Delete(tempFilePath); } + + [WpfFact] + public async Task OcrJapaneseImage() + { + // Given + string testImagePath = jaTestImagePath; + string expectedResult = jaExpectedResult; + + // When + Language japaneseLanguage = new("ja-JP"); + string ocrTextResult = await OcrUtilities.OcrAbsoluteFilePathWithLanguageAsync( + FileUtilities.GetPathToLocalFile(testImagePath), + japaneseLanguage); + + // Then + Assert.Equal(expectedResult, ocrTextResult); + } } diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 3c6d82da..3ab4dd2d 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -62,6 +62,9 @@ + + PreserveNewest + PreserveNewest diff --git a/Text-Grab/Utilities/OcrUtilities.cs b/Text-Grab/Utilities/OcrUtilities.cs index b6741a26..9eb86429 100644 --- a/Text-Grab/Utilities/OcrUtilities.cs +++ b/Text-Grab/Utilities/OcrUtilities.cs @@ -48,12 +48,18 @@ public static void GetTextFromOcrLine(this IOcrLine ocrLine, bool isSpaceJoining } else { + // For CJK languages, we need to filter and merge words intelligently + List words = [.. ocrLine.Words]; + + // Filter out likely furigana (very small text fragments that are single characters) + List filteredWords = FilterFurigana(words); + bool isFirstWord = true; bool isPrevWordSpaceJoining = false; Regex regexSpaceJoiningWord = SpaceJoiningWordRegex(); - foreach (IOcrWord ocrWord in ocrLine.Words) + foreach (IOcrWord ocrWord in filteredWords) { string wordString = ocrWord.Text; @@ -70,12 +76,79 @@ public static void GetTextFromOcrLine(this IOcrLine ocrLine, bool isSpaceJoining isFirstWord = false; isPrevWordSpaceJoining = isThisWordSpaceJoining; } + + // Always add newline after processing each OCR line to preserve line boundaries + text.AppendLine(); } if (DefaultSettings.CorrectToLatin) text.ReplaceGreekOrCyrillicWithLatin(); } + private static List FilterFurigana(List words) + { + if (words.Count == 0) + return words; + + // Calculate the median height of all words + List heights = words.Select(w => w.BoundingBox.Height).OrderBy(h => h).ToList(); + double medianHeight = heights.Count > 0 ? heights[heights.Count / 2] : 0; + + // Furigana is typically 30-50% the height of main text + double furiganaThreshold = medianHeight * 0.6; + + List filteredWords = new(); + + for (int i = 0; i < words.Count; i++) + { + IOcrWord word = words[i]; + + // Check if this word is likely furigana based on: + // 1. Much smaller height than median + // 2. Single character (especially hiragana/katakana) + // 3. Positioned above main text (smaller Y value) + + bool isProbablyFurigana = false; + + if (word.BoundingBox.Height < furiganaThreshold) + { + // This word is significantly smaller - likely furigana + // Check if there's a larger word below it (main kanji) + bool hasMainTextBelow = false; + for (int j = 0; j < words.Count; j++) + { + if (i == j) continue; + IOcrWord otherWord = words[j]; + + // Check if this word is below and overlaps horizontally + bool isBelow = otherWord.BoundingBox.Top > word.BoundingBox.Bottom; + bool overlapsHorizontally = !(otherWord.BoundingBox.Right < word.BoundingBox.Left || + otherWord.BoundingBox.Left > word.BoundingBox.Right); + bool isLarger = otherWord.BoundingBox.Height > furiganaThreshold; + + if (isBelow && overlapsHorizontally && isLarger) + { + hasMainTextBelow = true; + break; + } + } + + if (hasMainTextBelow && word.Text.Length <= 2) + { + isProbablyFurigana = true; + } + } + + if (!isProbablyFurigana) + { + filteredWords.Add(word); + } + } + + // If we filtered everything, return original to avoid losing data + return filteredWords.Count > 0 ? filteredWords : words; + } + public static async Task GetTextFromAbsoluteRectAsync(Rect rect, ILanguage language) { Rectangle selectedRegion = rect.AsRectangle(); @@ -373,6 +446,23 @@ public static async Task OcrAbsoluteFilePathAsync(string absolutePath, I return GetStringFromOcrOutputs(await GetTextFromImageAsync(bmp, language)); } + public static async Task OcrAbsoluteFilePathWithLanguageAsync(string absolutePath, Language language) + { + Uri fileURI = new(absolutePath, UriKind.Absolute); + FileInfo fileInfo = new(fileURI.LocalPath); + RotateFlipType rotateFlipType = ImageMethods.GetRotateFlipType(absolutePath); + BitmapImage droppedImage = new(); + droppedImage.BeginInit(); + droppedImage.UriSource = fileURI; + ImageMethods.RotateImage(droppedImage, rotateFlipType); + droppedImage.CacheOption = BitmapCacheOption.None; + droppedImage.EndInit(); + droppedImage.Freeze(); + Bitmap bmp = ImageMethods.BitmapImageToBitmap(droppedImage); + GlobalLang globalLang = new(language); + return GetStringFromOcrOutputs(await GetTextFromImageAsync(bmp, globalLang)); + } + public static async Task GetClickedWordAsync(Window passedWindow, Point clickedPoint, ILanguage OcrLang) { using Bitmap bmp = ImageMethods.GetWindowsBoundsBitmap(passedWindow); From a741d3c603d38e4f53824f2288027bd6691cfd08 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Sun, 9 Nov 2025 22:44:58 -0600 Subject: [PATCH 2/6] Add Japanese OCR support and refactor test utilities Enhanced OCR framework to support Japanese text processing. Introduced `PostOcrUtilities` for modular post-OCR handling. Added `ja-word-borders.json` for test data and updated `OcrJapaneseImage` test to use new utilities. Updated `Tests.csproj` to include the JSON file. Refactored to enable language-specific OCR logic. --- Tests/OcrTests.cs | 17 ++++++++++---- Tests/Tests.csproj | 3 +++ Tests/TextFiles/ja-word-borders.json | 15 ++++++++++++ Text-Grab/Utilities/PostOcrUtilities.cs | 31 +++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 Tests/TextFiles/ja-word-borders.json create mode 100644 Text-Grab/Utilities/PostOcrUtilities.cs diff --git a/Tests/OcrTests.cs b/Tests/OcrTests.cs index 3d53d064..c6084549 100644 --- a/Tests/OcrTests.cs +++ b/Tests/OcrTests.cs @@ -9,6 +9,7 @@ using Text_Grab.Models; using Text_Grab.Utilities; using Windows.Globalization; +using Windows.Media.Ocr; namespace Tests; @@ -90,6 +91,7 @@ BANK CHARGES 344 670 326 49% TOTAL EXPENDITURES 1,016,684 2,065,620 1,048,936 51% REVENUES OVERY(UNDER) EXPENDITURES $9,749 $0 $9,749 N/A"; + private const string jaWordBorders = @".\TextFiles\ja-word-borders.json"; private const string jaExpectedResult = """ くろ からだ しつ 黒ごまは体にいいです。タンバク質やカルシウムが @@ -381,14 +383,19 @@ public async Task GetTesseractGitHubLanguage() public async Task OcrJapaneseImage() { // Given - string testImagePath = jaTestImagePath; + //string testImagePath = jaTestImagePath; + string rawOutputFromOCR = await File.ReadAllTextAsync(jaWordBorders); + + HistoryInfo jaHistoryInfo = JsonSerializer.Deserialize(rawOutputFromOCR ?? "[]") + ?? throw new Exception("Failed to deserialize HistoryInfo"); string expectedResult = jaExpectedResult; + List wordBorders = JsonSerializer.Deserialize>(jaHistoryInfo.WordBorderInfoJson ?? "[]") + ?? throw new Exception("Failed to deserialize WordBorderInfo list"); + // When - Language japaneseLanguage = new("ja-JP"); - string ocrTextResult = await OcrUtilities.OcrAbsoluteFilePathWithLanguageAsync( - FileUtilities.GetPathToLocalFile(testImagePath), - japaneseLanguage); + GlobalLang japaneseLanguage = new("ja-JP"); + string ocrTextResult = PostOcrUtilities.GetTextFromWordBorderInfo(wordBorders, japaneseLanguage); // Then Assert.Equal(expectedResult, ocrTextResult); diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 3ab4dd2d..62113fa4 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -71,6 +71,9 @@ + + PreserveNewest + PreserveNewest diff --git a/Tests/TextFiles/ja-word-borders.json b/Tests/TextFiles/ja-word-borders.json new file mode 100644 index 00000000..3aeca8bf --- /dev/null +++ b/Tests/TextFiles/ja-word-borders.json @@ -0,0 +1,15 @@ +{ + "CaptureDateTime": "2025-11-10T04:19:00.6164022+00:00", + "ID": "5a9f20f1-ac30-4042-b6ba-daf72d32aa17", + "ImagePath": "a7b7a4cf-303d-45ef-b15d-dac96996503a.bmp", + "IsTable": false, + "DpiScaleFactor": 1, + "LanguageTag": "WinAI", + "LanguageKind": 2, + "HasCalcPaneOpen": false, + "CalcPaneWidth": 0, + "SourceMode": 1, + "TextContent": "\u304F\u308D\r\n\u304B\u3089\u3060\r\n\u9ED2\u3054\u307E\u306F \u4F53\u306B\u3044\u3044\u3067\u3059\u3002\u30BF\u30F3\u30D1\u30AF\u8CEA\u3084\u30AB\u30EB\u30B7\u30A6\u30E0\u304C\r\n\u3057\u3064\r\n\u305F\u304F\u3055\u3093 \u3042\u308A\u307E\u3059\u3002\u9AEA\u3092\u9ED2\u304F \u3059\u308B\u52B9\u679C\u3082 \u3042\u308A\u307E\u3059\u3002\r\n\u304B\u307F\r\n\u304F\u308D\r\n\u3053\u3046\u304B\r\n\u9ED2\u3054\u307E\u6CB9\u306F\u808C\u3084\u9AEA\u306B\u4F7F\u3044\u307E\u3059\u3002\u6599\u7406\u306B\u3082\u4F7F\u3044\u307E\u3059\u3002\r\n\u304F\u308D\r\n\u3042\u3076\u3089\r\n\u306F\u3060\r\n\u304B\u307F\r\n\u308A\u3087\u3046\u308A\r\n\u304B\u3086\r\n\u305F\r\n\u304B\u3089\u3060\r\n\u304A\u7CA5\u3084 \u30C7\u30B6\u30FC\u30C8\u306B\u5165\u308C\u307E\u3059\u3002\u3067\u3082\u3001\u98DF\u3079\u3059\u304E\u308B\u3068 \u4F53\r\n\u306B\u3088\u304F\u306A\u3044\u3067\u3059\u3002\u5C11\u3057\u305A\u3064\u98DF\u3079\u307E\u3057\u3087\u3046\u3002\r\n\u305F", + "WordBorderInfoJson": "[{\u0022Word\u0022:\u0022\\u304F\\u308D\u0022,\u0022BorderRect\u0022:{\u0022IsEmpty\u0022:false,\u0022Location\u0022:{\u0022X\u0022:28.417473608152424,\u0022Y\u0022:24.166040548665052},\u0022Size\u0022:{\u0022IsEmpty\u0022:false,\u0022Width\u0022:30.207550685831315,\u0022Height\u0022:16.78197260323962},\u0022X\u0022:28.417473608152424,\u0022Y\u0022:24.166040548665052,\u0022Width\u0022:30.207550685831315,\u0022Height\u0022:16.78197260323962,\u0022Left\u0022:28.417473608152424,\u0022Top\u0022:24.166040548665052,\u0022Right\u0022:58.62502429398374,\u0022Bottom\u0022:40.948013151904675,\u0022TopLeft\u0022:{\u0022X\u0022:28.417473608152424,\u0022Y\u0022:24.166040548665052},\u0022TopRight\u0022:{\u0022X\u0022:58.62502429398374,\u0022Y\u0022:24.166040548665052},\u0022BottomLeft\u0022:{\u0022X\u0022:28.417473608152424,\u0022Y\u0022:40.948013151904675},\u0022BottomRight\u0022:{\u0022X\u0022:58.62502429398374,\u0022Y\u0022:40.948013151904675}},\u0022LineNumber\u0022:0,\u0022ResultColumnID\u0022:0,\u0022ResultRowID\u0022:0,\u0022MatchingBackground\u0022:\u0022#FFFFFFFF\u0022,\u0022IsBarcode\u0022:false},{\u0022Word\u0022:\u0022\\u304B\\u3089\\u3060\u0022,\u0022BorderRect\u0022:{\u0022IsEmpty\u0022:false,\u0022Location\u0022:{\u0022X\u0022:128.43803032346057,\u0022Y\u0022:24.613559818084774},\u0022Size\u0022:{\u0022IsEmpty\u0022:false,\u0022Width\u0022:46.98952328907094,\u0022Height\u0022:15.663174429690311},\u0022X\u0022:128.43803032346057,\u0022Y\u0022:24.613559818084774,\u0022Width\u0022:46.98952328907094,\u0022Height\u0022:15.663174429690311,\u0022Left\u0022:128.43803032346057,\u0022Top\u0022:24.613559818084774,\u0022Right\u0022:175.4275536125315,\u0022Bottom\u0022:40.27673424777508,\u0022TopLeft\u0022:{\u0022X\u0022:128.43803032346057,\u0022Y\u0022:24.613559818084774},\u0022TopRight\u0022:{\u0022X\u0022:175.4275536125315,\u0022Y\u0022:24.613559818084774},\u0022BottomLeft\u0022:{\u0022X\u0022:128.43803032346057,\u0022Y\u0022:40.27673424777508},\u0022BottomRight\u0022:{\u0022X\u0022:175.4275536125315,\u0022Y\u0022:40.27673424777508}},\u0022LineNumber\u0022:1,\u0022ResultColumnID\u0022:0,\u0022ResultRowID\u0022:0,\u0022MatchingBackground\u0022:\u0022#FFFFFFFF\u0022,\u0022IsBarcode\u0022:false},{\u0022Word\u0022:\u0022\\u9ED2\\u3054\\u307E\\u306F \\u4F53\\u306B\\u3044\\u3044\\u3067\\u3059\\u3002\\u30BF\\u30F3\\u30D1\\u30AF\\u8CEA\\u3084\\u30AB\\u30EB\\u30B7\\u30A6\\u30E0\\u304C\u0022,\u0022BorderRect\u0022:{\u0022IsEmpty\u0022:false,\u0022Location\u0022:{\u0022X\u0022:26.40363689576367,\u0022Y\u0022:39.15793607422578},\u0022Size\u0022:{\u0022IsEmpty\u0022:false,\u0022Width\u0022:598.7807824835896,\u0022Height\u0022:25.732357991634082},\u0022X\u0022:26.40363689576367,\u0022Y\u0022:39.15793607422578,\u0022Width\u0022:598.7807824835896,\u0022Height\u0022:25.732357991634082,\u0022Left\u0022:26.40363689576367,\u0022Top\u0022:39.15793607422578,\u0022Right\u0022:625.1844193793534,\u0022Bottom\u0022:64.89029406585986,\u0022TopLeft\u0022:{\u0022X\u0022:26.40363689576367,\u0022Y\u0022:39.15793607422578},\u0022TopRight\u0022:{\u0022X\u0022:625.1844193793534,\u0022Y\u0022:39.15793607422578},\u0022BottomLeft\u0022:{\u0022X\u0022:26.40363689576367,\u0022Y\u0022:64.89029406585986},\u0022BottomRight\u0022:{\u0022X\u0022:625.1844193793534,\u0022Y\u0022:64.89029406585986}},\u0022LineNumber\u0022:2,\u0022ResultColumnID\u0022:0,\u0022ResultRowID\u0022:0,\u0022MatchingBackground\u0022:\u0022#FFFFFFFF\u0022,\u0022IsBarcode\u0022:false},{\u0022Word\u0022:\u0022\\u3057\\u3064\u0022,\u0022BorderRect\u0022:{\u0022IsEmpty\u0022:false,\u0022Location\u0022:{\u0022X\u0022:413.5078049438242,\u0022Y\u0022:23.94228091395519},\u0022Size\u0022:{\u0022IsEmpty\u0022:false,\u0022Width\u0022:33.11642593705952,\u0022Height\u0022:17.22949187265934},\u0022X\u0022:413.5078049438242,\u0022Y\u0022:23.94228091395519,\u0022Width\u0022:33.11642593705952,\u0022Height\u0022:17.22949187265934,\u0022Left\u0022:413.5078049438242,\u0022Top\u0022:23.94228091395519,\u0022Right\u0022:446.6242308808837,\u0022Bottom\u0022:41.17177278661453,\u0022TopLeft\u0022:{\u0022X\u0022:413.5078049438242,\u0022Y\u0022:23.94228091395519},\u0022TopRight\u0022:{\u0022X\u0022:446.6242308808837,\u0022Y\u0022:23.94228091395519},\u0022BottomLeft\u0022:{\u0022X\u0022:413.5078049438242,\u0022Y\u0022:41.17177278661453},\u0022BottomRight\u0022:{\u0022X\u0022:446.6242308808837,\u0022Y\u0022:41.17177278661453}},\u0022LineNumber\u0022:3,\u0022ResultColumnID\u0022:0,\u0022ResultRowID\u0022:0,\u0022MatchingBackground\u0022:\u0022#FFFFFFFF\u0022,\u0022IsBarcode\u0022:false},{\u0022Word\u0022:\u0022\\u305F\\u304F\\u3055\\u3093 \\u3042\\u308A\\u307E\\u3059\\u3002\\u9AEA\\u3092\\u9ED2\\u304F \\u3059\\u308B\\u52B9\\u679C\\u3082 \\u3042\\u308A\\u307E\\u3059\\u3002\u0022,\u0022BorderRect\u0022:{\u0022IsEmpty\u0022:false,\u0022Location\u0022:{\u0022X\u0022:27.746194704022837,\u0022Y\u0022:109.41846137312233},\u0022Size\u0022:{\u0022IsEmpty\u0022:false,\u0022Width\u0022:571.0345877795668,\u0022Height\u0022:27.074915799893255},\u0022X\u0022:27.746194704022837,\u0022Y\u0022:109.41846137312233,\u0022Width\u0022:571.0345877795668,\u0022Height\u0022:27.074915799893255,\u0022Left\u0022:27.746194704022837,\u0022Top\u0022:109.41846137312233,\u0022Right\u0022:598.7807824835896,\u0022Bottom\u0022:136.49337717301557,\u0022TopLeft\u0022:{\u0022X\u0022:27.746194704022837,\u0022Y\u0022:109.41846137312233},\u0022TopRight\u0022:{\u0022X\u0022:598.7807824835896,\u0022Y\u0022:109.41846137312233},\u0022BottomLeft\u0022:{\u0022X\u0022:27.746194704022837,\u0022Y\u0022:136.49337717301557},\u0022BottomRight\u0022:{\u0022X\u0022:598.7807824835896,\u0022Y\u0022:136.49337717301557}},\u0022LineNumber\u0022:4,\u0022ResultColumnID\u0022:0,\u0022ResultRowID\u0022:0,\u0022MatchingBackground\u0022:\u0022#FFFFFFFF\u0022,\u0022IsBarcode\u0022:false},{\u0022Word\u0022:\u0022\\u304B\\u307F\u0022,\u0022BorderRect\u0022:{\u0022IsEmpty\u0022:false,\u0022Location\u0022:{\u0022X\u0022:246.35935781555762,\u0022Y\u0022:94.87408511698132},\u0022Size\u0022:{\u0022IsEmpty\u0022:false,\u0022Width\u0022:32.22138739822007,\u0022Height\u0022:15.215655160270588},\u0022X\u0022:246.35935781555762,\u0022Y\u0022:94.87408511698132,\u0022Width\u0022:32.22138739822007,\u0022Height\u0022:15.215655160270588,\u0022Left\u0022:246.35935781555762,\u0022Top\u0022:94.87408511698132,\u0022Right\u0022:278.5807452137777,\u0022Bottom\u0022:110.0897402772519,\u0022TopLeft\u0022:{\u0022X\u0022:246.35935781555762,\u0022Y\u0022:94.87408511698132},\u0022TopRight\u0022:{\u0022X\u0022:278.5807452137777,\u0022Y\u0022:94.87408511698132},\u0022BottomLeft\u0022:{\u0022X\u0022:246.35935781555762,\u0022Y\u0022:110.0897402772519},\u0022BottomRight\u0022:{\u0022X\u0022:278.5807452137777,\u0022Y\u0022:110.0897402772519}},\u0022LineNumber\u0022:5,\u0022ResultColumnID\u0022:0,\u0022ResultRowID\u0022:0,\u0022MatchingBackground\u0022:\u0022#FFFFFFFF\u0022,\u0022IsBarcode\u0022:false},{\u0022Word\u0022:\u0022\\u304F\\u308D\u0022,\u0022BorderRect\u0022:{\u0022IsEmpty\u0022:false,\u0022Location\u0022:{\u0022X\u0022:302.52302612773286,\u0022Y\u0022:93.08400803930242},\u0022Size\u0022:{\u0022IsEmpty\u0022:false,\u0022Width\u0022:28.417473608152424,\u0022Height\u0022:19.243328585048097},\u0022X\u0022:302.52302612773286,\u0022Y\u0022:93.08400803930242,\u0022Width\u0022:28.417473608152424,\u0022Height\u0022:19.243328585048097,\u0022Left\u0022:302.52302612773286,\u0022Top\u0022:93.08400803930242,\u0022Right\u0022:330.9404997358853,\u0022Bottom\u0022:112.32733662435052,\u0022TopLeft\u0022:{\u0022X\u0022:302.52302612773286,\u0022Y\u0022:93.08400803930242},\u0022TopRight\u0022:{\u0022X\u0022:330.9404997358853,\u0022Y\u0022:93.08400803930242},\u0022BottomLeft\u0022:{\u0022X\u0022:302.52302612773286,\u0022Y\u0022:112.32733662435052},\u0022BottomRight\u0022:{\u0022X\u0022:330.9404997358853,\u0022Y\u0022:112.32733662435052}},\u0022LineNumber\u0022:6,\u0022ResultColumnID\u0022:0,\u0022ResultRowID\u0022:0,\u0022MatchingBackground\u0022:\u0022#FFFFFFFF\u0022,\u0022IsBarcode\u0022:false},{\u0022Word\u0022:\u0022\\u3053\\u3046\\u304B\u0022,\u0022BorderRect\u0022:{\u0022IsEmpty\u0022:false,\u0022Location\u0022:{\u0022X\u0022:415.29788202150314,\u0022Y\u0022:94.65032548227146},\u0022Size\u0022:{\u0022IsEmpty\u0022:false,\u0022Width\u0022:46.98952328907094,\u0022Height\u0022:16.334453333819898},\u0022X\u0022:415.29788202150314,\u0022Y\u0022:94.65032548227146,\u0022Width\u0022:46.98952328907094,\u0022Height\u0022:16.334453333819898,\u0022Left\u0022:415.29788202150314,\u0022Top\u0022:94.65032548227146,\u0022Right\u0022:462.28740531057406,\u0022Bottom\u0022:110.98477881609135,\u0022TopLeft\u0022:{\u0022X\u0022:415.29788202150314,\u0022Y\u0022:94.65032548227146},\u0022TopRight\u0022:{\u0022X\u0022:462.28740531057406,\u0022Y\u0022:94.65032548227146},\u0022BottomLeft\u0022:{\u0022X\u0022:415.29788202150314,\u0022Y\u0022:110.98477881609135},\u0022BottomRight\u0022:{\u0022X\u0022:462.28740531057406,\u0022Y\u0022:110.98477881609135}},\u0022LineNumber\u0022:7,\u0022ResultColumnID\u0022:0,\u0022ResultRowID\u0022:0,\u0022MatchingBackground\u0022:\u0022#FFFFFFFF\u0022,\u0022IsBarcode\u0022:false},{\u0022Word\u0022:\u0022\\u9ED2\\u3054\\u307E\\u6CB9\\u306F\\u808C\\u3084\\u9AEA\\u306B\\u4F7F\\u3044\\u307E\\u3059\\u3002\\u6599\\u7406\\u306B\\u3082\\u4F7F\\u3044\\u307E\\u3059\\u3002\u0022,\u0022BorderRect\u0022:{\u0022IsEmpty\u0022:false,\u0022Location\u0022:{\u0022X\u0022:26.85115616518339,\u0022Y\u0022:179.90274630672872},\u0022Size\u0022:{\u0022IsEmpty\u0022:false,\u0022Width\u0022:577.9711364555725,\u0022Height\u0022:26.85115616518339},\u0022X\u0022:26.85115616518339,\u0022Y\u0022:179.90274630672872,\u0022Width\u0022:577.9711364555725,\u0022Height\u0022:26.85115616518339,\u0022Left\u0022:26.85115616518339,\u0022Top\u0022:179.90274630672872,\u0022Right\u0022:604.8222926207559,\u0022Bottom\u0022:206.7539024719121,\u0022TopLeft\u0022:{\u0022X\u0022:26.85115616518339,\u0022Y\u0022:179.90274630672872},\u0022TopRight\u0022:{\u0022X\u0022:604.8222926207559,\u0022Y\u0022:179.90274630672872},\u0022BottomLeft\u0022:{\u0022X\u0022:26.85115616518339,\u0022Y\u0022:206.7539024719121},\u0022BottomRight\u0022:{\u0022X\u0022:604.8222926207559,\u0022Y\u0022:206.7539024719121}},\u0022LineNumber\u0022:8,\u0022ResultColumnID\u0022:0,\u0022ResultRowID\u0022:0,\u0022MatchingBackground\u0022:\u0022#FFFFFFFF\u0022,\u0022IsBarcode\u0022:false},{\u0022Word\u0022:\u0022\\u304F\\u308D\u0022,\u0022BorderRect\u0022:{\u0022IsEmpty\u0022:false,\u0022Location\u0022:{\u0022X\u0022:29.312512146991867,\u0022Y\u0022:164.2395718770384},\u0022Size\u0022:{\u0022IsEmpty\u0022:false,\u0022Width\u0022:29.088752512282007,\u0022Height\u0022:17.453251507369206},\u0022X\u0022:29.312512146991867,\u0022Y\u0022:164.2395718770384,\u0022Width\u0022:29.088752512282007,\u0022Height\u0022:17.453251507369206,\u0022Left\u0022:29.312512146991867,\u0022Top\u0022:164.2395718770384,\u0022Right\u0022:58.40126465927388,\u0022Bottom\u0022:181.6928233844076,\u0022TopLeft\u0022:{\u0022X\u0022:29.312512146991867,\u0022Y\u0022:164.2395718770384},\u0022TopRight\u0022:{\u0022X\u0022:58.40126465927388,\u0022Y\u0022:164.2395718770384},\u0022BottomLeft\u0022:{\u0022X\u0022:29.312512146991867,\u0022Y\u0022:181.6928233844076},\u0022BottomRight\u0022:{\u0022X\u0022:58.40126465927388,\u0022Y\u0022:181.6928233844076}},\u0022LineNumber\u0022:9,\u0022ResultColumnID\u0022:0,\u0022ResultRowID\u0022:0,\u0022MatchingBackground\u0022:\u0022#FFFFFFFF\u0022,\u0022IsBarcode\u0022:false},{\u0022Word\u0022:\u0022\\u3042\\u3076\\u3089\u0022,\u0022BorderRect\u0022:{\u0022IsEmpty\u0022:false,\u0022Location\u0022:{\u0022X\u0022:97.11168146407994,\u0022Y\u0022:165.35837005058772},\u0022Size\u0022:{\u0022IsEmpty\u0022:false,\u0022Width\u0022:42.514330594873705,\u0022Height\u0022:15.43941479498045},\u0022X\u0022:97.11168146407994,\u0022Y\u0022:165.35837005058772,\u0022Width\u0022:42.514330594873705,\u0022Height\u0022:15.43941479498045,\u0022Left\u0022:97.11168146407994,\u0022Top\u0022:165.35837005058772,\u0022Right\u0022:139.62601205895365,\u0022Bottom\u0022:180.79778484556817,\u0022TopLeft\u0022:{\u0022X\u0022:97.11168146407994,\u0022Y\u0022:165.35837005058772},\u0022TopRight\u0022:{\u0022X\u0022:139.62601205895365,\u0022Y\u0022:165.35837005058772},\u0022BottomLeft\u0022:{\u0022X\u0022:97.11168146407994,\u0022Y\u0022:180.79778484556817},\u0022BottomRight\u0022:{\u0022X\u0022:139.62601205895365,\u0022Y\u0022:180.79778484556817}},\u0022LineNumber\u0022:10,\u0022ResultColumnID\u0022:0,\u0022ResultRowID\u0022:0,\u0022MatchingBackground\u0022:\u0022#FFFFFFFF\u0022,\u0022IsBarcode\u0022:false},{\u0022Word\u0022:\u0022\\u306F\\u3060\u0022,\u0022BorderRect\u0022:{\u0022IsEmpty\u0022:false,\u0022Location\u0022:{\u0022X\u0022:160.2118984522609,\u0022Y\u0022:165.80588932000745},\u0022Size\u0022:{\u0022IsEmpty\u0022:false,\u0022Width\u0022:31.102589224670762,\u0022Height\u0022:14.544376256141003},\u0022X\u0022:160.2118984522609,\u0022Y\u0022:165.80588932000745,\u0022Width\u0022:31.102589224670762,\u0022Height\u0022:14.544376256141003,\u0022Left\u0022:160.2118984522609,\u0022Top\u0022:165.80588932000745,\u0022Right\u0022:191.31448767693166,\u0022Bottom\u0022:180.35026557614844,\u0022TopLeft\u0022:{\u0022X\u0022:160.2118984522609,\u0022Y\u0022:165.80588932000745},\u0022TopRight\u0022:{\u0022X\u0022:191.31448767693166,\u0022Y\u0022:165.80588932000745},\u0022BottomLeft\u0022:{\u0022X\u0022:160.2118984522609,\u0022Y\u0022:180.35026557614844},\u0022BottomRight\u0022:{\u0022X\u0022:191.31448767693166,\u0022Y\u0022:180.35026557614844}},\u0022LineNumber\u0022:11,\u0022ResultColumnID\u0022:0,\u0022ResultRowID\u0022:0,\u0022MatchingBackground\u0022:\u0022#FFFFFFFF\u0022,\u0022IsBarcode\u0022:false},{\u0022Word\u0022:\u0022\\u304B\\u307F\u0022,\u0022BorderRect\u0022:{\u0022IsEmpty\u0022:false,\u0022Location\u0022:{\u0022X\u0022:214.36173005204742,\u0022Y\u0022:166.02964895471732},\u0022Size\u0022:{\u0022IsEmpty\u0022:false,\u0022Width\u0022:31.99762776351021,\u0022Height\u0022:14.544376256141003},\u0022X\u0022:214.36173005204742,\u0022Y\u0022:166.02964895471732,\u0022Width\u0022:31.99762776351021,\u0022Height\u0022:14.544376256141003,\u0022Left\u0022:214.36173005204742,\u0022Top\u0022:166.02964895471732,\u0022Right\u0022:246.35935781555764,\u0022Bottom\u0022:180.5740252108583,\u0022TopLeft\u0022:{\u0022X\u0022:214.36173005204742,\u0022Y\u0022:166.02964895471732},\u0022TopRight\u0022:{\u0022X\u0022:246.35935781555764,\u0022Y\u0022:166.02964895471732},\u0022BottomLeft\u0022:{\u0022X\u0022:214.36173005204742,\u0022Y\u0022:180.5740252108583},\u0022BottomRight\u0022:{\u0022X\u0022:246.35935781555764,\u0022Y\u0022:180.5740252108583}},\u0022LineNumber\u0022:12,\u0022ResultColumnID\u0022:0,\u0022ResultRowID\u0022:0,\u0022MatchingBackground\u0022:\u0022#FFFFFFFF\u0022,\u0022IsBarcode\u0022:false},{\u0022Word\u0022:\u0022\\u308A\\u3087\\u3046\\u308A\u0022,\u0022BorderRect\u0022:{\u0022IsEmpty\u0022:false,\u0022Location\u0022:{\u0022X\u0022:388.6704854910296,\u0022Y\u0022:164.68709114645813},\u0022Size\u0022:{\u0022IsEmpty\u0022:false,\u0022Width\u0022:57.953745389854156,\u0022Height\u0022:17.453251507369206},\u0022X\u0022:388.6704854910296,\u0022Y\u0022:164.68709114645813,\u0022Width\u0022:57.953745389854156,\u0022Height\u0022:17.453251507369206,\u0022Left\u0022:388.6704854910296,\u0022Top\u0022:164.68709114645813,\u0022Right\u0022:446.6242308808837,\u0022Bottom\u0022:182.14034265382733,\u0022TopLeft\u0022:{\u0022X\u0022:388.6704854910296,\u0022Y\u0022:164.68709114645813},\u0022TopRight\u0022:{\u0022X\u0022:446.6242308808837,\u0022Y\u0022:164.68709114645813},\u0022BottomLeft\u0022:{\u0022X\u0022:388.6704854910296,\u0022Y\u0022:182.14034265382733},\u0022BottomRight\u0022:{\u0022X\u0022:446.6242308808837,\u0022Y\u0022:182.14034265382733}},\u0022LineNumber\u0022:13,\u0022ResultColumnID\u0022:0,\u0022ResultRowID\u0022:0,\u0022MatchingBackground\u0022:\u0022#FFFFFFFF\u0022,\u0022IsBarcode\u0022:false},{\u0022Word\u0022:\u0022\\u304B\\u3086\u0022,\u0022BorderRect\u0022:{\u0022IsEmpty\u0022:false,\u0022Location\u0022:{\u0022X\u0022:49.674638905589276,\u0022Y\u0022:235.61889534948426},\u0022Size\u0022:{\u0022IsEmpty\u0022:false,\u0022Width\u0022:29.760031416411593,\u0022Height\u0022:15.215655160270588},\u0022X\u0022:49.674638905589276,\u0022Y\u0022:235.61889534948426,\u0022Width\u0022:29.760031416411593,\u0022Height\u0022:15.215655160270588,\u0022Left\u0022:49.674638905589276,\u0022Top\u0022:235.61889534948426,\u0022Right\u0022:79.43467032200087,\u0022Bottom\u0022:250.83455050975485,\u0022TopLeft\u0022:{\u0022X\u0022:49.674638905589276,\u0022Y\u0022:235.61889534948426},\u0022TopRight\u0022:{\u0022X\u0022:79.43467032200087,\u0022Y\u0022:235.61889534948426},\u0022BottomLeft\u0022:{\u0022X\u0022:49.674638905589276,\u0022Y\u0022:250.83455050975485},\u0022BottomRight\u0022:{\u0022X\u0022:79.43467032200087,\u0022Y\u0022:250.83455050975485}},\u0022LineNumber\u0022:14,\u0022ResultColumnID\u0022:0,\u0022ResultRowID\u0022:0,\u0022MatchingBackground\u0022:\u0022#FFFFFFFF\u0022,\u0022IsBarcode\u0022:false},{\u0022Word\u0022:\u0022\\u305F\u0022,\u0022BorderRect\u0022:{\u0022IsEmpty\u0022:false,\u0022Location\u0022:{\u0022X\u0022:428.72346010409484,\u0022Y\u0022:236.73769352303356},\u0022Size\u0022:{\u0022IsEmpty\u0022:false,\u0022Width\u0022:13.873097352011419,\u0022Height\u0022:13.425578082591695},\u0022X\u0022:428.72346010409484,\u0022Y\u0022:236.73769352303356,\u0022Width\u0022:13.873097352011419,\u0022Height\u0022:13.425578082591695,\u0022Left\u0022:428.72346010409484,\u0022Top\u0022:236.73769352303356,\u0022Right\u0022:442.59655745610627,\u0022Bottom\u0022:250.16327160562525,\u0022TopLeft\u0022:{\u0022X\u0022:428.72346010409484,\u0022Y\u0022:236.73769352303356},\u0022TopRight\u0022:{\u0022X\u0022:442.59655745610627,\u0022Y\u0022:236.73769352303356},\u0022BottomLeft\u0022:{\u0022X\u0022:428.72346010409484,\u0022Y\u0022:250.16327160562525},\u0022BottomRight\u0022:{\u0022X\u0022:442.59655745610627,\u0022Y\u0022:250.16327160562525}},\u0022LineNumber\u0022:15,\u0022ResultColumnID\u0022:0,\u0022ResultRowID\u0022:0,\u0022MatchingBackground\u0022:\u0022#FFFFFFFF\u0022,\u0022IsBarcode\u0022:false},{\u0022Word\u0022:\u0022\\u304B\\u3089\\u3060\u0022,\u0022BorderRect\u0022:{\u0022IsEmpty\u0022:false,\u0022Location\u0022:{\u0022X\u0022:566.7831547200794,\u0022Y\u0022:235.61889534948426},\u0022Size\u0022:{\u0022IsEmpty\u0022:false,\u0022Width\u0022:46.54200401965121,\u0022Height\u0022:15.663174429690311},\u0022X\u0022:566.7831547200794,\u0022Y\u0022:235.61889534948426,\u0022Width\u0022:46.54200401965121,\u0022Height\u0022:15.663174429690311,\u0022Left\u0022:566.7831547200794,\u0022Top\u0022:235.61889534948426,\u0022Right\u0022:613.3251587397307,\u0022Bottom\u0022:251.28206977917458,\u0022TopLeft\u0022:{\u0022X\u0022:566.7831547200794,\u0022Y\u0022:235.61889534948426},\u0022TopRight\u0022:{\u0022X\u0022:613.3251587397307,\u0022Y\u0022:235.61889534948426},\u0022BottomLeft\u0022:{\u0022X\u0022:566.7831547200794,\u0022Y\u0022:251.28206977917458},\u0022BottomRight\u0022:{\u0022X\u0022:613.3251587397307,\u0022Y\u0022:251.28206977917458}},\u0022LineNumber\u0022:16,\u0022ResultColumnID\u0022:0,\u0022ResultRowID\u0022:0,\u0022MatchingBackground\u0022:\u0022#FFFFFFFF\u0022,\u0022IsBarcode\u0022:false},{\u0022Word\u0022:\u0022\\u304A\\u7CA5\\u3084 \\u30C7\\u30B6\\u30FC\\u30C8\\u306B\\u5165\\u308C\\u307E\\u3059\\u3002\\u3067\\u3082\\u3001\\u98DF\\u3079\\u3059\\u304E\\u308B\\u3068 \\u4F53\u0022,\u0022BorderRect\u0022:{\u0022IsEmpty\u0022:false,\u0022Location\u0022:{\u0022X\u0022:27.9699543387327,\u0022Y\u0022:250.61079087504498},\u0022Size\u0022:{\u0022IsEmpty\u0022:false,\u0022Width\u0022:584.6839254968684,\u0022Height\u0022:26.85115616518339},\u0022X\u0022:27.9699543387327,\u0022Y\u0022:250.61079087504498,\u0022Width\u0022:584.6839254968684,\u0022Height\u0022:26.85115616518339,\u0022Left\u0022:27.9699543387327,\u0022Top\u0022:250.61079087504498,\u0022Right\u0022:612.6538798356011,\u0022Bottom\u0022:277.4619470402284,\u0022TopLeft\u0022:{\u0022X\u0022:27.9699543387327,\u0022Y\u0022:250.61079087504498},\u0022TopRight\u0022:{\u0022X\u0022:612.6538798356011,\u0022Y\u0022:250.61079087504498},\u0022BottomLeft\u0022:{\u0022X\u0022:27.9699543387327,\u0022Y\u0022:277.4619470402284},\u0022BottomRight\u0022:{\u0022X\u0022:612.6538798356011,\u0022Y\u0022:277.4619470402284}},\u0022LineNumber\u0022:17,\u0022ResultColumnID\u0022:0,\u0022ResultRowID\u0022:0,\u0022MatchingBackground\u0022:\u0022#FFFFFFFF\u0022,\u0022IsBarcode\u0022:false},{\u0022Word\u0022:\u0022\\u306B\\u3088\\u304F\\u306A\\u3044\\u3067\\u3059\\u3002\\u5C11\\u3057\\u305A\\u3064\\u98DF\\u3079\\u307E\\u3057\\u3087\\u3046\\u3002\u0022,\u0022BorderRect\u0022:{\u0022IsEmpty\u0022:false,\u0022Location\u0022:{\u0022X\u0022:28.641233242862285,\u0022Y\u0022:319.52875836568234},\u0022Size\u0022:{\u0022IsEmpty\u0022:false,\u0022Width\u0022:452.44198138334013,\u0022Height\u0022:29.088752512282007},\u0022X\u0022:28.641233242862285,\u0022Y\u0022:319.52875836568234,\u0022Width\u0022:452.44198138334013,\u0022Height\u0022:29.088752512282007,\u0022Left\u0022:28.641233242862285,\u0022Top\u0022:319.52875836568234,\u0022Right\u0022:481.08321462620245,\u0022Bottom\u0022:348.6175108779643,\u0022TopLeft\u0022:{\u0022X\u0022:28.641233242862285,\u0022Y\u0022:319.52875836568234},\u0022TopRight\u0022:{\u0022X\u0022:481.08321462620245,\u0022Y\u0022:319.52875836568234},\u0022BottomLeft\u0022:{\u0022X\u0022:28.641233242862285,\u0022Y\u0022:348.6175108779643},\u0022BottomRight\u0022:{\u0022X\u0022:481.08321462620245,\u0022Y\u0022:348.6175108779643}},\u0022LineNumber\u0022:18,\u0022ResultColumnID\u0022:0,\u0022ResultRowID\u0022:0,\u0022MatchingBackground\u0022:\u0022#FFFFFFFF\u0022,\u0022IsBarcode\u0022:false},{\u0022Word\u0022:\u0022\\u305F\u0022,\u0022BorderRect\u0022:{\u0022IsEmpty\u0022:false,\u0022Location\u0022:{\u0022X\u0022:330.2692208317557,\u0022Y\u0022:306.77445918722026},\u0022Size\u0022:{\u0022IsEmpty\u0022:false,\u0022Width\u0022:16.55821296852976,\u0022Height\u0022:14.991895525560727},\u0022X\u0022:330.2692208317557,\u0022Y\u0022:306.77445918722026,\u0022Width\u0022:16.55821296852976,\u0022Height\u0022:14.991895525560727,\u0022Left\u0022:330.2692208317557,\u0022Top\u0022:306.77445918722026,\u0022Right\u0022:346.82743380028546,\u0022Bottom\u0022:321.766354712781,\u0022TopLeft\u0022:{\u0022X\u0022:330.2692208317557,\u0022Y\u0022:306.77445918722026},\u0022TopRight\u0022:{\u0022X\u0022:346.82743380028546,\u0022Y\u0022:306.77445918722026},\u0022BottomLeft\u0022:{\u0022X\u0022:330.2692208317557,\u0022Y\u0022:321.766354712781},\u0022BottomRight\u0022:{\u0022X\u0022:346.82743380028546,\u0022Y\u0022:321.766354712781}},\u0022LineNumber\u0022:19,\u0022ResultColumnID\u0022:0,\u0022ResultRowID\u0022:0,\u0022MatchingBackground\u0022:\u0022#FFFFFFFF\u0022,\u0022IsBarcode\u0022:false}]", + "RectAsString": "107,120.5,836,700" +} diff --git a/Text-Grab/Utilities/PostOcrUtilities.cs b/Text-Grab/Utilities/PostOcrUtilities.cs new file mode 100644 index 00000000..de03e597 --- /dev/null +++ b/Text-Grab/Utilities/PostOcrUtilities.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Text_Grab.Interfaces; +using Text_Grab.Models; + +namespace Text_Grab.Utilities; + +public static class PostOcrUtilities +{ + public static string GetTextFromWordBorderInfo(IEnumerable wordBorderInfos, ILanguage language) + { + if (language.LanguageTag.StartsWith("ja")) + { + return GetTextFromJaWordBorders(wordBorderInfos); + } + + StringBuilder sb = new(); + foreach (WordBorderInfo wordBorderInfo in wordBorderInfos) + { + sb.Append(wordBorderInfo.Word); + sb.Append(' '); + } + return sb.ToString().Trim(); + } + + private static string GetTextFromJaWordBorders(IEnumerable wordBorderInfos) + { + throw new NotImplementedException(); + } +} From 954d717f697c5ecd5e25976f4e7b8f988dea4b32 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Sun, 9 Nov 2025 23:00:14 -0600 Subject: [PATCH 3/6] Enhance Japanese OCR handling and add test command Improved Japanese text processing and OCR handling, including: - Fixed typos in Japanese test strings for accuracy. - Implemented `GetTextFromJaWordBorders` for line grouping. - Added `ProcessLineToString` for furigana and main text formatting. - Refactored sorting logic with LINQ for modularity. - Enhanced post-processing to merge single-character lines. Added a new Bash command in `settings.local.json` to run `dotnet test` with detailed logging and filtering. --- .claude/settings.local.json | 3 +- Tests/OcrTests.cs | 8 +- Text-Grab/Utilities/PostOcrUtilities.cs | 109 +++++++++++++++++++++++- 3 files changed, 114 insertions(+), 6 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 5939d37d..3bc442d9 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -10,7 +10,8 @@ "Bash(dotnet --version)", "Bash(dotnet build:*)", "Bash(dotnet test:*)", - "Bash(gh pr list:*)" + "Bash(gh pr list:*)", + "Bash(\"/mnt/c/Program Files/dotnet/dotnet.exe\" test Tests/Tests.csproj --filter \"FullyQualifiedName~OcrJapaneseImage\" --logger \"console;verbosity=detailed\")" ], "deny": [] } diff --git a/Tests/OcrTests.cs b/Tests/OcrTests.cs index c6084549..fee6a342 100644 --- a/Tests/OcrTests.cs +++ b/Tests/OcrTests.cs @@ -94,14 +94,14 @@ BANK CHARGES 344 670 326 49% private const string jaWordBorders = @".\TextFiles\ja-word-borders.json"; private const string jaExpectedResult = """ くろ からだ しつ - 黒ごまは体にいいです。タンバク質やカルシウムが + 黒ごまは体にいいです。タンパク質やカルシウムが かみ くろ こうか たくさんあります。髪を黒くする効果もあります。 - くろ あぶら はだ かみ りようり + くろ あぶら はだ かみ りょうり 黒ごま油は肌や髪に使います。料理にも使います。 かゆ た からだ - お粥やデサートに入れます。でも、食べすきると体 た - によくないです。少しすつ食べましよう。 + お粥やデザートに入れます。でも、食べすぎると体 た + によくないです。少しずつ食べましょう。 """; private const string jaTestImagePath = @".\Images\ja-黒くろごまのちから.png"; diff --git a/Text-Grab/Utilities/PostOcrUtilities.cs b/Text-Grab/Utilities/PostOcrUtilities.cs index de03e597..f7b71c15 100644 --- a/Text-Grab/Utilities/PostOcrUtilities.cs +++ b/Text-Grab/Utilities/PostOcrUtilities.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; using Text_Grab.Interfaces; using Text_Grab.Models; @@ -26,6 +27,112 @@ public static string GetTextFromWordBorderInfo(IEnumerable wordB private static string GetTextFromJaWordBorders(IEnumerable wordBorderInfos) { - throw new NotImplementedException(); + // Sort words by Y position (top to bottom), then by X position (left to right) + List sortedWords = [.. wordBorderInfos.OrderBy(w => w.BorderRect.Top).ThenBy(w => w.BorderRect.Left)]; + + if (sortedWords.Count == 0) + return string.Empty; + + List lines = []; + List currentLine = []; + double lineYThreshold = 5.0; // Pixels - words within this Y distance are on the same line + + foreach (WordBorderInfo word in sortedWords) + { + if (currentLine.Count == 0) + { + // Start a new line + currentLine.Add(word); + } + else + { + // Check if this word is on the same line as the current line + double currentLineY = currentLine.Average(w => w.BorderRect.Top); + double wordY = word.BorderRect.Top; + + if (Math.Abs(wordY - currentLineY) <= lineYThreshold) + { + // Same line - add to current line + currentLine.Add(word); + } + else + { + // Different line - process current line and start a new one + string processedLine = ProcessLineToString(currentLine); + lines.Add(processedLine); + currentLine.Clear(); + currentLine.Add(word); + } + } + } + + // Process the last line + if (currentLine.Count > 0) + { + string processedLine = ProcessLineToString(currentLine); + lines.Add(processedLine); + } + + // Post-process: merge single-character lines with previous line + List mergedLines = []; + for (int i = 0; i < lines.Count; i++) + { + string line = lines[i]; + string cleanLine = line.Trim(); + + // If this is a single short character and not the first line, append to previous + if (i > 0 && cleanLine.Length <= 2 && !cleanLine.Contains('\u3000')) + { + // Append to previous line with a space and restore newline + mergedLines[^1] = mergedLines[^1].TrimEnd() + " " + cleanLine + Environment.NewLine; + } + else + { + mergedLines.Add(line); + } + } + + return string.Join("", mergedLines).TrimEnd(); + } + + private static string ProcessLineToString(List lineWords) + { + if (lineWords.Count == 0) + return string.Empty; + + // Sort words by X position (left to right) + lineWords.Sort((a, b) => a.BorderRect.Left.CompareTo(b.BorderRect.Left)); + + // Determine if this is a furigana line (multiple short words) or main text + // Furigana typically consists of multiple short words that should be joined with spaces + bool isFurigana = lineWords.Count > 1 && lineWords.All(w => w.Word.Length <= 4); + + StringBuilder result = new(); + + if (isFurigana) + { + // Join with ideographic space (U+3000) + for (int i = 0; i < lineWords.Count; i++) + { + string cleanWord = lineWords[i].Word.Replace(" ", ""); + result.Append(cleanWord); + if (i < lineWords.Count - 1) + { + result.Append('\u3000'); // Ideographic space + } + } + } + else + { + // Main text - concatenate with spaces removed + foreach (WordBorderInfo word in lineWords) + { + string cleanWord = word.Word.Replace(" ", ""); + result.Append(cleanWord); + } + } + + result.AppendLine(); + return result.ToString(); } } From 01b65c732b17dae41c495630471cdca78197b84a Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Sun, 9 Nov 2025 23:40:21 -0600 Subject: [PATCH 4/6] Enhance OCR handling and test coverage Updated `multiLineInput` in `StringMethodTests.cs` to test edge cases with trailing spaces and adjusted an `[InlineData]` test case. Added `GetTextFromOcrResult` to `PostOcrUtilities.cs` for processing OCR results into `WordBorderInfo` objects, leveraging existing logic in `GetTextFromWordBorderInfo`. --- Tests/StringMethodTests.cs | 6 ++--- Text-Grab/Utilities/PostOcrUtilities.cs | 29 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/Tests/StringMethodTests.cs b/Tests/StringMethodTests.cs index a0412b95..e27868bb 100644 --- a/Tests/StringMethodTests.cs +++ b/Tests/StringMethodTests.cs @@ -35,14 +35,14 @@ public void ReturnWordAtCursorPositionSix(string expectedWord, string fullLine) Assert.Equal(expectedWord, singleWordAtSix); } - private static string multiLineInput = @"Hello this is lots + private static string multiLineInput = @"Hello this is lots of text which has several lines -and some spaces at the ends of line +and some spaces at the ends of line to throw off any easy check"; [Theory] [InlineData("Hello", "", " this ...")] - [InlineData("lots", "Hello this is ", " ...")] + [InlineData("lots", "Hello this is ", "...")] [InlineData("of", "...", " text ...")] [InlineData("several", "...h has ", " lines...")] public void ReturnPreviewsFromWord(string firstWord, string expectedLeftPreview, string expectedRightPreview) diff --git a/Text-Grab/Utilities/PostOcrUtilities.cs b/Text-Grab/Utilities/PostOcrUtilities.cs index f7b71c15..b4cea89c 100644 --- a/Text-Grab/Utilities/PostOcrUtilities.cs +++ b/Text-Grab/Utilities/PostOcrUtilities.cs @@ -9,6 +9,35 @@ namespace Text_Grab.Utilities; public static class PostOcrUtilities { + public static string GetTextFromOcrResult(IOcrLinesWords ocrResult, ILanguage language) + { + // Convert OCR result to WordBorderInfo objects for each individual word + List wordBorderInfos = []; + + foreach (IOcrLine ocrLine in ocrResult.Lines) + { + foreach (IOcrWord ocrWord in ocrLine.Words) + { + WordBorderInfo wordInfo = new() + { + BorderRect = new System.Windows.Rect( + ocrWord.BoundingBox.X, + ocrWord.BoundingBox.Y, + ocrWord.BoundingBox.Width, + ocrWord.BoundingBox.Height), + Word = ocrWord.Text, + ResultRowID = 0, + ResultColumnID = 0 + }; + + wordBorderInfos.Add(wordInfo); + } + } + + // Use the existing word border processing logic + return GetTextFromWordBorderInfo(wordBorderInfos, language); + } + public static string GetTextFromWordBorderInfo(IEnumerable wordBorderInfos, ILanguage language) { if (language.LanguageTag.StartsWith("ja")) From 1a9bf09d217d6f1cf4f131d26edfa7aea6f633e4 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Mon, 10 Nov 2025 09:03:29 -0600 Subject: [PATCH 5/6] use new Method for getting text --- Text-Grab/Utilities/OcrUtilities.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Text-Grab/Utilities/OcrUtilities.cs b/Text-Grab/Utilities/OcrUtilities.cs index 9eb86429..51093869 100644 --- a/Text-Grab/Utilities/OcrUtilities.cs +++ b/Text-Grab/Utilities/OcrUtilities.cs @@ -91,13 +91,13 @@ private static List FilterFurigana(List words) return words; // Calculate the median height of all words - List heights = words.Select(w => w.BoundingBox.Height).OrderBy(h => h).ToList(); + List heights = [.. words.Select(w => w.BoundingBox.Height).OrderBy(h => h)]; double medianHeight = heights.Count > 0 ? heights[heights.Count / 2] : 0; // Furigana is typically 30-50% the height of main text double furiganaThreshold = medianHeight * 0.6; - List filteredWords = new(); + List filteredWords = []; for (int i = 0; i < words.Count; i++) { @@ -396,8 +396,11 @@ private static OcrOutput GetTextFromOcrResult(ILanguage language, Bitmap? scaled bool isSpaceJoiningOCRLang = language.IsSpaceJoining(); - foreach (IOcrLine ocrLine in ocrResult.Lines) - ocrLine.GetTextFromOcrLine(isSpaceJoiningOCRLang, text); + if (isSpaceJoiningOCRLang) + text.Append(PostOcrUtilities.GetTextFromOcrResult(ocrResult, language)); + else + foreach (IOcrLine ocrLine in ocrResult.Lines) + ocrLine.GetTextFromOcrLine(isSpaceJoiningOCRLang, text); if (language.IsRightToLeft()) text.ReverseWordsForRightToLeft(); From e6f4afebc7001fabe26e858a9e6cf89f2bdf0a4d Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Mon, 10 Nov 2025 21:22:57 -0600 Subject: [PATCH 6/6] Refactor OCR logic and remove unused namespace Reversed space-joining condition in OcrUtilities to adjust text processing logic. Removed unused Windows.Media.Ocr namespace from OcrTests. --- Tests/OcrTests.cs | 1 - Text-Grab/Utilities/OcrUtilities.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Tests/OcrTests.cs b/Tests/OcrTests.cs index fee6a342..22ea1a45 100644 --- a/Tests/OcrTests.cs +++ b/Tests/OcrTests.cs @@ -9,7 +9,6 @@ using Text_Grab.Models; using Text_Grab.Utilities; using Windows.Globalization; -using Windows.Media.Ocr; namespace Tests; diff --git a/Text-Grab/Utilities/OcrUtilities.cs b/Text-Grab/Utilities/OcrUtilities.cs index 51093869..ac0b3cde 100644 --- a/Text-Grab/Utilities/OcrUtilities.cs +++ b/Text-Grab/Utilities/OcrUtilities.cs @@ -396,7 +396,7 @@ private static OcrOutput GetTextFromOcrResult(ILanguage language, Bitmap? scaled bool isSpaceJoiningOCRLang = language.IsSpaceJoining(); - if (isSpaceJoiningOCRLang) + if (!isSpaceJoiningOCRLang) text.Append(PostOcrUtilities.GetTextFromOcrResult(ocrResult, language)); else foreach (IOcrLine ocrLine in ocrResult.Lines)