From 8672232ee2d932eae72b747803e9c2ca9fafb7af Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Mon, 9 Mar 2026 16:57:41 +0100 Subject: [PATCH] cdp: add dummy page.captureScreenshot --- src/cdp/domains/page.zig | 71 +++++++++++++++++++++++++++++++++ src/cdp/domains/screenshot.png | Bin 0 -> 13945 bytes 2 files changed, 71 insertions(+) create mode 100644 src/cdp/domains/screenshot.png diff --git a/src/cdp/domains/page.zig b/src/cdp/domains/page.zig index e029b6a6..d4b7b301 100644 --- a/src/cdp/domains/page.zig +++ b/src/cdp/domains/page.zig @@ -19,6 +19,8 @@ const std = @import("std"); const lp = @import("lightpanda"); +const screenshot_png = @embedFile("screenshot.png"); + const id = @import("../id.zig"); const log = @import("../../log.zig"); const js = @import("../../browser/js/js.zig"); @@ -39,6 +41,7 @@ pub fn processMessage(cmd: anytype) !void { navigate, stopLoading, close, + captureScreenshot, }, cmd.input.action) orelse return error.UnknownMethod; switch (action) { @@ -50,6 +53,7 @@ pub fn processMessage(cmd: anytype) !void { .navigate => return navigate(cmd), .stopLoading => return cmd.sendResult(null, .{}), .close => return close(cmd), + .captureScreenshot => return captureScreenshot(cmd), } } @@ -514,6 +518,57 @@ const LifecycleEvent = struct { timestamp: u64, }; +const Viewport = struct { + x: f64, + y: f64, + width: f64, + height: f64, + scale: f64, +}; + +fn base64Encode(comptime input: []const u8) [std.base64.standard.Encoder.calcSize(input.len)]u8 { + const encoder = std.base64.standard.Encoder; + var buf: [encoder.calcSize(input.len)]u8 = undefined; + _ = encoder.encode(&buf, input); + return buf; +} + +fn captureScreenshot(cmd: anytype) !void { + const Params = struct { + format: ?[]const u8 = "png", + quality: ?u8 = null, + clip: ?Viewport = null, + fromSurface: ?bool = false, + captureBeyondViewport: ?bool = false, + optimizeForSpeed: ?bool = false, + }; + const params = try cmd.params(Params) orelse Params{}; + + const format = params.format orelse "png"; + + if (!std.mem.eql(u8, format, "png")) { + log.warn(.not_implemented, "Page.captureScreenshot params", .{ .format = format }); + return cmd.sendError(-32000, "unsupported screenshot format.", .{}); + } + if (params.quality != null) { + log.warn(.not_implemented, "Page.captureScreenshot params", .{ .quality = params.quality }); + } + if (params.clip != null) { + log.warn(.not_implemented, "Page.captureScreenshot params", .{ .clip = params.clip }); + } + if (params.fromSurface orelse false or params.captureBeyondViewport orelse false or params.optimizeForSpeed orelse false) { + log.warn(.not_implemented, "Page.captureScreenshot params", .{ + .fromSurface = params.fromSurface, + .captureBeyondViewport = params.captureBeyondViewport, + .optimizeForSpeed = params.optimizeForSpeed, + }); + } + + return cmd.sendResult(.{ + .data = base64Encode(screenshot_png), + }, .{}); +} + const testing = @import("../testing.zig"); test "cdp.page: getFrameTree" { var ctx = testing.context(); @@ -547,3 +602,19 @@ test "cdp.page: getFrameTree" { }, .{ .id = 11 }); } } + +test "cdp.page: captureScreenshot" { + var ctx = testing.context(); + defer ctx.deinit(); + { + try ctx.processMessage(.{ .id = 10, .method = "Page.captureScreenshot", .params = .{ .format = "jpg" } }); + try ctx.expectSentError(-32000, "unsupported screenshot format.", .{ .id = 10 }); + } + + { + try ctx.processMessage(.{ .id = 11, .method = "Page.captureScreenshot" }); + try ctx.expectSentResult(.{ + .data = base64Encode(screenshot_png), + }, .{ .id = 11 }); + } +} diff --git a/src/cdp/domains/screenshot.png b/src/cdp/domains/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..12fbde5ddf2767307d5cbe5ed3533c6b5bafc7b7 GIT binary patch literal 13945 zcmbWebxa*^&<2VYhl4vD4({&m2QL(NC{mmP#a$2X?v%E8ad&rjcXxMgf8UoYf869I zce2?`o|&C_X7_4Qu$VmbU3cBR8 zqPq0Q$HzYe%D04$mezJ)`|Rv&d3m{~r|0PC=<@QirKM$kecj*R|MBs0WMpJ~d^|in z{O#@S`uh6o*RMbzP(wqbu&_{8R#r?*3>_UE1Om0Uw^vqH!okD$_4V!U?&9O&y}Y~t z0D#TSO=4nVV`F0)YUuc?jh?(X;Z z_l}N^larI-WqqlssbOJZhlhtZPp_Xpe_mW%{PX9}ueQnR>gvde{kge0*{G_rva-#? z^SztLmeJ+R%*>OMlZAzaq@<*Rh2#4A`Y&I;Of2s{y}m7+-nF*2dU<&j7Zs~av~IdyS$H8(e(otI zzUbJ#fC4fuZXKmgA1o~`tzX=8b8`y|3$I_^L(y_CZXKr19*~7seko|{n3#of&4sdv z2(2H4k~J1cX-H`sdCALhhl1jYkdqQucV9m33i$86`RBbA;}^sHr~dyF4dJ_^Vz|b%wL*W3cYGn<^eV(&zuV?#p2i z&gkUXe+R*&85X5v_-Q#@CzN^i2kpYKm4B9IZwPmaMJE|T$Y4s-PVe*18W&o2rY0X% zfJu{2ll6`nr;fH8=*bW6lbRKudQTtDjp3qpdDoD+O8zYFFlqC74-0H_7n`sv%fX^wIA~%MIOVBzAv9pkvNf*aI&mR1X3Vg>r4x zaKJb|BQnYzr4CURAa<^ymm*T>Bom1sbp%79$)+C4$=_>y3e$GXNTdW;X>uVEZvIBf z+TEnMXm7}fa&Sz?k8$Jxk~7LBB2Su#3nIWK6ms!zY@(tp^Wn!){fb1q6kx4&WCF^` zr>fyLTEfiFaYoDb@cz^6Nci`;aSc+u0|-f(^sdEXH$(~}Q5=U+m{~gH{9ZMMKnQS$ zRh=bS2=1R>BvxthU(@4Ap`+x#`>A+StkMCak(KbYcfT9uwnIW)e)@AUyv{Q(V&9)t z9PI9~ydfCIl+}PH#w-?~ly897 zg>lgKO~OO>8O${JDHR?%O6ppNywI`DpG(u79L09hyI+ugl@%DxoxxNI+)0dBP#ewn z2gn7{p!EMyKG?|t+|d0O2Z6-cg(BDZ9AdQE+`nQP@`EBuC3kQ@^L0G`LTtIbiY0i8 zIrd;D`Z1A_9LtqJ(lpFVJT3Y4n}}h!2X35bJq0lgLC?Lrh=ryYk&V80m=%ngH?9{a z|Bi6nrwcRjo@dnS&5CcM2T>2eJ`OmUx*biUUJ?}W*JyrH8Oj@oUK{QW6HuzU6XBRm zicaAd-jWlxbe+9ae9RL>7m>XO^Q{GU-{nx!e$KQBL(*+yv84s?PXHZi=M4yRfJ(|` z@pq<6vDH^Ldfj=51pGxGao0&8o#Eq$pTALWxvLtdFemqy31|B1;qH-iPD_hx{>T0n z;dSNV8y6lXLM?n&vd&coBo~=W{W~CGD!}!#H|-EwLcWcdx80N6+7e=OZ z-aVNunfQ8-Z#R^aXsG~;&wPl6@UD6;OSZ`0erbRmx&<*)0W`r>IOw|2@wGDID$*4E z z9|%?e^lG<&I$X^HHVAU`1?n;G7CHa+_vfX^#7HD%aNoS9HZBw`Quw4LV_!v}idlA@ zd%V$3FN2j$2rjfe78c&&T!aB8`LVFkbwq2jVCLM+kxScGfpQy?+x2;5#=!9MTJ>~} zD#>fXNK5OhbE1Kg4s+9X_0)#vqCJ;TvP{c>bxxDt0qL5kztL%wPUWAx5D#b#cdtt3`k|9$fDVo=km**B~y*}M?VzaW)$V&fI=oeEsgm=}{&JU!gKAZh5 z@y?@kV6w%}t}0y0){jxiMskKg9OpP(K6*p!ULNO|@3Tr>}=6XPu@2JfJEKgQ4 z459p0NdA{q8f*3U3)474h~&(7fKZ0`U!+^Ae1GlS5Zf{N!ITx3SZ1=cups+rGB?<^ zmtYvvHYL`v=5)N*Ej^>Ra_>=>5r$@kTMyGMDu<9AJF6fGJJFng3ndSEGA8mL3vqh9 z(&WTn0PryUy9*ZcW8h1o5tYcHd>5hSyt-o9v;TS%6{INUz(!4Ne-4&irUePRs#Eo}75O4@1Y zaI@jOji(r~1cICRuaZm;I6bf#VO1GIBWqu}x86ONknAN|o#3;d;gHgI;%u&IG~o)O z*%ZNa@3ZF)w+#M9M|XqVwHD_safIj|lIy+l;y6gyzU4QW#s0(e5>9 zR;gf{Jk?X1A4wLNLPpt5)aHjIRpza(OqDk(JPa8Sl%8D3HMz8OXC%O41r*`nNqL>r zp$b;O6Y~c7A8Hzn%u(_(mZ|I-tfN6VN^uLDmY(-?nq|)e6%(=r2dc_aLsne#h;1--L;&Z=KPF$X(484$u2M=K^ z72_zf1a5)f4@d@Cy=rRC(v{T)xqC`_-4D~kP8jF2&OgjVE<**t6R{r_miDF67|Hgh zY1hDBTDL9DwgUb^i_DU*IjKUlOf9jw}5i2DR=PM zDykmiousHHeHUxy<1Sf0gd`5a!KRg;ClAV_1q#VO$Op0u7>E&7v0_UlcjuO5`d>x^ za(l6(CQ}oct2nm&cm1*zIymdoyZULF)u$-tItHCzf+#mxLKoP1F!#G~Yx=vDY6VRdRz5;>g{= z-aZiHE|g^dI0$)pcQQbVHW8KmICBP92r;vM~6ZqRL74^@MzI~x3gnVkf?o&N|g zLru|Pko`rZ-oj5m?HKl2f=wmL#_%!YHQogYc16E2?r5ET?b^yvlp2GL%m+HYB8QB5 znkOQdp3p)AS9(vyZdP5V;6qfZ;OAp^(jT*!DR+K;w#OQ6MZhppp`j?x8^dQ2WENBHuibeo;0%@WzyVUDZOXQP}sYyW} zu-K)NcYf9Oj^Rq7*SXuddhkODEP~)ehC;cdb$@Qr@IN03F=ffx^T`1fMc6GxKOC8k z!RpjS;Nn?93``_dJ|IggbS!#6M&VGip_lDW?Z-wPdBfEBfag5hvGckMwZVaU@gr>? z!EOM5V+3oF18;Q{7PrQIR)W3@ArREcX@WGh=i@&+H{u)A3--lSo1|Z(oh=BheMkEX ziPF|97{gs>P6Z-Gs5QF7ph+k`3poS9G5o8p-+ZgJkJ|FDJE{Y;=l8JCBC zGf~3^SSDt!X^?9zsk)csZA#UkwjN98T>aJ0w}w?=c1?s$2=9zm?;zb(VsB|&n z*KS!GtOg>=)S&SyO&2f^4PehH2^hPSsD)2zA#FkXA%0kRGuXhjL@LbL3XfH~JCrb6_WPU%9NvU{~tV z$^w2g*274pI_5l1>EAnzd&A$1qWE`KdOr54+aSDJZgRq*_Es>-d)A3JBh5AcM07Us@K~%HdvbF7s{?-Zg@ACq zzOLK)7C_(?l#(Ill|Bz1Zr5ug#C?6%^WR4wGq&Z1ZcvP5XFR=L_OjP$KJ;c{a_Ms@ zKn>uL+)Xs2(m6%UXDewV5R9&!6eqK|hlMjoZ1i&KrJHGKNl2d3C5TjG;8pKuwKB;^ z4Inc7KH^f`42k2*=T5XS!5+hA*xOj>?&fURJcckt)h3O8r|@ezC~NLflHFcs(nSYfO7#JTgt6OMfA%X_cIg5XBfs; z&}DBgd39)Xf{BSqlSS-6-4eC5DJZaxEdZmSD6LeAqGt)2lTeV#LIzQrwCN6P&RxSA z7f{U6`5u%WF}pD^vFGF#CQm{#a#WL5fu(d)$xOSVahX%31OGyv)0BPp+zL628kc0i zw782*H#?4&{fCdU6ist8N<69yVI*ePJ5+>VOA?Q_C1c-u=C@f1jMYj83*oP+sb;+# zk^ANxS(M7Dw059+wrw76LK=?R@lE`34FQ>kAc`vH-1K~9z9LaVlEZ7v`)%O>yM+F0y$mJ5^6gV(Z7O|eg)RyPX~Zm;owm`XZR1-%be+gwnEnJPJLd+OEb@Z@mnc7 zP2U!J_~sh(eh5!|WF>rG)8ixkS^yzx5uGP{_vPVDFEU{e#rqWr@9%*UqvaAX z^0OL|8$T@tqz;CnP*0D(weEpX!_(ti9G(Gz4S%#QIpC(~8V*wN=I-Fp==eAa)|Hq@ zS)T!!*jq*Tq8-v7s{8m_+-Yg=&!u{+^8__Jyk|3t`)sBju|#d`k>hr392(MI zH`Z?Yn2^L}5pV82a6{XxIY_pxDe1vmEW{&tQ`G^TS)AnlE3rjrBqbd6Yq>{ik|iw# z`b{L0_gp6L_X#%*iD7|JWCaUYcfB-T6o4SG5S}xr>W|lE5T)7gGiX_{LF7^7?7JC> zR_0M$_(m8fKUZtWh=9apmt0N}U;)&`A&AjPK+a6WCvj~?W=h#1916Czv$RAA-W^gT zA)(+`3vT?hn}KS3+a+JBh)Dkz;(2dA$fQp~@zljGMwVz}o=r_+a5vG5nL^14O+wLB z1x13pE5pl0TJGay=LZE-vt%t06W7{^RyDNy7dndl@I0}@3_$cn_( zwM||Eu}&jT@gS4Szm)mnw&vHdczQx$e5r%#IhR)Di4Zi1U0ATnr@HS_jK4mpJV4`H0i+bUj1f0$sR@3QVD53L(oF)h> ziY+%x+=JJ(pbU^JZ5?LW#C%mkfuc0bUR-^u?LM9P{cf$NArqd(2z{JYF;tRim^gvs ziXJxGYEV%=WjsEikX4ewT_??;uH2B5i}-XVm`9Oo+iim^GnX_*&AX@a2R6+_vSic0 zNUBMWmtLNZF3=y-NFm6)>fQQN%=`rjdr-XZtddYRp7*n^E14nT{BLesYDgiU77(4K z7xoBHN8m(WQz>xEjtIY;aHC+mJ)fEGW~I6kf@&PJLrG#wd#I9k*;wXpnM_m=I%aV@csolCviy#RNpJC-nw?j+tcJY|@Emx&bQDrg5 ztat77ET|+=kD&AIo?Y_*1l&w9*&PQm;OmrsvI&!mkUi6cC|}U(PI{CSVGS6j)VN-S}YJ zErf6eTZn9-6ps+s+a%jbDx!f}TFH9ox-uAIUaC46I>oLQ3QR7^;=<^kN};xDM~q^4 zR0X@v!Z$rY*k#la&6b^}QAzuC5~+@qITY~hq~yLaScio~5yoK3i#1YqcUYduB@~v? zR+$|=Vn-G%j!8USKD`HB&lFlo^-fA+b+=k-T;}n@R@WIkU0~=zS;_Wt_h1zittzBI z`<`IQXCBJ7?4a?M;{|irNh+(?VWj}97FzDrSERT#b+5aNBE2Xt=mn1K2{>z3SC zglrmCvPIEX&T1@bDD*Q;A|O^na_qO2Hg|$o){q!#DxW*ZiN;RZ+ESZPaqQUY(7gG@ zWf{Gq`XWdzA#poIL@1z2F=&0dD!B0cjmC=u-7EF3=*)ruhr;RlmTpeC)J!4{UYclm z(wHPncb1unf`rDM|FM2;rR3_3u%xEgsAg4)Dm$z_6kBc10srU6XH6tJ9;)7e24CBp zHM5|V*)QE#m}_aT85*r)j;kkAY6#jAd{n+3>)$Ts6JvJwOlmp<1VQxZm@C0h%bz4A zn3gau`86YyiFRmZ!F+spEHQCZ_?R&puA8D4YhS^L)WxDpf0bRIQEG$BD+dZ?HP-#jYyf6aYOwlvoM0Mh{$ZUIqnfh! zx&qIxU6z{WXctR1bm*0Rd3k|B!C7bgdU9ToFG(|pP()Q4{r;w}^!qmrkB9-HjK?O; zi%0@VbOn=8q3E>YY$?SmC&bMo*|uVk+#`!n%xycOu)74$uD@qX?nsvv6O7PX(a>k1 z_k1KjYtKuK%`WU}iV04!0B00?%^3^oCzV_?zp zo4-`(m#Gi)1A=+q1Tx8*O}uu_FVO|%p&rLcTn#SzCkdy)hD=%@91jg-_QJTI&~W)- zYq6YYQ(vQP8@$4jwT+z*mqY93J~LL&U=c_&$jSVurii6S3y4!jXd$l?`7u>FAX&z? z`IxgB^;xk{jVizPon|rafSq2O*2!JLleUFNmZk9!5I>G$G>tor@pg#bjUrNq#?vH zM?@(}9>T2?0=rmJhiCy&BCG9#VaS-)b(SGxR5y%Nu`dL}ejQv0)H|cXQZP3%=0|CYi%X zA>W$*;vb8Y_##q5C^X59OGvP+>54-kd$-^$DWawoO9>$#a@cmo@5j#V<{|T77s6R}e8i!+ZJo?Cof@)g zq0=D8XyDw2@)S#L(s32kN(xnk;|o&*VwkKeWLf=7*+6yFVZ!Q#qwkn(JhC@*ogi5X zhOmYw6Pq25|NKzou9h|deaFf&pixnsh-v93KcVdj2h+k(HlCNEpR8^eFrAu^qV?(vbhxzL!ZO{?;?dmK@yR%;_(={mA-Qj>F9hK|4U|S>o^N0+ z9@;=V!tq?Dk->#H<&(HBz*Uc79k<`_7m17$%i@4baW$#X^ozh7atiZZ4O_|eRKn0U z+q#}VId}KN3cp*r2{J|2evI-He3eFBoH17|#OHWq9$=-E-fDRIHfFvz=IzD3+}_eX z^Tb_nAGN=1VB@Rt_pB)~pMQ_Qt&%Ys1Sd-{JxCn?lc{d%>E+Bkx2 zjK*v;Yvjsa0Wvy~Rozcq%p)+pMX3fR_we+x@2R$Ghje;BWbSoys~4YAM_iWE!Bja} z3hA?y&Awab#7*a@VM(hgTVbb3(y#jWl$begUY&lTnbNIgf$hP;OJO;dlE!8n&JElk zfUNQ%+Lqw3nuB2KlM+YHc=2!;=b0xZN_ZEC1yV9LQ##1E=8h7aaD6rOQtmW}V(26$ z@Q0DyeJj+l%!B*;BCW2IE&9Ua4?hFsEZbxSh2cG?D(zyD4lg$+r$+j5#%^x(({JTt z2jmpltOQ#SVkcTol(U4XC|jf#L#fg|w}H)fS$h1iUmPOl-4i~>%p_xxo;hHRih+mV z9e&jXS%t1uEc6Jjk|%>}o8*;rdZ8uXgcUPyQ#ZmYaJ+*%O+mQ z7rg>8UutlzW!dh<^zKCR36eOToll!d!g#yXz}(}dl`}@smEfhRU^t-G!%Cc5nu9zn z3#gEQQ9-5Rowwy!r~D;f?YB3g>}Iw zF@{37V@^cRp(xM0H`g$|B4#>x!+vYg${>Puf&LI}{I9pFye+%b$wqyKW!|fmWdT0AflFWJDwrwOXg%hME z1Xs=fX18nmaT&R7>zxPQU2}mid!nx&ZyIQyVTNTDZ(GLZ<(JcOb0q@p-OD(y6_-UuV5GtS_QR7#uLL2OOygFFcEqA6n-2PDH1;+_-~ zyj@x&$i7|Ve}_BCLv+!?=-1jOlfeLKx{!UR_F!>an5}-B+r;V_N+NSfCne59wEcv? z7=bY^=6pxq97fsg*^gVyo8R*6{5*X1nTF7(XA0YhLsz!`Ym^5fxsc<7N2Nm7=}L+; zoYk3+CLG>@-y6a26E?5y!+Wkq$STmv&>$9uhn&PQxkb_Z=COQ!aX1H*H-NnG9ka5N z=cg;ET8OnflYyi~mz{@m-M}tS#c@%YijtCTI$)G3n z`U;SpB_o6r(2QXMcdJd?^K)&{D%g5S8sMTn9 z_PP%?^M<~)IBUomkKyp#j5A-!>yjje^17BDMtL&(%XLZql1-Ws=4aF$l!RV7d0YnP%*x3?mUeD+~V;}i^|g#7j#%ioPQ8pF_CHP%0%FzHthmQ)aqemQjk_E z;KWa4N}Ij5;2FiC9|nV*Or>LrotMKcLpk^n_wTd3Ehe97M`NO#Uh&Zw{04&n1C!d|F+6 zQlm)4yG725>Ql36K{aH~u%VRO_>^}}+kCmapCpM&*}0AN3+6rqlnrTZq7oAEEY0k03lm9YOn7_HpNp?)hsksk$He*ItGxTlr(Ds9l>RH0*1t3 z^wn7SFrcelGY?!AgEnU(R@evO1~ki{R;%`X7D)Xso! zHEp@V*=e;V(PiQZG|JZU21l+>iD+<9@~^%cq*D5Ug9c9xlR3R`hNHqbw?^>kIKV-h zU9*%BDtY2sZ}dPmRYZjhd?+rf9(cGBC`@)(3UB3A@yR_J4CtODQsV_&cmnb3IK?oj zTSRS`4EU_#A2I-u$%wBb$2uNTOUbbhYyp`%ZaLNE)DChW9v+*XLrPMQ!RHro6f)eci=EN0WfOcGy|hcDf7A%E8y@>n?Y5Q zK=PH%*+;;JXQVL)dT%&gb`L)igP2h{XW(T3U;oV}C{Z%MNGjRj$sOH!CDTnz9aCyz z%jM;Pr@s^8%HWkpk@bVQQ+5$a%>N0 za!%KNwLLIfSVfnc?2|7lU}m4whXonTmGv7~RScy=)v!-0b@>8NeGEmJa7V8%+T$ux!>t{822DY6CM>)gn{Gum`Vj{u2=w zAP|T2VwN2!#n96Q>zGC3iODY$m!`J$$zA;Pe}X8-^;+f$MG(a3-It!dZ4%kE|5uhv z0?C%HO!>csLZAn*hI-hSLd1f)rjm=e370R2OR8U6+R^hxR0MaTRt6R&aza}eWC7(o zeo)$|J@Ui*1B|rbw_ts+3Q27eTduFz1jP@D=nN3WYk64Y)L>wCe}@Yf^kSYrrtqFR zgr!3p0)9Brbe2V8n;GYZJ-eP^54-qFWR#Z%UP7+~p4ZJ*W$PTOPjoh^-=qIJ)`@!gKP+4gOn}E&W^@-!r(;T$94zss$5B9AQrx(KydCn>>=hLvae}^s!gxHX;rcERw|;?icsLbE zRRW1Z#4%jRU4iYYONKrADDruAQM9eOriNp$Y-!%Ge*Z<7yX7XEbK{R}5zLOSU;pNt zxn8oos~8c#_vL(wp^||ybXDIFO!Hr&%B@#|)`$HN>fcRdpN%?goO0oUSvr4tC@!%n zSZVQ!X(3I74*BzG`HHEN5zstWjWC@4bt8Le`Pmbjy3pDDfPHIwVRKrPoLvR$MBZ>S zzczjM6nQ=ij|b4pV%mHmg07639Od4z-k^ovLP>Do*>YOZ`WLK((E-OpQyW@U&|Zw|>b}2a<>V=_ znl8F|PNmNw6XE@?yGxFTS4de$rK+~ukfY|ccv)#o$pHATEk`ize+_xR*#Blc4)|4W z!Jk;cICq^{$^^^)KfU=@Iz95{cK;&&O^ddOK^(wWDovFHM9hK#I%Hu51Fx^$FvSrq zqz$X+5Ceh#A6K{0@9*`Z4=OQtqB1f|uepCL5`3o9uZKki6x&PxU{jZFD8qkybN}5A zxBCF!JIdt=&EDcEpjzEbDGwb^K@A=6FEuECg8)1Ic+II&-dmU-iQ36M`EAkT`ur+~D2#o}R+x7Sf+ubfdesYHLkO+qne|0fCGlp*o>tkrt znjB)py%rI_-i|Fymckj-&cP>p6ghJeL|7M!T09N#YOhZK zx9g4D*Oz^nDgJwWW^2)5S5Gt?c%Q9V!cMTw{JnO0|D`ro$;v^0RWo)&i;0;Gu%eB- zFPaoKbT>EIQ>Y=Mwt`8`1Ba%Hsxej$F{VO79keE5$rU{DO%?VTW0ZqRgdQp*>)G=z z{Drs0e(Euu-c-e!mrhKa%ED1QY`o$e*25%(HQeblXFv4}F@ZgMks`%~w#Ak$wX0U~ z`aQl4!}Zse8Sh`#GjV4QY}d5fVX(DeL2qatMZ{J~(_E4A9aXC~UwH8I}$vs_)Jd7MS_2&gwtA$@P<0*gR zC^U#nC+6p?A6xkZNa{-a8aW&{cpOs>{o2Z4ZiWMI-_9K=R?cU?l+pf2%jUP>zo9m{ z7k_)fO%<#uF|Get>&wwK`Mv~tq9&o7oovz~fJ=(qjJqKND{9woHwXp`HsL&IT@l+i z6`UrS(tnR>`E0-bwt(4+?PgswY>t+ejxasjwcdGuL$$v)wTyv}*W9`Ynuzrko?8KZdS~;v2+t(Ar)a zjuQM?Z2t2ped^CJmeLp}x$W6>^ugrmvMv#qVhk4;xc+x&4f=_`3XVAIY95E|FL&B| zDHnP*6Q6f{a$8a%lvbII>FMaTj58qUswh3axs8Q!q)_EFlWPV_t5XOTVPGg-Xfb*{ zC~%4r2>gXlz&NJAbit9&g&<}p0K7>44STxOang;6PsjYSkzH5dT2+c#m{BGShfZSN z#hASkre5pcc)jKGmoED3Us?9=iW7B=bKB7D{mR%j@-v@^OhSiBqYA4iUedafN^w#Ox-r!j7^wZU&07Asj=9%lF7ve(xUI&Xp7P2Rt1 z5C7kO_1E$&Y(vvOW@hdF7&Q5$_2eoQjvjD9Y4)QsnVDc{;td@UY-!q{rRpN8$OCo zI8JG;!wIzi&%RW`bo(u%Nqj2I;~ICnAJ3zWrih0;Zn&^Sq_)KlTIRx8?eOdOxO%1oG5ouH^mn7O<(q8Mju2OA+k>Em4==Q z>%8w;t4*dSu!4tD`xMi+Kt%--W1M9RkAx|}2sjIBv10tfHjNC`TMlHDX_BB5BTHz9 z`we<~+WLu)IQZg*VZgEl+1vJ?tYgM0;&g-LKbGh|Fbg%TsfH^-FBKVsK>sL0Y=ooG zxfQny{0}AbUN-I5tvQc3263SvwV&nIKJ?Jw%+RzKScoU_jwU+Nmu8^Z6(W68V~$Ll z2!JkjjFwpTxl@B#HtrqV|rde{nLQoOrm~yzPgm($QxLQxd#GI`1bUMyV*G0^l z=zz=1SfK-!f3lBIi#QL~)Vy^><&}y3X;Z-V%!#C#|0>%&-qpIcapTONHT56FX*^#K zEhrqf9p!jWOc@U?rubEB8bKa}-m@MpiDGH!_-D?? zYYDW(Mmpd#U{RzovsMj78Bnf89pDdUnPJfHIIoBwXa&JeED4Whd;pOel319$$;@6h z9T$dtagL7t9}Q?iB6Zw=r2m^t_hHmVzx`!~hVli_l3^7uDsJp`spGL;OF(B zcg>@D*7syx5`bKGVfL_j&xma>5zTj1v0I8tA4guRUwA@o5%ZXg!TsL`%X4}W-pNu0 zW2%xVnD%_1Gbl(E#O$30*m(%Mch;r^Sl-B!1IqbeoH7MN|8qI;E%5AMmZ@{jBOw<% zU9Acq3#|+mJWeH1C<m;%MJ`shdKIn|T3Pr~ToT$O;?Rp67xKSIl3)Tk z=LfI#-&ms!Ax=AW6=_qrL5XwiJ{n2w{NVPbh!V{4Eetr5sUvj$Mt_n q^8KdRe+IG1_t{AG|Fr*C5re%^-AYg~7EbujEGMljRUu&<_