From c5c61a35036e6043f6b6eef42da3c0a566a328f4 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Thu, 6 Aug 2026 22:59:27 +0200 Subject: [PATCH] fix(functional): centre text vertically in buttons and form fields (S14) Both renderers placed the baseline at box_top + height/2 + descent/2. Centring glyphs of visual height ascent+descent in a box of height H puts the baseline at box_top + H/2 + (ascent-descent)/2; the two agree only when ascent is exactly twice descent. DejaVu is nearer 4:1, so labels sat high against the top edge - measured at 5px above and 11px below for a 14px button. ButtonText also sized itself from the nominal font size, which is smaller than the text's visual height (17px of ink for a 14px DejaVu font), leaving the button too short to centre its label in. It now measures ascent+descent, with a fallback for font objects that cannot report metrics. docs/images/example_07_pressed_state.png was stale - no example writes it, the demo emits demo_07_pressed.png at the repo root and the docs copy had been placed by hand in November. Refreshed here; the demo should write straight to docs/images/ so it cannot drift again. --- docs/LAYOUT_REMEDIATION_SPEC.md | 53 ++++++++ docs/images/example_07_button_animation.gif | Bin 20987 -> 21030 bytes docs/images/example_07_pressed_state.png | Bin 16522 -> 16548 bytes docs/images/example_14_interactive_table.png | Bin 24437 -> 24113 bytes pyWebLayout/concrete/functional.py | 43 ++++-- .../test_functional_text_centering.py | 124 ++++++++++++++++++ 6 files changed, 210 insertions(+), 10 deletions(-) create mode 100644 tests/concrete/test_functional_text_centering.py diff --git a/docs/LAYOUT_REMEDIATION_SPEC.md b/docs/LAYOUT_REMEDIATION_SPEC.md index a75e0e7..cc347e8 100644 --- a/docs/LAYOUT_REMEDIATION_SPEC.md +++ b/docs/LAYOUT_REMEDIATION_SPEC.md @@ -28,6 +28,7 @@ It is independent of every other spec here. | [S11](#s11--partial-block-progress-is-discarded) | Partial-block progress is discarded | 0 | | [S12](#s12--background-rendering) | Background rendering | 4 | | [S13](#s13--word-spacing-and-alignment) | Word spacing and alignment | 0 | +| [S14](#s14--vertical-centring-in-buttons-and-fields) | Vertical centring in buttons and fields | 0 | ## Design invariants @@ -1158,6 +1159,58 @@ Three defects, all visible as a right edge that wobbles from line to line. --- +## S14 — Vertical centring in buttons and fields + +### Problem + +`ButtonText.render` and `FormFieldText.render` both placed the text baseline at +`box_top + box_height / 2 + descent / 2`. Centring glyphs whose visual height is +`ascent + descent` inside a box of height `H` puts the baseline at +`box_top + H/2 + (ascent - descent)/2`. The two agree only when +`ascent == 2 * descent`; DejaVu is nearer 4:1, so labels rode high against the +top edge of the control. + +`ButtonText` also sized itself as `font_size + padding`, but the text's visual +height exceeds the nominal size — DejaVu at 14px measures 17 — so the button was +too short to centre its own label in. + +### Evidence + +A 14px "Save Document" button with 6px vertical padding, measuring the label's +ink against the button rectangle: + +``` +gap above text: 5px +gap below text: 11px +``` + +### Design + +- `baseline = area_top + (area_height - (ascent + descent)) / 2 + ascent` in both + renderers. +- `ButtonText._padded_height` derives from `ascent + descent`, guarded so a mock + or unusual font object falls back to the nominal size. + +### Acceptance criteria + +- Label ink is centred within ±2px at font sizes 10, 14 and 20. +- Label ink stays inside the button rectangle. +- Button height is at least `ascent + descent + vertical padding`. +- A form field's value is centred within its input box (±3px). + +### Files + +`pyWebLayout/concrete/functional.py` + +### Note + +`docs/images/example_07_pressed_state.png` was stale — no example regenerates it; +`07_pressed_state_demo.py` writes `demo_07_pressed.png` at the repository root +and the docs copy had been placed by hand. It has been refreshed. Worth wiring +the demo to write straight to `docs/images/` so it cannot drift again. + +--- + ## Test plan Findings were reproduced with four probe scripts; each becomes a regression test diff --git a/docs/images/example_07_button_animation.gif b/docs/images/example_07_button_animation.gif index b0f87a3e548dedcd277f5c6721e259e4d9605c5f..15df78255b2ee7cb6b2d73b72113e2a633e02c21 100644 GIT binary patch delta 9998 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 RSmR-=@rkc*oa6)D`EQxj6W{;< delta 9960 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 SEzIh_*45pq)wKioUi?3YQx8c1 diff --git a/docs/images/example_07_pressed_state.png b/docs/images/example_07_pressed_state.png index 1539ad1e3babfc2df603cb6ee148df5ed720ced6..49f83cea822fcd9f7bc5c218486c9149f4e74b9c 100644 GIT binary patch literal 16548 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 F{}0fdR6_s& diff --git a/docs/images/example_14_interactive_table.png b/docs/images/example_14_interactive_table.png index 573582d18a0af8a924fab544cb907d2cbda85aaf..997fc39c7ccd9e6e8f5c28237a229ec9993ba139 100644 GIT binary patch literal 24113 zcmd?RcT`hf`!9&1U;$CF(NP4XOP7xH7OIGp01AX&1VS$=Dk{B84ZRbjmk^_)aE%V@Dn@I43>fdx~r-3@R47}%H;VLi1ESabwXNE4@tJ4^n`&U&QL6N z=&p*&+jsGa7vA{sB{kgP?TY=Nlp}WA?uU+uZ5EW|ag4C{-`PV1uBHkSrULt*ZEFNC zuf8!X-gzz9tUqKF&)qTS*>nbcABAHCoUhN3I%#bxpdzr9D?cuCEAfBayjVcK@Q-c`i!ul3&UOpKHm#bp>6^xQrs_mUj83Yt?@#New0kgApl#nR9k99Zfb_s_pxh?nhQM9( zdKbf52ZPiH&!4>-CGAXyYLn)BIh{w#4eOkY85BZV0vTL2!p*pwya*B)*_{cmp~mMn zqE_H4?T!!khETrlU2z=YvpuOs(!Q(9mV&q8EDj2A881Sgc;$2eQ9!gUxpREnz0I|7 zq{1ABK6+GQnA#RbO_$b>Y&#AR?GHUBAs&qTiuB~TI8QdiLoke`)(0%b%s9_W;hufW zGCn$4{__kxR;?|zUGd+C2{8)Cw`wi2;A3^pCZzr2gEiku-`+Iw`I=fsZ1)s8P zX#}bs!wO%)*I~q;emR5t{E`w%=tuY(ml%H6VH0zRCH}0kL58!1{A^hp%nJ`h?`_P9 zB9iVIe5vzU!N+!wR9Y$2Sb$N4g5~0Che_@=uC~=QX>gnD*@X3`eK+6Un8P4l$LpJT z9~E2Ar%HLjT0|20O#14)2)ejVwosR;BHc`Uo%?ia>}cKrm`T%}V^(lyBRR^{zL`h+ z#LC+^O{7IrkG;6lum{TnkIy4D_If)3mZ3**qr!M05L zsb$OUZ|~`mV>R|D%cCt3HI{xYiHh^FFI@s|Bs*&f%n( z7cxrB>Pq*Prvlf+2^)ow^qzX93bqQ&b-u?RaSWm`u}-B$@mxfm>$t?jgRAB8CY6>e zTImuMdpg9y9C!($H&X`Jf?GRH2=a&y%_eo@YQX|(;x#U{m1h&NeaBPdC14SHw1or< z{*8AKWw72U#e9!viXz~l`2brSj%Jb3DO5tm!xsh~szu&PvICrtd8XQG4AM z|7P#SJE|Y9#hJUKM_O<_?_ozuru}pZhwSN+?h=*MmzW)@{mi)Z&Q~Oavj%8v%yx0W zmxiCan-ls|gb-am_au)T!Rv*S%5a5J<1((Bf)@8vdqykFJJa<OjoGpXbesDV)@!6riY z=2Bv#-LuztDBg*Iz2?p18v_UZl>>Op$04?Jd(DYM&SYkk9&m3nvj*&lJ?EI0c(xm%|Z2zN#E&I zw_NbMzSKGACvS$@oU4=h8znjA&>RuqV*sJiO|3nyjh2ncA#7TAN)GH?qflW4)$Gbc zQH8!;md@W_!zU{qiAzf|VceC;SZ+a%`wti?ZFA}$A!PlBhU0bu#F=h= zP@Fc>-M&n5=j^H5FW&wA6-L)As*rYmfI^)u_K(;zCJ7e@%M-9m77mt7R@aWFVfBY) z_VJlIOsV)N$l5pO6tUh&2V4zmwV989F6d|r2eH{6vqx-69f9p(l`01|R(mi8#^h>C zrv7MNd#uln(f+T?y=o47vW`c0)iPfPqSKxmpB$2693LFywP#7pO1k{CKF!L_{z!Lc z%=*;R;&CmP*6!y5Ta4XOd31_{;dCXeasfYHQc~;qb`&8(q^+BR2A7{7Y_IfgWk1Z3 zpDh4e(o+TXO@e*v(Y84W-*umtFk$t{`sMlRmi{B_gFnt`qcO+d-mE6jw?r|Dck5sU z%eII(x{{uktwmzy7bxOhzqqWZ(s;0nn)^zpAT~L+@3eqc5%qZ@p zfh~me8d21t9)m&*`LsKiVwLtbc`6F`+&fKRa5VuBCmyV|rKDH}@Mvx;Gd1hUZ6!J; zPJ4W%g)Jvy-v~|+2i=@NrAz=Db!x{aiN|n@6lJCbX}xE|nvJ;e26rnXpOt`Oq7UOh zYqjeBux5dtT!mr5hx5`IG}Wy;%YS?Y{?ouLN##jG#cb*Vww!nja$lcc+LYWoorGcI zwn2KcWI>-)ZZSXaxV-dHKUkUo?ueOS&+W~eOVz*@^!H_x@Grh67i)!D3mw@j>_)4i zn*{_&lG8yPV6}Ue;m!#t2Z~q+3&9jIr1|%w%m!BsmlKl) z?8=qP`=a{_Yu)b75}41og}R40r!jIB>6qL%(_Fq)9@l-2YoM^8ZDBa|?rzq2GvVVw zrBVU!`^W0(J}eUBKdiFZJ5MQAKUp6a+E`nKuI|*>TWC_f&V^vOxZZ8LUEG+Y%K!C3 z->x?e;?_oTTa;YzZ}R*7N>=h0Pm{;*AkUi@&tC%#wmW(~ZQ8~+xUPB?^=7|Woj^Tv zNcUAHA~$u0Ypb-Ir9mvd)b4_2($Lvn5&QluxKNF6f7=k@eV?aI)P`YAkg$LG47&Pg zt%6xbP~!)54Pz+>{2(suO3M&`B?v);@$#>DhHS zG?%k|HE<(8Fl#+e{UfWB^a}DZq!;4a?|GU>^BeW+=yda1krURRZ869^gJNT1{rtrM z{II5c(Mf$kO_zns#B=?(m)JN-gka7i#4@9MYd9N`3p45xN3I&HP#I3I`JU>{9k^kE z8Es8ZkE2m<5TP$KDI7n^5xHuxPBmF`pNsv zBc$16&(74W<2(v!-Y>sipVMyB8EB4asf1yQC8v@rZ}2CvjGrV^uc4LeUGTT;r%-r! zY!cIPT&cKI*Ppd{47ZW|eT2}#95?;tse$ac!`5Rq|GlmC4Wgbx_iCn0e0K7S{F~yZ zo~};#PgKS*%V(6^J^V6>Tff5dsPUc7Ed?%JwXf!BwT^S6qpo*@_i3u1ja7QiZ!SK- zuAxsJb|uNgT;uh$JN(V!TPJ#=T(a3F6D8y0B6)KBX+b7|w%oENTFk*aA@QE68RMjP zO;gU_UYwQ3xhul^a(jLm{MilvX>4u?WC(HVGD}W7!rhC7B*onsxSe-r{;b)JkM_== zjKz4ba^dWHc?I0tjNDuX9?ZHPesaiRC^T$+X6HE2SaMC!Oveq4j#|vwTuh{!ne@P} zVa?!3$u+ptb*8|fqn@xmFTzJl-O8wRcGVBp>CB-Nad(|ge^Cy<)NDoQPLeUjyn3yj zE||HwEc*G3Et}rHdxm3*AT&PiD?)PeckRP+S{t->@%Gv@ll|24m{%jN$aD6G<1r*j zx^8c0qd|_b5T2ytkm61AM6M8(=aa%~xw-*3fOPR)<2t;gb`#ZH>Fk(tdOBoG&?Y zlNNy{I{V(qhZ_X7y|u(e!x|qstX=5*rk;`+>pO`nY?qf(Uc-DkBAMM7CBLepf4j}H zH|OgnsAVln-k-`|CfyWl6lIrQ$=jJ0r@P#I126Xol~HbSoX((L0G%JLfC_{i?8GPW z9fw*=#Twr2fS;lGEwY9gD;$TRnV7ycY0CU4v=+*x}H z-h@aI6ei97NW~nY0&&qVz~+I>WWt?rDja?KVap;;S>meB(t7p7bcm%>fJKI^|CdM( zerSEho~%8RZ=BjN?cJqw2Rln#l8=P=D$$3phTVe_f{y1-V|ytE%i!sO`7V1Gp36IX z`N}wY^#@nmVW~=tni5+Mzx;BZ(tS(AFq^mN)w69GG8?*>Lk?7eM<4e4L(6O$iW@U zgRclYyfWp>n}}|sNomdAjqi3{I~Ql$)luC?bnpwx5Q?~eI47~QY)JJ*pT!|na*6#^ zelgDG8|kRGjoJK#Gv$j(2YL$4&|XN!1L0*N{MqMsegAMv6j$l~w~|PYA~AEG-@ko( zhnc&8xq$Lsyqdg4?f@uMxO6jaZ9Dx7N`m*+C1QhD+wb^GrCLP`e6Bn7*{$~ci~OXY za`(Ib`y|<;T-irt`tNVi_w4?>T+jKofJ!(#_TvGw0Q~ae)(y9RNHvWiifo6$Tm$1N z4~cCK^rfpdh`fqRcj~6DUWBRv@-5ajm*<>Dd9|7+%U$EKJ585d#@YU1;qGcuaWXRX zn$3H_T_cmF&k^L+omo`O(41WVB}vy+>bL%919Y$IJqAAz3T=m5IIAuw%@e^PWQ2yQ z+$oA!rPE*dO{;(CDI8rR)ZnZ_p>C~vu$_YhZoTf{ok{LgQTt@2Hy65IpQl$!E5z42 zatI3x&sUl^xM_Y;|2vwmNl&JHGGu%OyO{iv_)L-kfbrD7Tz4)A^pGDeol_nJ?eADy zXq(G;Jw*O^*M{M~^$UJzv)F>z*7uuMU=rd11h_lH`jdzVJ&pq)T(dQ3Zx$oeMkDPG z*p!5F7e%pi1bCIlZ>6S{`tiH=eHrmQMqDMPHHDFE3YLPFEw_@lC>0HVB+A#d4CgQ= z9VrNu-i%Ezn=f*Epl;}<==b}}*}YAyp54z+PYbmXuylNbyAZg&OAlz{e&@x=$KMbz z`$2A{Hyiz6O54k0Hqmxjvi|wri$fb=w9@;+_fBsg zeV?dJDZ5e^ds~^R#H2FoaJ`ca=M8=B=}N;O@@+y9FhzNbjKRBqpZc$%cY2z)nB{`^ zy$9fnQIfP=nH#HPF>F8#u9IYKQ_uP5%e`^l$g9T$puwrwNcq=xcD~8XGMgzLpM{p z3oH```&NI_d}_AGOh;5<6=7_0c)Av>y|lM=utETE-AjzilKok_B`96#cv7gI2FSC? z1w3N)pS|K0ys+mfj(c;#$F=99@K^6%oJ(-{C6;{a_E+)3Hfr>r7sc_Po+|sVf4>=q zECckUi07<^|F4(SIuv^gc`@C}baJ+C9dDYg%q|HuSW@G|lqs74on&%+v}5%?DoVVP z1rfZR+N37-32*{Uk&NQJmgu{if$dguZ#~gm`O6f@-WVdC#@7q}e}YD{gIM>Te?3)J zy8WjBGi3KgWM_Jqtvg2c%eUVPJdipf8xdQEO%6UKP~IXPvqRrUY`G|;(48p0 z`2>zCg6+Y}Cq_cuCFyxpgaYfnNUoYf_}*wkIww98e-Z=}|i}_Z@5lS%9x^aa;>4>e;=V&v3ZOg0RKI6w5f)+o6Elc7#wbNt{vOEcW zEJX%H?;{$q3a63M!tv@=D}ebSGv+niIWD3D+p(UGRAnw>)$w0d^0UO7$jy^arqvkw#hi+Za*? z4q1Tj#gfnj@oYOEhrP`-tskx2`Kn;8OX(ooENpDr_=$*9P?jH}l*zl3vK-8J%_N`6tzu%;cgaTwKGbt~HlKgN6?=O!g zT~$>99a4DZop-U50iV;dLA?j@#3*B2F$VVq;rspXSfFd+S6= z(4f}_Ov>g%?>9<})(#fVEXp#u?l<|s$I*elEpVzCeu96^!+x1y5!VL}4N^zg@;aZS zz{P?Toep;4hW87g;pSqH_4~AlFP5Yiiyh#9@`)O?ixPYVWnw&Z%+3>ghh2@=eQefm z!2Rxd2PGA%Z<85EGhq`W2kYpY0!0=UIX$v!!p^wtc+;go`An@v>k{Yf>5Iop!c}b? z6n7_vXsPE)#xY&-ZQI&=N@KHmoX=6(H+nO>#Qvzu@1Dp7hv5AN4pD1AO%tH}agq`K z=!3tGC6Df=KjP1T%=1sP7*BGd+8XZk*@Hn(7y+w4?kC?&sW{3mVZs!f9v~IG`s}mU z=PTW^=Tn6W-oK;yeY7*}&RU-H@DqVxJSH2B^}VLx(JBedE=j%I->;t8a- zJwQ#)Ry0gqFk=l)%ay;Tgi3e3VaikyfLz$~t=L>Fkw$8X(%^STj)uLY$U|H(lO>)| z02SW~8eCJL^?9}K3oTZ}y@l0f@Mli(W2uWBe&r#6@`K4JlMeaY!Q`F@s`puRU%<|) z>#N$6K7z0c@vb?ONSi@G@R;fK!R$`-zlX%%Yjvdu8C3T){m_VDp{1B{oTJ4 z2y=1iTHS1+Y;mNUGd`2!!3(u6lYxd=sm)r=$L^7nwlTZO#6!vr8F0}{-|P1991Ho zRtK?Aq{lNdT`-Ka-fxoFFMU+9zh>DQhae(VE(C*G~;W_RkTGAdPFJ5235A>wS8$1g8 zOy)oBytBx<$(fgo*Oss{ol#6Ty`cf}M2HMaSaC-rgG~ovNj$jj;Q9}_oe7&)QnY`( zk0_}xMM~-^wcR`%;lS}-POVLM*t#n92Y%Cde7m!|S|GgOgys6|q?<|eBMIc@3G`6x zGJmQclHgJq#$o4%oh>Pg9;RUFU}79F{aDwBS$fW^WpWf?>$1P|y2q@ZFW-xOKz0gVEASd#pdu`of+;xh8m4l2u+SI} z2*kH`(09yz%7(d%KbNE`=%O6(wIyID{XTh?;nO;Pp!_hA$4JL#0C($~M*i5=TI+!> zuL&|^LiN<;w`=GdVe*n=hYmo%(oH>CLgQMp97@AHR=mV;5A%o3pM5vcrMU$v0=w|S z%pY-5`Q+$1s;4q6)1@zyU}wbd4M%PR9im51&>`l+Q5QWxB#3u@#WbB-|kaopqN#gND_LA-!U2yC1wSfYhiAE4vbv`jGk`kQe_*Ofu{18|myzch3HUYW~M9 z1>YMj1Wj~wA{#A`pUOW0(eJ4#9r;!iZU20G|Fn{do9eFezdQ_D^a5A-<5Lp$=6;^G zV4_%7OQ+BEpT=#+mSyp)Qo0dmY3K5xNOJFg!+9`|DYjtoTdA3wSNr) zEyaf$0*uCUwDvX%LqvArZ7KW`yCG4a>R&v!CRf_47vFfD@8TXBCJDq7Ki41KW(TGU z#dur&+^WHHyGtgx4lueNt6sIE5A#RNpZ%Afiz|a0gnl6)Zl5c7| z*XM6TjDX&s9I&;Ny7H*(F{Jfy19SH$UvYO5pEelMJ?sn?DEOu*AJ^PReE{9$1IIwT zxbtHk)9QTLgXNm5OiWD49rnM!ym5>Kz7Y2B@87++e=Yc6JL%|P2UG7lJ_}$)@+nHH zAHYr!rYjS2*a++js#8HE$XJ6rVv~r|!`C@;1GEI0m#YpRZNtooY*_&_!6N8uel|Q` zosc4fx_r|b7}?vf2apgakgmznSee%f*|5KfqY=etkm@4b<`Ps4i{Dr0A5*v}%baI=q{-+1Pup|UFlzUCWab00_^7nvZ<`G)> z5@3y9$m7a?<2+0-kj7wX5>9NPhtH)#kAqtL>)odXsp(k7Bq=vPI;-hf5!Zbd_Jq9` z^h=&p<#s&=vgw#DpLk$-)ZF6=vo5f!{INT?)Hz z6^q|Hd-cvh6U#FGoDcLLRLGBIx{pV1{{8)R1L*3hBr{y#{-)zjOMo)OJrP(C{*6t= z5k!at^aNbm)2X%pj1+~}whG@{(ldzI-p9p>S}9dZ+87@V!zW$Q)JC~GQSH2Ca=X7K1Z}?SJMlbxxY*#U?N+S;uEdCY z51^45{v9Akb^$$H#|EItIS~L1{fMJ#PXS|KJCOab8Z=@Fahs|ruF#XC9T<~U(E1z4 zN8ph_as9$>EIPZ3!g}sQNl0cr3jdFu}FKzzB+qR3zRMh#m=BQnj5GJKs{fY!Zlm<5@w?TiJj~ z5yE8pPC2?!IqA1px1R=GHgVUfRfyts|9=H4?)_haioBd<*kb-e2W%E&~1t zj%5fFDy?q7tpxuQ*fJWtSMVdYsx3I7HXjZ0{yI0t-&HKMCd!NEir3iA_YN%d=uQGji=B#rX!O0|TC9?i$(?X?d)Eu^VeucszCDRwnkh z(e&3E^WRx#Rx9|h80as=u2h9PVhhEg3UZ5qM;42d&^dAIx8W2xGlV$5DH?N?ycP`SkXz*>X@ ze?NG+s`F1z-pgENAQYvD_V1W===&qJnZBJjy>ZUlE!@*}*Zi^j2Zx&)nXL|kcM_gs zKdZWbJOsud7%Q|=FpoI?FQ};l%w^pRWo-NXU#qbU1SmYm_|588wPXeq!5?zxg~mslNZeQh)NF3aV- zP@>Yx?-@KcA>z3$y(qazCl#{)N~|OUr*q&;5T-xmq2(8{Xy#8HDvARmp6WQLgi;Ie zVHi;~37aVeQ}Aig*>++A0y}9zH+V#x&l0cMCL?c7DZf7%B{$`=UiO^|Z}V><7OR{i zYQ4X6hkaP>GpL#@1W^`LF74@p6DRft00MWsgDXs16|u|#I5U*2ql5v<&|iWg*1Psp zf$y&^zeVLxv6^~dwtE0JbJ>B_)G1oUaGxuqbFT)gl=NiRUmtFdyFcYYNl3r+Ao>1# zSl>DPgYyIvbh*rH%r4o>W{W}^3Z|3ry@u|;9zglF0*ypH0=!gfBGjXqg%QrU<@PY@ z^zyc;*I2vFgIzP$C-mKQtmQ3JBq#j^=OFjuX$6XS8~}a=A$t??Qo)QoR`=2mTUWY$ zWAhwVbqcRkGGsP14-2;&wr8aF)CPC7gK#8BhNzu zE-liY?X`SO)Xc$qj6Q#L+`)C)q(?vCC{HdSAnLKlEayD@!ojFYXZ1^vgo7YnL_ zp_$A*vFo>et&_#wNpzNb;$KJoYBYUlH8ZV!I9++4ow3(?dlAb$D@_%9c!^noFI+E6 z5-NMLd+;u!%}=Utb23~PGm7S(OD!qOC!U-x?20&1==$`0=eeV=je#kO^s(!LlkevQ zd%^Bh(uu;96rvcc=R-4pP07 zcTw#8a5lXj01T|1fg`NjD>wkh^j?or{0Q4o#A@#P#3@hLF&1ePBbJxrI}cBj7?xc$ zlCO&^qLmJo^Z51IZ~PvBEB;GXm6m9ood+u-MG6mdqJ}*NblxG&Ywqs<3}I&mdpD!Q zS(L<~Kv}8~i%i+WY)i`hK%_Bf0f>V*#%oSHd(US&nrf!^KVPH$C zgN-Qf*_+kE8Q>1qvQLk1(En;3K$bGUm*XC%`?k;Q-r&Ec$uM1#+!d|n)nHI`sg`LS z0Daru5GwKn3PJJcgfNk<%o=Th0^b81GII{kgeSsB7@gx?mjlJn!`UB5!7N4CT$#(PwA z@6XH*vI|$s#aU8o#{K@VDc#68_>sXIz)JuIG*!K3q%4Xl0SNlH)8~KCP5%EcDUKHy z(Kmp?_Qo~%-+<1ANEuIwQ~mQ><}}5Nb2R5_Y`g9#9Bmq9^pkD5O?&g{lWTfevOGJJ zz6i3{8(5j|0$=$#$27Ah&w>e0jDRP=Dw?Sxc4RQD`N_niCqv4JY^Lr?u97Rc?@_7ezJI{*Ix$AnHT`f=nEnh4ezjn$-Hv&^4nbC9j*>N2^p(*O%DR1 zl!*0b3e#q9#{owz%Fm!&VOoO^X3;xSqNG6TYz=0!=}c*OJQ9lP-d-68-XL*L&~#(c z-CI-d8XtoWPy?uUl}=;8rVd0t5dvhS)VKhMXHSLLVuz9QX-^tGawdGnUE_hj*2|^{ zP)8ymhki|d>(6qN=qXNj{aeo4{O`$me8ve}kbHngMf^8rUtpQnT1e4*7NT;Jlimr# zrN-Kz1|*dq*oFbC><_>_+HEcja^33zmRvH{!PmR;o$U38PLP8CL%8co6EEx9%#!i# zlY=lJ(+V)Lx5I!tSnqguhHghpZmJT!oz^u!*Ow{dAfe*USnG__^_=VG{Qlv3LX}PD z+gi3X(~!UI)OG+OoN7SBiH{wpTAIdUz^wfMGE6UlEo3*R)@>4xc3B)MfJiZu)fGfc zvt@7eK^a{>bKv%}@U(FNWI<{j2#=kqt82~YydkeWp?5daZUN_W)B8e6mD<1^&_ee{ zu|mVM){68HKK(@Z;D8$4FwLTJ(6uB1bkq%Gfmf>utNjBx$^}5DnU`q+4r(s*N|K`o zzSNjc>iHj)jJU0HZYar~dqUunKw)GJS`qi3L7;AGfX zT)Jij1+MlV-&VK&9Ku~J?)(zbowV(WReM`k$;(r6iOai!?X+PWYe#;f zbT#P4WbnYD9uI^mlmZq9@)Z{!^o|#3r|7^ufL6hEU**pbvbi&wMKr2d5lE}{jBWow zvjWhHHWxYO8P#|LgME@ZtN*uh?U+0pAk>QZu3AU|kp0A*uvc4uSzLj9&uiX9$Q;3F0p3 ze|q=DDH*ha+3g0re)q|&iA~Ol|1bld(;%?EpU?VZfmYX#IF31T-s99PP@H+mbVHND&2D)|hT4$Lfc-xaLOPdsR#9u+Z70XhNVedW z#hQM3(XFDFbHMd)2XL@S-AN8}r6TTlB$@5=bp@mXJ1yVrdm|RwZbBTJB=e`z4}zA> z{7^1_uM^18Q`FCnlCUCq3!XJ0JlEVs`vg>_Q2fMqE#X`+4JS{fQyJCkUB-HTwku*~ zx0c6@hVnHzfPGfP7q!(Vb2SBabu+q=P_;e>#t6WASC@K6YzILZR(k~x6cOz1P)9nU zI>27x)=Ft9L#yPJpE58v-JZjwq!eWZXvv>UoVb5?c&uM@!z{!#(s{HUHPKgJ_q(Kh z3aI)neQn7C=4omODzHeC#fSs^zsNRv0khisHqg49%C$O!OknjtGeVs^{vR6V257j` zU#Rq7SzCVeiS&6|{K^viO1HRh(5^?Sm}7jGEiI$ij~)|{co3bwDAZzI9bN!l- zJ56avbaQ@Zo3|_L4=IpOfUI~8lVDnHuWLV*BJk1pS-)khoO<84MG<_hr#!=+HSL}r z0Y6md@2Gju))VKGGmK1J0$QS-b^3>S_+0K7SxZn07N3R(Utz$N)YG_{sBYNx|2m#1 zNe8;1MtcSW1XiJrsgA#z@qdyKX=UAR?8;Ef?xl5_u!z$>h4MgDmY;%l95VVXp%vGk zG?iqW^>7N1U=R+O0Wm#k7zEe!?E3}mq$ZP;h6SEhyQX2Py-Bj)%@xl+j4Zr_wyBIN zCX2zpbUJT4&0um~8sGC-{`s^zsX1%7#3-Gj96Vn0(IKCNUz@QCgiCC0Mz>bFI#V`y z{Qk1)_1H0QFx9H&HC}ENWKF~>#YJ=5vyT@K^of)_VOhS6%ZqypF>NIFpZ`qGa@s&u z4gRblB}O&kMrvd_m0$qt$v;3-Zd!a{h$NfntP`e0>w*Z{=z4I#>Vl9ZDxs2=d?N^f zYtG+9Z;fs&Zlw+!(QWyi{nuuMZ1f)EY;%>ryM?iG+?6c-*l9~Em-Hh=6)BW%uGVMC zeZ+NiyHn39mMe*r<`}vn3wxzNLdSB~STuT@iWKN^Bt^hPvAJ)2qBfUBN?#(WI*lV> zDNnJuAB|1vJ;(lm?!Rx)%R0kWD%>TtwLJeD%A#WN327ARmF3;@{y4^Bu`M7R?=^vF z9p$&mcAOGZE}o&@azjaci)*Y!0@jt)UKXOXkf5>kR52|R%Wf4WDmRryS3S^Sb1y?D zV(Qw!a+uF4?3TYTmZZw$63}VYFWMh|-QNuaqy9mk5}O%5jPt-cfgw5{k#1hTGlwM^ zghjrNrlXxpJGa$*&IiH_Qaop*wZcH#zQ~_vDiAKpdjIEstz9z3bw1A1yj>7QF^<_M@hikIigL_*TLUPX{R)Z`m?zoz?$}p>X*^5N^^)9rG z6hxm-zA)coTeu?QGirNNd$#wP@eyx4g!e`t7tx0$1}~U5T`^`~ZC2*XOjgI| zewL89(w+Y6s^SZMgmekr(14qibgN2duTHu|Z=|U~0KwSG8OMfpuAp^h;n?{5m5w@N z-rIawPy_J5apbUCmASNFVaU;bR|}2@0TZuHw>vpLf~akiGgx|^l8$fLZ}kc;Wd_pn z);%vKypvc(s)49hKCo5|flL*4V5G{JY>sc)8a02IGNP7!?EN+w%698_ggo4Ujdgv( zt3Oq(B3gZjq~Gsa@NoLi_Xr4=BGVWi@~GcSP657rWjS~9tT@N^H+sE~)aedt<$4JD zD{zRzV@%i3BCSO5yAM?I`>8(kBWq493Vt(Dl3IYRdWEd?U7MQc8Vo%-up)gM0%Yf# zU<5Nh*a(;N^0&4Sde)2C>(ho)3p(f7CpYgyEDujK-$tQgv{6hE-T2H-oyYnp$}CwggT9OynQdvj!~Zk9 zX|rFOQ|_BtSCvD@KH!!qJ=LExBfh!lg1@fLbZI}9!u1h#dLh)Swmp}QZU9w}r&t&% zOG#yWuZQ?s)TxLFD30P;c~#hH%;ebapsdYy-=M(_nvjX5`92bTG`woPf1ld*kQ|8% zD%3mPWWBi{2h{r7sxm^pgl*lG#Rv%pLQkrVo4mv4qOnt%Ryz+JZ46P8GK7NBu&m1c zZ%WV`K7%w_Suy+2d_Bal^WDUvDXuy3oKzw7)0~IaVB7Cm`?3@i5JGgOy#VK*Xna2N z3aM8~i$3+|Fq!aC*hVL|U}kyqq3CZscPCkYJS|N-wOJ>>@*wAcZE5|2&kALu519B) zPug_Wlbsh0D=|rS=9jcjWOy}@#M<$uAH!@77&;=-TVO=-lR-ht2Ett;waDFxVooQb zUNG+xZ6tTk^t00wH}S?v|8k4)^$-7L{l5i1#~e*kJqCpD9R3aL&g(RO?s6&+Nk*R0 z1tZJW+3V49DmC!w;GN99F$0yMwaz2RvLwkmYL=3?54k4Ol>bOEX=Ak?8~JA{nGLp?#e z4714asT)Sb4B+r948S8Pd0L4=QE}fEb`(Li?;#BEs`USd;pgX1)c@q4U;LQ=_Tqnj zQUu`d|NRN;E2Ti+ss>@qHmo~{Ih(eH1ji?z2bZSU9e?lCQ((NaooeyF-5W@(Xy7up zo^c-kO;Zc92|{3(-8i`E#Pa6g=R!TmSPFm^G9DycpMc0GSBj98?nX8E6bcPX$9VsP zD+C}^UK(!(v>i8}3DUdG9q8yS`%7hpm6mdvxGfQCkS0X?v?<8!VmhI<@Xmj5*CLjwBgG?2l+$4(n|gShQ7N1W<=H;ff#bPXum86 z0I~@(Yb{2}g3u-3aox8bpgD%I>XD(}-o?iq`e*qXl1ZRdml*d0G0M3wZ!XH?yz1<_ zfI3p5JXr)9o~~v%QOT@`HOzO)UmZBrSWB$hTF>7HQC_F*-(+u!B|4xBpqdHN8oo}O zY?Sd@hKB-2*T-MYWFqJCcbjve`-|NEq+UroU@+zK8(578#9ur)2x@I0>bNrAa5DwN z2~bl4h--BL0+wv1i3iczk8>;$H-(Y`8<)zi2Fa8j;cv6;y4o}oEu-_7;Qewq6nMY* z09z0b@VUiQ$bqXVuqpPt209Jqz8Nc||G5w#7V9`xZKq*JqnhnmcJ|_pA0RmnxnJ0h z2Z(v5f`i$P2hfGrfcwn$yfTyk!lpXzAqN#1^1)`XzKlg=gM9G* zN7o>5A22k)#0ijeiPXi)mW>bO?D+XoN%HbAxN#X^GGl;qxB-TbP0A=lrHgI3$OsO=G1JP zY=#a1mo>pU)}g3)i0G>{RfmQHLulAy8a_{*4y5oSy4aoG3QKk9kta%{Q1~^7gvQWQ zuqcE~5Q0G4CjRohXrFG4@xDosF3XN=+88N+jITF$v0Ar5i&jbO30W1N?MF;U5>RR3 zeGZokCpE-T1#8LzHup{^nfp_r_svkP0T)e7YY)n)ymF9OlAqHg43jL-cK{%KD$Wch zXX!X}cv4dMa2049u9kGHvJW240h<9u>?>AYdhm~crgBfK7^3J2OsljLJ1Bx0)S26W zIoG*A1bA6Mu;c~?18>ep)wS$_>3tTmcK{@o)Ie6nUF3~4^8T<06@B@HuJvPZs=9=w{DZeb1&@Ll#3@Hz;$w9*@;x@jIog{ zGNZCTXMx5KV;%>XBUmtWzK4MvVOfkWei$BoegceIU;tQXkPg>x@2(f+_h_5La+vNY1f$ZZRJdwRdvt&9iHbIGLF`ly$Hom9~7Cx&pcvpd}_ zL-#L3^pV$sG@L&-dNML9Dhb4MMcKZFdgjZ~4eRm45jH9r95dV6|#_UjHYTS)Rxx{J0sQDY7S< z6WZ)OKMgdT@I+I6VDzrk`FQ`~TRJwksea)7I}-zT2-NaK6V~=RpqKc7#2-Zp0lokc zn+}>(8DFGDi=P3A``*oz@lBrTjKPrUF<@8O0F_A#tTo%ezuL*s0xYfT?2ou=K;PBq z01JA)aoOYN-33GwDbLwhKy_+^lQMcd`=rSc{)8*0^)9K4X1AD__&_;~2RnPJr;HL* z4OOtmS2^_A9~|fq&#lKQsuy=hwTA$FBo`Zz)^nQ;mhP=fHlV#)kAA~l%`c%0PCnlz2K+n(7dLcdi8OnB}q zZF3KkP$14eriOSZqQdR17W@jAde+du?v4S2td*Sro6_{$fi zfjg)S9QLrfIjm16F9&rTL9ugocN^0ugX8TUv8&y6bU6t2X2=;1B*WL&BdE( zb|9f)JLT;E3XpxV#FC;c&rA?{FDF-Fp9-_OZa7F3;6T%10a^hM7FoXmmlzd03d@g2b-F!LF8qhoqD!c=d?J%6qmJits7n(lxcpP zyfwkrk2}9d4otsO(WUu#kKqfjYju%h+u&q`6kzAx%TQm#1JF5Xdky%E+(h03mRq-F zPmJ&K2XJ@|nP%s-Y7LqXCOf|8bHE=P7VF zz%0XI7pBM-i`I3Z*v{901(h191S;a>LE+qVi+@em)dy+f&NnI}a|CB9TbdP`W152t z7?P}s7wpml_=75N>23Qz(j_aWXL85OK}0`;CcSMdQ4lKq%d>S?T0u#GP&XPr1ee|_ zGu4sV@jww>aWmGnAUt5AN5GO9cP1QfGT>{&rLmcQzK?g7kgg09M5Ldq}`Cz=a7pRfx zi=F`>HS_-cd*R8ijNr6`W`q-PJE&U)wh5Wl7N1wDyIsCk7m<(6#%}q!>p9}YSN03n z>QFy{!8zZmEo61CmYAW7eOn}tCTs!jg|*1co2D)7(s?o*C9XU`?J zk!GB_{VTR|fNg`F-01=V0rVBxIffBu6hAskrrVfF$#?X{8vPq!P6II~;~) zyzJy_QDFnM#9S?QIqoykYoA}YbkFwsY61mSa~eH< zzhdCar>z7{V(PjU@YsoC()1?xh?MUCy@h4z;Wii{L=tfsh)N)`nS0cWWSU> z+Qh;v5?e>tP4}-UhkCaRfaZ@A$h}5I9;w;8m@AE5FV1KkCH7hc?@CK#jSpKZi+*PO=4cZL zxb26;5uB%VA34w@-yc&37SC|_Q27AxjQ9A7^gnlP?fY&`NcY{7?fkSzI4{)}=8DNV zD6!!Lt-h&fv`Oc1oy(ZWRM3gx-D|bhHKy`f~)@5*(9HRn<^=N{v(iW7QcnMcC)2e50$mL{dv;t!Qeh)lV zwC8q~$2f#qcXRW#Q~D0U5gFSLus9@?&v7U}4jgLp<7f;bIca;m)pK=;2u|#1u2BW$ zOmy-B9e`}$K&WJcJhdjmWUV9lz$#T%V2$K4%-6_RStI~WCPmhNLjzRx32f%p_l}OV zU$vkPa6K4dhl8Zo5OC4aXI}y&{#whF9j|`2Vul@DzCRf7HE(+|WlTWC?;aUi0@)1% z_-?y}ft*4Ya!Mufo`Dh-9VgTRGnh0K;KT2|cuOr)phxRkta4B&za=X6xso#H*E>O8DNml^#I8WB~TP#C~!L zq$X`a2nv=39=`vhoof$=I^V;)hwLd-&*_wq$|j|-G>moKuqhFemJ!Ai#wC|6WQ`_; zl#bjlVcj+nQBAAZ02 zeShEg^SwEL=WqDS67U#R*n_S{rvxToZV<1L;rlIN*3>jsZw zdpeAUypMl`S-2NZDEoTf6#Q?5f<0ic@u?WZ$wqLQ3aIB6Y8NKKHc$;mFYbWcJnxha5=^+a2@%ffajV= z%M}k^h8t*t@lXH}%3>`E@I9gX?_v8CwSq0aMoy2~ z@&Hskz&Mbz6C+Yw`XIrjKMQil3%NL?aUngJBD60nCR013npSDNs=mDJpMKHC3d zg!U_B8AhyMIcEF_MhASzSmiZi5w_37Aw#r2-yK^(D41yu!OsNAaa{A3`oKJ=Wd}x7 zhGbDJESNrCA?>miniEp~-?rZ4@RmfPKuylP~zt-S>0Px{4b=0uL~Lj=VzGq=EQ zN@p}cB8&3T{VnS~3L2oyoM@!}^oR{OcdvAE;3`;IyWIGafo=Tiwq?$=so%S(VA?{J zYOJJEvbpOX`yTn?oUwX)PeB`Tj@5YSjXO&mJhVs5!pM<@CCj$gG_6Os-iP_^5xi#R@P2-$lSUIKb6S1U{CFB8s33>;IImfa2Mq z52j}?vNqtC3QeWl%rv24mJiw>|32%athgp(k_`~YbkiVpBoi)gCCwgf>qg~j$$NYC zllpJXZOJUltCcQGx?xW$1K7}f5#dthVjIN09az-=I{8>Qx5DS1!QRqqo*{K-0l|}F zVcd$xIq;6(uu;S$b`QhG*8AX5I8Lo*9G3&SRR`PgyO22?-Y9hNx!Li39#ucw2|7=; zY$pV6FH`*SGM1|ZBQO1w@yLU$l$l1F;HX-aL#Jo$^oXO!A4m#^x=E*5k((fy&ZhtP?X?N13gFb zL~U3EY2Ti#8@@$QUh*qT+ZW(!MXDuBIiG;A*;wEE@vm8hsoD5t~=bYdnaKTUBIDpff`AkjXzp~X=Nh3 zA1VZOpoFZkTnzgQ4y6rj_aG>Qink8X{4;!GY+KM0|H8RiW8Dh7#MT&EpGBeS(e#5E z)XnJ6-Yws+{v+(AQMEyabm_{{+JScaL89;utiw%B!s=$mEF4xltx?j|W)>KwQW&5! z`T^BE1NCj-==g_0=%AY%>VgX}S^f`bl$m4}E2RlUZlDbz=f58$H1@$MHf0FG!-IM- z2A}SF4rn+N_B)(XHYe60Qfp5>(NL|RVsHqVBd7^1TP8v#yDwR?TgMt?rw z7B-Isr}#+uC<8B$$YQSkRa~JbhOX{XUAG%3i_0<#9eTbxeEJ!0S{(;tfl*I13e+9G81NzN3s?w z9{lKu!tw&a+qRFoz-N*%A0gqbg*N1i9+1vp`cr?d0LZpQRfr~$tw@uEfyfWk!D+DL z=UvcqaUdL{Nn3gnOKixiw5-MS>3v^jGm8{nHbXDjA^(l|} zDnaEz;QpIJ_6yeu`NzB^6VXGUYgU}%!6!Jli({sc6P$)UrM_>fR9aj8nq>9h_Pdm(}^<1)RkF0{q zW{BYEjE=3@r2V=GPl;b#Y#2U|v2+0Fv(I71m?FE&m+1t4MDQSn(Vb0FJE18g@*VT`ZS#%D)&M@S@DG2HF8TUuiKliEKZ^ z{o-un*(bwGU^q^Pb_bVF0d0jvG8u9#PjC+NPby=XDXjo7U(R>3p)`>pg=gPG<9XvS z6~w_hv;5VZsleqL;Maq@8$oQb?sr5_^XP2WQIcLV;SxP{+cxK@qnD$`-=vs|^px)c zn6o0(#W^6M2!>jkX`nSYgDE5P>GXJ^pI!DIsjff7)+557F^c8B$$vRpuB?;W?H8x9 z%zj}bb8u=C8D~2cS9MkC8{Lh2T=e)m>V=hrjfV0+0MulvE7V>4+s?z+d!oR^^2qadTyg5(`lA_yxsGnPcXdtr$N8Nr-T8|9AJW3HIE z#R|S-J*(ax-HtZ#mc3N~{cyah;vPft#F6&8BSmI?ve7CDL9XrOQX%(Vl1a`H%vCU} zBpap587{8h!cUYt=&rj$o&+p+*yGs;jif{I*9Jy0y(4d0iV)p#v-VdgrKsU09B`=2*SdeT zC>2x`V}0br_3Yzr^C#LJEFhGn=~FqP7O7p)yXgHX*p_B?5_Cj6Noy>TG z!QIo;uhGQwMfJ4}GB%Uz*h046HsWjb%;>K-|3++}FB5&qV?KSq@c6$ZIG;@uFx>0s zTVwp^#ap4p`uX)w|M}qlk00FO5!#icB-^13v^KxUu-G2a)}3QF%Cr1#zoA)ul_fY1Vj z8dM0OBZMOTuK51jdY>XkF#5?n}0NeZr?wa$WPA|Wy3I3r8E z)?EMp&Br;$8ZjoEU`+U5e0@{XZ-0N8f<%iIJjT^r!$!LzK5Ubt(Via#H6vu>f%W|v zjufeb-8Fh^!{TRI(`cu*{RImSQ)QRKwdM@p)!{;wk;-07ZN?oOWy(BH3SP80=F)9` z#r-WDE=m|nxzfb4=Xpk^fSLJh@RcY!LFH&BX{YRssb=-gD0+pNr=Jjhdmnmn%CBnP z%~$t^G5U5_Y{W>P#Bv)oDYge5&qwgKnPEs|Qo-Pdi`|!xjIFcmRW4FZE0BACZ5ZZe zJppqoPV=1azPf}(z!Q0(hWyt3TsI!-#Qy9E>n&7C`Q;I?GFqN8?T%J+MG0cMt#bly zzREgx;d-ojS9ID(4aD*8l?Ht2eCyk}}V*-&bMO4lvZDZRJRN{Bubn2f#L_{*G!ZC~=*(d(nDF^ca_51-riqNdNu zKEBWx<{JM%<)(Iy^y!h?psUXqEC;)e~5w405!wP`D?oIpFwUhqU>@U_(b3b%wb~Z^bO1Wlv%pKHIN9fR8=# zn10po0?EA(D!lE`6Kaxpwb3$Nl-JM`YRUP)jn5%u3z7kmOw!oaX8(OxLJL?-RreL5 zKY6ZCFVP9wC6=q_NL3N=1Mr9XRSrdaYf~5xaQm427%4jw>EmtmI&PsiWuR5R)!PNL zJX-$bq5eR&Bnn@J*fqqo1sobr)LFVEv1@)EE!AcdKqw`!Zc5S9)2~d{Sw@um?{8UU zb7&WR<9q_%1{L>QeHF0zopu3?X(`liZ=K3kv;_ek#l?98tPCi%#P48uf8goyUhE2s zewFb80{kF+cVl*AdAKmbPt+N)lgy#LJ`F>yoTp)B`BW9^h<9wBxw%n~6>^$hJXzhG z>w=<-)Z9`wGo0I8vP4|ICb6o~5qxJ_JY2>$?nsA>CB+6@TOFseG-&I&T4gRW$fsSX ze1l-JGFFMIZGF)}USFlLqOh_xAbkoM5U}dA*?l|gCO2yjM)Y;lgXaNN#<1ZeFlB`7 zvHT}1TC*8~_Q^h#v*-FZf2^L$bR|{W;`_7QbSs_@wMv_PCegOS8-H4FSusxGfnKmZ zB|peM6Ri02Td)NssoV|JaOv7qbB5|$&j)@#LMSYL|MjU3zUr?gYc#B={a|FKsR? z4d#Yk=P`cngxJv|7z(hZ?_9JL1v8o8F#TyrVgp?qgZyQiCh?%*8KckqRb&EN%t#e< z3gMI`ZQAC~I+g35bsp-(K6N|N%HhLJZab;NmFo7Myr&VW27>kZ13QQaTHbVrc&qj* z9g!a2!rluQzSWJ?A)Gzy05TGX%D`BBHVHc3IqRrjY1mjGA4TW7(04nH-}1vm6>s~| zuqt}>XOcsN@ET*;lxCxyF6`CM*GLPjOBciD0{W86Cz~|EClxEBZpytC6ORjT@684t zau*!aFA`Tc73tmJ@Q+kA8b!hmV(bzQbQi% zE3s()^XuD%#$lcY{rkq2h`1~ z3W3TLMMrWDzst9}{?goB?Iv690;$=VK3}11KTVu!E}p@X z=32Ev4h!ZO8fy`rYldlHp=J71c|zZ#^hK+ROrD5ZWfE3LO10NrQgRb}X{q`lHp4R0 zNdKi<+HB^uRpxR7_NX3Csw0MHo+c@4(~BH;r>>QpsAr12Snl?NrBhvc`f-DU&5x2K z?sf1L89DDiKf`9ym{4bJg7bwHUFd45=pc^o6g$7?DmYix*`;GSz=10xp) z29ADrP%pp|Ug-4KN_(vv(^&X09e!#0j)7D%9lyUg!^-AOFXmp7Rs_`pIgnN>jVwUj zw&^;_z4OG?{c1pRue&%W(5?l(1f>zA7j~S4pdD4|GtwQx%1 znYnaE-Y&{5)SvUXlVzpSa9H3ubFkUPgiF#lQHoz1e79adAZ1U7! zHe^h$PSh8II9$1ojsvY969$HIKbxx2M zUSDPlt*DlS|AC_Z$H4gZEk311J+Cx|X?>vOb$Ts!tkx!j+v-B5azfu((~gx(%4hPu z5o6Wa=-*$=kLlc>IL{DP@@0)J$NweIr9T(O&NxO?+tMl8r+HN5SE=+K9q-Mm5Od3) zuNqyPAXaXznp_}>W0v3G$~XO#;tU}p3AX~lM<~k4@sr&y8%PjHC<_V6m5|I^kJMag z&yu{tZ41vpB67V%M>drY=4O`Pvt={i0(Ii!AFLl{VyBog7)e3eUWs6 zJxAf0JZ^k7^}R`O+Q{f9s5JzW|r%{r>Ld+JJiO+MiV8>tdwSJJQ?2?4 zF<4A_&x_3r4=p8~_{^WV1=D{Uz zgPXhun(K)CQm)j!C$6*?_077PD*5+UcuT}TckN9t%qYynY~A2wGh}ca_vx;fn>;!< z4ho#_IYCFPr)!f-fs&3}I13qeLm_92*NPiW%(IR%7cNukj#W&|@50yPvI9&LBWQWL zFAhj@((+`gufMz-^77@&`Qxk32Qyw6UGBLy!;*mS+s>v|lb2?L)}ppQ&v08z3``4ZQli?H$n11j?JLXE`gXdJlCf0rU_UlfA1Yr#FdSrtcz4L%@)MH)+;22 z7kJVH&C4z^h$<#*i?JW=aP=3rrQAk=PLa zEr?9!hQn+M4W10N&uFs+JXjO+jC-o0hujf{#I`4M$H5{_6P0z#()Ok92oL#VJeG$g zv0@N!hpB_hX~#2=+hV`S_MO&WPPFZq`h42SsC-hk&)S`w+J5m*psLWzZyIs-PPopisZ57;uZMm7ecb0f@izRHWX{qp zwu_70Bj!?f@mEP$!p;#ExJ?m+lmYjn=dK7kIM9^Dd_9cKrww;!%2896)K`mzTIo}j&Hr?(Fqtkzcg?2j|W ze74>kW>dIXsR<)8v3Eq3FVJ$|Y-%C7r!;c*4Zw-7qz95Xz3P(Bolg;hiY*Nvi_*jf zY)&Y+6hT|umEN@;fG&H((dz{I+^*(BPSPq*BI_-+Pd^V7J-N`wZn7+tUx(Q*$KAeNigIc zvD+O)e!ju}eDMS3=g~3dBRWoe0Ea(aYmq?x>`MT3^xj@cSmYASc8ZblU;xwKcgEzT#{aq#LJ(ZW43M5x}x}q79o=W!%47BgtwZd$6 zMra&S_6$9>X6w|f8O845?|IWpFYaud-i?v_`h_edhnDO5$|n7TGH%^2`^Jr z4vV1Os33H=SoMIvV1R6h73{s^_`$yZ-*>CiJ5l9N6h6_5hVzu73*xMOci#)MA0EBE zxPB@8+2PR~-vYU;{u6kZT}lE@<@C{9Nki{+`Y30iX?0cI(ZDIw^UDR--@7o9%6#g} z=0mwpRrozQ7NNC0Z2euACgr##XX~v^!(mVsd3y^?VZIu@hV)ZP|t2-}B`7Ke0qHa(V>DXa8 zcbV~Wdp1~`;cXv6V@d%wN*F0Du#oOV9&X~*(5l~)$jO99B=0rECGTN*GlVJ!M9KRlkVOh-f}&hU=~z;^u}n(s_QX1fG7E?A$;WwQTnJ<7ca)4QxYyQRhl)zuCIFY zSVW&O@JPw$cbw;olJm@4-#!`hq!j(=@Zg*Q<#RWIxBW)4yTStZQmyaZsnH(4TKCYC$NWEwD+8t74yGSy3mfnk#9_U3 zV?#`<;}f+gXnDW)4(tjN#aaw4DB~&s0%Yw?=8i~3yjp9-^Z=qJ zVfuS8?%rB!xXuVoY~69IXFG&FhppdlrxzEOgIS}^uo*bD)Yj()bLP-xycQ38)WwrR!+1hf%3gkzw z8S7^|naFFscB6f1xzd)fwRwcnnw@(Y(r*4aq1wlPRCDf1a?-GzTFMZkq;EIifdD1P zT^D%l3!94;SAEj{IFy{UxYW7*ch$jrLwZh!Lr?t#R`hq_(=`1KVLaRL)tFo1d<)#7he?Ci>#G zx+`#}N*xz`RL2IjH~kIfTlis?-3@|ZFrM1#HrGJP67?4n2FIEL@{$^qHq7@7^Shm1eGm63V(<`ICu@ZTBL=eHSTMi;8rs{v*( zCz4*6=LbaY9iWkn0oGuRkvgy$JpPq-egfgwJdxtFH6N!{sEh_`7{`(zn5}bP#yFjH)7QSyrNy2$;I3J7O{Zm>@f{G_Rz&YWyg00lalqFYXDP_ zUNr>5Nj$am(IA&m%n-U*odTL{;@z7jPz-PN+g*(VlR;#BWG8tf7bq^@VF%RhBZSyw zvomkGezg$6swYv&u)*eTR}7QafVlT>GdJv_7@%9&@dG(~1`6koO@8ageCfw%^j>C` zxLGaEywihA?ps1D!d_2UOeA->6wxBHkdR-3`Gml*NmkD1%+{;|jvGVutnlX&&Nm51 zISgF|FMm2{r`*Wz3*}%jCmq$2C^(xa3B?H>5Q0c+wE25;iLap0;*8O+TuP(9*3F!e+jt8Cf zoW9Bp-=D-3bfgKgM1$R^G8k`bI;R$iwR;lwV-+8JTiuu9r+x5QS_oecBCBotsL3Al z?BwKdLxh0JmYh2KHr;WoGuzrrI|!Z3YwAQd1`uj;(CM+h-B^Wfc354%%*vCV!pEtM z0}9c1_->0yN^g6^;t8_n#D!VavmII%?3+$PC>UeUoV%b1(7YN+F}`HC#Cso?63SC< zfsE(3=)U5!m>Z@_vj1l;ruTk~#7myew=Gb^%iPV`6qBJ($Y6Zto!#IU+1S*xAp;Cx zz8ggqAT`DG zuJaiKX6G&B8ASDW-uOGjK2JKd>ZD z{T@JipzvUoI%CnDX>ILJGh#Fjh0n4kBUSu&C#-RR>y4Xof^+}iZ+u)j5s5MJQyVT) z<3wc$3rE3t+x~ookT8Ht9w0@>B~L~OtO6%ROIr<@zIT>4F}Y7D2q%D>yX~G8G8UGo z9$MF*CEg3Z()M*4xH;B8ts3Qel^497S`U`X`2!Bz6OhYO&DD{Nl9Lu)kn$u+KhFfo z5!D@f=hmz42EP75VE?lU6ivuLon)I-FiFxufrJv%_BK)4%m`nv*??x)=U<}Eo4>xJ z+)({H8?&Y+-ap@ptX$B;YOL@j5(MvBQe0u2RMY8lrAluQ!!~dqKI@5B3oF26;T~WF zmmaXnTYMcb!W9dqz?1Em$7^1i`0O20Ev#qI$%vx!8hcB~JM$MD_(|7Q9Lu_YCg3er zPI}i~Y;1bc?`6h%O^!k)hW3KS*@t>c$=2lk7r%+_yAg2kNOCc9_iT;mgeVh_>x z3sdDmyodYJ8LNFG?Jznh_qVRuwi+c95LBdJGF#%>mc&A%eW@p5+kefyS@r_2-oB=ejFQ@M+)rp-GRW{Tw_*K&gug-Kaf$mK#K^IsfbEWzqIS{*=CFC&93=|=O znb4gx$&4Kb(3`78`X~JnX86vN<3qKN`4Fg5|hZon++V0}a;itbSyB|2i+= zx%>NPhkhe8#HI#6c=wE9)!#vhBitY{%LhdZlmG9>Zst@}*W6VH%vxa8f&=WW|n zN@3PzeG848O+L;kZ?irC3&3aT4;|u6brjD)`L1sVIYa&RaL+FfwTmKQgxqM+=@yT2 zGF>Jn8tNxxl4)O08Xbq}Ea?rpT61`ckGlxTKLUzdev*qZB-2}|m2|A8C&?&&b- z7v3g!JX-c#uFXtck=q`28A#7USEc*5F7BDKdtKY{GAWV4zq4wDjBESWsyctnqkXuI zDAg`bH`nc=#=h#Hisym0e8d|xJI(ZoOn|)Gy-o5euph77%z0(4t+aRWvj%US%}uNk z`|QQZWAu{Uay6rKnVv%Ihbd9mBOvQ4g@&qfeYMX!n)wckpIc7NlrbQiQJ~YX6Mlrq z&%qYz1f}p36~q9?ZQUy435ZkB;hJzxlw6pQr-yNlkd8-10(M)r{=sC)D0!vOnd)S6 zn6KBD$wvE($Z+yEVq@%lnD>i?ho%EjVjjz>pXL1I-VB$2nf1v%lSQAtKU~#KcUH)D z_?y2x9)?RcQoU00IM;LIj;O1}pykqL1?`&qr93(*|4+Ab%JJJtPWsGySikD?3y^R$ zZf^Ok`;gey3keE6;XznXghfYS2Y4L&(w9 z>oK{5gfN6tI`GIlfni&aobQ^gXTZtP{(_e!sod$FNqyUFszV@TverDT&!^*bgT45h z7$rtX!Hq#q8Sy~RANMWzYTVtk*Fv#8CISPKj!|Y;n$UbzbkA+&H9}=kHY?-O$H;SQ znz_J!CMoB&5~=DDsU?`j(o<72BQU)W5!gSc^Q4TZ$ER z@~#Xlx)b+cmmQ`U|M{4b$OH`nZ2?h?bWkq<0LS9fJ;dcIIfxZG!`U|g{3vP-O?zLa zT@Drtzy5C|@_&&f;U$;|PF7ZVV;=qIH^+0Igw=3S5YZYlzvt?BjeaN%*}rksh3}Io zZz6kUVe|1;0THCz!t_`+am)IlK4jU)kl)Y#bQ7@Q7E_FLjBXMOAMsV;`uo5#@$ZY6nQMFKkRGu* zmJ#z}HI9D&3@Xd*L>mW)eOkw8BcI_ibLP~Y%6~WJS9JLc)w!!BGz4K0SNYO)dwGTR zVPg`-PCWUHvR$``5L_;E-77KST=^cd$5CN=L$KOJrp{WKlLl1mWNn5V9MCcyKK>%} zERl`l2G4fYKepww1DR4@$Pu;VOST1j&>I#%Fy$Z-MlR;*I-|b&;wFE(|bsqA&*>4x#&9wiIabULY4KGB#N@q)ib!Tw)?HdZ^x9^FBNTzdGDfeTbr-BwW>6PVEC4=NNC0N`}fX#_>@=IQix;sX3GrNuR$f80P$K_s>(VA>2 zlw)d!`Vz-ye4h4tG&ViA6Y)&tp~a-*MU^>O2Oj+1OSf%2r3o&p4{ zIMw9X(H0GgbtDk{S=G`ZaarPCJpf;E0b@Y|NB|~7TSRc#Xg7MnPc)|4ToK<^DQJ1MpGIW4Tt0LCL%Y)Wu$a z>!vqs&9Du&+6kkarfsdN=@1Sq-l{*-w#YWW3D{y>K_vDjILx%@UcMt@#zVd+g^A3T z!W^#+&YA!C9HNygdoH2UVYUr{)U(@tPT$LI*bqC{71Pyw1+Ej{xvB)FQPST@LK}WX&wMnNLM4z9?+E6eRf`_ zo3QsLai(17Hq^le9y$`*Of1m2(;bZI!cMlNAB!`#wa#Yf9$<;n7e(XlJ|TBw6eT>^ z_IF!glJI^t=q9P*6F@}E`x&up>Wmz^Wr?ND8p|TNIEq-2|2?F9{xDV`A+2d|(I5fJ zl`S0Mb21QZu2uU=s8oYJqhi8`%MKpdZ`z2lXw-9(ei){noswVNpW#rZ=4!3xyo}q1 zMUcjjDzFn+EZP!TAD)p#jb(~?D3|G$F9fRRR2$T@{(zV^JkI|T>S-xAJiMNf{snPz zu!33vnFyKXh2;QJt+0Wl<4EJbfyJD|gOzH_-PMVrTA2Hi)@?C&n^sHv{71lYG2HBI zI#Kf?6sdTS2Tce%+El2ICK4x*hh34k&Cf`2=Sb}d`D|IBZy3M}mR;fQOE0k)ep`)C zfNeo*WIlQ!n`(EV>9?0dn5)xSG2ZReQS$&~{DZB}WYmfwXU?&hcSf=XA080~3IQpz zwbjd;;j@$KG+LG<8U&F^7qE_A@|3n8?zwriGTY&L@^U&Od{xZbP-sK+?tuF9yXq&rW^~QJP4~8U;L%DM^dGvH85Gp? zG~i^}B5s=fnWv{4Wi|x{mH#)4X(JCYZyAXfQqzI#J&EU0d&rS2TrV+HP{3v4$6R2} zJP3AI1Lxn}<&vUleJ9l_^-lV^;o8uUA|G@)%D2(@bkW|5Lg`8U>jMp6+$>DCI!cOa z(9~~DP$@&G5`>It$R~)+hXBO9pj$ zvg74H5`K;9n9SCNz)*tk0&7fl%mh9G$UsBa`R=;| zQim1(1e9fFH=P-pe8KNOs7oh#C5O@Ajgi`0-~Fz zKH=ZAqN|FK(eiH1n#GqJ=r;k?IzNC)+2E&Jyfx?}EBZ{xcU1G^97iFs$UePgk5N}Y zFr3x*_0YGCEr$AK)`rlXB~M>a>guy;mU3hkg`UK^n^Ov2k@nlIfaS&rwT@Lm?OR-J zG0&`}IT5!by*%~ngD$W2l;#lr@`Sh)({%hKd%zZw4F@FuNBX-A0B3qiW1O6_zvQ(M zsj==U9E{8HihYU%^W7tcsoYHZ6RC1@j~%#1G4J!5&lb~=@Zldy0`^2CYETs@F94lt zc23Dxh)EAts(0?scbV&4$JrAh=9=sBSv_R|H#)N={>=3(Itx+K7qSD*8vQ#OJu8EK zIVKI8T=n-8#V+H)PT(x2@*C(x;MF-xp4#uGh4Gd*F(pOkr?i;bFR-P5>uga??nx;P zT7^(MXL}Haj!;{1a?LUxKYn{%@;-sQZ}*YfD$AGY(pJ84Rn)@q?9rj~w0lJ~bcj4; z_*5{~sP@;X`pbYS{ZC|SZZ=Jda0y!>O}Lhi5J{|i18JfD509Xw(SWs1Kv`9Z(2{M7 z46dhPsbs=DuZ|6z%Qt_U?Yqpe58O~+wRVAR@|Vuz3v(OemvPg-_a=x&7a0ZHF+;ej zokjurA1En?c84B7OJ}$QKfAW*E$Y z!z1UrV+1E(ex9Jf6ae`FDU3SDu$LFbvbH?GqRbcnPN)YMO_&^roQ3NM9l$!y6cH6`R%?qsZ+H@sm+%UGp{_ zTJ#>1sUyr#XVu)>RF2-u5XTaqVYt8DeRX;-u}a|d+#Jh=j=1~lV5*>~=Nt?h`E!Bp zK0iOEUEzi&r}1=A(*BK8@7f3K7F6K8_E$=zJM!A`hA>yl|A;IL#1AWH$^<_R??b|m z)|zKk9~D^+UUU~D*Q8>SnvO)ET|8T!A>vfC#5`073u#|ec{+gj=lgNX7rnudr4LVC zo{fe+hzAzyU7ORL(R9IbO- z8eV{TRs+bT0gzs-?9zt$@_o8RwJ*RgG@Red4)DpftmyLLOyyq4}m2I1{^ToaISAHLm0%|DSKUs-%mocQd9W}itsW2OEc6- zw|Q?C*P7YKdwBE5G>Mt;g@0l7Tn;L04=DYif$RVQn0t z3@i&m3Mve8zKFN$jFiV=vg)`U6 zE8>hPlysXEtH`FB!u}c5_{Z-%EO6;$t^{7#sV2D?>npem^=`ntfkCg+{(`trO{<% z9}bRMDUZ`yUbKt5D_7UDJ86#tNbUU8*1wSC#i^~}`OiLa-hZM=^124>_f$f+e15g+ zw;MC=`^*H^z9*Mtkmq)hK65UV193U~*d6BIY8H{mnW(l7t9s>1%lSP#Joph%&OrIr z{>^8jvKbo>IO>Vx0}we~fni!@p)ak_?8|F@yOEOEqEcLMN-uZw^gWERTNKfM5B5{W zS)+4Zi5%MVEh|+Cs%iX603j3t&{+poe7MsQpr@3;!svSvQx2$XO}%Q9EOyJY@z+SN z2HVvB*C5JR8?XPD0cH9yNsVcHV4c^7$v%KBs3R;X`?at!FW}9bN3!K4RvI<;fC6hC z82b|e2ga*Z<`00S79jXJ4_5zd&V9$Zrp*;8Cp{bnSh6OCTG3A5^;utru(}g`O$Z6( zF<6auzC0DsgS_T{eUEsr_l)F!YLYy{NA!B#=5_C~Kj3@sVUm#CPl_tzvU83fbUibT zl~3AAw*_*f-D~N>5Zqgp+IWrtx>6mMjI^8F!v)VQ&)mf4hE$Pv>3*^7k9 z+x0nog=M9Yu(w(}W)f0XcKz{3RGYU&nv8+$auC5Aqqzx0*lSOp%6R|$dTxKYyxL;$ zF4k4pZ29iXdmpADAP-`;5qklAj*IWS)Awr;Llo-zpZRrO>M-M3M1J*;| z__LjzG(G7*=H!qL{`B9BJarq4hon4~6Lre zJvnqn&*%t@)z;P)-L0VI3q0P@ua5-cBZkXVAU1b|LW^AT=3i|UY7$xjitrxLHgwCH zF{tZtjoZBu!o5Vdmw>KX5O@1uB2)^f33%PIJNE9@;1lXJk}Y6rPEla=`DR>l(K4gq zlA*M(Q~+poVhyJku!=kao{EK|A$)e*$$`Cg5Z!YyKsxU(Gx$E8V(K}$;J$8ZyH9%5 ze%#fco)KIM_x@r4LY6C1cyaMYo zVsWlf{KtC%T3p}$r`e1l`Fg{o2&6%cFBD7X$@0PK_LCOa zR`j>Rw6lnN>o8}3AJ8YGmaN+7Ffoi*vue0H39w$m{$QsO;JapWMmZ0^*y!t`e%qQQ z18+Vu?tYlW;cERB*o>)Fui9+%G?R> za=jI*&&_cWOzlRIW)|Su&W=*#O#ujrs{I%u3pz?re%Ogpv#yZ#e@)>3M6i0ZZ2@Y| z7g7I0u&jP1vxUxps~p66EDk;f5lB4YHhSX)v`RQ=s$gYkv+IDUgr0B9j0pa#g#MXV z!t)ASkG^k4STNYN=&4bPX4wB;3bk1mS{@1U zyf5YcEAt4pzXg>Cu~kf+y$dN{Eq1|OqEP4R)ho+Pny7T|Va!3>aCse32#x7ck1&F{82|8h?JFhO~dM1bizzgW#4#+kHzFL8Ho0-h2(8!ftH`gO} zSY?%tapii(pyjKiFrC{(S@4W(0E1lsv*n;_Jh|VqS3e`#=a|PuuPy_{G;^@F{vfL? z1P54V)lRigcB*Vhz2{10bNU<<{?V?8Gto^I$Sg&+DcS>$)JMo;y< z`P-N5_qUH!7k9&2+FxfK{xcOF*m{?Zee?ypq4a=-pS@*k3Walvh3lB~{r9Z3wLA%h zTb9dA;;4=c>luGYP`y9P5Vr<{$PoiswzS$W=j@o)W%j|%M6IWCmkmz6?J=V2VheJo zv7E=I)f#@$hiJU7rPv?@m~spF<|uaT;6j~opBkzJe{bGVPq^aCriHj2>zRaRHl??3 zX_s`i$W42vVPpOb4qtjpzV@pS`q|wFJG1RSM4n-?iJ9voSUt^N8MEA&Y~<{JOC?#9 z^$tdJWf{K%liI#D66Pfno~Rz9K*fJeObU|tcVUl<)&`vh(k!y0p4p`+;RVu1T}9?d zL%G%c+-%6#0j&_5V&bcGnMo@1qoXn<)$Sd!3Yp%d!8K}+pmXATq$J_)#H9jdYc$HT z+Hm)OADxj#)KBa4wtLE@FjdmNqlWZjV)FAt3QSZYYD>BvYp)w@(X0teL$yX?JVho5 zUu5ejM~;=?_QGX^?@l}*epHFvEu&c5Qkr%4`2!ymkDZWgsIudoWxl*9I47}tibJa) ze0y~Q4RQBy7$r7Vv|OdgkCHiFy2K5~KKe?FSjn@>YBWk0vh$FLXAJXo;G{aZ$zyE8 zPdeE>=9K4~xw5(B&KWA%05=0ITJxpF+5EynL*lu-R-H$|gG0s2)4 zMx!8uCl@@|$w;m&5uF9%<4e%(?Xr3yS>L^N$Cm4Ypq)!H#*=Nhd~ouH!s+0$+L;DT z28VP{)DU@ur^~R5nXg9+NO%j+oE80j!=#TVsMoF)rTqX=Dg*v?Ikn%cA-8bGcV(p1 zoX$L5ux+?DM@(MbdEq0KGNSCDDToWob4{o?y9;FBf+;a20?S)%( zzisFA@fb%9!@xD{>YbDpRY{YbuWtfxdJfGg$0)4Tm3ZZhxD;l$!|BfB-&nP6zkW*| z0wf4N9D09S7`h{_8!Eeo1^uMmAA8YmPk4hDZ^;Sz|9K^_`>bo?ty{*OzjEs$Bo>WZ znTe)pInG_LHp5Giki_v2;n;RemS@s=3Wob%soRSsT1;;o-mJk-4~oBEer|}mOis6P zO9^5m9CYGOsHZ2nx8hfAFzc*pB}KP3Ui;*K33|gN4$xQrrKcVvBp42hrQshZoQ_3- zEP7?;9>_<)OIiTkYJ|_%yy#F(@n5quXmPhC7{evWNP-uM?oUd^jlPN)n~%b@0Mq6E zpNo3U#>xKi{93rZl=-Xt{-4lN{vTlaSISW!7lG;_Vs;5^2zUh)E~L~;Y<6O{R7wo4 zQOghl736B{0%B*(D1p0XvvK_uxgl`$r2q%xLBJbBg`ZgAmURFSlseu~;XoKmdQZcq z?BL)KpF(V&lG##D=v#>q0af~?sXEYs&eog4>(%Zw{i(Kfvy)z{*$Jxjth#mULxatb z>}5s?c7VF#uilsM23iyMlXsVt#$1WSf?^0%zVUDlfH5LC9sc;3Yzf${klB)Z({6Pj zK>GkJp*FS*dQyc5Oj7>OL4k6gh-{`kryRGw)0%!W7{liTIq^g3%u0z?#{uAn+u7>A zAJYvu`nifz&=0)@v=Eg9R<-#m_J2--tvbHd%D)%t-xiEOMtL5)DE*Qs%ivpJcN z?YGXi;thPk^IzXyluB_cv_xi!HL+O>is#{r-lD=xXW}9MPN`eD{2RV*cRx#XDPuFpH|L5 z=FfbBx^kQUK8t#`#Bhn`{dQs1i3ZyQqDbkxv;2T)cYXQ&12U9a>K0r12r%X$bEJb> z2a42luojiT5>}B|-%9M5HsiGJ&(x|hdrXsfHouowzbf;*=Lk_4Cqh$@62f#k-~85f z8f^>k)5Zc{W$FPyIQ=(9_1WFqGeunPJwB8Xhy;)?33QidkQjzaWtrATw_IujzCyI% z4zbM>DEFzC@~mzpm>&`Q^MWnto}_|l5Fs4|inV`#v-JQYO{&P;D#WNT1_-rq{7_#u z8_LA?rkeDMd-({nTxb&L5$yp7I@os-phz`q2qoI7Abn;Slbif^#3e#a0;-^@E?sJ@B_YLa>1~p_A z6kY&qw(CpjKZi*);4;e$JV0UnwSHj<;bgka$`3drE4Nv+@B^%|Cg-&9ESeJ8?j6MGD0B8l^f*N!0QL<0>#aOQw+U1gJi9bJMiI6Q*Y z+qlUg4K&SmSlj?r-+f>li6XQ(&$jUcp*#&7%3$2;tqJ^l>>ysG0BRcv!hRuO^oi{S zD!_bO2w1{+4sBL6l_MY->HsT6NCWLbiV-)te!QW!!2r-=|ln^iVQ78!fsmKa_ceCb(ohGG3j zyFi8YYwe=?vk7AA13NsK=)T9hw(cKfw$BmeMSY!TraL$70z?iQfK?XA4_ZV;ejBu8 z-`O~ce!4NY)=BT2C>3y!8ZZZy;*Jnddoce;3Y3L|=0@-@c1>*{U>7+x0}l+5OE>~X zi!CtQNFeX6H#>j~lnms00hAkpC#CoJ_JBDSQ(NJ`MibQauF847J8pA%62mvQA#usw zVgH~)I@jL|EK|92DNR3kPHBaRXKE0c2#K5AhS!}Bh>-=o;{s*{u2*GjTLku7?`V5a z&|a%{k*W&{09cRJaXx*ZG0klFwVTviMU?}zPz6F_OY|Mlm&vuSe##P#fYHRG(gZQ- z0Xl3kz?KRHr&oh&DCyP+YoYmTfP2>5jsHn)w>wFY_!Y!Ow5VN<%@8=QSU7%5;#e zrohwV<}u5G2ZfI1=U`G+ni1fo5~zM6aE?e(%+Z4fGRIBw>>1brq@E9}5c-Lrt7B|6~+p*a(n z)6N}b^0r0CX!QCt?&P|sJv+8b){1!%)Rh;VvLl6+MP{2KH!CN~WV)J$F=5n9$)LbY z!xPi)4Nkpokfw2fg@@p+m`|e7Z~Esr21W7ZRg!Og84tvoWRENEk9z^>O{PIHG5Ar? z$q~O*UurBK^9~QJ6KcZE4ij~0JjK8e5f2*V(2^Cv*-~^^>KQWPvklu@0d~byW*z#k z2ZnG(F)A@=^xi?DClp=f4E_+ImMNm?ySuuStq$*Bf;f8H&20my>O#Q`Ok|z|`W4Ul zIDnxFb}>)L7=gi4;shqmcJ)nWI*DW^kX@FmGQgQ8=Zy8(SB$~J9{4*v*u$?0o-~e~2R|!J!y5+gD zt~ZRcU!olqk{;^Lqt&Gadq;s6dCrbu1>eXO(zCE3vUA;maRWy#CgeZbSgbMd#I!59 zeZY4tq*!AGoSF5xC-~8IS{~y^=5HH*f_@1e0TT|;ykcbtwk-}&2YeIU3^|3Mk%SqH zd?F}{l|iM7dVYTcv$KLp0lvEo7tI6XF6$-erQ@MZS>PN=&?(hQV9QEk0|f0&(Jg+0Xvr-kq%XQ_@#+-b59L| znMk^m5Eppz);=Q-;RJ3)E6?I0B{h>27kFz^18|Ko25f5*lc*}$h&Isn{;zhfG^)vK zi(;`@71Tbo3Wz|ffK{+E*?=IRMI)ku3Q~rsl|V!SSY{a&wbrp9A^}7JK?W0 z^>sf3aky96qGjuYcEVIk2jdq1x^fsMA%9o+z5LmB&SKrA?qL(s_eszWjf7D@_h6z) zs$w7$*!W~y^r5Ot-;-mnX^ynaX$$7@FKvjBeworRzP@-r#sYevp+PB_YH;}HokVN~ z$#yKpHLUz8G^g!gK-DYaqyW~ebd4`xObfMzJ<;a?nehlF)<_v zJ%L9t3q8i{CV9VaQ+z+qd;MtnE+);t-P2^FII!&ezLuAsj#92@7xK~Br4+sGru`F$ z3oAI46WIyws8G7bh!XObTiqyOG;Xb{Z?A5v9NrTl9c$L?t{5p!+F2*^WS468RAM{K zKP6J%>8bxnw0M}#@cbGbZ$hD4b~SvWH>gg;p$&!fWo0@OH?8~C)vQtE)L%j%gmS@< z&Y$oQ>EYgxC0xjHDrT}}XU!`?$9xishrIyT2;UQ@|ud>hmB4z4*Uez1c0sAU< z7{gSv(bJp(`9jkuu5jgX-3M)zuW}1^_EmpNo_7_uNmTi!*riqS(7_ZfU4HG*72+sm zPwSuU&%0iK?oL=YM;e+O5p`ZQf=_)ou3Yo(xd#2$6sznV(pnL0W3R2jWr`2w(^{K0 zsGb=EFYghNeN|r#7H+eKch$C&mJ}B||6m|ugQkx*qhT<~ZV^?mX1v{_8Nu~#W7hnh zi8DfB5EhG0`_>y<<7y>DE}5{FHfN#szKk4X%7nxF5$|e`5(S{+ZPN1qXzYaglU|9M zh&3Pz2^`ssurh1X~k-0RoPF3d>_P$0Zeu|+M})*sad=4FZ-;0u>^Jy$Y?v&Ui{fof-@>B}`} zGxxd%_dM1RE@^PP36xHSm7vWiWuTH66pe}(89o;3hBqtdKdz{(>6mh)xlw$cNA?b8 ziZzqv3IdBa)_J|@Fr{Vh_6>Z2hK!q8jzU0~$B)gk%@EkvkWLVxcB6;!tiMtBB&k-( z#0qcvXCTbWXAMqy?YK5T$V89B6b~mB$xFHV^BP-da!CK?A4RqU6iuqW8|lHrl+GFi0h_zMH}~eHzSArfJyFW+TD{}J0fxP$ zN_n%#Kz&xM()1+|n1?AY_9U?`(Tx}miKoiWudC||GOWw5=g?@R&CPX8X$xvU*Ts%` zfitmaSg2rZWG$=V6=_nKSGlk11ASM%(yR=>r1-(P3c7|@M0IjGN8L@rye{ZM(e|d; zn@uOL0!uKbP$0eB=~<3*;4b(D^Y-0WgS(VsS2W}$MKvncglwcRCkM*SADmtzeg8I; z0UBJBkjQ`fGb_otLzk;)R&!P!4_HxeQr@>C44=!~PE?e&dNyB3s5>$-a_>n%^6#%p z|3~W~J7@DCXmZh;u)e3mZ$U0z1?Mc6WBiYEw{5<#EDmHit&;T)uS@Ucs*xKwz2@BV zyt#{Yw?JtWx?O?>)^gN<4n>oAXZVOdTGUP?Lp_ZDl^}A8$z79G41_9UUK7_bV}ElY z!wo-OrSyk@bj!h8-fKtN*D^A>@!B(9;D2?3FnSqiFk!j?ogBOP`-BsKnwX5X~Yrk;1j+o|zr)VXb7~u0Q5Ad!CHdMG#}c0xL2sXjjsP;n zm;^Ej^`+fk=*O)SP5z#z3*K=W3`Ne095%GuAGpvEM6_76?1$Wz{AVSaPQeOLn24x<=lsp~9M zzCw8LNM8)aF%KUYXa*)QGvF5xi*Kt=m_`HVGTNdnb6xmCc9~8kWg;qW#Wbl_&%ctC z-N3Or$YpUrcF$%;Uq7Ok$ZDu}1IK;`3VEeWeh*X=olu79SzM6Y5AOAeP(V}7yHBtA z!BK!{Xflq@idMeMFu%bUK6mJzdrvf}JTnJ7lwd40u4iW8q@8tekoM7JA7X7^Yi>{S z==-V7$PkjcsV3YyD_Cy+veiHy@t_CuE7-PaCt+StJu}*gd-wxr$?CWWgofpqp7W!+|MXn;2W4=)Aw%c1i&G9<)Gg|3` zsq=KOEP5<8jRgy{Ib^xl5c?MHnv0CmD3{2Hg$mom1lj|Ia zzIeRh>@~AONmgU&<=I$HUTGJMi3ZqVOW^j_hj^^%~zEEnNX8*HHY#e}rhg9>MtaerZ)tOHx_Q`8t2^ZJDW6BnITtAo| z(~LtYI7c_Q?OXQgyULz@9&!BTd||)%3pKcV#s+^%{O@FB_y e@<-(I;%V6hjG5mTgy*G_52#;l-^8}uckEwz>-%s3 diff --git a/pyWebLayout/concrete/functional.py b/pyWebLayout/concrete/functional.py index 557b139..3e168b9 100644 --- a/pyWebLayout/concrete/functional.py +++ b/pyWebLayout/concrete/functional.py @@ -153,7 +153,22 @@ class ButtonText(Text, Interactable, Queriable): self, '_width', 0) if not hasattr( self._width, '__call__') else 0 self._padded_width = text_width + padding[1] + padding[3] - self._padded_height = self._style.font_size + padding[0] + padding[2] + + # Size the button from the text's visual height (ascent + descent), not + # from the nominal font size. The two differ by several pixels - DejaVu at + # 14px measures 17 - so sizing by font_size leaves the button too short to + # centre its own label in. + self._text_height = self._visual_text_height() + self._padded_height = self._text_height + padding[0] + padding[2] + + def _visual_text_height(self) -> int: + """Height of the rendered text, ascender to descender.""" + try: + ascent, descent = self._style.font.getmetrics() + return int(ascent + descent) + except (AttributeError, TypeError, ValueError): + # Mock or unusual font object; the nominal size is the best guess. + return int(getattr(self._style, 'font_size', 0) or 0) @property def button(self) -> Button: @@ -237,11 +252,18 @@ class ButtonText(Text, Interactable, Queriable): # Total button height minus top and bottom padding gives us text area height text_area_height = self._padded_height - self._padding[0] - self._padding[2] - # Center the text visual height (ascent + descent) within the text area - # The y position is where the baseline sits - # Visual center = area_height/2, baseline should be at center + descent/2 - vertical_center = text_area_height / 2 - text_y = self._origin[1] + self._padding[0] + vertical_center + (descent / 2) + # Centre the text's visual height (ascent + descent) within the text area. + # text_y is the baseline, since Text renders with anchor "ls". + # + # top of glyphs = area_top + (area_height - (ascent + descent)) / 2 + # baseline = top of glyphs + ascent + # + # The previous form, area_top + area_height/2 + descent/2, is only + # equivalent when ascent == 2 * descent. Real fonts sit nearer 4:1, so the + # label rendered several pixels above centre, against the top edge. + text_top = self._origin[1] + self._padding[0] \ + + (text_area_height - (ascent + descent)) / 2 + text_y = text_top + ascent # Temporarily set origin for text rendering original_origin = self._origin.copy() @@ -360,11 +382,12 @@ class FormFieldText(Text, Interactable, Queriable): # Get font metrics to properly center the baseline ascent, descent = value_font.font.getmetrics() - # Center the text vertically within the field - # The y coordinate is where the baseline sits (anchor="ls") - vertical_center = self._field_height / 2 + # Centre the value within the input box. As in ButtonText, the + # baseline sits at the top of the glyphs plus the ascent; centring on + # half the box height plus half the descent only works for a 2:1 + # ascent/descent ratio and otherwise rides high. value_x = field_x + 5 - value_y = field_y + vertical_center + (descent / 2) + value_y = field_y + (self._field_height - (ascent + descent)) / 2 + ascent # Draw the value text self._draw.text((value_x, value_y), value_text, diff --git a/tests/concrete/test_functional_text_centering.py b/tests/concrete/test_functional_text_centering.py new file mode 100644 index 0000000..86d4162 --- /dev/null +++ b/tests/concrete/test_functional_text_centering.py @@ -0,0 +1,124 @@ +""" +Regression tests for vertical centring of text in buttons and form fields. + +Both placed the baseline at `top + height/2 + descent/2`. Centring text whose +visual height is ascent+descent inside a box of height H puts the baseline at +`top + H/2 + (ascent-descent)/2`; the two agree only when ascent == 2*descent. +Real fonts have a much larger ratio - DejaVu is nearer 4:1 - so the text sat +several pixels high, hugging the top edge of the button. + +The button was also sized from the nominal font size rather than the text's +actual visual height, leaving it too short to centre anything in. +""" + +import numpy as np +import pytest +from PIL import Image, ImageDraw + +from pyWebLayout.abstract.functional import Button, FormField, FormFieldType +from pyWebLayout.concrete.functional import ButtonText, FormFieldText +from pyWebLayout.style import Font + + +CANVAS = (300, 120) +PADDING = (6, 10, 6, 10) # top, right, bottom, left + + +@pytest.fixture +def draw_ctx(): + image = Image.new("RGB", CANVAS, (255, 255, 255)) + return image, ImageDraw.Draw(image) + + +def ink_rows(image, box): + """ + Rows within box that carry text ink. + + Only the central columns are sampled: the button has rounded corners, so the + page background shows through at the extremes of every row and would read as + white text on all of them. + """ + x0, y0, x1, y1 = box + inset = (x1 - x0) // 4 + pixels = image.convert("RGB").load() + rows = [] + for y in range(y0, y1): + for x in range(x0 + inset, x1 - inset): + r, g, b = pixels[x, y] + # Button text is white on a blue fill; look for near-white ink. + if r > 240 and g > 240 and b > 240: + rows.append(y) + break + return rows + + +class TestButtonTextCentring: + + @pytest.mark.parametrize("font_size", [10, 14, 20]) + def test_text_is_vertically_centred(self, draw_ctx, font_size): + image, draw = draw_ctx + font = Font(font_size=font_size, colour=(255, 255, 255)) + button = ButtonText(Button(label="Save Document", callback=lambda p: None), + font, draw, padding=PADDING) + button.set_origin(np.array([20, 20])) + button.render() + + x0, y0 = 20, 20 + x1 = x0 + int(button.size[0]) + y1 = y0 + int(button.size[1]) + rows = ink_rows(image, (x0, y0, x1, y1)) + assert rows, "the button should have visible text" + + gap_above = min(rows) - y0 + gap_below = y1 - max(rows) - 1 + + assert abs(gap_above - gap_below) <= 2, ( + f"text not centred at size {font_size}: " + f"{gap_above}px above, {gap_below}px below") + + def test_button_is_tall_enough_for_its_text(self): + font = Font(font_size=14, colour=(255, 255, 255)) + image = Image.new("RGB", CANVAS, (255, 255, 255)) + draw = ImageDraw.Draw(image) + button = ButtonText(Button(label="Cancel", callback=lambda p: None), + font, draw, padding=PADDING) + + ascent, descent = font.font.getmetrics() + assert int(button.size[1]) >= ascent + descent + PADDING[0] + PADDING[2], \ + "button height must accommodate the text's visual height, not the nominal size" + + def test_text_stays_inside_the_button(self, draw_ctx): + image, draw = draw_ctx + font = Font(font_size=14, colour=(255, 255, 255)) + button = ButtonText(Button(label="Save Document", callback=lambda p: None), + font, draw, padding=PADDING) + button.set_origin(np.array([20, 20])) + button.render() + + y0, y1 = 20, 20 + int(button.size[1]) + rows = ink_rows(image, (20, y0, 20 + int(button.size[0]), y1)) + assert min(rows) >= y0, "text escaped above the button" + assert max(rows) < y1, "text escaped below the button" + + +class TestFormFieldValueCentring: + + def test_value_is_centred_in_the_input_box(self): + image = Image.new("RGB", (300, 120), (0, 0, 0)) + draw = ImageDraw.Draw(image) + font = Font(font_size=12, colour=(0, 0, 0)) + field = FormField(name="who", field_type=FormFieldType.TEXT, value="Hello") + renderable = FormFieldText(field, font, draw, field_height=28) + renderable.set_origin(np.array([10, 10])) + renderable.render() + + field_y = 10 + font.font_size + 5 + pixels = image.convert("RGB").load() + rows = [y for y in range(field_y, field_y + 28) + if any(pixels[x, y] == (0, 0, 0) for x in range(12, 200))] + assert rows, "the field value should be visible" + + gap_above = min(rows) - field_y + gap_below = (field_y + 28) - max(rows) - 1 + assert abs(gap_above - gap_below) <= 3, ( + f"field value not centred: {gap_above}px above, {gap_below}px below")