From e8f207ef546be8acf36471b508ee75b9ea7cabea Mon Sep 17 00:00:00 2001 From: Harald Kuhr Date: Mon, 16 Mar 2015 12:02:31 +0100 Subject: [PATCH] TMI-113: Worked around a rather nasty bug in com.sun.imageio.plugins.jpeg.AdobeMarkerSegment by filtering out all APP14/Adobe marker segments from the stream (and re-inserting to metadata later). --- .../jpeg/JPEGImage10MetadataCleaner.java | 34 +++--- .../jpeg/JPEGSegmentImageInputStream.java | 10 +- .../plugins/jpeg/JPEGImageReaderTest.java | 114 ++++++++++++++++-- ...e-progressive-negative-component-count.jpg | Bin 0 -> 33518 bytes 4 files changed, 129 insertions(+), 29 deletions(-) create mode 100644 imageio/imageio-jpeg/src/test/resources/jpeg/jfif-exif-xmp-adobe-progressive-negative-component-count.jpg diff --git a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImage10MetadataCleaner.java b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImage10MetadataCleaner.java index 420bd1b4..6b88eed0 100644 --- a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImage10MetadataCleaner.java +++ b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImage10MetadataCleaner.java @@ -155,24 +155,30 @@ final class JPEGImage10MetadataCleaner { } } - // Special case: Broken AdobeDCT segment, inconsistent with SOF, use values from SOF - if (adobeDCT != null && (adobeDCT.getTransform() == AdobeDCTSegment.YCCK && sof.componentsInFrame() < 4 || + if (adobeDCT != null) { + // Special case: Broken AdobeDCT segment, inconsistent with SOF, use values from SOF + if ((adobeDCT.getTransform() == AdobeDCTSegment.YCCK && sof.componentsInFrame() < 4 || adobeDCT.getTransform() == AdobeDCTSegment.YCC && sof.componentsInFrame() < 3)) { - reader.processWarningOccurred(String.format( - "Invalid Adobe App14 marker. Indicates %s data, but SOF%d has %d color component(s). " + - "Ignoring Adobe App14 marker.", - adobeDCT.getTransform() == AdobeDCTSegment.YCCK ? "YCCK/CMYK" : "YCC/RGB", - sof.marker & 0xf, sof.componentsInFrame() - )); + + reader.processWarningOccurred(String.format( + "Invalid Adobe App14 marker. Indicates %s data, but SOF%d has %d color component(s). " + + "Ignoring Adobe App14 marker.", + adobeDCT.getTransform() == AdobeDCTSegment.YCCK ? "YCCK/CMYK" : "YCC/RGB", + sof.marker & 0xf, sof.componentsInFrame() + )); - // Remove bad AdobeDCT - NodeList app14Adobe = tree.getElementsByTagName("app14Adobe"); - for (int i = app14Adobe.getLength() - 1; i >= 0; i--) { - Node item = app14Adobe.item(i); - item.getParentNode().removeChild(item); + // We don't add this as unknown marker, as we are certain it's bogus by now } + else { + // Otherwise, add back the Adobe tag we filtered out in JPEGSegmentImageInputStream + IIOMetadataNode app14Adobe = new IIOMetadataNode("app14Adobe"); + app14Adobe.setAttribute("version", String.valueOf(adobeDCT.getVersion())); + app14Adobe.setAttribute("flags0", String.valueOf(adobeDCT.getFlags0())); + app14Adobe.setAttribute("flags1", String.valueOf(adobeDCT.getFlags1())); + app14Adobe.setAttribute("transform", String.valueOf(adobeDCT.getTransform())); - // We don't add this as unknown marker, as we are certain it's bogus by now + markerSequence.insertBefore(app14Adobe, markerSequence.getFirstChild()); + } } Node next = null; diff --git a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGSegmentImageInputStream.java b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGSegmentImageInputStream.java index 096d99d9..ff58c0e9 100644 --- a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGSegmentImageInputStream.java +++ b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGSegmentImageInputStream.java @@ -117,9 +117,9 @@ final class JPEGSegmentImageInputStream extends ImageInputStreamImpl { marker = 0xff00 | stream.readUnsignedByte(); } - // TODO: Optionally skip JFIF only for non-JFIF conformant streams - // TODO: Refactor to make various segments optional, we probably only want the "Adobe" APP14 segment, 'Exif' APP1 and very few others - if (isAppSegmentMarker(marker) && !(marker == JPEG.APP1 && isAppSegmentWithId("Exif", stream)) && marker != JPEG.APP14) { + // TODO: Should we just skip all app marker segments? + // We are now handling all important segments ourselves + if (isAppSegmentMarker(marker) && !(marker == JPEG.APP1 && isAppSegmentWithId("Exif", stream))) { int length = stream.readUnsignedShort(); // Length including length field itself stream.seek(realPosition + 2 + length); // Skip marker (2) + length } @@ -133,8 +133,12 @@ final class JPEGSegmentImageInputStream extends ImageInputStreamImpl { if (marker == JPEG.SOS) { // Treat rest of stream as a single segment (scanning for EOI is too much work) + // TODO: For progressive, there will be more than one SOS... length = Long.MAX_VALUE - realPosition; } +// else if (marker == JPEG.APP14 && isAppSegmentWithId("Adobe", stream)) { +// length = 16; +// } else { // Length including length field itself length = stream.readUnsignedShort() + 2; diff --git a/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReaderTest.java b/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReaderTest.java index e4a6e2b7..9c9eaea5 100644 --- a/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReaderTest.java +++ b/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReaderTest.java @@ -418,9 +418,13 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase spiClass = (Class) Class.forName("com.sun.imageio.plugins.jpeg.JPEGImageReaderSpi"); - ImageReaderSpi provider = spiClass.newInstance(); - referenceReader = provider.createReaderInstance(); - } - catch (Throwable t) { - System.err.println("WARNING: Could not create ImageReader for reference (missing dependency): " + t.getMessage()); + if (referenceReader == null) { return; } @@ -1196,6 +1193,21 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase spiClass = (Class) Class.forName("com.sun.imageio.plugins.jpeg.JPEGImageReaderSpi"); + ImageReaderSpi provider = spiClass.newInstance(); + + return provider.createReaderInstance(); + } + catch (Throwable t) { + System.err.println("WARNING: Could not create ImageReader for reference (missing dependency): " + t.getMessage()); + + return null; + } + } + private void assertTreesEquals(String message, Node expectedTree, Node actualTree) { if (expectedTree == actualTree) { return; @@ -1347,9 +1359,87 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCasenZUy1J%DR@HQKH+i=R!g?VkBL#wig#kSWHqhM?u8O3) zr5Ok$FAsVK0)dc0NH7mUa6kzL*g!BuAcUV~5XcCI`0uh64Bc-USb&BV1mqxGV0VOJ z`@0+zYzcz@-8Ka{b^|-m<2~13M^O_yBU1_`3p-~!Cks1!3T`GA5E}~%J0A--A1f;b zD<>Zt8y_1tKn{XShxygt-5m&&4*NIX(sa1L%eVK0KthCp`z;&dzse7T|6lUMko~Lt zfG&Q^3G)Q@Z{9dGzu5g#=DUBV!#xn%&#@SY%Y$I=_CXjwA&|)%fCJ3k4CpNg4H+5v z0W#Wy2WZ%+D5%(Ym}qF2c!W4OcsMwO*l0h~@53+PzvnRMsHo@|=vWvSShyG%7`XQt z2JWvW*#B=B+_aM&<+bHK0>@7iI60Vxq+ex~0;h64-)9sv;v z`2h+lUzhl zl!;M4#(UXf#LnU9KO~Ms^Iqj}>B!iL6F;*&;EBWsRTIh9EN3o)VYOw`izXmCIK#0Ck2RvgO86lmaww46R$I)#fqewTM9C)$sP z87>ybd`jZGjoCd`0Rh+V4pddMkJ@E@c17ha&)w___OY&hb`q;KAvMn9fKAG2AH_x1 zoRKdrG?eLEVR#41_^jZa;R1QJGCph6r~S!AtaDkNaI^42h#On?; ztQtF6dr`Tf6|G{EN46vRjWh5z9OG5tINx}~Xn8REIAgRso+Bj<=3wV|h(LXDdx{rmEDGV`cvigsRnZ2MTE>9#LuSYAx?g zIAhyR{9tNRiBmf|>#G-KzQyHL40Laicu+qSwqxsy6wfp1AQY**Qb}{BEB08$Agz)q z=i=fN!ev{;mJu3jX#c7bwtvXfr(w5b=c%>z4e^x1z-gT4LYAO=ZD$HS{8@$}(}kf1QGmyqJq50PQ4U?sQ~sw_dv_p|_KztBpO-7~rT4T)B0J4QS^BcG8gQ45yF=%5zWb$bBs{eMO zVI^P`2*uXq*U^rplnqvb0x6vWoLRO}mY=m<>$z$>Z{8gY`RX_4=FSFlzcQ07LIaHx zouil9)i=v$4j5|!=40&RfBFJyCtsG)h4F^O1Fz}#pO{_C}Qmx#2_dbFl)iz8-| z#S40zqdn6Y3(5z+)zt@QRVGo5!?Gm4#w(Rwo9BBBb9W##>zr}Uv{5Vj$Kgv)v3K=f zX`gM+pvn;IiiZr|JhAZZbK5WAfAY;c5#NhsM78l-q+fqQ+Z>cub;7pP?!@aR{L7ZN zbgn7*Q6o74kx*bP;%pa(X-wO7!SVGD*|lu= zuJ>Ej%X!)K_Ztmn%Lh3^SvuJd{_dAMbrABj4O&cH&qP~nVcJiLEbC=?oqIK;G)F#$_ziovKw?r_^w$b*UN(qy4jmz z$RqQuf`vQK1A_~-))Yt7ZI`_~sywLc%3NIy$1Ql;V2ktW4#b+8m*2=QobMeDM@@4F zTAVf_ZkRtX)gQQ+VD)IL_v`L9t;2`&a0lyvOBXzRIz5Zs*0uI-2%1N7Aa|hgu@e24 ztkl`AMhT$H`bOc5By_Q+&kE!!m9j^&H5@SqGu~kH^cfwEI*8|djb43%t3O@cD!0xc1oSV+MmYq3FBeH3!-=SC#;n^!+`WqO+sZ=E{1^f~cvnJ>he` zl>-;+w07)~GC9Y=CjvzM+yiMt(=|T%X~&;p$ou%ycGz3g`j2Jm-s)za?Kx0ya&}e1 zT#J2#<;(|PP>z8*FFx!M_4r*Dr#XHegMONn$1~m|Xc7neG;lmTQ}suS%zFqLcx{TiS zdrm7M=8#aycrv%(dJ2$m4tfu2%`PQhajE!}}SNp6~YuoQ3~@JWcCA`J{+a+&9Vydo=$E}sumcOZ1N=Kc(D*(m$ytxGybZOG#@hg-GCsYb2M zI>zpfE9!auYj?7e^tgxKs5%WN2@8Y34f77Pgs0n(re-*!qcSOLpiEr7Cv=dhJI-$2 zn0k<&6?tSop5ff2<6%&2Cfm1vqq>7VxAg@2V~31v2cCa7m;cK2=qde2@7(6CJ5azC z`Q7LmE9N%g!YDa}QO8$Jk5qZ1H6HtDwj($cc*u2)*sY>kZ@0h|2&A1d4SLl1srDXs zbF;o4@{IV5nskPlp;xJAK@yg>t-5i0ei9+xbtMb7-`ca*^RV|KInl39`VR;apm=OPrhL8bHnq6EzQ*&w{G=)E#3GSk%{j@@ zwxEP_WmrLQwX(9Q6NOG_A^Q$gHFMQ0e%joh5AO<>PlWI29PhuN-P9uK?k(2GRn>7D zB(0Q=?ajh%#9O5XB~G-ifyNqK?+=K3G2BqY4e{OJ8|vwuYrym|!hHLBo1kNysDCyE zZO*>~nGL86<(QrFIXW_TertGkzKAu=lxH8TzCL-bf?<3k<>|Zb>o0NhA@mB4VY2_y z9kkNa`YNcp|KglYxK%6CF7HNC7)?UrvCKPG2*?+o%qm(gpd++9_O3HsYO?c;0VGXPQglrjmJP z2d34YX%m`)^w~{&y9C-3!G6ltkm>Eo?NgzNx@zrS{2F!IGqwF`aht9@w(1@h@kG> zvu}Mz)egLlEw5Ap21<>09XuF>Hh3d!9S+VoZTKI>Ro1T~jT|`8o+!*EPmzmzahFNc z!tPkZ)+cRLT0Cc&B934yyNUL+=)tBm*3WTXe9m`?6t#oK*kS*e2a5UFUy)`ZWi_U$ zY{Z+hF%i!b-#f9rqC0-ijY&pxnS@~Y+%T)dVC*LPZfwoIH_- zea3v^p$DU_!Rc%TV5&TA&AB5N2&E9uW(~ehzq;0V-hfMyYCPT8mtdpI%8qFo3B`W+~9OcpOUwe)@G`(eTsA%u+7_{#&g z+#+vV%Ze;dU1z23en|0&f1vMd`gm)(w)FR<~zJBNM<^&J$fNHt}LDhg4 zZc&`i%Y3tBSvnx68%i&20nLpW*6ZRDkzPo^HTl{dh*vmk4XppPj;#y5S*-K zmPTsW^{hocFkjF9d}H42y*$y>ED>SOT6KXKe{!B(WS?dnT+FhtQ`&MjzUCmDZtSbv z452jG(qhP>`nFOtkv}})PQV-Nd}}v!=F4chylz;75tO`xVQY(FX%A1R;%bI1JTSJg zkMcEBKFp?5M0xjgP-`@GVxMzPf_GhJR`y)j%a@nDxNdw|mFvL873>Z!U*7{S)j+Q% zD9?&`{Jb3oh93ke;BP^8$dM4pVQN$7(V7l3>3s3r{>|F^2b8{FL7f2Vc?bFdaKmC_Dk3wtm*-)aZ#Yx@ z%8yyKx4EO4u-9X4VC=0?_v5UtQ}g=`HNQ{5f@~vW+LrM1JVkw1A9XztUzw@AI|jUL z`hM{bexLBh$a5z0y@dMPF9Rp7c2&#Rh|ums<_GhGg6QWu`yOCOgegyIgbX~U(7so)rjt-wbxwOkkCx zo@UGqRrASk922ABYDQcjX8$mBEM7sc4l}#kjJLR}da2%!=EH~B5CQuzvfP{yyB-!R`G7v3@(- z?wynOjzXB9?VkLs!}z-aNcaut7f1A;xO?Xv*-y$x|AgUAe!>ra=0FDXs};&m7;r=0 zx1s$d56VxsAf6;LM#4!FB z<|oe!00x2V0jKM~O>mp{)&I8Y?*B{OPt73T^9J3I6&N?H$glmk7RdlC=%)+xmwt%u zK{!bu`!$|>5Cpn+ua*KkGRO?%2(kl#LCzpckSS39$rH%`neN@Zf2I3&f4OJ>!u_rA zzY##;w}AI3u#1fiaJcs@BO2McfK8kfjjW6T=r`k+#?Gt&bbsfc ziBWzL-(&7;WNghTfW!ao@x!?|+Nerussdg8R_?v>|A~=zGB^DP2GP*QS=G?|A2Q$|e=+I5D}W{-1v~J)t{*tt*(}h4=kFIoikui{{xQ-oMMarPX6=i!@g$*g86;_xet{5GTdMHH?(^YM-<5J zm8A*nZ-63j#l8iKKp27$2qOFmJNyYdte^b}JNyYd{0Te!2|N4=JNyYd{0Te!2|N4= zJNyYd{0Te!2|N4=JNyYd{0Te!2|N7%JM8ewdrb*A%|IYEUspCm^v8?Qi;E$y03QU z18Qtdoee46ZEe6#eC~p@KO67?<$E+UEyYg~XKO)PO?f2>XTTY2ZRt$G#>B(K#>mdj zKw;)+Xlv?b=V(pA%EU`DR{j-#c$v!xxF!Vqxr zI}6gfxLBI-8S)yju$h_hFdDI&a4@p6ns753@o;f5nz5Lga`PCm@*47((xOoOHEu(D zdHH|e<6mdM)|MIQm4ZS5K*-DUN!S^?*qVZ!Wh8`tY0SjV80gmiuMg4rc+GeXjd(fP z009im{x<>$+@IfnG6ROie6PZP85c0tUpPRmfP$I-K56&j{NGQ1I{A-u{6}4X)b$@} z;6Eb%V_kpL^&e^AKO+8PU4PW|A8Ft}BK~7tf7JCKY2ZI1{$pK#)b$@};6Eb%V_kpL z^&e^AKO+7=wJyNF^Osl16b!h6+yI{sFm?Qi=)M04@L1iuo8Ulr@NkGIzrJ$4$NWw| zr<5bY!^0!MBO)LmK0pL^RAfX%WK=X16jT%xG^}6q$A2IG`(*N8FbonB5;__>Ar=3Sg2sFlif@ zLJkKc_`h4-f9QpU4gU}afr1s6g;JEAN`&n(2OhN;_Wi7MICx}uL|9nB?{@ExgTuyRne= zxQxEV#YGV%33@DC9^eltpU$-7*jnb@C8u>_%QwHCd@9})CIom4q7Zq?zGs%=@&mJP z)9H!E)E0fb+U#{F_enW)H@jF~KE;0aQo649p!?_pbMTk7gC+YsH3N_KwLJ`^{`O_W zJ@#qD^snoho0tz)+CxNW{g?pS{TEkR*m@Z*wwyLi<=Hu{AVR zV{^HEatK@9!}fD0c**_(i-+Bl0Z9Qv)GCHJg1ailw2x%8;N>2VPs##k&ic5jDb#6n zDL0%F?p?%>%`c?72e7%dT!eexk*qa26awP2i%?ZtTX)Z{uMRgJls*AHlHWz7%xzzJ z*3AasQJh`R5j?79?H6Q1n?H)){+0q6bv!kL)|Hg!;^nnQ$)sm|8uGXUX$W*vzC=oQ zfN06O*OeGc2JC*1mr=HHzhXZAVa^tSm_DfVWAp3P=TDoA8q^M5Q?}^)x=dzn?!Bkx z3>ihILZm~~89nxIrg>2q=7ZB*44*wnTAV&Qo9PDvGa)I|H_RAfP5Y1qor1j?EO`$BYnWhkc| zKiuXMt%M2G(Y@r-ZNg~`e?%>VE~Dt+k{d@%?2)pfnMxJ`OkoTWlTY|;VX5Z0lbTte z(A3BtF_WNL2*!X2*8uNWRC0K37Z+k;K#|&JdBf@|F^6CI+E(?CeAm9MVhmKluNzQW zm$qVaDK0{*F(>DHHg-z{a{HXyKNp>8@v^zPd-P!*mf?|WOg z^`%r5PEf6NctoioCe|Tz99q}a&p7eL;cq-rC@QWX91;8cydr$?#8xlY%(y;dxoGnn zqIp%%CX+38!N&HBm+70%dXFY7`SfiOCb&B^EV+5CISM~5FQ8+yu@gH70wD}Pp)LUaC~}XRt)#Zo)v-I^$rL5{N4QvI|F|VhrU^kZk_^Vu$8^(iq859e^SaFino{l};segNtr!t%GTn)fJ5eFk~} z^`#UaZ@KmBbJfWsP9tyf?CyiW(-AV{v}nX9{5sL0sPH_tGhsaX7t1z@=IGN?s2H;L z2@oy;Hc_{B z;P$+KGMMn85`jx(B#x=0hFt81s) zDVCa!`lK*C5hHRA@++gAO^)ZOt=o5GdThmKw->Vz4c+Qm+)qQrbLZ)PWmI}{L~waw zadZ*h-m2KKlJE!<_vF?Um^& zh%wS27|@N`_o|fcwrMbh=YC+Y~4|1FZl7z{kaApiM6Tz~z zqw6;x@{*uH=Udac$YlIBgYy0HCM4x~(_QaX=vrdRzwf%IX0!!y#^OhWv{;wr%mxxU z8AwULrnec1hU*x`9`--aW95jn-M|d=2J6GQ#vUU<-tBhN> zc2Hkq6)u=z*M{-^3axJExwW5qM%<6}PlWMJ z9ek2|%f&%O=ScC8J$hhTON;;AVFlQsj_utimY9eSj#L$)EUV`$rI|5Zz?v84fO0G&nba#KW!z)t5GW5J<@amT9JbZTNk+bt;Uxh&- zN%UOcsY;aaVaSwOt6ofC+_?zydx0(EXv{!HlgN*}rkwL8Ams33nk0PjH}bC|-)Vj> zCwP7bdYwLmq4*lJ5I6zhMd!(Db$;dSPm{sXfU146FKGOHX#-JvKq=& z2P2ChUbJF=5wgLqwdl2L+s^fOb8LjIpT4zySU|brXcazOCB1eBW>WKJnDnnEjX$Z3 z^}Fi2X1{dZQmUT9k}i&$=-P0Ne!-_bN0GPe&x>Xz4vdpL_uZA@M)Nd%NTRi)#&OxA z#rG94S9;p;L~xu1;Oq}%B!9Oztdp?->#<;JDr1d z8opH2mrJY-_WUZmB+@2{{dH1RES72Sg;NyG27U5u1JP_zKXM1(+mM{u9f-><>w=~v$e-|Q6J~zgdfjg|@Tpw-!_}9jFhj%?n}fON z-EZnjn5*_#G$r`PNMD=HVw$`^_I|YBIh3)`Hu<=D_8OImvD)Ok1#0=I>4~hYaqx*> z5U?`AWY;?jHhsEvn)lBKwZ|j!=h+u0PGTFDmlvj?8Ka|&7sd-s0nNxVhh4=|l83j5u zOx#*;o0TBt!mjKa*kDt}zMQ)EHEinZFJ^mm66u|2z$V3gvG*wab+Os*f1*?el7J;~ z?w>2L_s2a`bPtjbzqNWn^ROh6sfo$|vYtkd=rsyaQjB6avp<#r z4W89CSJVxi^l1iPULoZC6aiCo6J@kqD8RxKQ+~_jMY*7S#;K^@)|@4stNoTOW+vUX zl_*)<;3$OaEd}hEMtQv;ddBDaEcJ|2VsQ=UpkiwWTnoNrpa=@z@+8tJsN{4(!KKvB zn!}sZ(n6^Y%B?O(fM1fv9ZngAediTu6-E}IPESikx8^AbTdFLsb5*A^1)Jo(sv$4+ zW@xM&m(vACLOE+*(qz7?S<(5K)%I&WE&aC?DNV2!Py4qx9r5bRE$b<5i>pjc_^CoS zIiEJpi3kpHDLf5)SSmGqiiHa)`{L4yv)(de(~84NFeRWUD=S8R-|Hi6 zxw*3^EJsvSmi)V2i}oeiOcgo+HaChCqoW(jKe!gwD>7Md=!uYY5TD@Z^OHARVfuO6EL z@BJ`PTE6jRptK~EDOv_)jLtJqKVqBCCDwTqg%o|8&J(GEPaa7|Ja??#7E`N0K0jSO z16e7<;3K*iohtRP8emYHUP;B!Z_$uY3Y>)sb^35von(ix0B#k zJxQnI0o`tL`igGSIrHMq`v;N*V#>bTq{~oB3B+_5S1OK?#PAO;lqU(Z6-|G3Lm-__ z7M`grewu?h;`_^>VG)I4r@549-t^AI|E1G=+$f_KRv@Ny{?K#(ouJZRFI6xA9>YE< z^WP*B1U=Z`YaQJsjK=CLn}?S;VGOmLd@CvA4H^02St_Z=oe-ZwNFzmKNr0}SaQT&g zuRKkj?4vS6){a_m=2gv=U4 z1muM{^Tkq=k>69qhMXl2oZbi&4ByV!vG*aI!wuP;j^t?h=2AN>l8%TCq{B%k^D?W? zI9qyHl3Gph$S+iP#%S*+o$jf3^tgU5HV=)bn$Nn`m=%yiYAc7)tL=s+CATEFb*S3E zWwN6b8)=Ko4c+KaXrFm=_`*DvKQfa$v^17Sr*?>dr{2g`d+!s|81;BDHs(tbq5@oa zW6%B{OjBoXCwzCjCjw@hu2vAvzOI~z5JDLRh)+uhJ+<=^9jkuexVGs8*e5N~l=Vnv zSM!9@Az{tNl{rio6w<4%gvZLohZM%DZEf=jrC<@wwTZrWz@+?fL^5a@fj|^Z98^m1 z0!}ADIuLHVA=9Fx{Yydv-Dlk;^mHN=n^Y7+Mj`DM{7$L-h%?}oLoY^IS(%eQq!U*J zSepTq;Scm}T3F8SEq#Ibs1&3>sZvbi*OGAh>+4h^dkXLO~P+A!a57C&x!9jvzZB0r zEBs`6OAwl;?V@dnr}eDE%2o=6V5Xp1onBln*!2$7{=?Kdg#t-o2oIE7uj-2A4h|zZ z&+Hv<%b^>xtiX>Kdr0bMdD^H`Wj)73v^ZZA$MHT zteHlzwDyUP{LZ+2WIv1FMW!{$Jj{6-y-N8a%k&q-joKT? z($)|3h^@cqAO$X2mBz`uAO5td9h&aDcdXr;GV>8rf>-VTja zg{8fjs_AeIrl?9|4e)&7ENq(ntu^oTWr7Pz{f3vbolz8FbbJ3cV3;AJw2Z~qM79ok zf&n?!9!KQIDijpG&ZN(n!)rcZbbKB`t7dseTjgo2n1Ajr^u~uT{!F4l0NeJ9?jVae zY@S}WNis092Nfe)dnVQPB~RJ=EK)ASwBKC%6#i&t7ugDxOnmB^J6+M`F~aQJCgod? zQN2vH%(*Z3BwOB!t;UV#8{T$J==-3={DKkArTfG1TF``R5E!hsQsBPYa0 z2XV_FjDg91J@#dqk=GAOu@b!6w6q|e6O zh_bg0szO1?5P5}28<{PQV&3{>hhn;r72Rq4V8XA)DGnN74`Z0oh;Q+dDsKx%+BJ7x`r(BI z8WL*YzS6!bb$C2bX6YpURAOPxCO2j+f73SrB6lhUG{aSI!P)ST?8C))*wp6)%Uh9vM& z@qb9muPa(+fN`&%H{i8T%srQyIrS(Ldqb1-ITtx}WyuA}jw4ACF%7R#sjhiP7Oem#vUt59j z#cUpnvilM)3Oo~mCEa*tA#nN6yUx5SGRacOr!yuDJdx+wd0A4!RQm*5!o?V7JZb4v z2I<4Yg&t({{MRnIov#kldc1ob8QeTmKxB`KK^K0x$PXf(1VQf2ncsX#GGkEC3QW3P zE+25st~|ExfIgo_Agvst6h#m1Mr38(m2BxxSyU;82FQL9!?ej@t8APL+w0fG#5zzK zr^sSddP@)j=YPwCV4XraaRf(Mxb=`aple^Q>W_YQC`qvgdY>)cXhW z@E`aGNyTj;V%ERjXW(FjDObPsH?GWGSM*<+e>m={yEIQb9-v)IRqPd5i1Q$4IQG$6 z|5*v3nb%T%f1>X|3@5i*8`l-RmqsMk)VV%RS6eFBJ5dRn5GD=n6bNjoMsLAhZ<}HN zd3`D!7HPCV>usw$=05XkK)JGFd9$W0MjIkcDNcMM*p+ZztD^2HNEktQ*+im5?%Ns0 z@pO+-m!|%#TM`EDlucuVLxC@5&=irwMbLUBF&)7=OtKY0`%QTMU>b?8B*|aWxYULsAq5wlme5X(JnB6fM}?ef`s< zABa3Hu|~L_m4v`o_*@bApjFifF@#_1iYkMS8NJ3K`Gi#X>AMEH3QK(?DNv6HjYu2W zmYohs26TPG)@X_{vspV5X-Lq#?@J#llvOZ2%q69d?=*p23#nfai=IILGld2%uSLY8 zxh=&X&J%(wgvbPR$n@<^t(N6l9F$6t(UvR1A8Dv8t6ZjmbzW?Op`;5eMgx-Vzz@Qd zwqCjVw5EweNGxG#UH9HJK`193G=uojh~&lcA}n0(r^JI0bZcUZlA%VHJ+PZn^W%t; z^MU6+##h@S%(9gYnTJ*^2$PcXyPGMu^BeA1PYdQjnDH+XEPv2|refmUxEb*0hmy}9 zDT3ZOL}%*bB(>^{>UnI1hv0dCbbBNw(2n#Tx@|wtAk=7)O6>ANR>)JNS>e!WTj>UU zmT9R_?}8E6!7;KaIBXYvG3E4vFj0<(#4Q-k2`?d>L&I?~KFZv3S8DF+eUYMWK$it} zG_a&@2iq^tL7rhRCZ>E8tg3)%m>;H|1EpzvP11Bzv46~0)e@T28SeCkv z1nLZ)&a8PDV8PWiHuZMxCYu0yTAJEe_8!=!lHjTEv4XITdRyEe{H}ll*VF%J(VBD61ja>z!v>fPreQ4I)LM(1F!RNK=apzX*Yj%#Y zbte7eGI6ludSDHS?KjQST7I6gyl*q>S)x;K-^KRleD}FAWl-%%5hxM#dQmPUPtrIq zAnVT=(wolfVQ<~V`jubRGs!y_4fohJ8n3H2TTpt$FhOlXs$X6$<%GG$gH#W+;ya?3^3x1z2`fGw{s( zVt;s3bKzTGC!}DKUY|a$mGjjOYYtZM3TMXN)KzaX(pGcg->)-`e9Kl0H^P>#;R3b2 z7NlVf~{cE>5a~T+YY|pwkG3RsW*cVF$@J4j7m}Wk%VUR zIzOjHbQ}*PjB88;JjR|!yRevT$6>HxP1r{-cBN%?`ngUPH^L%Dv7w?%?v&alw`Ux20a&`GNg+lXGV7JkYX zBxGM7ky;s$NFR$mVw^-z5zgwfijexu1qGfDh_{fd#LUd4 z_tl9CFni7QIY=>GHE)|lH_&W(H$J_*164MY;;Nk@we?Z&PF@DsoJ2h=A$jwxcA9H9 z`!%{2BO-Sm^@~eLV1VF>0n0;-<2pR)TuN?m=}OuD=*4R+D@Kf4)S6=~1KyW)8d=~R zyk{`}Zzh(nf82p&KLZP(RdnXwfhey-X1!$xW=`m;RVsv|1q7tglAL6(#c?KQuC{Zm zvymc=k%Dvz?O-@($P?d)3Sp?6qrjNz%F);y!HjvCmLQil1Fxdjm??dXJ`9^Pv^0vo1I@9o^^+yf0YecAql15Mvg3Y>#_T}wm_ojO{$GZhh4U$1^t%M={-Ei(sU zO8ZP8nY|=a)i{FsUmU&mg?g+tLa)^X_*(H&}8*n3Y#QjK)r6rlAn!A~srN1q&H zk-^xtTf){BA4j}?K}1_VV4fVC?a9#GN<+E<3Ff0&f5y6oEwKDlfuVAB>>vT-)}O8X z`#05wvV_mNh!I59jXkddbsD;^5SrRngnJD4zw0hu7~!jDZ+PW6IJ+lW(3FhBISZ6Q zAs^N7QiMENkc;s*7V^(AC+4X<`d&%36X^^&h+-{SP`$QB@r|c@hK!*9a=N)sJ9*Fp zlk(YE=@hHNLe?V%1x2*Yn_B^NtruPkm4$V z%7?4#HzAH!W~v>1PdiVV|1P#c@Ge_{v3E zc=ZD1#Fy3m@lo`d11|Jz;47=V9z?j^nEtHg+JoldZjbw8{%vySW~abH#q0?6NcTWK z{3m*(=^do!bt^%NyrmYJ-X!YHXIe*|7?2p>_h0EQZ_YL`mg~6tiaG_+3HGc^bg0JK zGqT-F8-JLk(s;PzJxp^xM2&qivm{*SzvZ?q33{Y(qR9SW5UTyYs?&PDFtnrQ%$Js% z8fx%kcyKFC8E{eKykWv==1pwlIvR+6&Tn*R4}mNb@A8Hn>CdsQ8X%tYZmJy|R`4D? zJxY|hXl`!rwO4h5fa0TIR04!a|H2V5!@=@AdxqEbo%5YIN^FtIE&xqP6pL-Sy?|a;b~* zOjrBX4$M77h+g_!e5`526h3>om5>)+XH_KZ;!{ZK62oly5tOC7Vl!$LTrzYNnp!xw zptfVRPL{7e00b|nl5}8p?m(g94SO4QOC~rjT8mi(l-7z&8ZV<0o>~+yY`fCIn|eW6 z)VaIz@Hi)E3vS3xOfCcmXn#Hr?ATri3n(O>b`aM~KMyiBTpj;Ro2aV89Zz?5IDja|rL0zLWE+2D zaGZIF8_f5Fe*96^#GK=6QQcm{n=Z6f0>eG6n_jcv+ceKDm=m9&s6zK7PeCmxc}B2! z4vPPT=tVT|MOWp;0QrgldBmX)`)-1(FyWc+qs41;zoS&lPIupXXzAKKfEnqYgh#Pc zFuK#7P2nZillg_S-_Fnb9{{!eIZ!o5W*{*F+s`8-Mvk+m#_%UG)dD^1Ew-{IYKL~G zv#7@9Wo!ImatL18QoFJb^|iDx9;10e-D}yIgcBtwzu-#qOB$iR59qa^ zPZqF0rA}}iQU_-n&az*&6+oY9yoh~9$~t@-%N=1Vp7Bg|K>{lCvR-4DDWc91%9!ZM zT-+Pa#P4@%ow3cB&>n0eZ@!VKF;*#*6s?@E*_6)GUhl^Z0m1|Og3ko${aRl>`goj4 z(?83N;`1rvRP(eci-n#F$?{4o?~*C|B^{Oflpp_vGOx zL||;L!9&vz*Low?DcaFoou3-i=Jduh=MF;oU1&apY}0r*bnqleVd^BeZSD=K+Vdo) zVQ+GRn{7Dya9i@S-DM>aRe6Jl=6RFbFZF{|FhjNCJzvld92+ER6cx^`+h?5@s6D6@ ziu~Shrne7^XdK!<|HKS$<-CvQi1*b04)i{+{aNqZdGhDernE9SovS)Nf>5xAa7z%W zGz*kpO4U8_`b9{HU1lSe5gN~FX~>x8DT9H?I=Qq@RxpWzO&R5vr&7M;EX-IbZJP(fEy?lG3I5;0D(0CN+B+PsF5`VFq;s0z zEl3NiJc?3l&txo18fYWCy$r`}&q;#F&wqY9-?n*zHnmk`KwIP(A3Tv2zRc5>U+IsL z6iY8io!;&a31jCkbOF8)GLzB67f+CfmKX<-G7m+y3UX{e4abMVtG8|1;!b0~cx#Rm z(!vGVZb>{ZFt%&i<$nN=3%Hrrt&-t-AA{NFQ|S3bRQ*Xu+o11l7+iBWR|U>|Cx*p zZqFfA+jJfcfqC_hf|G@w5Y+bC)j

gu^N7WreImSmAuYF9qIlocM;CCw$jd^%ieDd^&AQPK;Id4nTL-nlqHYYx}H#N5te%aJx z!W?7*;Cu)>HzDsP_|eofZ#XGg#7dTacF9{c!u@TY(9oMLBNqVi)tShc1EUI_$d9lg$(v#&J3=Kv z3U$-jVI@ZdfQ`t4Y=^Wq`AF4YiH4J=$(AZZ5!y1C3(ZttDW87Dk;#)HdRr2~iJv3V zhY{E>l&~TwCpW%>s-W@1iTZb{O*R*`frurWRqYA)SfPee)`+Gnv z$L2~cl>s=4I}m1@L9Ol13Aww`fZvV-4wfk=$ChEq?n_C@8> zhxiijd|J&6;1lD>zULIl&lNkM>3@GkvssAudSHAiv1esjluM)s9t#=m{N{#zFsS7E zGuyJiWT1I`Wo4~WbjHFs{Gr*x<8b6Rv!m+`4i8Bf>u#Y()q(6HQSU}}E)*$9OD?9I zpuv&RSK@ojvv!MDb)k0q zUkAQ4m(lJv8J-jD`{LYzESNs*9|#@aw(T>|D(*J3A29C<9pCg-o#}t!g}siOqAu95 zybN%1cv^lXnH02?7-W+_#1y#CnK6sx4(IZ)x!W?>>j}Evi$2RVI>AA<3gCZPX!dpz z({LRz)FrCj#(STIRSo;*D{rAr237-vqj0|Bwm_+=!0#Mb9?WsuP)ljfJ^1$-mE&x z3)DnaGpSFG#s4c!7qaMpseIYdWp}nqfcQujplK>liMmmjD^Vzk0BE;UBfkp&TKq25Nnah?VgUxXiZQ}uI;4Fa?B4#j@SXc_dzDU6mP8Pveaj3c& zw_%;}bky)B%c*Zp3^JaKtx91}{?Q7We+gJobp10EI&l zNcO(QlE{et5E@d^WVtw~vaPKcxj~1ztmfmGyv3I?-ri3r*aFT5EdJrX{H#29M|4#MB=5`_X#G+?yd5lYj1XQ#|BZE-7( zTaAv3hLNj-$PHjTRxem)Dy*1oa4RK6J2Mp}IoKr1C{6$cl7s?VKrhk|hs9ndV$hbX z82*bA>vkwdB-U_e$C0rkG%w)WV-d*2mYuQbvFeibeA)Lc|2Pd(>9~*H;0&)U~Rtsu#&YJ z%sB>E1KG}lGd6O@Q4=Wvu>sqYTAOz`M5Z0XA{TxXNg5CH(Ym06B<(V`&5D%pA-Odz^0QDN)_cJEKXXBzb;-%(seMFm%V?hO|?TH+Q59|9Xed8wXWo0km}&=DSfa;fA8A#oVxhnbqnD(tunnr0!BdE(q!y0RWXG3- zl2j6vcL&T7N(~vq=3^4nur=FWh%Mlj@&v3g7Jw+BFsjaXDgBlu7K?SnP763+ zU;>nJoYZvU>XVPexwTi0CGb~azYI%bX~!OAIA?wo3wX?3;UuCDbs!o7%nS$0O)sWW z3T(r2`4b`7LzBkIWTYWCU{~8HnklyA@bW|l3wNWF;xrgMd5B>AvPBZ9v52OtAJabf z-UKu|#_4Sg1+}Zi?x%<(7yxCmQ=+VJj?7trH%>UkLf*AFx8fRNnn_fS-o_?`nt~_k z%5C?`5fjC*iq(b^po%K%x)hq*NmC)D8zl=PW)2SsgRxT<8*~8ny_1>pdy?4&uEACL zmNH=D5zWDk%kC{OAM6|&cKB!B`@|8FU%A~}!5&>|24Ptl-cEcm{@@|6hAA;?x-$ZE zaEo+t2t$rrZI)`g#c>!`t!lQ9VE`zgCkBh$ccbOEFS`D_wmUdGfClQ3)2G%4A zw5mCPQLrBFBS2p*!!FmLJW3*EtqPpf_{GHX0*$G87>9t-7+qB3Ysy+-`q|X;N0d@x z^TV9S7$EKV7+N|^cy`ARbgT6{idT~5lM(XEQk8O5MU|g_+%_n)7UEVN{{WfyuJEz7 zTVmW|ETvI;gczh#j?$U{Hq1&W_H zFsZf@pc7RxKm~vL#1XYMVY(JgZ?vPldd%t}uw|qr%111mdPSW9yaO)~)JdawQ1BKD z?x71|A{@d}JCO853ZMo;B^=#mLnI5DXfUG$14&{O*=OKI5lkzn=PWAD5a?@na#Ie- zAT8F#x`=)Su_fB3khx15q=ALlc!8&dm@GY`1zJ1MfEcc|B7VYZR*PDfZgpni1&}59 z9A22S{nr|16?LlJ;%$XC*m0Q8%`z_^awRZ&bw>#HpxCPFqiiy$hbvatR26xZzT8#NKM_Wr=GA4{ z+(29^Y7th}^dx7G8$Gw1kge@x%{89%LyW*HG!~X@i=&5!Uc^{U6Pp(~x%aN|q75cP z4&2IBZQoIuC;}}Fy@ZxIWH#`;`pcjqi%;C%U{E2H?m;QFqZQudpk+cNrt}wLET~c7 zLvKaR4-2JGdcqUR0l|Xa8nD3a%2yiGc8=<$XG44Nnmg(pjiSKR;mr(j^pHl)ynW&?+v0Z#3eYH7$`R0fn)k35UtRGz~#& zfE1z|aG2y?sD;W3Qtel`xEgH|6au{vWvDPLZgXfY){NFT`^1BjzU)x-RRfIBa&QK* z1M|kWfI6RY?t%l$OW2Fu>dcB-!2ee zORS@J;svmTQM(>;J>sKlIyN9&&fzE|Qa$5rgo-qa&ul~(5jm_n?FLnD-RX|n zqOE4D`i@7uAwjmVH?OL>rZYqleMnMQLB>g9l~;DEx8e%S0M0Pc+H#7jI}|iXDGLyI z1DB*Kg%oyZzimTJh!@Ef=|TShYzl2kTk#aFfms(d`#`GIjFw;SWd|&a6r#o|(5@ux zu0oCb#?LeWtKcKK?MP%(TipAH89F5QkTvcF4{YuJ#J$q znz}4u4*lihRzmy*d#-4k%H${RQiYo}H;HgKqDVca>dz4nXvUj!PsCsvQHzXLFLN{& ziI8}n!T$hY5@0Q=v-2~tbT(0IxM1X@P5`S2V`#z9sgy;|~i zgZF&I3W1RnGT3ezl??NU0RkYU1@Ka==a-nNtXX;DFJjxc6d~CoLlef$>~2tOTR}oq zqjCciDN&|7(hQXtq(GqIhj6Kp1Z>M29EymQv2~8ZYH6|#;>%R@PqFuw!%PI;%@Mmk zw>g%iCWTTn%yoCs8>KJ5F~sr-ggIY$5ZYu3uGmMAG2ssC6w0q_2_Ct`9T6(@O4az6 zEH0~Dgp^f8ek@$BwIRbqdnqj2{gL`*Eo&|DO8v`NdA`n{xMy!-=DeEVOA}dyB88xO zz`>}cOEbF;DjNb08aIj^q2e6EwFeggp?cDL5APB(K~iDbJj1k0Y8UpEC zs@*VDG)I9#4MPYDr)7`6Ofm_#ZNx5;grKmO0Z5~Zvx6VIN8d==Pn5=eY3dLJ1 z6w`3F0rHj7yHq#|AgTmZebHG1%danVn^H8u5^@@wlyH?tdyEESSOhyjZ#iH=0XF2c z&svLjKDeq`y@V3OG*t_e+{<7lr7d>9YMDokWd&O*y^_Lg3G_;xUSX)EA=j&;jFN|n z0>*;o=ihSTqhgJ{(M|}_q^3B^2YcEb(!QB6qcOArS8kDPxW8gf4W05YBv@9#NR{ru0c{{XY>zw9XcKbYaav-2$H z?EK7}pMNtK+kDKX&%c=c=lhht$L~3RZ{}k(ecZ;-2o<*&>f`^~00;pB0|7q}P{lgZ zx+DJpfmA^!>Z%8C(r+BXQxeRmT2(M5N|Z{8Mf4t`rF0GYP5KdxnJQkphg!55NO*)m zxQeg2N-uht46Vt;Vk?N1rWixCxTI6&jzSWf(kQ9n9?9Yz`c1kpI*L7h>3h zV-;{^{Q!X#%q+#8jH8)SDSa!59uPZ98^St<8@ggDdZ%7l&!p*jh_Gh*y0eG{8-ytg z`^<74mo5|ZPH|>EFQgsgm_i_tapF<6QL@M)^?_2XhodH1$IwR{;ONJ?iuW}PBa#Ls zfR`x6vv{cFV%$qgn}DJUXB|xQk3DE{6#;=5Lsjh&l{0yil6I^@mAO+SwU^T4o^yYp zrWXY>3m{CojhCo^QWcr5aT==e^Z@8*oWYmTXA{y(${B4%JR%&!xQ^Q99;V`~Lh$M? zZ=B=y>2SbAmY1`r2V$kUUSN$>rBdaGeBlcrt(;s{k`i^ZV_#U0WI9RF;B8{oVo!GMHVA;E9L)k)W3c8X*EYRlB|~8EAyn*$l7mP%jlAVHmo8-CraZgLi$PaJ z7h{<5hH|?w!)v?B&`=DbZB%5musD0c~l53>k8 z5f)|OhJjG+g3$es9`^-ysEXBf5&l8_eW~&P<3t*0e4MXG+O2aVd zEzN{uv3N|;+>tDKoc$rpI#_iuFxu#%l9lle3lxw}_>QQk?xG%*?#YGE3P|-w~5RD{%03 zmx|(8Aiy!y-s>>DL{cGTOppjLanV{W`b(IZmn!ippx;1@782r%23)&?5JnT1q$TB# zc6n!(d1sbC`KNIn?EA#?&n)g|mL1r4Czg3(+?~wwLT5Ao+5iXv0|Nm+5GK$c{zxjS zs;Z2yUZu+=b-1wWWF_=mvNICrA77%d%m_3xLXbCt!l-DV$rsZ}dqMV7BmP1*Fd2O9 z?F6W}C{uK*Y>;5aku7R6gx!KF5}~>4G&&Pcv+za(5q8S~j5&)eTEm<(pKZgm+6ZTy zwu8)E3AoPmj_hMpy;1WW>I+1CCdaJQ>tnpWE?PlML2lSt;#7i^$C3miE{c>xpLt2S#sv(&Cgz?$}-DA{e-{RL4Aa`*h_tcx9lTL*TFK^ z=$G;EOZ|ku*hT$>zt~ItguFeIbJyq|Pf4T+Qlo+e=s|@hdCjoISm=;CB-!gYdi?>v ziGmIy^h=1`FTBG917;FpO(5IqM*jd=!`J8vn}9@&SyNy^O63J;%%ydx*6@I{eJWI_ zbM>2^zd>KARVv`oz@ll><*m-A1BD2PR@by%h5^2}F#g%bAlG>oz@pf`RD~QxeGR z!_YJ4i@Z?cP@ZRKQs9*+V8jy$?M;tfvdm<ytq0ny!~d!uhAMx zvSWM0P*Mw_3<}pVAyc#(i2Tl8vr1({7`uM6W7cceQV6c44DMYpfgzz_681-B!qCiD zh(mF?NK>Dz*!7x6po*9=*hB*0!8HmA+Z@tfuQH}8V^qYOJ!a>u)aXrd(Q@gyn}=h} z!4+O3Rm))Zfodf!ii_fEfj(e?b7{NQaP^w?(FSo22$83lz`+I_OE_DC+T1&+w^xW$ zJ)zSaO6FinKws8z^@klQEGBHwNWU;RAlH^{25uR+{pwhd-c{;g7+|=C!!)Q+R=ds5 zS*Jn@w9rjR?mV)A=>&gGDV4^ zVqk{g+l@`-Gk>hu^_q03UkRv=!=PL%Pf4;amvLD*nnD!_GRtKW)7ETy&3ZAFxqBn3 z^B!Ta!Gdzf4u#Cp5tzo)8ndZNow;c^dd+$;WKz>8TtSo8A;Y{Ju(=v83Sza;HZD}c zQN2psLkO;RJ!YM1!>Dy%;sbBuX52+)EVBTDa|JNwH)iE`5$g;Bicy`#e99cecbbvI z);vNTYomYsY7vBI4_K*!cqfK