From c2fe5ab73632057bedc759cb9fb42f7520608273 Mon Sep 17 00:00:00 2001 From: Harald Kuhr Date: Tue, 19 Dec 2017 19:32:32 +0100 Subject: [PATCH] #324 Support for PackBits/LZW/Deflate w/FillOrder 2 --- .../plugins/tiff/ReverseInputStream.java | 57 ++++++++++++++++++ .../imageio/plugins/tiff/TIFFImageReader.java | 21 +++++-- .../plugins/tiff/TIFFImageReaderTest.java | 1 + .../resources/tiff/packbits-fillorder-2.tif | Bin 0 -> 71008 bytes 4 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/ReverseInputStream.java create mode 100644 imageio/imageio-tiff/src/test/resources/tiff/packbits-fillorder-2.tif diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/ReverseInputStream.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/ReverseInputStream.java new file mode 100644 index 00000000..3a76b64b --- /dev/null +++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/ReverseInputStream.java @@ -0,0 +1,57 @@ +package com.twelvemonkeys.imageio.plugins.tiff; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * ReverseInputStream. + * + * @author Harald Kuhr + * @author last modified by $Author: harald.kuhr$ + * @version $Id: ReverseInputStream.java,v 1.0 19/12/2017 harald.kuhr Exp$ + */ +final class ReverseInputStream extends FilterInputStream { + // http://graphics.stanford.edu/~seander/bithacks.html + static final byte[] BIT_REVERSE_TABLE = { + 0x00, (byte) 0x80, 0x40, (byte) 0xC0, 0x20, (byte) 0xA0, 0x60, (byte) 0xE0, 0x10, (byte) 0x90, 0x50, (byte) 0xD0, 0x30, (byte) 0xB0, 0x70, (byte) 0xF0, + 0x08, (byte) 0x88, 0x48, (byte) 0xC8, 0x28, (byte) 0xA8, 0x68, (byte) 0xE8, 0x18, (byte) 0x98, 0x58, (byte) 0xD8, 0x38, (byte) 0xB8, 0x78, (byte) 0xF8, + 0x04, (byte) 0x84, 0x44, (byte) 0xC4, 0x24, (byte) 0xA4, 0x64, (byte) 0xE4, 0x14, (byte) 0x94, 0x54, (byte) 0xD4, 0x34, (byte) 0xB4, 0x74, (byte) 0xF4, + 0x0C, (byte) 0x8C, 0x4C, (byte) 0xCC, 0x2C, (byte) 0xAC, 0x6C, (byte) 0xEC, 0x1C, (byte) 0x9C, 0x5C, (byte) 0xDC, 0x3C, (byte) 0xBC, 0x7C, (byte) 0xFC, + 0x02, (byte) 0x82, 0x42, (byte) 0xC2, 0x22, (byte) 0xA2, 0x62, (byte) 0xE2, 0x12, (byte) 0x92, 0x52, (byte) 0xD2, 0x32, (byte) 0xB2, 0x72, (byte) 0xF2, + 0x0A, (byte) 0x8A, 0x4A, (byte) 0xCA, 0x2A, (byte) 0xAA, 0x6A, (byte) 0xEA, 0x1A, (byte) 0x9A, 0x5A, (byte) 0xDA, 0x3A, (byte) 0xBA, 0x7A, (byte) 0xFA, + 0x06, (byte) 0x86, 0x46, (byte) 0xC6, 0x26, (byte) 0xA6, 0x66, (byte) 0xE6, 0x16, (byte) 0x96, 0x56, (byte) 0xD6, 0x36, (byte) 0xB6, 0x76, (byte) 0xF6, + 0x0E, (byte) 0x8E, 0x4E, (byte) 0xCE, 0x2E, (byte) 0xAE, 0x6E, (byte) 0xEE, 0x1E, (byte) 0x9E, 0x5E, (byte) 0xDE, 0x3E, (byte) 0xBE, 0x7E, (byte) 0xFE, + 0x01, (byte) 0x81, 0x41, (byte) 0xC1, 0x21, (byte) 0xA1, 0x61, (byte) 0xE1, 0x11, (byte) 0x91, 0x51, (byte) 0xD1, 0x31, (byte) 0xB1, 0x71, (byte) 0xF1, + 0x09, (byte) 0x89, 0x49, (byte) 0xC9, 0x29, (byte) 0xA9, 0x69, (byte) 0xE9, 0x19, (byte) 0x99, 0x59, (byte) 0xD9, 0x39, (byte) 0xB9, 0x79, (byte) 0xF9, + 0x05, (byte) 0x85, 0x45, (byte) 0xC5, 0x25, (byte) 0xA5, 0x65, (byte) 0xE5, 0x15, (byte) 0x95, 0x55, (byte) 0xD5, 0x35, (byte) 0xB5, 0x75, (byte) 0xF5, + 0x0D, (byte) 0x8D, 0x4D, (byte) 0xCD, 0x2D, (byte) 0xAD, 0x6D, (byte) 0xED, 0x1D, (byte) 0x9D, 0x5D, (byte) 0xDD, 0x3D, (byte) 0xBD, 0x7D, (byte) 0xFD, + 0x03, (byte) 0x83, 0x43, (byte) 0xC3, 0x23, (byte) 0xA3, 0x63, (byte) 0xE3, 0x13, (byte) 0x93, 0x53, (byte) 0xD3, 0x33, (byte) 0xB3, 0x73, (byte) 0xF3, + 0x0B, (byte) 0x8B, 0x4B, (byte) 0xCB, 0x2B, (byte) 0xAB, 0x6B, (byte) 0xEB, 0x1B, (byte) 0x9B, 0x5B, (byte) 0xDB, 0x3B, (byte) 0xBB, 0x7B, (byte) 0xFB, + 0x07, (byte) 0x87, 0x47, (byte) 0xC7, 0x27, (byte) 0xA7, 0x67, (byte) 0xE7, 0x17, (byte) 0x97, 0x57, (byte) 0xD7, 0x37, (byte) 0xB7, 0x77, (byte) 0xF7, + 0x0F, (byte) 0x8F, 0x4F, (byte) 0xCF, 0x2F, (byte) 0xAF, 0x6F, (byte) 0xEF, 0x1F, (byte) 0x9F, 0x5F, (byte) 0xDF, 0x3F, (byte) 0xBF, 0x7F, (byte) 0xFF + }; + + ReverseInputStream(final InputStream in) { + super(in); + } + + @Override + public int read() throws IOException { + int value = super.read(); + return value < 0 ? value : BIT_REVERSE_TABLE[value] & 0xff; + } + + @Override + public int read(final byte[] bytes, final int off, final int len) throws IOException { + int count = super.read(bytes, off, len); + + if (count > 0) { + for (int i = 0; i < count; i++) { + bytes[off + i] = BIT_REVERSE_TABLE[bytes[off + i] & 0xff]; + } + } + + return count; + } +} diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReader.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReader.java index ecf4462c..28fa1450 100755 --- a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReader.java +++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReader.java @@ -2152,28 +2152,41 @@ public final class TIFFImageReader extends ImageReaderBase { } private InputStream createDecompressorStream(final int compression, final int width, final int bands, final InputStream stream) throws IOException { + int fillOrder = getValueAsIntWithDefault(TIFF.TAG_FILL_ORDER, 1); + switch (compression) { case TIFFBaseline.COMPRESSION_NONE: return stream; case TIFFBaseline.COMPRESSION_PACKBITS: - return new DecoderStream(stream, new PackBitsDecoder(), 1024); + return new DecoderStream(createFillOrderStream(fillOrder, stream), new PackBitsDecoder(), 1024); case TIFFExtension.COMPRESSION_LZW: // NOTE: Needs large buffer for compatibility with certain encoders - return new DecoderStream(stream, LZWDecoder.create(LZWDecoder.isOldBitReversedStream(stream)), Math.max(width * bands, 4096)); + return new DecoderStream(createFillOrderStream(fillOrder, stream), LZWDecoder.create(LZWDecoder.isOldBitReversedStream(stream)), Math.max(width * bands, 4096)); case TIFFExtension.COMPRESSION_ZLIB: case TIFFExtension.COMPRESSION_DEFLATE: // TIFF specification, supplement 2 says ZLIB (8) and DEFLATE (32946) algorithms are identical case TIFFCustom.COMPRESSION_PIXTIFF_ZIP: - return new InflaterInputStream(stream, new Inflater(), 1024); + return new InflaterInputStream(createFillOrderStream(fillOrder, stream), new Inflater(), 1024); case TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE: case TIFFExtension.COMPRESSION_CCITT_T4: case TIFFExtension.COMPRESSION_CCITT_T6: - return new CCITTFaxDecoderStream(stream, width, compression, getValueAsIntWithDefault(TIFF.TAG_FILL_ORDER, 1), getCCITTOptions(compression)); + return new CCITTFaxDecoderStream(stream, width, compression, fillOrder, getCCITTOptions(compression)); default: throw new IllegalArgumentException("Unsupported TIFF compression: " + compression); } } + private InputStream createFillOrderStream(final int fillOrder, final InputStream stream) { + switch (fillOrder) { + case TIFFBaseline.FILL_LEFT_TO_RIGHT: + return stream; + case TIFFExtension.FILL_RIGHT_TO_LEFT: + return new ReverseInputStream(stream); + default: + throw new IllegalArgumentException("Unsupported TIFF FillOrder: " + fillOrder); + } + } + private long getCCITTOptions(final int compression) throws IIOException { switch (compression) { case TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE: diff --git a/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReaderTest.java b/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReaderTest.java index 37828072..e14b7f94 100644 --- a/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReaderTest.java +++ b/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReaderTest.java @@ -101,6 +101,7 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTest3W#iA{^(ltM;ZKwlz1Tm|Iu&g1ufX15*Spl zMZJ8!=f3x;HXY`|p+y za9}$QF8+M@E3?Mm*_BS&qDsZAx^EWM&01aiBSygHptmg=*{EqYh!3sP*g3M!xN-;~ z^EujJ>KgcriM#y=cOSrvc@6tkWyq zw7-n>_3G}JuD%mjqqqzcn%1j14}RXT;oZ1x)lp^K@5WtmA_|;%y?;r+3RiwsXA|Ig*xHP3-NJuJ1&xTJNFlz+S6`IS7L< zS;L>(UMGgv{|jC-*x=y|uVGZxoY`ibx@GJhHFta6_-JwQ*gcM6Hy~&CAg0e+ zHm;vycP(Z2rEYv6w*O@(uGBPUXV@M!?Fw>1BmCP@97g!vG9&y}7~ydRY3t*g*91t$ zcMpd5N~gxR#BUhiF#K{gI~J3BEo;=;*4mk2@-9qfXgZUT)hxA@U+dY6E|A68Ry@ zD_Y48ifh^v%ffzOol*M%3UK8`kiLrQ7o!-a%KIu%ZG6_T{reUlSrM{N_?!P@UUT!` zm=9Tq3sDs@d#b8*Hzg@o;V;Za?f4_uJ7R*dnn#YL36kYN{sm1NjBLZTI%2lk^C{CK+Ox5i3D{uDVGWy6a`bf8&ErlZO z#OFVjn!m7*N0m2iP-Zn3i6vi@T_u{3xV}oxmP6WU$Ir1Tn=~5MuqvZ5?U&6|t=N55 z*mke<9T$ynGyd`wIXOqzB7Q90p6RYv^A|1@L;^k`GVzEFcw6**KKZ9hORlOp~?EqC?y3mB92>@GJ{+s&3kzBem!Vl+^^~ za-e(m*WikptzP%IbBrD~?%zWF>e@G}}zgZU3Z{>ZAl zV^>_X?%NCcR5qt~j;kW4s&9@Ha@JWZ{WO|m5ousypt;JRUy52MzkanU_xzJsAW*lx zjZVKb%Gy{3I`s+TSm--3977C6gOp?IsiK*6magX51$;u}%!;IJUQ8XZ6)Rb$);a%N zQso6auz}J*~o%L1vd;31t5EQGl zm7VBrIjjfEfqWG?ffA){YKd7Eh;eM(i~HWQt|hy5;G1FPS`zr*+1z&RC*U~i>2KX~ z7T|fFB{j~HZckddTxFjiK&r#iyF4Dq0DfCr{CyOdI>vHoox!8_$gAC6>}-3hX^jrn zIgCk%^1wlU+w3}qr-o^^AL5&9*7Yq1_Uv`!8xA=c8ak~S;%!uk!lzxVR_43Tt~k36 zga5?2bzt`^aH`xov^~@n`d+NM5m>d`WB^d~M#iS65p_HQT6ywoD^sxN8eWDiQ2F54yhKwt@-E|kU@qB)? zA4aIjoywhZ3yZKU?GbC}Zli^Bu8qwLI|PIC+AeMn_xH+z8!df=_yFvYk!v4x0m%qAGS{S#E<9+*~ZX#>`vUq4I9@(S74mN`P9Ltf3hno zX;*B?U|>#jBcBWIhb&gp9U>1%HZ^*Po-#f_%?zt`j`3fGnPCJMT$jJ8MrV?nmr1RKBh~&5JbfDi64$(>=}V71p(xbBltIeZt@TAM=`<|HgdC zLR`5#i;nwb&EI4%v_ZQkhGR~T%OhWCf2)! zb(?*UhO)XvF~E%Bhlb}qMCq{qjJ#VE?ta_f#>y@Gw&Vc)vZX{EeIj|18pnQb85`Zs z=x#DrM)5HgCgSf8?KgKDtP&_Qj1_H!eNXITMUhKgohs23o{SY^+9zXWOU**wb)4qJd znunEi(l0tSglb?&aE17tl1Yr+D!r3J-J`q~M; zWCpgsK!3CRgDCL)vnnr0I>B-_FCoJq>_|<58Xl*jnD?CR(R`?h#quPFm*XNth?C ze9E-;u8!02Ky6>^!~rXRaGO4^dPU4=_c5Wkb?JVmJVb**&1Ww_8F6*bqBe5& z-{QI!z~dug?%Fb)C{XC)w2{w_=BEhN)i+vfhzQ)lNXLQxz^EI6QIQ5hd0(-@8JC`3 z>_v=YzdLQCi#h#T8gT}napjEqX~Z5yLCu5uupYdhR)Q#t}ef>O|jYAt_DDFSR(uVDgAZ3lvYcRgi`Jlu`oU`FNHe-q{AX3U1kHxNY z?70)f_(=rdrEWj9CtY90x@99ygFlS#HnC|D{@&JMQ~F>S(+2|%rFb6@_(2~K=Apqq z-3P5S3UfH2P;4w*$26tXH*=Wp{+mn=^-4N#Sim%O;TkVu`gvo*c{f9i z>&jq*tx2)4&iQLhj9&Z!>n+j;`&3s^uu+fnPOjJa(#shapf{_ zc|#{_E@#?W@K(~w#;jgjtJWj7Y9$@21QO>&o;}{0>q}pe3zL5=LNG@(+GeWeOK5RM zEhpy)zrv5VIWDI2V9ZZzF#~b-VPG3n_`f^YSGsnGrX46;Xb zE#_}IkT-u*Mj_A&qj6fhYweV>!k~NhQIgGYtTW&#vSP&vRaXa?Vj6e+4EC^|e&~jP z?_`-P-U}blCnFk4sP~k?kJT$Al&APfAfU~IzDxL5f!?!eq#Wx@kroF`*o#5yPK8{O zZ2wR5z9>)_&N`g!%?T)n{`?;+zCMF^MO+JCKbn>d-s(9eGcscS4@b36ib|$w>)n&1 zrV!u(2e(gR$y?@R5bSxHgyjqGSxd%K67p6f!Vd@v5r>r!r&xJHTT7y>vD4u2D&F@# z`*HoZJaA^(v(Zd3H-+|;Tv)2Ygg7!Wrua`&UK9ueU6P%|ShReKyjY)4FA>|9^wRP` z@oxo*;0+6h1jmZFHu?HF^WQ&Xrp|mJFaz-y_owq~Emty~9m|MI?u@gMy&TBN2x+qp zwCfjhrJxioca?!`S%c>In1o0|BekShank5VAvczJkY9>vS=dA(d_>5ga$N0117*1h z1g-{7i2}0{-0Va)$}leryJuG?=%PuF6fIBMe-C6zJIzY7YD9sB_tsr#Lf&5DL^Qc_vV0QT%NXEeA@5&K{$&EwY+^ye(i{7Ix1Dpwp(O z>!Bm)_5opWKth7>`RyC>Ug9%LK6!J{iO?B%@XHR_QE9r4$tHZt>J=u$gOmdR;IBj( zt5$(Jndl||W5w5J5U+@9;p<16 zwV7aXeK9eZWM)klCg+!C@}g~Tao9oOE`((0W2R(DL0B?e?rXXl=aG67lxqk1PG3?6 zon%l)@tK%bg7nE*o)Fe@5fiREVZxvj=)dKG69M6t9`YiS2h+%zmtbBMKB0^WosvC7 zb)6-bnN5~2WO27 zkTrqWv~?;+c16vgi-zRq`3s$%ub+55f8jmuietG7N6*!IlfV3h-jIyHZM)LPCXjtV zalmA{Kg&tlxc?d1rBZe*3wxe?rsUG*G+Tx$cM6d~Q_7RueDB%Q4&eERXvi}Giy!(CAAliaDm9ZzA)t$zpVy92us*ppmI z*=a|1~iB5;mPi<^MyjlK~prcPpKG=eV*Pw-cuS2&R*i z7C8ym**vID;JP~mYPw7AB-cF+tGK>xKW7%$*OZ#V2RwFOj5=Y!_jLB2g8&-uT!5>S zGGV+k$Vt$Adb~%z>rFVH?;yao<@Ino-@$_)PWl;#dT5zGQwNje7E{ukerEDb%QZAx zU1rVnxoaD2m29gY&JveknI*5R>*X63{CX0@i zX|M#P=CZc!5&Oh&b13Ol`q+Dnz*XTMtk0op<*Ufj$BvXfHrWcSUSVCkCg0r(*}7aR z=i{13x|7n}-d1^0Z>voD_v7>L$}Er)b~9BoQXfmqf3EoZ^;PnZMK*0C`4NM%P&q@+ zmt!;477DfVaV>oPXybk*HLsg3{gd_=2iHu3>Gm-l*;wz!{1q+twnBK_ETT06LSR^g z2~5Ylbro-*wP<-l(p8VX(+0g&>u1Q82TqK^R|(e8r30i~tVufyWUdNNDO0>na@noy zSuuH8MxdTxmO zoRdp#2X#(wCzKT}PpHB(ObXRox;^$ZVM_ofvH@?l2ddtg!MrLwd?wd!aue+|#mkm= zDy=i|wb5#*n@na;` z`YYaE?7DhOrtuDepw-%uy@kPVSVtj^Nul!$X4not8Wdi~iW^rB3GJ`fgSRG1ommt% zcJO75E69F*!ewZ1zDw#Y`4KwY>t*mWf_2WmqX{>aXb5hW$z#yji4ty}^nGQlyl4&O zbWr`N`~qEZk>QDhj6_)&H{l$q*gSETLaDgzQ6&U=!%KW+Rb{*Hpvw*7_+f>#_wtlw zzA}y?k{BN&oP1@!#W>xP(ox2-(_G2t*!q154qOaeUD1=TfpjaZ`s%VMO|hkA$Ng-2 za6g-FVq<>D??8esC1fT6TJSr(p$JN;wd!PYcd3I4pg9kJ|5t z`NiH!4=mO6TO|q;jR!iyFPWs@@b7hkfA21C3g35zd4{tGx5Vjr#zC1$uWhNB8PQKL zZ}K_m-Vfs@NrxZC4e<+hJq#emjo*x^@6?0*g~;i%#|>4dXFJZ6|A%m9s&7e|tXc_I zium^4mtT<>3A~0VJk?=4s-l3ieU_HP+?E6;S^lJte#+(6=soB*@--r9lxM#>B3jVJ zfG_B}JfzIZo+y!9dKUA?eU9I-9%A+Shd2t^l__(mOj+8_tOj;}<2MKZu9FC}#Cd_; zSK0Z=A(?FWR13aY^K`0wr48M|FQ7(twYj{=Mi^#b?wjTmvtr}^FC=> z4wvBLt+^R5V*#w$=WiX7@)JZc-j^>i7@aSOvGw{c&qx}UugJ+cdR(c+y!BLcE);5M zrI|Noek^?bXyXpariaRO?Pl7?`*KR2U+Sk#+V~}#yYI4q#$Ed=7>blS7jAMHCOYW5 zzWn|qrD3Jgd-jZ9GbGE9-;9Xap=-ju3?!lQ`=CsnckIeqz0d05i&urelO+-33_VTw zWpKJTiKu0rAbK0R*HtH>H8gaKeSmz5YRHm?R)*n( z7P3Vw4@~HM!Gj`hfx8vCap#sSLtT5AGWe#gSY;w;UT_y6x0oJR)mBv>qyy?|Yn`>y zPop^u#dtvVX_Z006mfER_S06Ya?kWh2oMa{-bSZi8f9&)0-gGVad4WFc)Z{4m7+n) zu}41Fjd^ z2L%FTlnSb%fMn$KPUBhza?if6Qy(ZsI#s4){!Pi~ukwA%ff6EKBT5S|4@`-rehoGk|4-=o{!zcYDj)7U6g<>Nl8mIDBxmGL5%dwB4DLJ|-HqeA zM`0)&gOg|s+);%Tm!r1DFk8WVGOKTiS-_cm5g7j{#}G>sAu;rZ1Oyr_9fhMKYy8-Rt+CHKGuk%(kJSjv`6}n+7)#5nMw@T@O=$p4!s$6!9kwz<51p zPzdTxvu*!-kbd^_$|S6zS3QxsSTo#-56EnM2>2`G&lZk+0@6AB$Y1 zJ@(07BSav~e0pt5&8$?M=1qav2l3cNu_lOa1QX}kP4_aZSMYF)cakQmYT{L0 z_dFHvv5p$+-Uqu`MMzgLn63EwC5pyyZZ~c2MRMzm!^np#q3AJ1YZQo<*i6;z^S3b= z7dQsPd->=@2**50>Xla%X)C^d#3I!ej?ei^h+kMT-^DNkaa8;AP0&Ka^X=Bt7FI2O zVDEKUQ}G@=MDOR&m+04V-QoZz^Cqb(@oIoeOLWLPS{x9rc7q}k?3kk6rtWF!=Pgc} zy{ZG^4L_HHnk`f@f6D{k$>K4t;%)#lTVSG~GR&*OGs+^6YeT0*bf?HJ4D=J>!sr`~ z3C5!38F}m_8+Q~zGn#z+hJgC|Cp1bzq^+QX6&4xJ;hDE)M8*6c{-R5Z1L@MAy6fWY zbS4x6&`76p;5Q*)zfC+x`3s$r>KR1jC?iBd7XxH>9LPgiSDeo``I6;<$+#r|VEVp; zlA;Yx2N|qVWg;BZmRxVy1qV}Yt+UqT)B1eRG=UXVshYOCOMMTv`lgd;R_cY&OZY1j|5@9=k7TAar{eRK=L78_5d8JXZsO z6$EDySkb_}5|~}IJfV$56MaR1dd|iS*@l1 zO$5(#-0rZb-A&Z=Pkz^aK3drFI%_>|KaUnRwt9uM9|v4qFl>h*Q{qf%mm$M4m=2ls zR{MrF1K|Y5*^l1bSHeBAznxC;0>3vY;8G##G@kimVe2!p&vBenGG8RlKpYi9Mn)q^ z#;$#uGH#{P2b|xKT4WXGwbDr=FZ*rbm(6H7nA3VwS4#^imLnlem+R$$QrFvdgY3)_ zcx?h<0+~h2oq`wbYGb$2=}VyXh;<04mIFZ-@LzuIK$=ZFet<~em{gvR_I zj>-kcyJnHT>5#u7rAf@Y<~SjjQOLs-g#L0MZ~Uh9QjE$mm@XsB!h&eHN|58YJYbtP z!MF^>sd$lXnr0#Q&_%;~FP63RCn@ii0#3xkGl}>RZj1Wa=dYs%XCV{$;n;10ef^Li zRn(4j#k3REPEpA7HbAT-{YSKFhO9VX-o(8q<%(JVf%S;&Y-&M2AUyg&o06_o7@n&@ zZ=V~@eyE1(_6)bZGslSOV8<1)Gbs^+-HR)fLeH~O1pK5f)s>Hi$Ti)$Ch{&y6N`h% zKlC}6a3~`Bg=<;Fp_1-0gF{i^P>5}(^C}s4o-VvOW52%_0@3dt$7LjGn^~@iD&+!H zc$@|9{ERuT<4bT?_go$e`#PP5+B*kf2V}bC;w*98EP?3HLn@kc59w%eLa|fbXofr$o z^i9UXmWml(6GGBnIfc3s`F9B|BJ zodFE#7wxf ztrrQ69&!kUSnV#%AI8Hf=hV3InoN0qhe2TeHFQdreZ{Ig#7l?`$5Vwg#|R?K;6W$Q zJzk2{PIRa(3DUDYz?ze<+;`vUly9ST9wH#Zvb`+K1FHXqZHME06`t#Mu5{zE3Uu9D zGvB}q=_mZn|1q!G`ESfX7QVg>v(7>i^MyvQvzB+$V@ry+nb_$9ShLUHhFNdJtb+}G zMsUslu~4L~C{T|!9%fB&WF#VwQmMZ$KiaaT+r(6>W}WhJwm5gyJ6TjVft3xi3v3fp zJ!JGQPFt1o6^?BaQ*F#eSXf?px6HgX-!C@<@fY{)$^avP>}w&)e=3-=5afY<)Da@g z1KOZUAnx;5tT-8BXOVOBOd?N}-otVjHOBpJ-xWcTkgq6dI%<*n0_#Th0Y%Fvq9qzA z9|kGdQ(i0w68R@+dOSsu@@0lNi;S)37X7CrjzVwDd;S|`2uYbV|A%Uv<(Pj`%l`_G z*(dCUE1Fuk58l-DIDj7qVf*?WzGq!^7CN}NIgi@_XqbmVlZcs^2j%!zF1bA{PHnrt z6LGl_4|{B{>nJpJKRk`SL&@elcZhA=i_?rQ7rC+dp0J*LMs>T}7x$iAw&Tj3nB#tRiT5|SkW061 zm0)805S!Zg7;{@(fsGc}__GeTJjJEB+G|H`KtpARYDc8Am&KHGewgt-z_mwyGl|$A zGn=-=EZ|H&uiekoQN~4DRrv-wq=llRv>+NCRZ*I#otme8!H36##=APoeRRe@Ood9g zg^R8vQ6T2O9vOl@DB%QTYV1hK(1F+7PP{Ea<%(idByP+GYW3n|9N`|6XNv9F z%s7&)O~=ueD4%ipIF-*mr}3nbrkrne)0txLy3z<)ZFF6ynTdcs31oX;9S5kT)Nerm zm^w~k<3z`4a}Wdo9k(TC5qI)=^WLXVlFlN;mY53uU_A39be3IDI!n>lont(UXeR=l z)maFZiL|CpxacysXYrEJlHcWrKRvFKx=2$>k`4`q*fe<(Syh=*aa>j}#U-Xk=%s6@ z;TG5Bf@Dg?rHZoUp-AciMO5;j{FLxulD)B-sFhoK7W3ze+IIa%v)$i9Vf+xj-!|p_ zPqWpn2L6F^>;7Fp{-%(>N*8mAz#Wy^>8)O2-TPd(YzXPrT?rGM8w;{FZ5qB#M`S)+ z$rpV*Vx*ExLF+ZKC$+HHhrAyTr!H$+vtzS>V{tQ8v(MkUE4S{-wYV$SEugpfo-Tv$ zw!fZ+{`wUg+b