~r^_d*AH6mb5qr@k;tc1EAKJ-2
z>7HE7BH#GUhug9uXfJ&MkVW-<(e<^XT?&TEbr)uxW)NG=MkkZJZfr7ohk8Ao;K$`t
z| f}YiHIh`B(x3TKBUB;|KUtyw5;6P=HC6D0_
zUj#{&4Z2$-s2W@vdpM8`^H!<51Q1q3K5moh<*(L%`Vq9Fl-G27s2BxVs=$(|=hdrx
zX&CJsqLR1${JO&Uko&a*CA((LpdoXoj@6zZ;_@T6j|L1&%295jG==0(`A*Z7Ph+|K
zx<(l9i`@=`5rg@1@IbQqQ~45!L#9X{)Xw@RPVM&AaZZ`uf*nvYbH0U`^pnR^RFXW=
zc51j)1m&3lwliwrmrpW7UTeB1R?z@Wi^BHT`FF*r2Oq23rNF37>nCKSbZX##
zWQ8B;v3o|D}P#0cEqkW5xYx=w-M=3*wKlyzR@xYaJ(N*
zcfQ|9nrS7v)|zuxa%K(B`$XP123;jDN6>uY0%(TQZhh%zC5G8WnzE@Tt6e67NshfT
z8ooT){dC)~LlX+1fQn$drTFX?2n9*3
zZKLjehZTwaRLOR;4aMH%_@u+_$*Kf>=F3^CyN&aJ!0Y#B98W1tEv6JO#%k4Z)gTwI
zwf*Lbe`XBao5Qyoe(TDLx7bqWsH$!ke)4`kyVxwyzZL(j!5$PytoYBpw#qe0E9!+r
z`e4R;Vw;Z9+X1wOz>QqZ`i5QA4&8K7R)6G$6~pBnc!9ybP|cv?IyWw_cY4c-83XUG
z)Z&*@I$<1>G~MCXL
z{HdH^^S)dfRy2IBc?+`sF0xf)-z;>H*;#KR595>!v3uv$;*U?HDmu}PPSDtZk+D6v
z84QSy4~51O?-U8+&5@gQ*ll8V>7&pS6%&NnB%9A&vj28Lw5h3!m^2~(LbZ{M_I!tm
zT-pbD*g!k<;7!TUxQvbUooccRYJAwFKC(32{#>9(mF()*H?2bQr=^%w9!ekp-z!0o
zGM=>%Nzbhhdm!p(keC9Y?detY?k*-ikFms}nIiyVZt!32TXi`7$D6s!cYFLFH9z
zAK_IEYqzc1)@&ro#E&JnAhHp6Y6m^!69SUBMpBs>0unx~==R@gOOCMsb*l8q2wT8W+U5S0xF5{!Ph
z>_oxLaD6u4VZaytQz>XrkNv{bHmrndat*QTaaf;%)lZH@jMNZAVjbVQW=3~yGxjY&
z%UUUFxIz+cg_UTLAHN8|YY(ti)|dsMyTvex4I$dd4n)H@G@=rQUb_103KG0OSEDIr
z2ZQP`%}k3Irqw9D(cOIW*L=(G;Z5b`o|n}in9S|+<(Dm&IC_Cg44~Hq`J`lKvKFe@
z1Ss4VH3mY-|79NvkM&8sg;BQa4w$!NRDXt~HjiAUx-#^lPTW^2-)q5}G1jSaP<2zo
z0~16QIblTMQT+7NV@k6Jn(`I{zM-+b_=o
zGx%OEvEuODD3rG|A};3c#8YZB5~kce1hFgo*-7H?HP;{}G&3;xPnwT%!RGH`zxMXF
z*1hf8==Hh_$*ZIHhJvT6iB;KHZK*ozvZnRPG9bJROO(%NxBqv&{@Tfq!0Wg
zK^AR=9&4TYVVc0PatyAc!oZF4VTG+Ht44PdbSWh2Zxu$D`$-bf_y`^B1WJLa-(N8E
zjH3#93j^o*H_9i`cC57QfEMPofb}NR@ohJ>gLj&u4`jul*tz6)0|@HlDTqX2;Pj
zxSN;_@yxrqP9fAy_M3`OIp_QXoH6ipNUy!lqh8SmBf}S+Q&kxN9+DL(6HIxrKUfJk
ztzB4Nvaj!m3sNe*3VwDu@J>kB
zgX$3Zzne_#4t@awiMalI-iC2LiPj3MeC%_H{6ySeqIBUvUD>u&R??rl%wl|Ib0UoQ
z#5kA~ad%R(YZdafmVOmt99@yb_&8Cqm)|klcHHWYaglC%PIvNaqr71_0)ax+)zz7R
z(@RRzflxe64Ks?HnS*C!Vp67hW`iQ3=#U#wI6;V!jLe$(Kqcgja1UIDnychO2y#-`bMhEzLI}#on-juL
zWDtd1FI#lcK({wS`b@l!j0lzpJp|p27p#qSAG`EW-D$%QUwljSenpEW8os&F8U)O-
zjAW`nYBpsOw%ROj?XI$}EmVNfXr0&kjsGz|H`qnp6p-o-$@w{$G#f!zp_(*0q!PBT
z3cBh*?r^4z8Cct{V8;#pt*VmzUm}GS5n}c>ZB0momn7}y78mJ1Wumbw((shG01<*~
z!4YS8wy{a0h52IWpb@6%rsRAg5P;`3Epfuw{b`!uvY9b6AgE=S=DWH64
zd23L7fJCB@>Km(mNdGwyOjsJw3~A}F74rhNQ7Y2R%8Iq{Rq5ELmN;ErNz0n-8LPP;
z(y#oceG#=vMbhNg?*MnPUQURV0!L-9p!dBa-xWyz6%oALlD|6jCq|-wT22t;
zL9FfQZiv<3LN_dzn^s0|oilfdR-yUiQ`7zUwR&G=^ddbQkijlgpI=O5!wN4v_t9Pa
z7Z{!XL7(TN*8$wGQOX3%fj>^y{3Swfi6GIYvYcrb$4s!&wOsYXDFc+43`V8EMM
zzL%~xyb*?7Lx#(Ml8LvktS!6*Fp*b0f_qQtY}IJgE%v9kS&Qhb<=w}JHqy*El1&-B
z@2HItCG#|Cbb|(a{#27dKLML@E9u&g`M2sNnuIW?=@qz?&XKAO?j&wleDaXfI_oyX
z*z8IMDGK)%xllEH@xY&BpoR6)z7#_@k}G${1`
z*J7Of-}oik+=-TVG28C5UM_>1&{YGnpG#IO_C|vJq?}>B&^d|G+IzqXfJtxf8~_K(
z?rXj6KW{4~Gfe@3VxktW_GPf12kl0aUn3io8e$CNT8ZXLKl(Sao`6Eo4UGca`GHid
z_y~eaFFPPs(a@5`R(24#ONxrE_87+usoS1EC<-?Jd>{=Vm_hrzG60#yYowU{r{tf>
zffH&nYbF|zSF&?1CH39$?w`sz`h^Si|8*ip63~gNbU+hvvrmb?O65+>w_@%7?V#AP
zIeEW5njq4-P(2El`QEO94}Fq0DiTVSbm|9x*K#u8HqE+c@8_>=u82@Q-VrWqM;38z
zeT<5Z9MEAFpelJAADa;s=*OuFVKPw0lmDwb8n`EL_m*HfFR}OZn}n?n7ksKY|xsNXs~-suWPDWV*vZxOa!B(LP)be
z?5~Zk=pWZk+F?o^+aj0UFmI3^gtfZ_pwa%`bnE#JN;r{wXNg|y%UOT0*mKP
z8Ty^YVq=b4F-(rwjFzHYg`}83%B;+{h5}BUi_1kyRG(h;gQXJPt?NNlDCTE4Mb5b_
zuFYL(3)9=;$=~}lj@EPulPz(GM@~rwZlAiv?2jzqes=)Oa4Fk+c-ZHxFEFR$=<1P?m63n}1-CQXs)hh~ct8y@-W{1ceJ}g0$kFmkV
z%$ydO@;2#a+%Aloq~Zt1a4keZQ#b!}Av|iliu_KtqO+;28;?r_h1h8KXQQe07^Yb5
zN~zFi#wmZmGf7$U_1yOEn0$Zkn3d?BIDBC-WH2
zU25>qj(=Qr#P=xyw`z%i5WB5+IcR}|mmj~(_Mnj+2(o!g6?}vL(Kr~>dl>)R@IWe8
zBBNrnE-;VG$&zHifKmB1CY2x$4^?e7xSS0B($48v74zx&?!NElNuW0m0@f>XgK+-^LkJa9#;pg}ps{ar39DnW0XFzIkjY9cu$eM=dr
zh0Hj)(^d*vCPr=!yx#%@2JD+KK@Md64dsf5R05*2Z~sx6{8j$_j%hm3mpFYH=>`ld
zx}%{sN$xfcB*zdtlRl?(=^2hs!mQdmoY{4aB3+O^XB9rA`nG}h1qf)^f%f}v-#da*
zu#{3U+K4`^AE2?FZ)JvwjRKn=)p>$S)Ui*M^yVVhayb-q6|dt8!fu
z;c)A3=wAn&gCsfk%HofRCv_~KkC_e{zplybdN6I5N
zIRwQo{QtfuUw;_dmxS4Xo*PiC+1SBmn2>g$8&Z_v*N{V@*wga8YaOZ7*`sHUj+pRD
z%W2~gVMPKh^1HZzOw*F?6CZ0D4xb%iVGQm--VZ
z)k+x3EODw-*6aC^djx?MICCWB(EFfCU!kJz`f5L-scoE7?s
z)2D!tc0C(WqD*d!yxGAMhlvQi)5?$e*1%4HWRa#VS;Fq~TNPtA8TQRJit`gnQ9i5>
za`uD8)?`QrHZV4f@&qA$qmC+aKVgyqA9qJFwt(EwruhF%nj
zs!6T7(b!`CM#okxbr4)L>q3k>^Lsk;0b%ZEak(o}U>&Z4~ldWfLFtDXqnH-C`h<#wo`OsO2>O31g;PBmMdG?yw;z4IK;pl21uJ>t#2s
zI+P8YpncVZtL%HyHkyDTLxU819pTIK`9As^;6or;)~St+95$HYa79=t)ej($GV@+A
z;Ft6vdd#E#b4<8(pfPmRnu{aprAmzWl%Z&Dwrc$NCi(+9wHNiVJ5}5whaZulK*Pcg
z@j+JP4F5<|TJMzX-(Y3JK_H`tH}$xb#y0HLf~L0jofocZh>S7H5a7?56f076SkT8I
z{aK1Fb#AfZ-l@tp#8$Komz4-9hcSYpOL
zOGS>VeW7#)^T3D0sAQQ-YYpa$qt7TX&GZt4;nNVlQ(JM9v1_s&kVg~UR7ljE`RLDn
zexPiQl{Di#_(^L)Ofq`a_TjJsQOL%vV~CTLs(-i_TJ1H%0Hhq0DCrsS?APLGrj}-G
zyhJIjY1hhHn-b8R^7;n1y75~0)Xqh3w1_e)DSpz93~ohwDx{5m6~}2K$~S&pv8w4*
z*2izw*<^2mdk5{;u~*-^mFNA&qr8+nfy%Hn}6u#+_>ah0QrTF8B;zFF_RC
z2LDgO{3VOkE4Aq(bL**TL7)ii_qQ(p*F#(!LN0%<8Bi8+(XRb1QJ-8T?TZo7p|%>h
z4JW7b6UwUquk>NW{@5Mz$iutm6WVxv1q^Ew!072H$-FfH)f@q77^rwrfoReOhHNB2
zM7-uzK2uovpa67s5Wu$7Yy2bZr=cjF0kugkE+Hqvk$hGvt7#^uH~otCYawQdaZq7+
zeX;`HFd)E@eRMCs&Z90O-duYr9B-qo4Qs3}q_(np{nr_Mk3Keef2eMt8wcpePgw~G
zAbQ~9a)o>3mDmIkc{KEhRG5JGjr~s7I|saV
z`Fe^yl9svRyqCC7w3iQD)qJbMXA#ic#XmoMCAqF_)RCsD&hg4_=pR7{8NXrwr9vwn
z7CCX|4KZStN5R|5YQgyN@pX9B;iAw}ZKgu(y*UAQ0|eR1j=5@M4hK8}=1%HKd!w4r
zUxgI{xMDFBjfE4QJE(Yz`V}xb&ON4eK3{{{6T2dQKfD0Azl(CX|sCA@%Qt5}|K+Fc)
z#|o4vE8vV7O{H8j?=XGt{`sMpzWJ9q_QeR^N|kI?K0zAV_;zk({)r^2OvbtyWmsPx^e*0
z+&WD>@zx>~NGj<5$4Lp>a8O3zr@R%u9KAd0n(baVQ~E)-paNfH+;flZvDw(Q4ttX&
z@xUbg`*#jb1lgu1NSC7d^tL(Q$9cG2INRi-;H$oS62~hf2!X!ww&8)sHWcf-W^;Y7
zFKs60{v)<~SqWl3s&|5x!;N#6jR5TN3z
zs?Okl^aQCQ5ik%#j_ufM+BfRMf6d$k!VI9!g^;+!OZ?x+^l7h6#;8}9EDAe*boYPL
zt8Z*H`ER5jwO?KfvnV>c0L<_T`{983+Wg;z!h#tXCJG~7fac#SmeBbA{rF5Jl7#X4
zU8F}A+JEnVe~ns<#{SC;TD(?)L}5$5rV{!jPbL8(+4lF+M8AJZt^B^kpD#L$!jGq1R
zp9JF-P|pEjMIrrPp;?p6(v6*eXpg`H7YinySM>tN$
zXd0GcN{GZD^Rk@HcRQ*31*u?u7+_Euuej|A$|wQhaEq-+vllG}Uuu4s80>51;Gp
z3zv=&{0Fk3^4W+9=sUQ*J@cJr9J768YOhNW68}Fs=8Y^}^L}5GPz;w?C^4{IQ|QUZY@@yH~kDV7l^NXJ7=+baJT96a|dw>fjYOh#N~T!f2^<%Uxzmc$GxW^mKnOf)B_
z$w?+L-$n7!F8}>KKSa{`T|)!0jy)|NS;QYr%M%dz6M|(dYr3ytJ>Y&ZAip6G{~3-C
z^36XL-7VME^eq2Lk$Zs1|Aqx;pJNxv_3Uhl_H?A8a7cD;L(JuJpFGXSCPusI0-MD%
zBuq&FvjGuJW@1f?Jbl-3TtROZxgR@lql4-8?Kq>Y49fEM{)ZTPmsy~U&GrmzbCkis
z&vYv~p00YMv(0qbgoQ5*!tovuc!8h7Q)sl|n}512QV3#ib&L@vBgpUVggqm-7}QW-
zq+OocbufK2upJ!xNgim^P^p3{E}0a;sZS-wWxwlJZ9y?w-QvK*^}I&{VK2ApYVjPK
z3HpTdb}&+X4HF53_To!lM=QlV5Gf$C|S|e3b(d8}0l@I&p@J9DaDMxLw?Aw|*
z^mV43%+}_I&xw1ui7SY^F=BkV42re^K%SXo?_y&`|crTSCT`y)67^
zo&ig%#PYhA{8-ID7L1+W3$UsV3GuS|7RZbuTg9D7&Ei$om2Eyz$}Ga3G6?9kn4IR0
zKg=Vhvc$`Yn}`7pJxDzis5?Gji)J>Tc|FAc)s?PqbcfdIcEZ}lhfQ(J
zG+4~%P=8<>BZdMhKO*C{Pg^e{xLK@LEF@#2AEpFQFxwdJneCFp{QY5EHp5v>932y?
znt`#ES#naWmmOyJr2;H1Eqj=-n`F3#;`=W4|5+4XFUNmy%G>=~BWpA@u@Un>bT?Q9
zAOREwBb4+X`q&EqO#|U#+=G9X$O}aL3ae{3g0Ht>Q;}YmCQL(<^UsWZZj#}?lwB{)
z&D~+ZCi+c5)n;dBS6AmZ9P(qEsAe`tJw@4m#K#J%4qmMtra};ZOPr($lmJ;5v
z9lF7`!HkI;++kMz7qa{xpx1N&76R1g1EBD)e<-X7Rw})f9&2;cmLZ$hU-fIcT99)g
zd;#IP4{Of)xHPJvk>I6#)680zJhE#GbWdYc{(Or$(ZSqjmWjS!K)j1-Y7-q98#yuA
zG8|azzyqnf{dl}uLNguU()gaYJW6;3I6dbyeKsf3>?(=sZ($s$vn#Zv+PT{d=j#*k
z^Huh68CX@rCELYgm!0Gbo8p8=pS5;yz1~d`U3!R1hD}T^yVq&lpg)%92fL3{CE{PW
zghz0%A$q=NK>z!jdjZSLBQCRO%LsK8#k{vxZz(A%``hRvRD)Py;E>Pgr;U@Q+oSm}
zNTaLn5BSzYyb@o+i~9Lox@Luf26nganvz(L9ZWpXI9m2iQ}X^;T7YqT8mesNP<>dF
z)foEs0Pr>C@egM?b{Vig!;+cEmErkAFrd8fa+`a3X+nu06~-+U$^DGZg<+-60Rk7#BbW3YiJMjoR+4u!uUPGo746R2&y0*-tV;=RRk*
zZJ-j2qX|jGUJj&>R^2K1!J1Lmgl4F7intRmrui^aWd?eZljfYK-u}5;MkY>`47bwV
ze)ESQHJObVz_5XNGM^2<+`1gZ30rDzM!wui+-%>N+?*j_zoz{O0RBuH+8y%O>3Q_I
zH_9LO&qT=gr%ky%k8vR#nY-GDDif}>3mFR4Cp#?qn1Z>#GVZK)V5uCSNmHb^XB-(Rtavt5rHbr-f8yYCGW?rU9;Z}RXK9zGZo(o
ze;hvd?pNqJ&HYKYb~`2smo!rto>>O!w`C?`iJitbM`}tbE{MK?VdtA)NZEUJW~jTd
zUtV!@?2S%tY#L?D>1Dxu=Y<{l
z^89h#nxXV~8rg`|n>K%p@a7agNS#A8W+GzgKwfrdg|EFItZAyfDZ$V96I%^&4JQQx
zoN{I;O|MlGdRYTHiPS=
z6YB{%bMGxpZ-wP$30`x*)ubIN-8?5&O6IUz@UTMe1h}DVw;K|fqD$1Taf_+U)D*dD
zC$lc2?KZBdymK|HQ&>?!YX^ff`u2E`htDaa(<4@$ftgvTVb!ChNgTG#e;am{pG)+C
zrigRqpAQ?PuI>{?2cVY)h2j_LaIH)9JO$8?zcftHs;mDzu%v%ZV)9ffQg@q0GgOvR
zUrOc~06xSL9?DIc%^7_u%;tL?{s^z>I%AW!f2lv_r-AiaycX8lqak7V|Yip5JY=
zFkqD#Vmw!W?&7&ythCN2k-@FLF~KOg!?g8mdU)>WKeKP4-*%c`b>2iuNl(w`hdONe
zJs{{vsN=Nk;G(ajp^^Gc3J$<0c*P5vEqsr^e>yI79#WcKktrtkHNyMPmCu(Sc?}al
zm=0G!J>Y-tCk;kb3Gf~-3;Kuuk(~9$nhQXQ0w^!x)gcFcH7lhj|vaU&KimV>GRLFB@z1~#GZADw^#Z&jbInwci3d!S31+qSV
z&yneq9Jr|}`Q%~AokX1G94c+)vFUc#%pV=T_GWzdM4~cr%3_LW87JkMIx0bw;K^P2
zmR4(G0j@OR@tEf#TIc1^bJHqiFl;DorzAHNyxm@V
z&Z%|j{t2>Ha4VNX2Fpe5FMhV`C4|fKLAuEnjbGzObD{R#bE(PB2}UfdQVkV}D{oV+
zlCsy=(Js%!ax9%z#Sgm?ouAM~r5lm)#HJgRF6QhU`c=FB*89lQSM>;4GZDq=XvjY&2f3o#rk%xo$MW
z7IpX6liVMMBgdW~>_qQ7@iAr6&jX4YhsU~VA4hm@F_8vq*`MZw)ozIOI;{T+OFB@;Ys*c}O)a&})=ZC#}xy#I7PREkqx+
ztkq{9)%i|-YKB1mVoGj#H_zJ`zu_wMu(C6Q
z$;Rw+Wr-)N1$*1Y)S5E!wwL!BR>aJ9@h8}~xHqZt&5%3g=G0D-lMziwAgn!O_^~aU
zHvTn@{c{s#&cp2FqAs}HDP!_Wt+Pk_-DGCIcLs7B)^?j)Q4H8>JhiFCl()r_+VF+Z
zaR1LJ*F*W$r5f6Z(_M80k^)m2(@|Q__8Gb-jhxLUlgE0hz4XTQ;7ydv&jD)0!Ijzg
zD5mcsyWI3=X?K|J<}9eYRn7ws%VUEcTozQNSWmNZ>v28&^zLBS)V7aIc9{sSJK*aY
z`Nr6;VJvCx%DBuoXZ9D50u3YglPn}lT3ne~;WN+iSi~3h-j26C2GuMI%EYkQ5dm|
z>FfwGeUsxpWSGv)0tK(?$?`-=v@Jay!h7WH=xO4da6)$&IeOk6^VxK1rcZ=bc*^`V
z_;YuaPFCcyRp$uU^6VtAd7E>$bB+$V{%(m*CKaKs96fVjA2e%<6oMQu)
zM})EiA4dkO*OMSkg!bs@28lSJjc&6t6*k}b{NaJNVFua;*`ZJ$Yng{bGll$}wum1i
zlRN*=(r!n_d>Y}>0{9I_w{^&q_#NdZ85<`FIM%+cDc6+(OOr;t2b-VGd%~&AFMUEpy>&W%^ECW^mc6OqUnwvzhN86M2=J5p%O3IX4;Jt
z)K`RY=}ihBVIp{$H2Chz!53m#ozW@HbsnXde_;T3UtaU5V75guQS>ON2;TgBCUri=
zYeANM*-0!`jEt+;>@XeSpC~lg`FJnsV@+PK;DXYB9!s~fQ6c4YHY&AP<=k`~dULnb
zT00EI>NjVFT*rj%N|d%vX5;`v)SgQiXqw|P8w1rnfHS$$0Nc*mFD9
zyi0ax?akqgz3Q!x>6&)v=WDJe)`8`9&JvZiYMsJg*5qu?`vEZ-+Hg1M!@iT}jnfd#
z>$n-K&&R9TvpSx>=f_kphde(|x$eg!jrK=ZZn@o$^f^o>3#58*fH!6NT?pInQrpEm
zAA47`F09&xn(8g@IsgyCWH`m~AGI>G=#j$xK;6zy@U(|#-#b%o--*PUBmykCOxOUX
z^6T%Iy9*wdYc&@j<}tnAh&0uGrrKuFX(+bt+!>CW{5*>gXJwun{<$LCu|jdCOiRL&
zO6J}uJIk|OT;t-aLIhHmeK8OtRf`41iAT`%$+gnGU^8|HzC_39WH7f`;l^`3#&OSr
zL$Ft=7ZN=eN~-BRZ`)ht>+0&dM7%F)-8TyyWink)esbJOJXe1pAg$iy*9|uC-FbY(
z)q=2#gdSzE%)YpS`!0MOIwOsZwBqU%ej+dCBqbhEU)w!3*SnP&>sNO>*6ZLtv*dTS
z)YTQeXgAvm6=rt<(JMa24Mwk8@0edT1QO0trXzD~dAy)`gy1i9Y8BC$-AM2F_??k(oByn$()g|FR9C##jKGBUZsSd8IyZCZvtZU=lIr2PD
zU&U43Nxpz*qcPFyw}pM^*)Qc$OXj|%e&kH~^^|u>bE}bdXw;5#=XROqwgZ>5!L!@n
zr>Sc8MmCj-@m(|(S
zw|;YEH^k=N9TJi29uVo)7CxEJ`~Lzr1jzey{5@RZyeR(5ZOIEhzZo4rHjwwTj>{Lx
z;|0#g;->eIln0Uz*_vlnn_anlnJ!=XK)WFrXH0m!_aO#SI>@`Y=9+7CKKRj(ex&UP
zVoxwmYJ2S*_KoY;dHGK=BHxhjLf-4UKlus2+j;LH2kW=E*T(*Cx^@$r*?97QY&OU5
zT)vn2MUI>uGzSO
z%QneN5_3kv&z^Z@YEI|*_ba;-IDxk-?QXlZAaYVa9;S~59!B0
zd5`0Cg)5JDb-Lu(-vWQXx$RrG7Um_B=lI2o$NZ3vFuTCThkg-~k#r!%P+(8wgVq1o
za;`03ERf{mkQXZVY{6g)5wW|lu%HX11^JNWS+*-ZA+Lb<4$s_Xrks8uUbwuJ5cf+*
zj@C&*E8&xWY$n0f=lX=h&(BNcDB)S;7bee0_~l&eNQPO|g{C|w(ev)Hhx&P`faI^d
z81KX2!q0rP61Mj*UD3~ap<|o^OrA3r-e1Pc8WKWDB`@S0DE{AzyJYNZuep{s@rNK`
zyYl|~I*p_tT;g!$r4r#s-e+_1`zoiYi}q2RWXHw*HV(tub?fL_&T#AauiTgSAb#0&
zl=w;t=nTmg@>(@&`!fC;NE@}t;j_SN)+|}3oZ%M+y=ho&N$4B0Yq>;QQLBB`#
zN09dsRCbi?Kk
zar&{_{JhQ+{7?LoG!ZxRZQS5sH!fYeOcyWCawX-3Smz7#!lWXTmnUwBM;q8(`DWhz
z@w@uIJg;qf);Z6uqie3chBj~2s!#DjD&0pmTPVxYkb&@WB&Xw1D@4c@JeED7%7rcxo&)P~v%D11o=|)<|--oP)6=4#WxqCXWB;dayn*JWqT@J-nLHurfa=2n$4y0D1p{-)bKgqJAeK>=OOvFN|hMDB`&KJ{eR36@
Date: Tue, 22 Sep 2026 09:46:03 +0200
Subject: [PATCH 19/28] Fix spooler filesystem imports
---
PrinterManagerNativeTool/Services/SpoolerService.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/PrinterManagerNativeTool/Services/SpoolerService.cs b/PrinterManagerNativeTool/Services/SpoolerService.cs
index 2d5b8b4..752e0e0 100644
--- a/PrinterManagerNativeTool/Services/SpoolerService.cs
+++ b/PrinterManagerNativeTool/Services/SpoolerService.cs
@@ -1,4 +1,5 @@
using System.Diagnostics;
+using System.IO;
using System.ServiceProcess;
namespace PrinterManagerNativeTool.Services;
From 9bfed2f19e021480a62d918428f55f4661b128ff Mon Sep 17 00:00:00 2001
From: lukethehawk <98876950+lukethehawk@users.noreply.github.com>
Date: Tue, 22 Sep 2026 10:00:56 +0200
Subject: [PATCH 20/28] Fix native printer buffer lifetime on startup
---
.../Services/PrinterService.cs | 57 +++++++++++--------
1 file changed, 33 insertions(+), 24 deletions(-)
diff --git a/PrinterManagerNativeTool/Services/PrinterService.cs b/PrinterManagerNativeTool/Services/PrinterService.cs
index 77b22b6..a27b30b 100644
--- a/PrinterManagerNativeTool/Services/PrinterService.cs
+++ b/PrinterManagerNativeTool/Services/PrinterService.cs
@@ -16,27 +16,9 @@ public sealed class PrinterService
public IReadOnlyList GetPrinters()
{
var defaultPrinter = GetDefaultPrinterName();
- var nativePrinters = EnumerateNativePrinters();
- return nativePrinters
- .Select(p => new PrinterInfo
- {
- Name = PrinterNative.PtrToString(p.pPrinterName),
- PortName = PrinterNative.PtrToString(p.pPortName),
- DriverName = PrinterNative.PtrToString(p.pDriverName),
- ServerName = NullIfEmpty(PrinterNative.PtrToString(p.pServerName)),
- ShareName = NullIfEmpty(PrinterNative.PtrToString(p.pShareName)),
- Location = NullIfEmpty(PrinterNative.PtrToString(p.pLocation)),
- Comment = NullIfEmpty(PrinterNative.PtrToString(p.pComment)),
- StatusFlags = p.Status,
- Attributes = p.Attributes,
- JobCount = p.cJobs,
- IsDefault = string.Equals(
- PrinterNative.PtrToString(p.pPrinterName),
- defaultPrinter,
- StringComparison.OrdinalIgnoreCase),
- NetworkHost = ResolveNetworkHost(PrinterNative.PtrToString(p.pPortName))
- })
+ return EnumeratePrinters(defaultPrinter)
+ .Where(p => !string.IsNullOrWhiteSpace(p.Name))
.OrderByDescending(p => p.IsDefault)
.ThenBy(p => p.Name, StringComparer.CurrentCultureIgnoreCase)
.ToList();
@@ -75,7 +57,7 @@ private static void RunPrintUi(string arguments)
});
}
- private static List EnumerateNativePrinters()
+ private static List EnumeratePrinters(string? defaultPrinter)
{
var flags = PrinterEnumFlags.Local | PrinterEnumFlags.Connections;
@@ -89,13 +71,36 @@ private static List EnumerateNativePrinters()
if (!PrinterNative.EnumPrinters(flags, null, Level, buffer, bytesNeeded, out _, out var returned))
throw new Win32Exception(Marshal.GetLastWin32Error(), "Impossibile enumerare le stampanti installate.");
- var result = new List(checked((int)returned));
+ var result = new List(checked((int)returned));
var itemSize = Marshal.SizeOf();
for (var i = 0; i < returned; i++)
{
var ptr = IntPtr.Add(buffer, checked((int)i * itemSize));
- result.Add(Marshal.PtrToStructure(ptr));
+ var native = Marshal.PtrToStructure(ptr);
+
+ // Important: all strings referenced by PRINTER_INFO_2 point inside
+ // the EnumPrinters buffer. Copy them to managed strings before
+ // freeing the buffer, otherwise subsequent reads can return
+ // corrupted/empty printer names and ports.
+ var name = PrinterNative.PtrToString(native.pPrinterName);
+ var portName = PrinterNative.PtrToString(native.pPortName);
+
+ result.Add(new PrinterInfo
+ {
+ Name = name,
+ PortName = portName,
+ DriverName = PrinterNative.PtrToString(native.pDriverName),
+ ServerName = NullIfEmpty(PrinterNative.PtrToString(native.pServerName)),
+ ShareName = NullIfEmpty(PrinterNative.PtrToString(native.pShareName)),
+ Location = NullIfEmpty(PrinterNative.PtrToString(native.pLocation)),
+ Comment = NullIfEmpty(PrinterNative.PtrToString(native.pComment)),
+ StatusFlags = native.Status,
+ Attributes = native.Attributes,
+ JobCount = native.cJobs,
+ IsDefault = string.Equals(name, defaultPrinter, StringComparison.OrdinalIgnoreCase),
+ NetworkHost = ResolveNetworkHost(portName)
+ });
}
return result;
@@ -141,7 +146,11 @@ private static List EnumerateNativePrinters()
// Registry metadata is optional. Fall back to common port naming below.
}
- if (portName.StartsWith("IP_", StringComparison.OrdinalIgnoreCase))
+ if (IPAddress.TryParse(portName, out _))
+ return portName;
+
+ if (portName.StartsWith("IP_", StringComparison.OrdinalIgnoreCase) ||
+ portName.StartsWith("EP_", StringComparison.OrdinalIgnoreCase))
{
var candidate = portName[3..];
if (IPAddress.TryParse(candidate, out _) || candidate.Contains('.'))
From b805b2072d90d9e922833e07c9ae8cee36cd159a Mon Sep 17 00:00:00 2001
From: lukethehawk <98876950+lukethehawk@users.noreply.github.com>
Date: Tue, 22 Sep 2026 10:07:04 +0200
Subject: [PATCH 21/28] Refine shared WPF styles
---
PrinterManagerNativeTool/App.xaml | 41 ++++++++++++++++++++++++++++---
1 file changed, 37 insertions(+), 4 deletions(-)
diff --git a/PrinterManagerNativeTool/App.xaml b/PrinterManagerNativeTool/App.xaml
index 78fd014..856aec3 100644
--- a/PrinterManagerNativeTool/App.xaml
+++ b/PrinterManagerNativeTool/App.xaml
@@ -4,13 +4,46 @@
ThemeMode="System">
+
+
+
+
+
+
+
+
+
From fe5e09520778399369019f274b3e399164b066f2 Mon Sep 17 00:00:00 2001
From: lukethehawk <98876950+lukethehawk@users.noreply.github.com>
Date: Tue, 22 Sep 2026 10:07:15 +0200
Subject: [PATCH 22/28] Polish printer display metadata
---
PrinterManagerNativeTool/Models/PrinterInfo.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/PrinterManagerNativeTool/Models/PrinterInfo.cs b/PrinterManagerNativeTool/Models/PrinterInfo.cs
index 493badc..2754f36 100644
--- a/PrinterManagerNativeTool/Models/PrinterInfo.cs
+++ b/PrinterManagerNativeTool/Models/PrinterInfo.cs
@@ -16,7 +16,7 @@ public sealed class PrinterInfo
public string? NetworkHost { get; init; }
public string StatusText => PrinterStatusFormatter.ToDisplayText(StatusFlags);
- public string DefaultText => IsDefault ? "Sì" : string.Empty;
+ public string DefaultText => IsDefault ? "★" : string.Empty;
public bool IsNetworkPrinter => !string.IsNullOrWhiteSpace(NetworkHost);
}
From 7c347357ecf278c7b56abb1dc0062240a63c3941 Mon Sep 17 00:00:00 2001
From: lukethehawk <98876950+lukethehawk@users.noreply.github.com>
Date: Tue, 22 Sep 2026 10:07:54 +0200
Subject: [PATCH 23/28] Polish Printer Manager v2 interface
---
PrinterManagerNativeTool/MainWindow.xaml | 260 +++++++++++++++--------
1 file changed, 174 insertions(+), 86 deletions(-)
diff --git a/PrinterManagerNativeTool/MainWindow.xaml b/PrinterManagerNativeTool/MainWindow.xaml
index ba2b30f..2a5ce1a 100644
--- a/PrinterManagerNativeTool/MainWindow.xaml
+++ b/PrinterManagerNativeTool/MainWindow.xaml
@@ -2,15 +2,15 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Printer Manager"
- Width="1180"
- Height="720"
- MinWidth="920"
- MinHeight="580"
+ Width="1220"
+ Height="760"
+ MinWidth="980"
+ MinHeight="620"
WindowStartupLocation="CenterScreen">
-
+
-
+
@@ -23,86 +23,157 @@
-
+
+
+
+
+ Margin="0,3,0,0"
+ Opacity="0.68"/>
-
-
-
+
-
+ Content="↻ Aggiorna"
+ Click="RefreshButton_Click"/>
+
+
-
+
+ CornerRadius="10"
+ Padding="14">
+
+
-
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CornerRadius="10"
+ Padding="20">
+ Margin="0,5,0,15"
+ FontWeight="SemiBold"
+ Opacity="0.78"/>
-
+
-
+
@@ -129,68 +201,79 @@
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
+ Style="{StaticResource SectionHeaderStyle}"/>
-
+
-
+
-
+
+ Style="{StaticResource SectionHeaderStyle}"/>
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
-
+
+
+
-
-
-
+
+
+
+
+
@@ -201,8 +284,13 @@
-
-
+
+
From 82cc8a0e64795c40e914d917c1d0d91dd4e40aa6 Mon Sep 17 00:00:00 2001
From: lukethehawk <98876950+lukethehawk@users.noreply.github.com>
Date: Tue, 22 Sep 2026 10:08:41 +0200
Subject: [PATCH 24/28] Wire polished UI and contextual diagnostics
---
PrinterManagerNativeTool/MainWindow.xaml.cs | 103 +++++++++++++++++---
1 file changed, 90 insertions(+), 13 deletions(-)
diff --git a/PrinterManagerNativeTool/MainWindow.xaml.cs b/PrinterManagerNativeTool/MainWindow.xaml.cs
index 2c27086..0e98f64 100644
--- a/PrinterManagerNativeTool/MainWindow.xaml.cs
+++ b/PrinterManagerNativeTool/MainWindow.xaml.cs
@@ -2,6 +2,7 @@
using System.ComponentModel;
using System.Text;
using System.Windows;
+using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using PrinterManagerNativeTool.Models;
@@ -27,6 +28,11 @@ public MainWindow()
_printerView.Filter = FilterPrinter;
PrintersGrid.ItemsSource = _printerView;
+ UpdateSearchPlaceholder();
+ NetworkDiagnosticsPanel.Visibility = Visibility.Collapsed;
+ NoNetworkDiagnosticsText.Visibility = Visibility.Collapsed;
+ OpenWebButton.Visibility = Visibility.Collapsed;
+
Loaded += async (_, _) => await RefreshPrintersAsync();
}
@@ -64,6 +70,8 @@ private async Task RefreshPrintersAsync()
foreach (var printer in printers)
_printers.Add(printer);
+ UpdateSummary();
+
if (!string.IsNullOrWhiteSpace(previousSelection))
{
PrintersGrid.SelectedItem = _printers.FirstOrDefault(
@@ -73,11 +81,15 @@ private async Task RefreshPrintersAsync()
if (PrintersGrid.SelectedItem is null && _printers.Count > 0)
PrintersGrid.SelectedIndex = 0;
+ if (_printers.Count == 0)
+ UpdateDetails();
+
StatusBarText.Text = $"{_printers.Count} stampanti rilevate";
RefreshSpoolerStatus();
}
catch (Exception ex)
{
+ SummaryText.Text = "Impossibile leggere le stampanti";
StatusBarText.Text = "Errore durante il caricamento";
ShowError(ex);
}
@@ -87,24 +99,67 @@ private async Task RefreshPrintersAsync()
}
}
+ private void UpdateSummary()
+ {
+ var ready = _printers.Count(p => string.Equals(
+ p.StatusText, "Pronta", StringComparison.OrdinalIgnoreCase));
+ var notReady = _printers.Count - ready;
+ var jobs = _printers.Sum(p => (long)p.JobCount);
+
+ SummaryText.Text =
+ $"{_printers.Count} stampanti • {ready} pronte • {notReady} non pronte • {jobs} job";
+ }
+
private void RefreshSpoolerStatus()
{
try
{
- SpoolerStatusText.Text = $"Spooler: {_spoolerService.GetStatus()}";
+ var status = _spoolerService.GetStatus();
+ SpoolerStatusText.Text = $"● Spooler: {TranslateServiceStatus(status)}";
}
catch
{
- SpoolerStatusText.Text = "Spooler: stato non disponibile";
+ SpoolerStatusText.Text = "○ Spooler: stato non disponibile";
}
}
- private void SearchBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e) =>
+ private static string TranslateServiceStatus(string status) =>
+ status switch
+ {
+ "Running" => "in esecuzione",
+ "Stopped" => "arrestato",
+ "Paused" => "in pausa",
+ "StartPending" => "avvio in corso",
+ "StopPending" => "arresto in corso",
+ "PausePending" => "sospensione in corso",
+ "ContinuePending" => "ripresa in corso",
+ _ => status
+ };
+
+ private void SearchBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
+ {
+ UpdateSearchPlaceholder();
_printerView.Refresh();
+ }
+
+ private void UpdateSearchPlaceholder() =>
+ SearchPlaceholder.Visibility = string.IsNullOrEmpty(SearchBox.Text)
+ ? Visibility.Visible
+ : Visibility.Collapsed;
private async void RefreshButton_Click(object sender, RoutedEventArgs e) =>
await RefreshPrintersAsync();
+ private void ToolsButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (ToolsButton.ContextMenu is not { } menu)
+ return;
+
+ menu.PlacementTarget = ToolsButton;
+ menu.Placement = PlacementMode.Bottom;
+ menu.IsOpen = true;
+ }
+
private void PrintersGrid_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
_lastNetworkDiagnostic = null;
@@ -123,9 +178,7 @@ private void UpdateDetails()
var selected = printer is not null;
PrinterActionsPanel.IsEnabled = selected;
- DiagnoseButton.IsEnabled = selected && printer!.IsNetworkPrinter;
CopyDiagnosticButton.IsEnabled = selected;
- OpenWebButton.IsEnabled = selected && printer!.IsNetworkPrinter;
if (!selected)
{
@@ -133,18 +186,42 @@ private void UpdateDetails()
SelectedPrinterStatus.Text = string.Empty;
DriverValue.Text = PortValue.Text = HostValue.Text = JobsValue.Text = DefaultValue.Text = "—";
PingValue.Text = RawValue.Text = "—";
+ NetworkDiagnosticsPanel.Visibility = Visibility.Collapsed;
+ NoNetworkDiagnosticsText.Visibility = Visibility.Collapsed;
+ OpenWebButton.Visibility = Visibility.Collapsed;
return;
}
- SelectedPrinterName.Text = printer!.Name;
- SelectedPrinterStatus.Text = printer.StatusText;
- DriverValue.Text = printer.DriverName;
- PortValue.Text = printer.PortName;
+ var hasNetworkHost = printer!.IsNetworkPrinter;
+
+ SelectedPrinterName.Text = printer.Name;
+ SelectedPrinterStatus.Text = $"● {printer.StatusText}";
+ DriverValue.Text = string.IsNullOrWhiteSpace(printer.DriverName) ? "Non disponibile" : printer.DriverName;
+ PortValue.Text = string.IsNullOrWhiteSpace(printer.PortName) ? "Non disponibile" : printer.PortName;
HostValue.Text = printer.NetworkHost ?? "Non rilevato";
JobsValue.Text = printer.JobCount.ToString();
DefaultValue.Text = printer.IsDefault ? "Sì" : "No";
- PingValue.Text = printer.IsNetworkPrinter ? "Non eseguito" : "N/D";
- RawValue.Text = printer.IsNetworkPrinter ? "Non eseguito" : "N/D";
+
+ SetDefaultButton.IsEnabled = !printer.IsDefault;
+ OpenWebButton.Visibility = hasNetworkHost ? Visibility.Visible : Visibility.Collapsed;
+ OpenWebButton.IsEnabled = hasNetworkHost;
+
+ NetworkDiagnosticsPanel.Visibility = hasNetworkHost
+ ? Visibility.Visible
+ : Visibility.Collapsed;
+
+ NoNetworkDiagnosticsText.Visibility = hasNetworkHost
+ ? Visibility.Collapsed
+ : Visibility.Visible;
+
+ NoNetworkDiagnosticsText.Text = printer.PortName.StartsWith(
+ "WSD", StringComparison.OrdinalIgnoreCase)
+ ? "Porta WSD: Windows non espone un indirizzo IP diretto utilizzabile da questi test."
+ : "Questa porta non espone un indirizzo di rete utilizzabile per la diagnostica.";
+
+ DiagnoseButton.IsEnabled = hasNetworkHost;
+ PingValue.Text = hasNetworkHost ? "Non eseguito" : "N/D";
+ RawValue.Text = hasNetworkHost ? "Non eseguito" : "N/D";
}
private void OpenQueueButton_Click(object sender, RoutedEventArgs e) =>
@@ -264,10 +341,10 @@ private string SafeSpoolerStatus()
}
}
- private void RestartSpoolerButton_Click(object sender, RoutedEventArgs e) =>
+ private void RestartSpoolerMenuItem_Click(object sender, RoutedEventArgs e) =>
RunElevatedSpoolerAction("restart-spooler");
- private void ClearSpoolerButton_Click(object sender, RoutedEventArgs e) =>
+ private void ClearSpoolerMenuItem_Click(object sender, RoutedEventArgs e) =>
RunElevatedSpoolerAction("clear-spooler");
private void RunElevatedSpoolerAction(string action)
From ca0a7c32e406e42d6b061d39e6549d5828e56b38 Mon Sep 17 00:00:00 2001
From: lukethehawk <98876950+lukethehawk@users.noreply.github.com>
Date: Tue, 22 Sep 2026 10:08:51 +0200
Subject: [PATCH 25/28] Ship only portable executable artifact
---
.github/workflows/build.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 990e0d3..ec0526e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -34,9 +34,9 @@ jobs:
- name: Publish portable x64
run: dotnet publish PrinterManagerNativeTool/PrinterManagerNativeTool.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true --no-restore -o artifacts/win-x64
- - name: Upload portable build
+ - name: Upload portable executable
uses: actions/upload-artifact@v4
with:
name: PrinterManager-win-x64
- path: artifacts/win-x64/
+ path: artifacts/win-x64/PrinterManagerNativeTool.exe
if-no-files-found: error
From 9c8b13aa1803c9dbd86b1f5e217cbd87e10b9c1e Mon Sep 17 00:00:00 2001
From: lukethehawk <98876950+lukethehawk@users.noreply.github.com>
Date: Tue, 22 Sep 2026 10:14:09 +0200
Subject: [PATCH 26/28] Fix DataGrid contrast in dark mode
---
PrinterManagerNativeTool/App.xaml | 62 ++++++++++++++++++++++++++++++-
1 file changed, 60 insertions(+), 2 deletions(-)
diff --git a/PrinterManagerNativeTool/App.xaml b/PrinterManagerNativeTool/App.xaml
index 856aec3..d140ee9 100644
--- a/PrinterManagerNativeTool/App.xaml
+++ b/PrinterManagerNativeTool/App.xaml
@@ -3,6 +3,16 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
ThemeMode="System">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -56,7 +71,7 @@
-
+
@@ -66,6 +81,7 @@
+
@@ -84,7 +100,7 @@
-
+
From 06334185eb337358e9f26f07c450280ca2b7fa0d Mon Sep 17 00:00:00 2001
From: lukethehawk <98876950+lukethehawk@users.noreply.github.com>
Date: Tue, 22 Sep 2026 10:28:06 +0200
Subject: [PATCH 28/28] Align printer columns and improve search placeholder
---
PrinterManagerNativeTool/MainWindow.xaml | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/PrinterManagerNativeTool/MainWindow.xaml b/PrinterManagerNativeTool/MainWindow.xaml
index 2a5ce1a..3c68bb7 100644
--- a/PrinterManagerNativeTool/MainWindow.xaml
+++ b/PrinterManagerNativeTool/MainWindow.xaml
@@ -84,14 +84,15 @@
+ Foreground="#9EA4AD"
+ IsHitTestVisible="False"/>
@@ -123,7 +125,8 @@
Width="8"
Height="8"
Fill="Gray"
- Margin="0,0,7,0"/>
+ Margin="0,0,7,0"
+ VerticalAlignment="Center"/>
@@ -152,12 +155,15 @@
|