From 09da6fa73babd4dd48c038dd8a06196be44702d3 Mon Sep 17 00:00:00 2001 From: nulano Date: Tue, 24 May 2022 16:15:01 +0100 Subject: [PATCH 001/239] inline fname2char to fix #6319 --- src/Tk/tkImaging.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/Tk/tkImaging.c b/src/Tk/tkImaging.c index 5b3f18ace..16b9a2edd 100644 --- a/src/Tk/tkImaging.c +++ b/src/Tk/tkImaging.c @@ -364,17 +364,6 @@ load_tkinter_funcs(void) { * tkinter dynamic library (module). */ -/* From module __file__ attribute to char *string for dlopen. */ -char * -fname2char(PyObject *fname) { - PyObject *bytes; - bytes = PyUnicode_EncodeFSDefault(fname); - if (bytes == NULL) { - return NULL; - } - return PyBytes_AsString(bytes); -} - #include void * @@ -442,7 +431,7 @@ load_tkinter_funcs(void) { int ret = -1; void *main_program, *tkinter_lib; char *tkinter_libname; - PyObject *pModule = NULL, *pString = NULL; + PyObject *pModule = NULL, *pString = NULL, *pBytes = NULL; /* Try loading from the main program namespace first */ main_program = dlopen(NULL, RTLD_LAZY); @@ -462,7 +451,12 @@ load_tkinter_funcs(void) { if (pString == NULL) { goto exit; } - tkinter_libname = fname2char(pString); + /* From module __file__ attribute to char *string for dlopen. */ + pBytes = PyUnicode_EncodeFSDefault(pString); + if (pBytes == NULL) { + goto exit; + } + tkinter_libname = PyBytes_AsString(pBytes); if (tkinter_libname == NULL) { goto exit; } @@ -478,6 +472,7 @@ exit: dlclose(main_program); Py_XDECREF(pModule); Py_XDECREF(pString); + Py_XDECREF(pBytes); return ret; } #endif /* end not Windows */ From dacd5d6eb9ee5486fac6de4b7a575a33fe3a5e2d Mon Sep 17 00:00:00 2001 From: nulano Date: Tue, 24 May 2022 16:36:30 +0100 Subject: [PATCH 002/239] add decref calls to imagingft to fix #6321 --- src/_imagingft.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/_imagingft.c b/src/_imagingft.c index 8f19b763c..7dc50b474 100644 --- a/src/_imagingft.c +++ b/src/_imagingft.c @@ -303,7 +303,7 @@ text_layout_raqm( goto failed; } - len = PySequence_Size(seq); + len = PySequence_Fast_GET_SIZE(seq); for (j = 0; j < len; j++) { PyObject *item = PySequence_Fast_GET_ITEM(seq, j); char *feature = NULL; @@ -311,23 +311,26 @@ text_layout_raqm( PyObject *bytes; if (!PyUnicode_Check(item)) { + Py_DECREF(seq); PyErr_SetString(PyExc_TypeError, "expected a string"); goto failed; } - - if (PyUnicode_Check(item)) { - bytes = PyUnicode_AsUTF8String(item); - if (bytes == NULL) { - goto failed; - } - feature = PyBytes_AS_STRING(bytes); - size = PyBytes_GET_SIZE(bytes); + bytes = PyUnicode_AsUTF8String(item); + if (bytes == NULL) { + Py_DECREF(seq); + goto failed; } + feature = PyBytes_AS_STRING(bytes); + size = PyBytes_GET_SIZE(bytes); if (!raqm_add_font_feature(rq, feature, size)) { + Py_DECREF(seq); + Py_DECREF(bytes); PyErr_SetString(PyExc_ValueError, "raqm_add_font_feature() failed"); goto failed; } + Py_DECREF(bytes); } + Py_DECREF(seq); } if (!raqm_set_freetype_face(rq, self->face)) { From 02e90e21f45b59f212d5f9289773b6eb5653e30d Mon Sep 17 00:00:00 2001 From: Mark Harfouche Date: Tue, 5 Jul 2022 15:13:39 -0400 Subject: [PATCH 003/239] Release the GIL when applying matrix conversion to images --- src/libImaging/Matrix.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libImaging/Matrix.c b/src/libImaging/Matrix.c index 137ed242a..182eb62a7 100644 --- a/src/libImaging/Matrix.c +++ b/src/libImaging/Matrix.c @@ -21,6 +21,7 @@ Imaging ImagingConvertMatrix(Imaging im, const char *mode, float m[]) { Imaging imOut; int x, y; + ImagingSectionCookie cookie; /* Assume there's enough data in the buffer */ if (!im) { @@ -33,6 +34,7 @@ ImagingConvertMatrix(Imaging im, const char *mode, float m[]) { return NULL; } + ImagingSectionEnter(&cookie); for (y = 0; y < im->ysize; y++) { UINT8 *in = (UINT8 *)im->image[y]; UINT8 *out = (UINT8 *)imOut->image[y]; @@ -43,6 +45,7 @@ ImagingConvertMatrix(Imaging im, const char *mode, float m[]) { in += 4; } } + ImagingSectionLeave(&cookie); } else if (strlen(mode) == 3 && im->bands == 3) { imOut = ImagingNewDirty(mode, im->xsize, im->ysize); @@ -54,6 +57,7 @@ ImagingConvertMatrix(Imaging im, const char *mode, float m[]) { UINT8 *in = (UINT8 *)im->image[y]; UINT8 *out = (UINT8 *)imOut->image[y]; + ImagingSectionEnter(&cookie); for (x = 0; x < im->xsize; x++) { float v0 = m[0] * in[0] + m[1] * in[1] + m[2] * in[2] + m[3] + 0.5; float v1 = m[4] * in[0] + m[5] * in[1] + m[6] * in[2] + m[7] + 0.5; @@ -64,6 +68,7 @@ ImagingConvertMatrix(Imaging im, const char *mode, float m[]) { in += 4; out += 4; } + ImagingSectionLeave(&cookie); } } else { return (Imaging)ImagingError_ModeError(); From dea30e4c807f76931d5b37736c6be2dd2f8546b2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 18 Jul 2022 08:39:23 +1000 Subject: [PATCH 004/239] Fixed set_variation_by_name offset --- Tests/images/variation_adobe_name.png | Bin 1431 -> 1475 bytes .../variation_adobe_older_harfbuzz_name.png | Bin 1432 -> 1492 bytes Tests/test_imagefont.py | 4 +++- src/PIL/ImageFont.py | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Tests/images/variation_adobe_name.png b/Tests/images/variation_adobe_name.png index 11ceaf6e65b60a9a9fafacf1d72f8bc8227d8e1c..5168e04b99bc16ea9ba6c57f3a7d4ec3cf3a47ad 100644 GIT binary patch literal 1475 zcmb7^c`zFY6voq*MN-ukS@fW2CsLxNHB+QkBuEe>wvN@r5yq%V*R4@Ul!myf?s3$T zNKrkcCXS|5-PB0y3K3Tmwjq>pq;;&$?ChVrvwytzX5M`BX5P&A{nA{pcFI64AOHYR zM%yEChm&+z4vI$)yAq{w766c^qmkBbF{LZT4v_n<8ttp+{Hxz+*#`AK>gn*;dvOJo zm#1rD0O~@ld-P_wBkVE!j1*iR1k_Mz3G}xH2l_6+@x9j&4}$^+_l(KY(gVRA!D^P> zm)%DvSYV(bap0x2&^JXHYrwCR>mM; zzGz`#kC##7 z>W@$Am&*UteEg;oj$R zbuhS~wAAq=($@CjT4h>VTEs064-Jz5&QzxDf=I-iCab8ZWM^i67#PUK_|BM?{{aaO z30Ve#_T9bY1mDn;Oe8*O zGQz8Es?u<@_q?&Og0eF1>(`&dnn@;}cka}^^MOF2x{C8+adll?9fR>%BuYVRYHJ%C z8Hwu6%*`=v8d_TYgM*}Q1qFqbsMY}yolc(*lzsnRHyYgNRqq1nTt$}plgqN78?rum z5(qEHI&q}D&Bl?3u*BXwI z@80FJ(P*?lH!?EPK4*B?h4PZk{#5d}@XVHSk$8DIqdcs2dVG95HMOEbhn&;eY9ZD( zFe@%D#rv6c}$YierjCj^4hRMB8yxV^n%v}+2&x6up5Q4qyCuQfL}_ok*UdY_=# z)Qt*fX3iXcn4B!PHL|c!g=KBjqE61A4VSFFjkK*grKE>GeFJ`FSt|X=a!MA91Cw-s zAdmswi%49Aak<<~!qL(*#rzO32X8hLpJaUbvW;#&Ml?C8WzkLMa2gwP^!Y+z7Q@)k z@TA<_;$n1YsCD+p$cTCdKQYcGBhv%|?*a}uCnF@egTE+;kD6byVV|}_|Zr&f;0R#eT z>+88KE#F=k-L!PPa8(WEgvIJA{G5iw;qe>0yN2DWDA!6A=6nUDpJY<(c&Tm_^O%H0 zA|oOqu-Ghb*g=-4yu94Z%na-7jDGA^<0g~I-ek8VZ&Wqxo9`^f{%?x?7xUIs5@`?8 Ti!MliAR2&1VUf=fK1qK8@g2&Z literal 1431 zcmb7^`BTz)7{~Ei@1s2P*2)wK^9T>rT*3nnP{v48!;741cU2TbJS!bjmSI|@@<1ds zC2}gX;l)#A}7t#He4&+?_aPx`@`q?%zU0_o@YMK>v`Ul(dbZp9ZMY$ z2&9h+Lt=qD9XJtMs({s|3S}UW#(5O-$5=|kMs0#~Zj4E(@`Q2P$ZeOgBoA#jN2S+l zc_N;YgF1-CM%FCZQnKjHiI#}*YnuKsCN9l2&0`kIbfW78t*JL9KBT>$^>XG{cD`ON z!DM_nSHKdA#OuBaz2~V9>{UIj_eg5{#z-@eqb}%%M(L<(P`Zk}0Av&ox()&T+jD7A z{9xwv%*>PT2a62@o#8Op&A?X+3#k^nMRS+_nw-=_gh6j96pH=0BwE0`$%%=Gdbt*7 zZZ6h87LUi!y}i8`+n;NYNFU_2F(UGi->H5<@Ai&1V>;j1#$Yxhp z=a9)?;tBQA1wCHuJ((<-6FoYd>XO`Ruuh>+oFR|~Hak=c)iP68SJ&CuDHIB?Tv6e^ z@bUKU9T*ts>$5O6KK%xRM%&ui0UwkHZ*6UPczCp>F~fx-(Xvz;RTPQAJfE2Gw4{5~ zMmkj&Wn;FJ;x3QS3bP511_zHgIbkoRKA_~~-7=sv7_+8@MMayNo1ZEd7Z(i-N_u0< z5h<|xwKg2zls4>dn@J|$OiF{u5^*HbN!I-IbZN!pg`A)ukj=*WdUsvInKR|=7LkZ; z>1|2R&d%22jEscL(;t*3#Gh-&*&9t?Y;A4DX`@i6TrWSr1@P|9&O5pM^s(#6J6!Hz z+Aty@n2?bnmsqyBle1iPm-B#Dwcqh$p`EHfKTVdEsk6`(ilyac z#pF!C%i-haE9DH^+5-%vc*84V%~frlnxO0WOX= zWMc?UXH!!XkP86R-Ca{f&Ci!gr7OmHWO4(G73}z8WyK-zJQjQPEU*V%erZUgx(z8e zw;38&J?7=^o^rw>u0x{AY{Bt0fA|3yRWKMlU2u=j2gWEgH1w4C93H>F%gjwqw%@a{ zwz6_{b8F=SEdVpCbfTi7T$thfH7u{2&%aEi){U>df8RB*J2N}0rlxiPk!t`?7ElWc zL~)+wQ+J=R6k25&j7RBFx_3c}jmzTP7+n`Zu zP;7K8Kg)4KY7Fd|a}rQTvfE*vlJT jR{ob3{XgXbqGg8g-roW;Vn=-e!3Cj$(a7Hs$yfgYM*NXQ diff --git a/Tests/images/variation_adobe_older_harfbuzz_name.png b/Tests/images/variation_adobe_older_harfbuzz_name.png index 2adb517a759aab4a06e83cc7ab604f97f88c1d27..fa0e307b4f650e007f30bd2ede877c8fd6843116 100644 GIT binary patch literal 1492 zcmb7E`7_%I6pwQWafhNUDvoNjsWh zI~W5%bCQz;y*#~+4}r)OI$~|F(29ilP7b=h8n;#vrzOZnq4$jLv&Wm(*vHSprzA3D zRMYOa>o=H=GVWzDf-B&saZakzs2c`yP|{4hY*jdE7xmlvUQUD`&9b*e5xNEkUGR8iF&dy!>n**a zR=sdITt(T#n#i`~GzNoFyEkt_0303D_gGvm_Y%F)Qd3j&gS&lIMKg7^@6;~7cSDpg z+nS7bD%1EInb&*8&o5JjzQ(`3$0{i)82}MP!9}-+yp{cZF$(7<5{amPO~>u+uV3vUc#?Qq$mKG&%sK}1Vl2^UA9r^aP;O-Q zTV`fTY+~Zw55ZQGJRZTobCo5-!DmsZiL{R&fizb#ne6TTXtJ&& zq)o}Mh0b+0cFfiNG(OI^N!lN zIWmey>q%nHU#;^dvpt)!064$$OsAd(81IaFQrkl z!Sm1;RWaJy+Uv9;G;25*3WesvPa%;+A0Ktt`M8aR6<+mpj`;7@Wbw9h$tAk1WL<$| zU?w95agqQ4ny2E(%K zm9|}TQAUP^g$;z{*;A<~%!{h3%;(D>ALMmCy}VS-O*u>%NeNuxn=19aSJ#GUqPVy? zJ)5M3C0C6x9?xC{o1APD*)%1zlM4(AqC`fXF^q_a;H1FeaMrbZ9L`UE&!>bz7_|e= ze6wue0Saciva{p4weIiX;Xxog>71*X5)NpQm@a}%QA2e7>(@`;kB(}f4=Kt}!P9POFKSV`E9ag&gE@))=W>TwOO_-=mAP^WD8ZyDln45P*8hrCo0Nzf}5Q9EU8)3w!M+w!Xf)4SOk(2x+L{IR6ZmWa{W%EjtnO;; z$Mtpg7NCtdss<7oWc{!@5F5L_DHx(ssRz8GLuv+oO8o-^imN*;_p-^!$-%+Fg~(?> zf@zDQm9uz1sdZLQQC2>sAyFLb=H}+?{J^EOxe15I<86|^?|tr8PSGC-ijIh={8D@} pf7VPRoiti5U|6dEG=!?AQd6qw#UJIlP35F2aJGs47Py*ijKEFA)}_R`G! zs>dWBJ~%%!L5J$-=#1o23e)nImM#~0?RoB|X8hLJh)`KtT(m#Fy0Q{_;X>`}6--PF z#Ii2v35x}}k6I9mLE73TRNoXbxm0vzKqh;har95l)Ev?P&qP(n^0-`e*J%Umi?L}MLAhy}u+?-ia zA(0fA@(YkD=r~>bs;Vj&3?{YC?%96gkH;TV+zqe}st@`^9MHZpu(7p;yr-(J{`m=i zd)qm`zo#fWD+~O})T`_-_}cn<9-QX;>%^hJ24R#?RZ9y`AoQQEi|RJUeKF@3EQ!S^ z~2h2GSZGM5io*u$ji6g${;?$}&)t7z->u6mveEirk96?LD z``qt*0)a4o7FMjh8ihiU%jMJ4(+Wj$a(IIJk)P${S8+5Fn7O&Rqy%s8yTJ`# z%CPnZiwWDiyPZ6<@mu`7JguTZ*~W%02$UrP8yU6Ke&vWZ7I!ZK;|4rlu-^Vs8?OL?zl^U^8Q5I`GKI$h?CiBO_3TfsxVK z)5`JsYZKA-XZ-zF6J5e+8biUh)QpS_2m~S&3g4Tzw6v6$m&e4!M2${QPCnbkxIm%f z5?3cUJo5Z`q}8}cbW~5x7V72(DH$HMRmtQk1wp5OZ(G~dH7i*i9vV{9)I_R`Ih#bY z6$(XTBaJ&GV=x#>?sxntl%Ba_8cq5C4}Xv+rC7QdXzZt->BBTh3rb2vQmK8M5ZXvM zC%K;JGTqnLH#b)q+_03;hG`11rOxMP=PCUHmz4DN2?Wczym^@{aqZ(imQ5q8o8BO^Ic=frZ diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 16da87d46..4c48d4ef3 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -769,12 +769,14 @@ class TestImageFont: self._check_text(font, "Tests/images/variation_adobe.png", 11) for name in ["Bold", b"Bold"]: font.set_variation_by_name(name) - self._check_text(font, "Tests/images/variation_adobe_name.png", 11) + assert font.getname()[1] == "Bold" + self._check_text(font, "Tests/images/variation_adobe_name.png", 16) font = ImageFont.truetype("Tests/fonts/TINY5x3GX.ttf", 36) self._check_text(font, "Tests/images/variation_tiny.png", 40) for name in ["200", b"200"]: font.set_variation_by_name(name) + assert font.getname()[1] == "200" self._check_text(font, "Tests/images/variation_tiny_name.png", 40) def test_variation_set_by_axes(self): diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index a3b711c60..9a12ba48d 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -795,7 +795,7 @@ class FreeTypeFont: names = self.get_variation_names() if not isinstance(name, bytes): name = name.encode() - index = names.index(name) + index = names.index(name) + 1 if index == getattr(self, "_last_variation_index", None): # When the same name is set twice in a row, From f58c0ea533b70c0a9818817f00cf0583b2b80a7e Mon Sep 17 00:00:00 2001 From: ShadelessFox Date: Sun, 17 Jul 2022 17:11:52 +0300 Subject: [PATCH 005/239] Fix BC6 block decoder --- src/libImaging/BcnDecode.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libImaging/BcnDecode.c b/src/libImaging/BcnDecode.c index 22b36eb7a..9e830cf07 100644 --- a/src/libImaging/BcnDecode.c +++ b/src/libImaging/BcnDecode.c @@ -750,8 +750,10 @@ decode_bc6_block(rgb32f *col, const UINT8 *src, int sign) { } } if (info->tr) { /* apply deltas */ - for (i = 3; i < numep; i++) { + for (i = 3; i < numep; i += 3) { endpoints[i] = (endpoints[i] + endpoints[0]) & mask; + endpoints[i + 1] = (endpoints[i + 1] + endpoints[1]) & mask; + endpoints[i + 2] = (endpoints[i + 2] + endpoints[2]) & mask; } if (sign) { for (i = 3; i < numep; i += 3) { From b7715d1600b202d9316cf8b5665325184c822e6b Mon Sep 17 00:00:00 2001 From: REDxEYE Date: Mon, 18 Jul 2022 20:38:23 +0300 Subject: [PATCH 006/239] Fix BC6H_SF decoding error. Decoding error were caused by additional sign extend call after endpoint transform, according to khronos documentation, you only suppose to sign extend endpoints only once, further calls to sign extend mangles endpoint data. --- src/libImaging/BcnDecode.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/libImaging/BcnDecode.c b/src/libImaging/BcnDecode.c index 9e830cf07..4beb279fb 100644 --- a/src/libImaging/BcnDecode.c +++ b/src/libImaging/BcnDecode.c @@ -755,13 +755,6 @@ decode_bc6_block(rgb32f *col, const UINT8 *src, int sign) { endpoints[i + 1] = (endpoints[i + 1] + endpoints[1]) & mask; endpoints[i + 2] = (endpoints[i + 2] + endpoints[2]) & mask; } - if (sign) { - for (i = 3; i < numep; i += 3) { - bc6_sign_extend(&endpoints[i + 0], info->rb); - bc6_sign_extend(&endpoints[i + 1], info->gb); - bc6_sign_extend(&endpoints[i + 2], info->bb); - } - } } for (i = 0; i < numep; i++) { ueps[i] = bc6_unquantize(endpoints[i], info->epb, sign); From 18a3c249b60322d47fa19febd68f2f174063de26 Mon Sep 17 00:00:00 2001 From: ShadelessFox Date: Mon, 18 Jul 2022 21:48:20 +0300 Subject: [PATCH 007/239] Fix BC6H_SF decoder --- src/PIL/DdsImagePlugin.py | 10 ++++ src/decode.c | 3 +- src/libImaging/BcnDecode.c | 102 +++++++++++++++++++++---------------- 3 files changed, 68 insertions(+), 47 deletions(-) diff --git a/src/PIL/DdsImagePlugin.py b/src/PIL/DdsImagePlugin.py index 3a04bdb5d..1db7aec8e 100644 --- a/src/PIL/DdsImagePlugin.py +++ b/src/PIL/DdsImagePlugin.py @@ -101,6 +101,8 @@ DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29 DXGI_FORMAT_BC5_TYPELESS = 82 DXGI_FORMAT_BC5_UNORM = 83 DXGI_FORMAT_BC5_SNORM = 84 +DXGI_FORMAT_BC6H_UF16 = 95 +DXGI_FORMAT_BC6H_SF16 = 96 DXGI_FORMAT_BC7_TYPELESS = 97 DXGI_FORMAT_BC7_UNORM = 98 DXGI_FORMAT_BC7_UNORM_SRGB = 99 @@ -173,6 +175,14 @@ class DdsImageFile(ImageFile.ImageFile): self.pixel_format = "BC5S" n = 5 self.mode = "RGB" + elif dxgi_format == DXGI_FORMAT_BC6H_UF16: + self.pixel_format = "BC6" + n = 6 + self.mode = "RGB" + elif dxgi_format == DXGI_FORMAT_BC6H_SF16: + self.pixel_format = "BC6S" + n = 6 + self.mode = "RGB" elif dxgi_format in (DXGI_FORMAT_BC7_TYPELESS, DXGI_FORMAT_BC7_UNORM): self.pixel_format = "BC7" n = 7 diff --git a/src/decode.c b/src/decode.c index cb018a4e7..d3bc2b14e 100644 --- a/src/decode.c +++ b/src/decode.c @@ -379,8 +379,7 @@ PyImaging_BcnDecoderNew(PyObject *self, PyObject *args) { actual = "RGB"; break; case 6: /* BC6: 3-channel 16-bit float */ - /* TODO: support 4-channel floating point images */ - actual = "RGBAF"; + actual = "RGB"; break; default: PyErr_SetString(PyExc_ValueError, "block compression type unknown"); diff --git a/src/libImaging/BcnDecode.c b/src/libImaging/BcnDecode.c index 4beb279fb..aa4cbf647 100644 --- a/src/libImaging/BcnDecode.c +++ b/src/libImaging/BcnDecode.c @@ -23,10 +23,6 @@ typedef struct { UINT8 l; } lum; -typedef struct { - FLOAT32 r, g, b; -} rgb32f; - typedef struct { UINT16 c0, c1; UINT32 lut; @@ -536,53 +532,53 @@ static const bc6_mode_info bc6_modes[] = { /* Table.F, encoded as a sequence of bit indices */ static const UINT8 bc6_bit_packings[][75] = { - {116, 132, 176, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, + {116, 132, 180, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 52, 164, 112, 113, 114, 115, 64, 65, - 66, 67, 68, 172, 160, 161, 162, 163, 80, 81, 82, 83, 84, 173, 128, - 129, 130, 131, 96, 97, 98, 99, 100, 174, 144, 145, 146, 147, 148, 175}, - {117, 164, 165, 0, 1, 2, 3, 4, 5, 6, 172, 173, 132, 16, 17, - 18, 19, 20, 21, 22, 133, 174, 116, 32, 33, 34, 35, 36, 37, 38, - 175, 177, 176, 48, 49, 50, 51, 52, 53, 112, 113, 114, 115, 64, 65, - 66, 67, 68, 69, 160, 161, 162, 163, 80, 81, 82, 83, 84, 85, 128, - 129, 130, 131, 96, 97, 98, 99, 100, 101, 144, 145, 146, 147, 148, 149}, + 66, 67, 68, 176, 160, 161, 162, 163, 80, 81, 82, 83, 84, 177, 128, + 129, 130, 131, 96, 97, 98, 99, 100, 178, 144, 145, 146, 147, 148, 179}, + {117, 164, 165, 0, 1, 2, 3, 4, 5, 6, 176, 177, 132, 16, 17, + 18, 19, 20, 21, 22, 133, 178, 116, 32, 33, 34, 35, 36, 37, 38, + 179, 181, 180, 48, 49, 50, 51, 52, 53, 112, 113, 114, 115, 64, 65, + 66, 67, 68, 69, 160, 161, 162, 163, 80, 81, 82, 83, 84, 85, 128, + 129, 130, 131, 96, 97, 98, 99, 100, 101, 144, 145, 146, 147, 148, 149}, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 52, 10, 112, 113, 114, 115, 64, 65, 66, 67, 26, - 172, 160, 161, 162, 163, 80, 81, 82, 83, 42, 173, 128, 129, 130, 131, - 96, 97, 98, 99, 100, 174, 144, 145, 146, 147, 148, 175}, - {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, - 21, 22, 23, 24, 25, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, - 48, 49, 50, 51, 10, 164, 112, 113, 114, 115, 64, 65, 66, 67, 68, - 26, 160, 161, 162, 163, 80, 81, 82, 83, 42, 173, 128, 129, 130, 131, - 96, 97, 98, 99, 172, 174, 144, 145, 146, 147, 116, 175}, + 176, 160, 161, 162, 163, 80, 81, 82, 83, 42, 177, 128, 129, 130, 131, + 96, 97, 98, 99, 100, 178, 144, 145, 146, 147, 148, 179}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 48, 49, 50, 51, 10, 164, 112, 113, 114, 115, 64, 65, 66, 67, 68, + 26, 160, 161, 162, 163, 80, 81, 82, 83, 42, 177, 128, 129, 130, 131, + 96, 97, 98, 99, 176, 178, 144, 145, 146, 147, 116, 179}, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 10, 132, 112, 113, 114, 115, 64, 65, 66, 67, 26, - 172, 160, 161, 162, 163, 80, 81, 82, 83, 84, 42, 128, 129, 130, 131, - 96, 97, 98, 99, 173, 174, 144, 145, 146, 147, 176, 175}, + 176, 160, 161, 162, 163, 80, 81, 82, 83, 84, 42, 128, 129, 130, 131, + 96, 97, 98, 99, 177, 178, 144, 145, 146, 147, 180, 179}, {0, 1, 2, 3, 4, 5, 6, 7, 8, 132, 16, 17, 18, 19, 20, - 21, 22, 23, 24, 116, 32, 33, 34, 35, 36, 37, 38, 39, 40, 176, + 21, 22, 23, 24, 116, 32, 33, 34, 35, 36, 37, 38, 39, 40, 180, 48, 49, 50, 51, 52, 164, 112, 113, 114, 115, 64, 65, 66, 67, 68, - 172, 160, 161, 162, 163, 80, 81, 82, 83, 84, 173, 128, 129, 130, 131, - 96, 97, 98, 99, 100, 174, 144, 145, 146, 147, 148, 175}, + 176, 160, 161, 162, 163, 80, 81, 82, 83, 84, 177, 128, 129, 130, 131, + 96, 97, 98, 99, 100, 178, 144, 145, 146, 147, 148, 179}, {0, 1, 2, 3, 4, 5, 6, 7, 164, 132, 16, 17, 18, 19, 20, - 21, 22, 23, 174, 116, 32, 33, 34, 35, 36, 37, 38, 39, 175, 176, + 21, 22, 23, 178, 116, 32, 33, 34, 35, 36, 37, 38, 39, 179, 180, 48, 49, 50, 51, 52, 53, 112, 113, 114, 115, 64, 65, 66, 67, 68, - 172, 160, 161, 162, 163, 80, 81, 82, 83, 84, 173, 128, 129, 130, 131, + 176, 160, 161, 162, 163, 80, 81, 82, 83, 84, 177, 128, 129, 130, 131, 96, 97, 98, 99, 100, 101, 144, 145, 146, 147, 148, 149}, - {0, 1, 2, 3, 4, 5, 6, 7, 172, 132, 16, 17, 18, 19, 20, - 21, 22, 23, 117, 116, 32, 33, 34, 35, 36, 37, 38, 39, 165, 176, - 48, 49, 50, 51, 52, 164, 112, 113, 114, 115, 64, 65, 66, 67, 68, - 69, 160, 161, 162, 163, 80, 81, 82, 83, 84, 173, 128, 129, 130, 131, - 96, 97, 98, 99, 100, 174, 144, 145, 146, 147, 148, 175}, - {0, 1, 2, 3, 4, 5, 6, 7, 173, 132, 16, 17, 18, 19, 20, - 21, 22, 23, 133, 116, 32, 33, 34, 35, 36, 37, 38, 39, 177, 176, + {0, 1, 2, 3, 4, 5, 6, 7, 176, 132, 16, 17, 18, 19, 20, + 21, 22, 23, 117, 116, 32, 33, 34, 35, 36, 37, 38, 39, 165, 180, 48, 49, 50, 51, 52, 164, 112, 113, 114, 115, 64, 65, 66, 67, 68, - 172, 160, 161, 162, 163, 80, 81, 82, 83, 84, 85, 128, 129, 130, 131, - 96, 97, 98, 99, 100, 174, 144, 145, 146, 147, 148, 175}, - {0, 1, 2, 3, 4, 5, 164, 172, 173, 132, 16, 17, 18, 19, 20, - 21, 117, 133, 174, 116, 32, 33, 34, 35, 36, 37, 165, 175, 177, 176, + 69, 160, 161, 162, 163, 80, 81, 82, 83, 84, 177, 128, 129, 130, 131, + 96, 97, 98, 99, 100, 178, 144, 145, 146, 147, 148, 179}, + {0, 1, 2, 3, 4, 5, 6, 7, 177, 132, 16, 17, 18, 19, 20, + 21, 22, 23, 133, 116, 32, 33, 34, 35, 36, 37, 38, 39, 181, 180, + 48, 49, 50, 51, 52, 164, 112, 113, 114, 115, 64, 65, 66, 67, 68, + 176, 160, 161, 162, 163, 80, 81, 82, 83, 84, 85, 128, 129, 130, 131, + 96, 97, 98, 99, 100, 178, 144, 145, 146, 147, 148, 179}, + {0, 1, 2, 3, 4, 5, 164, 176, 177, 132, 16, 17, 18, 19, 20, + 21, 117, 133, 178, 116, 32, 33, 34, 35, 36, 37, 165, 179, 181, 180, 48, 49, 50, 51, 52, 53, 112, 113, 114, 115, 64, 65, 66, 67, 68, 69, 160, 161, 162, 163, 80, 81, 82, 83, 84, 85, 128, 129, 130, 131, 96, 97, 98, 99, 100, 101, 144, 145, 146, 147, 148, 149}, @@ -681,20 +677,36 @@ bc6_finalize(int v, int sign) { } } +static UINT8 +bc6_clamp(float value) { + if (value < 0.0f) { + return 0; + } else if (value > 1.0f) { + return 255; + } else { + return (UINT8) (value * 255.0f); + } +} + +static float +bc6_gamma_correct(float value, float gamma) { + return powf(1.0f - expf(-value), 1.0f / gamma); +} + static void -bc6_lerp(rgb32f *col, int *e0, int *e1, int s, int sign) { +bc6_lerp(rgba *col, int *e0, int *e1, int s, int sign) { int r, g, b; int t = 64 - s; r = (e0[0] * t + e1[0] * s) >> 6; g = (e0[1] * t + e1[1] * s) >> 6; b = (e0[2] * t + e1[2] * s) >> 6; - col->r = bc6_finalize(r, sign); - col->g = bc6_finalize(g, sign); - col->b = bc6_finalize(b, sign); + col->r = bc6_clamp(bc6_gamma_correct(bc6_finalize(r, sign), 2.2f)); + col->g = bc6_clamp(bc6_gamma_correct(bc6_finalize(g, sign), 2.2f)); + col->b = bc6_clamp(bc6_gamma_correct(bc6_finalize(b, sign), 2.2f)); } static void -decode_bc6_block(rgb32f *col, const UINT8 *src, int sign) { +decode_bc6_block(rgba *col, const UINT8 *src, int sign) { UINT16 endpoints[12]; /* storage for r0, g0, b0, r1, ... */ int ueps[12]; int i, i0, ib2, di, dw, mask, numep, s; @@ -744,7 +756,7 @@ decode_bc6_block(rgb32f *col, const UINT8 *src, int sign) { } if (sign || info->tr) { /* sign-extend e1,2,3 if signed or deltas */ for (i = 3; i < numep; i += 3) { - bc6_sign_extend(&endpoints[i + 0], info->rb); + bc6_sign_extend(&endpoints[i], info->rb); bc6_sign_extend(&endpoints[i + 1], info->gb); bc6_sign_extend(&endpoints[i + 2], info->bb); } @@ -857,8 +869,8 @@ decode_bcn( break; case 6: while (bytes >= 16) { - rgb32f col[16]; - decode_bc6_block(col, ptr, (state->state >> 4) & 1); + rgba col[16]; + decode_bc6_block(col, ptr, strcmp(pixel_format, "BC6S") == 0 ? 1 : 0); put_block(im, state, (const char *)col, sizeof(col[0]), C); ptr += 16; bytes -= 16; From fac18a5b6020b7e522b6036d9475eee59ef9df27 Mon Sep 17 00:00:00 2001 From: ShadelessFox Date: Mon, 18 Jul 2022 22:12:48 +0300 Subject: [PATCH 008/239] Add BC6 tests --- Tests/images/bc6h.dds | Bin 0 -> 65684 bytes Tests/images/bc6h.png | Bin 0 -> 7171 bytes Tests/images/bc6h_sf.dds | Bin 0 -> 65684 bytes Tests/images/bc6h_sf.png | Bin 0 -> 6378 bytes Tests/images/unimplemented_dxgi_format.dds | Bin 65684 -> 0 bytes Tests/test_file_dds.py | 28 ++++++++++++++++----- 6 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 Tests/images/bc6h.dds create mode 100644 Tests/images/bc6h.png create mode 100644 Tests/images/bc6h_sf.dds create mode 100644 Tests/images/bc6h_sf.png delete mode 100644 Tests/images/unimplemented_dxgi_format.dds diff --git a/Tests/images/bc6h.dds b/Tests/images/bc6h.dds new file mode 100644 index 0000000000000000000000000000000000000000..92c4b35a5f67ea0e935180f3fe7d628205b999fa GIT binary patch literal 65684 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 X83;_UN6XkhxCbb|)R3>1g@^nHyZ}oM literal 0 HcmV?d00001 diff --git a/Tests/images/bc6h_sf.dds b/Tests/images/bc6h_sf.dds new file mode 100644 index 0000000000000000000000000000000000000000..95b085ad16df9d3da52fb3210bbc06f86c7f2acc GIT binary patch literal 65684 zcmeHQe{3Arah`X3x|3t!bV}lwkWYJ;Ithamu;nI5*wi+Ui4dk}k=k{EDrw@JV<48= zq9LWm5Tn4}a{{wXP+0;B)ODP@Guns^P_))Rt^cUybpeY`f7u1{haIHMlPL3)Ad!z! z5|J{s+i&LW9mOTB)rqu1k#B8MoO}E0-o9_)C z0c%2Q53D_~_Q2W$YY(hFu=c>(L(2y&AFzDD@&U^SEFZ9Z!14jh2P_}3e8BPn%LgnU zuzbMs0m}z0AFzDD@_{wY2Rz5-i`jg!wVyBMWmSm^zAE$uPmQ|6!R%CVFa_=YbPyaF zb?**FHee6P?@0%TMqL$*=;9c9<%?k$9Ll-``Q48|{(ZQPSE7bf3Vz45 z@ARvDKK1z0w;jZ^8E=k7e9Xk%A+Sy7<_y^8J1HLKtj$)WzL4i;e1Bh+Tr) zJa;$C4`46S53oGq{kT~l*F}6F$B+0`$VXg}ZyR+-G|NxT%kHUWy*Jfr^7ldi$^K(X ze@BjooS$jd+t=rI_r+#?kk6+7aEkTmz#kJ;M9y0MWA)EU>YtzR_)_UcsgDXg!X~QO z!cNBPxVk_&O)2jje~h6hS6vyo0xX&HY1hjVdB8I#hy-eT&EX0ymY>&k^*8!Yx8Ss5129zLsYF zpjclR{N8xt4q6x==u#GaQ-D5c?j6$Ez!Cl7dXSwKwe`sl(9ZgKSF36C!za;S{Je%N zA6?UYbouKU1poB)x0O#hKzmNZ-omJ{1>D;zhQo8+;y=Us!oyg+7~k$Ld} z1E?SQ5>e@?vmLDRA6E&_oolYp^%Pd4Y%uqq0Q&cA7|w2FuLwSS)( z`Q9DIe*3=-ydU}+Tkg)bLU_B@&gX9TSSP%oyfaXI;4|BWKj`{<&w%2zsI3pG$5JKQ z>gBuD2Xg$xEc^>9?DCvGi+79nOW{2}K9cZH-hPZ)oYCOd@O6JT^Ycx8f9}%_-X@~c zFnM~fSDaCEaeS(>eDY5!iYKUw2Yc!L*Bw6tD!kx&wL9pUm%?!~T<`Fa<(f2L^K=k}QPoOY$Xu+PY(x8ZoA(q|y^ zQ)v~W``6$|!WXc=yN~^cMylw6(zo@&P2daE1OA@iFUbA@@SpXZ%n;f5faUuO{mq2_ zSQ7u|fd`r{hSSAtuh_@-DPcUv+hu$=nBN+$X6)T` zv;F|?$qQ4R@elk5u&_o(e6IJ*FN*Mwr=C4C;rzh>Ft`W)Oc=Im`%f$Sx?3)J#-Bfg zcv~>L;fc|&&`EqP{=SG$>UuV%Z#}rJ%yI?(&=cbv|7^Ke^k)|8wp#29ZbQ5>K!1!+ zhmCGd|INe3H+7wVxNyKkN%eGVbUvb9qYB(Wgd<=QC>A z^FnvJ)dxxYyA3}7?3B`b2qwL60`mci@!kB!1$szldA?$V=M%<%;hg^!^vBiie|x=^ z^=D49{gZ+$0Cy1f3;x-@j(g6XGn4(#!_4MkT6bs@|BK_fTRDz@!TUqUF`jwS?YQdL z>%kU>AFn#TyB%LamHEO*wwAqz3gujx&d=M8=cpTtAD2KuYp{T*9agO7`7xDWt`2E=S5+Q!K()bs=PavLEMzUae zalX*n$A6an0ra<%t590&+J0&wDk8?m z_~&>?Q9DD0(djpqJ6`PZZ!O=x+Wjl|*Bl=wr;q1@65{RM+JDSvDZ2N5#IIrEtJ$6m zwGecN{~Pi7MdNpk+y5DvPe^a45-_g{9-cS;z!t?zC%^|Vp8)+x6#`|C_fL}Sw(Wlf z^LrJ_w();cdGCSd%wyrz9$)r)t4m)=>YY4)630LL1DHQB_y_+=!h5_uiU)%K?>%s$CwIJ! zfAt9`H|N{@zV)t;#NN9a_TI^zB#egydmnF4vi}`^uc@$|R5m_7ljJpze=|R*k)M#$ zjJ}KG|H?P>`;32<>lwsTDb=&y$Nzf&{(^sMVg7Ef zR|p^0aeODqmxq6`_oU$ACgDBazDDt%Y4f4ZeBch?oi+}V2 z@QW?pKXJJ;|Bv~Rel@h|F^WR?{7Ibro3N5$itqQ^T)?)lDyXNAK_htZ>3NAj|Fji&wQWr z0lY`4u+dYDUxs~$4r-|R;i{Jhc zu2+KjwM=cC3ctbgr7?d#F0;Lt`2G|XN{x>t#zzdlA8D!z{DZ24Jiq-fV?DyTf&7^; zR0Y--i|{Y`&tG(^llf0szQ4}(os3tW-y4L#SwX(+3funxrE~U8NW9u3__Im&J*nN; z|JiH}>wmCbMe6;)esa7Fcr*2Sx{|p*j@VyTR40)SFS17c9HN4$7liX=Ygj=;A4ck2 zJ70H9(W|^ZsEzMYbs^2FuA0rq?KwW~@O8#JW;ok&$G+1PZG2^5l4@=L{!RR&h|j;K z%8H(8RWNV5|NFrE;eqXDJcN5;F4e4atL z{~MQ&f62e^;rT*Yf5Ui~pRZa8{Cnnn9>>QKuZY{v)gaICz>4NK`TKLc&K3RikFR%R z?L0DfJ`CgfF0$v~@2rpJ-sO6d@A#T3Rhy|4EwblJ;hp38#VjA+)Ax|*GvI%4$VYdr z`|*zPv49V)cR9EX_Kw%<3{q*u@L%Bibg2KOHVXcE{9kGPpGEip{>}LW|DE%@f;X}M z25-xhYi$3^f#lz^Jtqx3eTeP9xqgjvBn&?=eEmC$PB0%pdxiIZbjt-+1M&Nh%TYXI zR5hIb;Jcgsytn@p?D;VH;P12HY#iTl8OOVvzhD2NqH;C6nPu_sdxte&f4z@y`~5Gi z?I(S(r{KxUL+T;xcC4lwLnBS0kXN*DF3?FDqZGNywR;V|E zdM2CN>-$xyd^^nk-5=JfOu=M7@+Vv~KclEM9jXal-z@VrdpLgBzg+(<>3HFH6VNwU zFTnp>d|et}tG$2Zd6otL7>g^!{|g5HWt_2XFD?GT7cSl+eZVaJW&C)zNaGCWN8N>= z8uI)79|7$68`Xd4$>#kBIR7$uw?|+Xh$lw%Mo=#l?Njw}XFEOI`nH(u6P{a?Zn@aFP))nbVC{TKYj2e zRfhS0HZ1covb~A@XZ&m4;Z_d^LSNzo#=D1NDJaQ96OmjS+*F|q?Cl-A7q;kpaT&*N zMg6bQdiH$SdUz({1K=Yoqz^3q*BJge-^Y*mKjL$7ec!Dt;pKblH)lZda`%oc{|F|6KgKTZ=OmX)4T%+6P8Q??3%%h=2duH>a6@b39$* z>*LeKSE1h+?=qkM9pL#dlotHE!2cNP9V(qhy&_j>UT+INj^jn<_68>BvAN_Gl)F!> z#&q%JYM~5S>Vc6DuYaaE$M`3W{%apEvZ5zOQSX!e0p^`61Y=dh>&=!b{VM>-&=Q!D9Sl{@)!!EFJ&d zsT1>AlJo=cwW{lXnEZj+<~DB+vVDwx5PfkN`k_RdlK6%Dbe8o?hAI!z4U}B}i}?`G zH@MlasoPPI0sb9dmb_CP|6k-bzW{y_Kk$y#p+6ef-#jn)KlA%9oF9wjgIN5pLHu+6 zUvm$sw>1CPIiPzEOeWzwseQHMe^Gpe^()WE_eS-(!55_7POqb>&+B;;eUOL0ZuEy) zuZN-^(8Uix1bLQeE-6$nivEAKY{g2DQ|5oq;_6II&Jq7poPW;qR>zCILR;1MU8m`2y_Ge*aIB zez*32;qyp(p4VEt;QuJ_FZ1_l;}3X{$1q5E{YXN5C<*^&G~#!JZ+Ra#&kyK3HS${@ zOB&B>9sg}UpkS}ld18LF6#g^7e~Hq`{6F5`jCb@l)efgUex8And_v*>yj~;e`C9xh zd>+>tzPr6&(FYaaAI^KqORf*nE&i{Q0a{^wVB+0gmgol25h`Pl4R*#{?*a967NHPv6DMqIxrdWe{K1{;hWv!W3BHm_zzwM zAK>ayxp_V?jxn_aALthU@$m_Lpa}1&W&fapMu~ULi9Ucd#Q8R>dj5a8ue|@WG4o^2SNMl&>iwdRIDcV0Rid6&f4BN4jDJu-KJj>};BR1l}c(}6p;0+yox9bCPeji=P1cyes-e9&iTK!kFZHY<20^*Q0jM)-Vn)tS>sdEH@4d&*+D#< z*F)oY6c;7^KH=k>AKz5^_ku7ZGG0_z8&mpo!A)G>1ks4;3}pJj@u(iD9;Vt@buJ7= zrhSEdkR2T=Q=e=fh_}=e{b|$=Y>K=yk`bhn^?Tow`lBTKkE!xcam%0k+GZ;K+E?j! z?`idK$Z4z}WG%9bsATGYx~Lz7-L&H0sQ(x7r6%hCYfiU7FD;x;jPU<0^k zXxIOY<+EYDnl0LYt;rm(ukbfabG(lF+>+oO^Mj1{Lhn4@rzbN2dmQg^ne9LGf0YIq z!p8P}7;^nsMiX)nEdDM2R}%km{u-Bszy1(o*?VyFMvO<{3t!#Ke1Yhrt;zL;N=_5CWe$R7~?UxPn2$o2np zITd{M{M}T0fUOz)_f^gZ zB7DCP<=&pmIx@=nI)1&0dN|YJwy0iSBl6F;vAxH7^^D8)^6*N_NXCff z@!1M5A>;f3zptJjL*y^uAAVnE^=9w~_TPaYe*XykiyG?naJ@dk+fnw>*UXwc*8q^7vPnE7o1p!?e#UVm#Ei^ zJ3@TIwD(qDXT)K?^xyGK#|K(V*Y6elJ%RXS`+8jO*F}BB`Rf%GJ>D!194XFh*&_Nt z9Yy|71J^_RLGaD%i^^(-;}P!mQB%LjKN5(H{35PTP;K{jB&$dHZ3Bo`MC%d0h57*D ztBn7J&)dqjKa3@j-&d#)y{z)i0Fc1Lv*(UElzw#JlXrz|&oKT97@yfOGZT>)<2ig-F?72!SGgZVV@S1;uE>7kvZKG&E-ALa4z%mc=M&CPkZ)0W zknCfX!-t-)u&dU zT77EuskJB8o?3fq?WyGhmJe7yVEKUM1C|e1K4AHPXPUWA$kdd5D`7dB1(cpkMd{{ zy{(8Ye0~3m&rdVw+?jjsojdoQ*W5`)80nA_F%ba(K&q#!X$k;%_ftH85OhCGQK(1& z0KJQLkP2}V?_EZ!C}>W!PdbE}?+igMElmP`*r!^T{HVytTo;#T zD_vA(7^n9{mrJ&ij?}F5^^FH8=lKif?W)fMNNHC5WQkWlV|cuv?hKk(Ymzw%5of*D zmY4en?8YC%795Xy&JU-S9ZM##Ugrn%b*F!O!jC$(3JZ1*4hGg5bEdqO08GV8IXt*-=Z!<}O4NX)Eq{#K#1-vY^My5rU8F@HLH#5Kq2QEisO z0l@{UHOL>yG#X3L!Hxu-5g{SMQKRI>aJjF?YRdTrUb5kLIVCKv)(h_c6e!;!aXgRt z>l`|n#A0|wr5v?c8)cGvI~aXqcvWvd&AM~}&oWq@{|M+n8ROeFQO9IK5$Q*?k7eVg z(#^WlpRbX!eJoX|Dl5-3AXWc|ZTB@xu7(p{a#Xg!odx!BY^Sv&;?C)oKB*hvtPI4zS-v1(?`C{=9 zIQ1?hD6Z*WFyivce@z*^zfZ1hd=z?}5)HcT>`}M(oIP*v`F+BTq9N;t0*boI-ecc2 z_Yu`e1S$Kh%KH>(9hJ8*H_bDvDcA^7MoP8+;z~I8WLlm`qh^{jq!z>WdcUtIR^*0> zU=8T9BU~>SSH-f=k+lu_VJ5A)(Lazb*4&ak`K8E>E&$0j{14y|lcwCx?vT2YXc{IN z@H*yXZAU2Ajl>bp|vYpDdO&~?v7n9 z=a@g_mSn`*=;Gm%WSnDaZapoZ(5L?h5ubzRBmrZ+Z<=`*>ynYLSA;HK=C5Hk!}SbV z@t(MS$VeE?{@g6xPhLuI%gHLnuf;L=M0Ed^@wI41Or_)kG4{9mqw?#iaiVYz{JN8O zeo~*#T^ax&gy2x`g4>o8Um>eOa+0uZ_E?pv^-CdTD*ex}nSa%}U$2RUNPKc=12n2t zxm|-r92wirt^Ru1IrM4`2Xh7z*sS~< z79Ae-%I=z|U@K%@^#coO%F5L>KmAq<_yf36{Jm>KeeFTASYl8qW9xu)OC4@I;mdK8 z3^D^)jw_&o?_EO0^|8-Hc9C)C9-j7}#x<3OhE{b&%&yA1fx*YyC9h4rZ=(KG9E%$J zB|2=Q2QLP*O3NBKd`~-$xFsm#WEn=%qCCU_G-Zf>&&<37hw6d*jYXco>`A3{kb}5l znAl6!zi@RxoDb1}Ab%HP=0u@8luxK+8!`>ikYKK)y&^_Bf_&MP!lEVUb)xseZ+*~l zR14*#`NT-wv%cW29uBPPF(Z>#me}i-!gcvXE=h@*4__H;KLCTPuo?n!wBKbK(NrQc zQMxkw((vb9*-R3!#e@xTq+4|52%PuI|0XeFrFOb@*OHaYEV_AXW%iA%nHHO|-$WL? z7jkr-=NK|g5h-|+c}LF=Y=oNBN+0;&+4{?Om8_(;=k6Id{gle05cv>m8g%jXAjF=_PL&x>cKqG z_#>dn_l|Wk%WH4=O&}n~#JhJ^5@Q1w z`C@y3%c07Dy{YS4@4DA1@Isjz{dVy#G09iGCAnlsa_G(=rS#$0xF%5XJFUB0`_~-eUNtb(+>yq9X0fs?U*33rH}H&5#ZE-* ztV!3>EL#UdiPSZnJ0W;b&X$FIJaHt^S1YXn#k;Z3Dhf%Urs0eFWpu0tJaD#~hOmNB zA4#|mv>!$*{uBqEE8G+|S-nd7{lM8~`MX8dzQE80{jWvIwR(Mi((LLF&CCqp?*!B? zDLHzgzxNw%k>etE%oO@<1RD6BM*V8_NLm1G|0~gj&q$issmmg8N}dw`g3j@AJ~Yy0 zRNFIQ0hE#O3e%mM)BOV@a%fXkUby4#Vyu3^T#-#AZS|^i&%yCWiHLm_ttRGWQA+2La zLx-kms13xYT2m1|Nd4q+HvhXhJ90Md11CSi8gRP_U$YVHPqO`kU3nR~rkZs5J*`RN zeJ-|d_+XOS0s`j}yxj_8)TSw4p!Ddo<15Mu>O4d&33IVqKseUo>H8UCiiLDaPm`B# z3xpp0(GydSfFBul*^c{LU#~6cEVrGVHf;Ob=NfKl&nt-2+4VFfoxQJq&cA znMkPzLi}dwZn%+qkJpfx0B5cpizc#jPBWdRMvu8_@Ja-4wUPM6?eRgQjbE$rNbktH zNt7*X&p}&!(NT)v`pQecg0deO4*49rsems#2iM1w{vn6bwyH-Hg!iYC?!5$zg`#u1 z-)5RyPDV^KA%Xs81k*umZ)R93Sjr8P!$ly0?cy(dJ3x7~UqGbZnLt1Ft!FO9kkvuW$Dx zf`3EWu$dA2dku{?s6D)GSkZLT!~rpV*)ompS6GoL=y_HpSw07;=Y>HCL=qMJX6oi4 zGSs8Z1{(0OxdOBcQ`N6Z%tn_BR^mYLluPhLx$7iU~cTv30eDeRc#Q**z+^?H@JQ%OTfp3-6RYu%E_OvMk+C|eH zSXEjKY!Wz9rDXR@XP1&q;=~&t;YfWrEg_DX!4w@0_t&goB-_Hp?R4GgCLn$s`v^|a zZF1%EhS}WI&~r5JD@)<%ovVL`2ulL_+9%VRjD`XyU=vG^e5LCtVl3cnG+DsuQtx8k zO{r>W5)EOA6nqmGmIrgk&NniMB^ZT}o~iOV2MU!9yvNCnv)cy_*x$#|mG)Gn_Rphw zGNv-hhfdV{V$Sjo~twQQJ$6NwU1`uleNVd`oLZV_1U*}q{4#emaKi2Y}aaSdx%|vn7z<54)X#;5)$KM0n4UA<9wrN zo3No7GYZ>#Tob&#LYV_t;{?NQ@qipb0YSj#u-%mp4 zpQw~GUZIgmCBsP*Z&Kae;TwiDhF*#BANLZv9hxSDDUeir%YvTkG$w9HnCZ28OdX=t zHV!t+DV!EmwZIdp3B;$VAUh=L5fW{p3Q?X*Lkt%&z7Ya!DAm5}HdPRSsLN7gtO8({ z7&ketfcqW8&j~wqvRI`(U_lgw8`KAx<8&Gb3xIT?Et<&f>)zldqCbeJw1)^oR6M~Ndkn!n-TTVgizU0NyUhRonvqZ6$c!rcxBTEgG<%pCx# zHNGF|}7rC4I{GGK4zX24F!%v4~4&0w1B=2fq{1a*zXgP|` zmit}%f)A9D$9__5QW2HF`W{7nO~jZ%tkJ4vAVrCFu9=wV+W^f}t7E5+5!yogAw|?p zqPFG9!0sF&{;42FGWrwZ;4mh z5j7Os+`uY9&Fn0*B&AS?iH@L`sc)R-*O->tiX1>$ILf1(VdY1Alt)Y!?oL4MpN&2? z*9|w}R7((WnBjF`GglGG+MM{a!zp_ogYQ=CoifcbcF%p!tU#*J8`Gn!GHj>bCndJt zzHdMIZOdD=9t2PWT+(ojCS?v0>qtIv{N*&Hliz!*{U(O72^c8^t`hTHu;`+(n zA~*9ZdjT!Wv;;~qnXntuufWD?`>|meeN6Lim&Xc_x zl#*1uy&HIHS3&-e7^x>oUG`TCqwC#^3zc|s2O$6!MCb8v<2C@69h>mQ+JVfj;7_eL!{^dID0GalL%tP z+K?^;`+q#&ZPT@e;_&$HW8(j;ViHmkr!0+$RrV0R;o6w>S@cCyf&kkW&hf6Al20Pb zs(pyaAo#b-N0i@VN@5b8%utdI(%@yNrXVQ2NFjgS)@PjMaNke$-vbsjyWdi!@C(c?%yHyPD#X@VOkUNjflMavCa*#%X!CgEzS=_ z5&}4lc9fD@T%JC}`uGEES72Ktw+Py1^FR3_K&Vm1k+X}NeYHl4@eStWd9ji~;!d=} zzngrwM@M5K3|n9dP5yskm%PXb4{+7+w#iQ}N&(tIn*nL>O6axb)W)Iw0N&;nG`{Da zB+T@@m5+tn?ZjYi?Mm!Y7A9s@PD74@$^TJVgG5nz(|1PQR1cjP3-LbGeeX5v35{m@ z_AbgRs}1E5YeRb;RaalHF3+J;sTmq{I`KaO$oVx4+N>rnfN;Aum-fCGJjG*cDlq zhJ_Ok-&BT$hkGw2{$luYuD{B_5@)=+^gOdq?N@|NMveaRKrZ@*^TqWVLj;N6%fF*R z)?fqBCVQEH^v*d+jsof2vr#%#-e)MPK_rRx3At&;X%T}xMas}MJU`lAOGVg8P$YI9 zPOTgp3a5UuImpCNVANXt-ZRB$>%x;oA$2{lhTQlms42AQWlJRIjj~j)BJiXi^oBq8 zs|4$=6H|?+VAV#^tq{MkKw=T-4PJ-+CC`=lKp9Wr*i(gNY9pX8uc>SchkC5SV+0JY wNl_4NLGrnN8U|KPR{6A3{*U(F$L{c7NKXvBEx7Z)|HAq$m?HK(ATpZT^H-V*cI%wL^qIr0@7 zs^O8}W|uPM%3kKH$P`Eig1p@>y&ym*uyvgL?=zJ^RxyiCvA_7bRv~i%NOTrPKX!Xc z_3mdnlR@MkJI}c~D{4;L%~%6@kdJh4e7*EWiv5+>wJJD9kAc_8Pz*6fcQ9m$4YC>8 z?}rF`+x-3yodi0*)T6E9>V3NXl`ZIaM-Amp4nsS}sU)O6b4VA)nG>%Jjdx3%cnhv4 z1Rs_e+y0=Esd?U73C41opVFx{ zY=2$2{vxBvm{PK-QlpWetc4(^2*)U`jc(LFX&*F8C^2ZN5X`BSwv@cSMcU>J5VQL7 zsQIIkY0S3TGcb#&)`+xsRuUEPJsjYB2)g8-zbE8OeXlkX{{iC+MP~_PN}jUGo)$Le zsw+EzlKEx|U^-g)wmt@q)_JHjl~`Vae|_02mzgNp~A{5^c| z#sQJ15q?q#L%1G4mXkG^dp%|(K@9Zu^)2P%bvSi-q-N= z&3pc{-1jkje7H>F-IKyQXvh}sdRR*LGD>%<;^BSE4*c@&C+Qu4_dZx(ov^;JUo+1; z-RVc`Z#n_^Ba99lx)|zo4&roG-}C?hP4iqQ9d0 zeW1e_kV_-lDxN(==O0&*0l8nj{(~^3Z={gy)qef#lAQ$s27#?oaQ+D9nz9Gi7fFlX zg7aB!1*)M&p;)QDz7m6Uf<9nVyDDuw{M|d@c^9$80HVSVlu$NG6kwf_Yc%}Kx zp&O0oeK!PI+ksq~*WA0}##6(#8~r%wBciOlI+!N9J~9Uvq=Wt`k_UaYg66N3Ys(Ht z_j~uUS*!Ym9A;fjcLq?kn>f!iC#Zz(|4cvMnX1j|Z@&=u&jAQ7|EXEj2+L~ZY$85)hUj zNakmDGWao?m`n2cTS7SXeLF4v2dux)2X|!hhP>c>2`BFN*OSJU(88zI+7{rv^$l0W zy8es$AoH90z!9!O>Wi>%yzIT z@XMt1A@_S5NjT9@SltTY+IQ>ic!txHnD#0THU3MGWu{k2Y)#2v6r0BE;Is`81fPKO zt@v)-M+9rpAqaYm)#Z@-;vwC3lcN&w4Hw1Unu_r5yIz=yVV*+tV`k0>Z^-`Qj^{Vh zV^^h<`T=C7(kfZ~^G!KOMtFbFyHE4kqXK*VzSeOFzZp4?N(h1#uxIac8qyb~No~aF zI!yA98vzESJ%6^rqbzr!8af{xf2qAv+18I0Fc&_Q%DBu9$HA z0iWp&K{LfgS_7Wx7>A`S@)W5Zb$rrg1KW8mTXaX!G3LYUXS$0B=d1M_J-En3yP z>>_94o&ysBug?ODID`Y&e&SflHt|Bh-;j4T!XK#*lGxn}#p@N$1p89@;LYS=&-RSGw$@Pql@y6+n$@c}aKGm~%u3>bQSt5pN;Y!S z$^dKMewmB+vH;7=2dXokRu4rf@H2BcHDKWRXwFRGx454u_EC=@6ho{od(~%vKfO|N zEPP|rI8$X3@1A2Y8ir#^$4mk5Lk7yop0Y%h;-b5HHFs1?;aH>O)=ILSQV9R7 z_;$I43=IWzedIrQ))&>kFs0%PS4?31ejAa7P#p=;*8yxD-Pr~@ma$JY*2s?&Hd9gbtn^}VSCBWci^#2@KTT=m#fG1Q@k{0ZbAl*sR= ziRws*AAmQ7^h)&`(R){@nF60mbDLut=IC%uV_GS(ym0Jo(4%_1F4iI9~p~{J+IN?|Jh< zKmHwkmH!=o=U+YV=j&1N&nKBXANTucg3Z`XFWS^k$(kUhGD zaNuX|;P5l)Rg(Eb?;)NZtWLz>lZNLZf4oLPEG^9z8&qM0sp^s964Xvpt|+J^NtAbNM@eQe%WdxKaR5h zQ3BfsilUFt)5U7FkSq-{xmWXnQE&MSi zRsMsytf&h|c1V%-eJb+Tg|r*ikIqE)82t5)`l6MB za}fSM@PR(dS)2st^M~o6_!`IX8IO)~@dx%8;l0$pd3R*xB4X{H(23GWKVCIhGQegS zJ_mmT;t`B#YrE$CBf=2*`#6TBA^kWX`~$GxZV*pw+8<3YEV%jr^hKAE9rz1eJc7R< zwhtl(dYq{e(pUE8QLEsRO7OjcB<6!YI5HN)ZOHp6LHd9;YsniJ*T}jqlze*m+t4Ti z{JZ=bMYxR=Y*m*AwLS>vq=C(s7lvwa^9j%Agbi9>R`llO6Gp8@@e9&V-1SCBnKM%? zS?XV{-@iM~v#&X7KjuID)ArvHUf$oyZ~Wco{XGAV`4imtuZ+>LjS^{iA0`AK^R7#bfJ{8qgnZwJPMe z#{A}_w+ww`e-Yl9e)jnTgl7d0?|_fNz11n;|GydscpCliA%~$A;{^AgIfUjrUtgL$ zVkOcSwJMnyO`K)Z_8>`TtQe^eq-W&Rj6WsjG+E>j!neG{9{+b&o0cd_6xv_$gX;^O z?`YrK**ryWQAjY;TeDJQ&=7V;q^!I&7XEu0Qm5{5T~a_Tu5a zU;w^fK0n4xfqea$6$Hyr5|ds*FeIEILtX{(f6hpZrA9FDSf`R0@DF5crl9!J1>wDC z?<*-+EySWUO4E-mSLy_Sc_ z*l)kqamfFjmopOli?|97WDSu%z^~rTd;jSEY|4IPnD6Mi&Z-ks{EqznnNQ1~kKYV3 zqg5&s(u3HVng>~Wj@X!q9kfY%G;c)9-rL4!%%AEVp9H){@Q&3@o0+h z{(XpFV7;m6RKYj|WH-s@OZi9u!_l5>y%#bXn3NxjxZF8Xf68Ee`2R=!lfM`5{HOdJi-7#_8iIj2frhx{HGmfgVfWMsLYs?s{3IXd3JGhlw`6=U zoS#E@ydkNWR+W^pPG>9SkK@V@)-5k_%}02H_4h4)E)e*++)w;m&?kOQ0nJw;_h?sZ zVgbt6k?{iJ=f-SJTZ`uN1y=;&y%3*Z0r>=bU4cnc4i#Y2I3`(NmyRQ`}PqoPw2@Q(Pun6}G9<4XymoI$`Kj}b7g zsARP!B)qu7l1+#+HTQ!$70?=y_|D4q<*$r#c17<JW7d*0b?y8iXX(+ z?0L2Z^a1$$Wc)9bs+L8UIh8bPvIwOQ^aYMZuNlx^p>x~>*;`*eX^e^ZhG~gEA(_k% ztO35kcSaPFkv~v4ROuMzcadX@@_Qindelx&3j0}p?Ft`3JCIBBnkc^E+9$q&k6+Y| zj2^tlM=%odf$Zf$IT=kpEA4M-ZA=R|1*A#G_uY z2nRzF?_S2~&LqESc!VG6hb2yjW3Jxn1b<0G0Az}vuZ8>;TU-$1FsAb%KCQyd_U&V$ zg56nx^g%WF_d;=LEmsUXd&Ncf;|RaF-Q|uIkWWDT0P^=)M^-LlNR#uC`8%AZ7G6FB z$&@|z9YN{xU@vgaP{MXT57(^0$*`~bx_@bZfY-^DpiTV2Z<&!hSTM5ls2 z=ypJS!$DtT6hB~B8(wkRhy{%}-$*py7kmTw2iVU%z9GS;7V!NGC%NH{yIPU3ICALc}t*d{eO?2>(YVzI**@g zF*zau_k_pZ+>G$<>nO^Ej5*}%&6RHL9Eb43 z(T_w8SI8U|B=yhbunkS|4H=qZ2S~i@;x&7AH*|K&3I+$Gc;5HQ0N`^)rwH6f73d!p zksQgzJH$7k1G-S|!RRyFN<-stJm>>oK7@Z4r?{BgB>&NhWCt1*@5A|bkbd~A50Wd; ze4r1;h65jF+xHx_rM|NBRhU$u}IrzQs2UES z=2-!MNOPT%0QpB72p%ZD;jFxbxR;>C9i~29QVH|{=%b)ntWSIcmw)7lVG;{@^$l!) z#5Z*Q6MVyW?B_q7{~M0?ey{ z^gZ_02wP2}>IHQ(gKRh2-A$e)G2#G;{~0&D-#+W?l@;2@hwQy?PA}juvR53IC5872 zHo~W*lQzjGl6a4gKhDEDl8LvQ-H@52@XvsR28Yl(2=T#F#D_Q(&_K=~0=_I3{9D2> z4(S6F?^F40g7g=VKA_}e2D+X;vgb=`{ZfcNLR_NjcnxCI_ylE5F0${JcM^Pbp_fL% zX`a?LZEMQ(IRi9Bp-aHmhXP;E;~QXplwaH~B}eiNYGNecV8PEXp#HQ-ls3mv(RMvY z1Mn>Xe1jF@8!Sh9(a8AWqi91zMA325{{X&$eVEKIl6)Vj55C|VjN!TUa@&Ybc9cF# zx={-B3-Ar5Yu-O546DH3N5{)Q2)$TSkMfbI-obHGKs1G49{+fViL3bKT{Kv$+CI(# z^Arf?!_JyT-;?x}+AC~qegY+kKu#g~M~jI!5Pzg{I4?@m5$YxXJ-(r7&MDv z;h(kt{|fKJ#~tBiQ2qc_Z_CB|Fn&&ZvsTQest|}*sqydgb7FhT&cl@2;f07F1Al(f z+7sj8A1o89NIw26ey$z(IdCn2pHn&<-S`Oc@gyFd>b~OVBxC(^a%v06{5rDd-{$AG zlKdQL-vc8e>TgtOlK6z$>ZmACzb5nk=WU(AVeeccldiYTLJK9ba{7M#?TXjMX)uIe=bSp zGY&vKL-G)^*Pr-4z`Nn`qGLR~llV6qa|qog!v8&jA%^pHp?W3^#}D1JBnUCg5kLm~ zJhGR7e@~*6dH{_JDPm!FY-+}>Gl*G&>JJcp z_ZK0)LH-EJFCu(*4Hm_pGz0vpzCix|B;Xqes7LrKzF`OA8&JIyI-lYjApTh1vIX!z zqUR0bA3?vFu>HXve8o3_tYz*(Fcy&GWW7@OH~9wOBgyz?7~g>Fieg<*PlEL8Rp48P z@eNl0Ip6TV#Xl?^F1{)LpC|L`AGmn`Pxv`~&?(rVl9goq&B_$S3Y5Rd*MGs!=>R{c zd;kuP5&(X#3-R%jko_KEt-ZMw;(w^GBlGoN@^c8Ui3vAn5d*-lxlw%lm;4;UJD!-A z)LgrK7Z2~u0Z{{|qZc>vWY*>Ej-K^&8zldkpVP<**EuETFj>SD`RlsOZFko_4rI(9 z6MBG@Nq&w1evY4p>^uIJ&4zqE!zg1nS&!)2P7oEzJ1Rkq;cuZn0N$qsiq{oEe|S9x z{Q-EdnB9Aghj-8i5U-;AA64&w@XbPlsTBW!z6XX4uBKaShgcVTk@Zj@TkFx*Oo4dc z)e`grhGS5#BP7m*BjI=nqz@8NeOw5tk291e^#SDnsCs&KJsPL-?}!g4?o#;#6z`Mz zfN{nC8R!EhhWI^_Z-9IO$u~fKjfICn9w)yS@{3fwfOGiSo0O%aEKXG^B~A(LIjz=gJ-4qhI}%=2zVX(T2w8&2)u1fb(7zTxL@@eQCKFe59_2P=So$X!$GhWG~f7~l^a zuux3jCCtKvFkQ$uBYRHwHp*Zf5ywnT2u9+dRh3ryrpLIPEXMRFi1@*km+@P>2cr7< zJ>OPL;lomUBfFmiUq$w#bZ=T5LsqE2rsTTpnIX`j0{Lu8_Qvpxl~o6zo=t+-A8E+< z5$gZ@I>GZJv@qlVLN(#1{+<6+{yN|12=9lq@7#EVx*tANa`jjIoHxbKL4Ex&eomg4 zR1W8!PbiWwK>aK-?Y*8LZ?iki<;RBcb226{>M5Y)hWL0!s~o~RkDuH76+f3^|6v$E zhvJ1#{M@x~^K+0HmgoFI_0xoM`)TmL>~2A)$Ib3zFLqED@pJ2epA+3r;{6jphw}BnKe{#op4k--Q9S>i#CviC ziFYr`UtkezbA&gTFBAAqs9$z)q3{m%0&ss(#8`|~i}=p9WPcF)9x%RM@+-VQQKX~! zKz~rA;rG@Ih2e|Q^`p_fR?u>>s z5tI+YuSSG46CVxV!TU${;liQ7iqTNN1@#;Xr2i+jd0t}4uj=S~qkL}EWcqaTByxR1 zy=IgEC}30{#t0f}m6Q4T^c*+@@&#mkI8NSrS}e#ety#$4)GFXD3Rpn|!NG4GXe;JC zAtay{?d=ML+{5FK%INn{cxsv>;iL`s%fovdAJ1Q=ll93A=>Jj6`p7|e|KHmGzst`d z{&XjJtW-bEpZn+3JSPOgpt?Ko>tsC7ni+zypnLqL5}CiF`se7-Kd1DmzFu*1v{Lf# zWWFBZf7#2`=a!Zg*rD^$_ldKOJwMc>wN59XRSNKq`sWZo_ql)W8~ogpMEIWQ`@#9M zo?f4k}bv?U)pF{UY@pH@9AbyUt?~O^DTVy+HRYdvu(KxP7 z@^h#^kBoO*e;)$86K~KnzD4;*q@JG*&VV<>LdOGsM96$SijM{m-U&g3KW)^nFc{Ou zA}VMIe=8sVG*m^T4;4V~3yakEOrc-bMjoN?-M9#BO~2D)`Uy&o9<>Uv;`Br-hLSOZ zRhP+r2(CZix?|O4=y-&BXKMT&V`b*XsuN&eEQtYeECt;p)JH=<1n8fQWc-2R8LmHK zsCIcA+!TiV8Mr^Uk?4N8{)q1&;a!o?NyY~#e<5G~4Dtm7I9*^0ioX=iZ`vGYB3tB_eSG7*{Otla{*cZLCzO> zVr|;h-#mDDzdv)@Hgm8?Xg$b7uLqbQs1MU(3=MRu$S>T_%@5KIE;d8G=ds>Um~!fq zbilh`tpe+X0+td%5IFdE>4Q}qi6Owd1B7Elu;9^&^SO9$HjuD}h>q)@Nk?W2Xp#AB zUVV!u#^f{;P)-=Ky?*Z>fe)KWA;2G+l^Glak23akq7CbD^n(+pWBB1{*h;V`- z>7`xl)Nhmla)iYch$n9CZc88BR3#0^V|p`sly{U!-+H&(4C?U`()>7#El*;G4m`m# z>@XSz`6!Ebi=yVW9d5yeR$9diNUOgxkrofHsu?|(C2?Wz4zTx$gCS3>GU#4HE95ehvKXl9{2X9^LRD#QP+_ zM)}(gH;_I3B42-xvbSVD&OeIar?G0!AbU;c)w6p--y(gnYsF{424>8;X`D7qV6;| zxwxzPF0xW$i;@16Sv6&v*VA&r>-0jhzsVP^4ycc?(mQ8K@ulGJKU~@qW{;Ep7_!$| zM<1>4OQJ)+HC2ywDX(oBufGcPe{F?4u%isfe=rYqA-wmNoo8rv7OX?{7)U#PLSzfG>Us(qQ;(yFH+{QF{9ILiqWntcr7d*Ex;f~}^)Dd2yS_1bdyUFbj91pb9L z${&XgC^z<9fbrKd7d-2be<3jl{yl^29}PVsJ)Mj9HWLXeMK9xGXKuZEvB=l;Y(|*p zU=cAuK;9M}_sf2xKg<8D{YUFZI+Aa?#83F|PW|*WO19pJR~;BU(1sUNa@n3xupKe= zmX}dKCe+ub24gR3iFollc)27dVNDRe10ih)AGqas%yJU}{0`~|t929FC^`{gQ0bVN z3R!O&43>oC=b`^oDhSn^V%WkdrdsEKpN0Aq@XvdM=A!d?{V0w~ckUD5pE_TLey~+2 zUySWZdhnX~cmd^O$oz1`4F;!ojQlOZU4j-cTpHBYQ*oc{FQ17+KMMi(BY zuMClRcWfDe{wI_l>;nITY9>D&8MEAqnZvgE3U_~E?4f#JN z>R0B)`-q=F{?hHMexk;bHdgzr{d&)E5?35Q5~ zFcOOO>x;~0>8ILX1$zMBd%j>dhXME^?+4+%Yj53?TQT6DLwz*DD{kkJG3%GULNp&) zk73c)3;k90Lj!eWzjR6a)ru9p;LicRAwMuG+JRsg#d?wP7n*OJ^)wIUev8^B{C~Fp zOfej;O+62v5#%>+DTZL&eyKXT9=HFJ8h^Wb8lTA+f2I62R6i!NPc)n_t!o#r9v9V9 zko}jE(qum;>JLTzA3{6FBq5!wbgX&{!kg|y4On^-x~M(_9{CPuOZl;fg>HX0Ro z1%N$&@Np)=AoqK4L-ps_#Vgl=9~waUZ0Nd!xmpuF2J&m5pA7m#Q2bxk0R8J?fD-8c zFoAKX-|6Xm_|Yi%jprUxf7G`ZSs%37gRT$rm-!fd?oUJG;D3(GSTKD98uuN61t0Xu z?)I=aH44c>D=nh=J`O%GmySMy@XbDf2U@N)Apd?pnXi;DnO|6u-y5tYbefEJ)v{Cq^$nW%H%go)>!Wpl))N*> zxGgtbK4uBRlkYMij0N$P$;`7<{Uh85Ge-86kJUVGzY~V-*j95gb{`c_BY$spdGm|x ziWIWG3+yw*?+tm`eb7$?`UkBY|IyWi!vNTKZakNzvQzI_bpcr~1^(RwY!-pyF*1KL z8KYz2n7}mXC$#(UxXcj9l8Nw%VgH6G1C&n^GoNDzQ=UB+kLthQ$lf7<{5lRg3i`IbQW9IC&cdS1SHVFSj+5?+*jk&dj9{zV{D%L=}3ibI& zABeF=ynMF{=ho|nq5P6JW^AlD^)5W}xwulu*Ft}%P>9SKlw6!~Z0R&oA57H+j~uMR z2{W=^96pZFJ{qQ^7f~v2XVgPScwhF5)VYM1i}qwc7{*+fQ~0?b4EY0_6H)!>#Rlj{ z8K(>@ZbC5{{- z^+RrW1?U5+-X8fIM_)frV|Nyh6@Yp_q(3T3P`-?qNbXlrVQUMXxjyGM;Ek%!1ALPC z!hCN3E5r|JU-$@ZQVs2>m5Y`nAvb zR_Vw-d%E*X;8(o4{``ZU?GkpFjnuASxQw!2`bBwL zsL#^X#+m#W9rS_eS+swI-KYMK%kpa%_9!BKP;*woH9Q9RV$>gsrNkORzLndL5{|~t z%&;%saibsz@k0pjmo$=z&gS0~UW@w*a_dhyg9P-y8hMfWMK`f?$EY^Sp8$I@knTp_ z-yE>Fq&{G-X;Z(k=sca&SA3WdGp6jpKHFHbzus1R0dryOsb@BmPK86g{cMXdf2_U` zUT0z~{an#k4(d&_-IFzDaDZQ;>eGb$nw~nbYxBnn9N;JWb1JC*9aNu(>eWCWFs=k4 z{+)@VdR>SI-k^Lz{vg`#2G#elyLtM6$|n$0(D+EBB?q7#ppDEwZZq?s!E;mjJ9ZX@ zceGzZ3D|dHhv8L(cYgdG%D27vb>#(iGV}|C@F9HTZz%mm@g2ZVb}f?_GuS?7icGW# z(g(iVq_F)bv!ChBkY5J%mjhACh_A!s&eug!{=e<5{*FftUOfH5JUV;gu}k;JdQRjI zh>e-l3jM)k{!Ruf5CYx#!Zi9?fX)!?*Jkn?+Hc7DFxYn#PsrQ2q5a3w`%ynQl=d=Xmnh zc+Xe+wtaVmn?d@3$FK9`&v^W2@jo2zyzBqze!ki=YEM3m=RAuTp0Cr|77!m zX?$zO0v~N#U`WV?ua=$5-`4|khLiOElN(pOf z{e<)}$+2|M-^XCT4zQm#usI0D&t8<5T;fGT>-@IkB2~A-LrHeaJ4|qlzD9e=r+fm$ z4~{Rp-w;I3kaN|{6Lf2&A1-J_LOt2f4k@bbWIY+#zl!?T0dHvBFSqU8y!W6Fz+ce- z+0%eMX+|O0j{^Bde5=Mdvi=STe^s*`8d|6xmfN4}R&T4wu1KCB0QG{%KVVSxHstfe{x_fxIQ|4# zkHd%V58`!0gnzC+fchR{4YKdk@wK~8rPn?Ne<7Zmzbgp^K9Z{Mi+T?huIgnUhmhC5 zit2rFEg?lJ-aZs}F^y;_8Y?QiAL$ESfz>B|S}@u|VZ3A|iNC!vm|#hEhu*YAb0oV; z!#9WiW%~u1D@lI=-Vba15wYZLw`9>cyzgbbiDdqa!L(%n1@@brGSF~SGM?OE)t5Ew* zu^=8}hLZ6=s;^40vAdB9uWZZVJh0c$PiAbMVhb|t4*~IjYRd+|D?OO3*C74#)yUr8 zoFe_oJpN*Xq`Cm)hf+G{X2*}4leldh@Z2`x>&8Q0+A{-U>uHumO zBmFsT1e+Zt?#SOYf{X_eH{@reZ8bpn1pF^xAEWla0{lZiierF@BH6!+>TCTiU~>_u ze~o^)=$@i4%6Cqj@oE1JQXlMejQ`nJy}>|AJRHRjxQdX2#%ziYfclm&8`VJn!P+Kc zEs0B{Kg0@pNY>v`{WrD#DGah+Hvzq|7g6vu1VQ@qhFbp4xtZ*mZ}M~OulczaL*njO z;ZgfZ|6a-=ZhWA+jl%e-#U%cZOy)b(mjL`+ycrs&___K|{2ans$NUN_Wq06rxb+!3 z6YHN%%s}-SQ11cbJbrHOFn(^)Fn+EV`m@pT^oO={PL;FJeupIAJB*(bfSeA=&jki_ z%Iiwa&=f`I9J_lK>6@?kInW#`!&|9qW#$tpdW=A@7?kj7omRLLPSRiE(87Hjj5== z%pw>(0v_K0cm@eG!*x~GW`TcjrycnRJwo%T{35C+1N?r)Hz0lB5qooRaG+6fFU*JX zizQ-kKQwB;DKyUI8_u8Hz?li@lTUm@BH$ln#5eHj=|CSqF~rlNx>u%+sc1gbkJ4UW zGgVFko=qg#n0vB8UR1{=zIK}?pZYf zbD5kGvnEfZ`1sj^kpBRFZf53PWZ#iJ?wDR7Dd-ON81cCv13$N}FR1|bL!$EW-g#}V zPiuj1-$wHBU-5HDU-VX=BMjP7$^B7KeGco_hkhkhtF?Ik0~7c;*E7|w2+zJ47RIFb zIkMkF%*aX=P9ga@q;J$}i`+q;S`GXlna@iZtOfa;EUFg?cqR3p@^d^qZ%+ z|M%tpE&d(hWN+&wu*I_Aea%{GaEe{EL5_Z}@crW=rl*jq1H?{fsT%Zi7df zy7qK|y(P-OPt;(72M_ysnx94cOM!nNATSHtY#t5q!$>bd@DF}P{iI~R4f@l*`XC1UwQ5Ui@Z&th~gh1zXAFH_y&EXZ~W)L z{#;}}0r>B6*$bx2A%6n+2Ig?S;o5|uPke(=$)TK@_&hO9G1wmn`3s0|P$KyTvY*H1 ze8hF47S-FC5}ROuF2v8ZCmS}42p=T#ck(UcHxH`UFWe~w{SjavbuZ2g4YwotIaAX9 zFA!j&{e>-b(w8Ir>B7rn#>{^cWP4>iX6egA1C)m0lcT*u0i{wRF6aYpp?hY zarMF1{2X&s7|G8$k?{yQzuFm=S4IuF-_qo-__^XA;OE-D!Ov0qyZ#gW-0<~1Ty})# zr|?6q_u=DzU;h8W|M&WV|Ni}d?H>(4pHBH33sAiy3*H^qKlow3f!ZIM$2aWSP4W$} ze{?vPZ?Ks>Cjq8C_c{ypgF(OQC%&QPU-AuWfo}l+Al1qT?N9ApWku?PulWWTmm3KG z-2Xx9gFLifF7WfP-;EVqoZ4Sow1KblT>^?nKA!4)Bi#)48v0v7Kh#7lCn)|9@~5P~ z@wD{e$kouF4*hbzkWYa9y*qqXB?%+_AY^2M_=X1-GGil1e;|#?;~RuXe?hKf@tVXH zYZQbIhLHIMH&q7Y7e3c3-_i>kop5E`bomoYP<~Gy$Z*a2BGW1JQXwBl?N^>FTejlC z>J+f2cjZZZ7T?ymvCmqO)DLj~n2blPN{M#?(g%~#^Ley&Q~CNw(Ekko>8nc-#MU6x zABFt+FZns7A7XcF6}YE^{sI5tYksbHt~`@Z5t2i|&r$W_|1>{0d_6kC{UQAF@?YN@ z=i!5QJml-tzVes3znAfOoa&bwKHqTp^YOg>H~+Wz|JV2P*Lddn3p2hO&;0fIjokfy z$qeD={eJ&#zJXOi?e}zez$in)3HZzLpZNx|-_2A4!_6uFfz01&8#DPKaRK|warsBy zepBDz8_@o=Sjs^o*q<8J>*PQ_!5G%haK7Op$v3nsyj$TvjBoJyif^za`3tN)Z!@)@ z#aDcT$tS)c_yF|F?X#OCg6ao^<}47w_uPJ9Gih8TnU4^v6^W&DNPR`t53IZKZ1FNw z&x7_i&m8hyFUBl!D zRTv^X_b-Ejj9&P?l+EyaDIouepPPaBxfLWo=L=oMn2Ds;vRKopsDFDnKZoBE$8=4g zUmNv9K|duf&Ew~&{otbhLw=63HwsW+AC`qmpN6mZ;j$w<4{6W8)1S{9|6H&0*Xx^i zJWqe{@RAe8!~5{_|9|oS-S_c>`vVU@|38(v{=whm8~DMbe4B4zo&3Z%ko7w@k4_(h zPp;yZh3s?B2%Av_z-W{CMRI>?s8)F)r-5~wIN$MB+q?>`L9)aw?gZv`JH}Lk;M)eJ7zfKv{r;i- zJ00P9DF6Q3d^vCYU-=I|y8r$Ey8efE|L{J(Sr0HAzn_o)w*LA&{?Fu3xccC~#y8-T zD89i0?H>z#1HN1eGe!GTLw*tMHzl-Okldeuehl^}K=C@CJca|r>Bp8RBYzy}gD}el z)7Iwk_(zg&Aotft{sG|QC&V{!f8T=S8?-Yiz5(q=H4X5LU)3-qo-+)qfZzKL-+<~R zrXs!p#S;ZfZ%$h55Bu#wy)xn(pq~Nw28bV|I2zyP=k@|WcazJ{F%dtonz$6ze{OP*u6!;Deh*JBNE7^nPyG$3UI*;C(l5~8 zkTkd%^*1<^@qg^i1w+6$pn9Pn;v3Sa`iN6DwIsiw-Elo}?RM&XWPjBc+^AFDMYp@K z75dpgM*N&=2A%AmMtJ=OKR1B-=Xm^FcNXfOJO8PFjz#v*)oCF7lKBDGO;L`iRa;0r z59^-;eoo_m$-N8q7-%NIN^!u@V2u@8jr>PmYeM}K zDc^g?tR+fmo+_1NXkDo5bUciQzhv`{zt<1rANd{URlbAq+^nqM9hrvA1)-c3+!^w< z8uuT0?ZBI_E4q;}A0PcuHkmsE0p~yEkD6O~0nX3M%5nv{o}W3V@)&i!u11Egqv%K3 zrM%-CiQ)1*CX6rSjl=cn`W~E{+$&}`G7MR8J+xmIdLrUp#eQfKCr9C*y3qA2_*t_m zZ9;pAM`o$^u)jjY`Il&XOZNF&AJKR(zolWN)t*5@d`6KyvzC3A);Od54wQ3N1!D^g zuK0#w%!r-wq00<6NxqdbSScKj(NDjW$;(MuZ=YvqU~}vQb}#YOt;i=xuBHNc4o{@YjYk)DATlZ~H z7qhwz;~j83E8pkf1!6EgKgHUpl!F)~gBOV3GmZAANObxK8 zwhQ*`KibRl(DCvhH=p0mPU^qTx6-b}pdaRI?P+K@S6&`HCwmE7GZVvBfWHw^OWZPS zc|XKwfH__--gDhhy=biVrb|f*23ShMnFD8ZM8fGd*=q;^1I)Ae_{#d+*X2=j&Mbi+ zIl gz!hok4sU#Zp8!xBj*w7Xvc`i&0Hj*ORoY(r9g`U&{3j9Ct)x zQAuv=qf=IAFG*Hr3KH-5wXXdl2|sd)exwQBe>^QkwQ}yRe&WouDhJ?yk9Ow6Qsv0a zf0zTUi?xI&4Og$Ul?9>qXz!y2V3+u>Wo`Z@)&HXlB*au*-D zo(&oaI&;)V+3CFX0qgbfb2h9La{YU zV1B?y49Lqd`sm6Fl)Qk+n$W(VpU*&YKXI+;`Evyr7sKcyD|dfB{yCrJw8~Iwz7`k< zmrcC#9W{>bKbDUXd@NgQ6ME5^Q)=%U zT6g5$pIV=G!J((d4LA2?x4zH4pD_@pUOuTdRE~AEbabT*L0HemcCISu9Mbc$ztJh0 ze_GYQdcRcp(O2>pG}v!$S2p$?zGHtan7)+NKViA|E7<>T=2)k!xW}DcvQ7QOExxc_ zM=fWD{IOi!qxeXzmTd7KYl38UiF9|&EiZED)!;l#l}XPL@O+-HI>y_uKCk0?AEgy{jYSpdNDM1)s|Ucb?w9f3_y(RZUW3SVZSS-)nid zX{}oe+X9L*)#BezHr%zvB|0N9cG~uY(*vP}yExK~yL9$EeKP0i{q12zii>(Wh0Vqn z%RcfRxUU{oD&gO)xo(u&rP~iL+`g;6E^prv{q5oUGPC;q-}dc$y=ND5QU31>XX~X; zyRK?ps@xZ+&G|_Csb>B&((F0LVJt*M!D zCq_?--J`d$=ar4I+Bp|hSF7}UPB{^#f@xAltvYL47OxG}y>-)2a;|8nxuy0>OYL7D z#A;Zs4=nnvSyI++rmUUD0fEa5Jt3Q4_w0SRHcnbEj_+M>@M?cNc<06q>j_3=PUkaA zM(8R0M!}d|Q-TQJ+u7l1aALhq_^qi@?gp#fbrj!CHT*gJvGCzut;k-j9eb)y_ixy? z#7En|{B8X8H(^COo9j!B+RY0N=XsVEy(uY-a(-#@Yg-xgUq zrIJIeuBQ)%JbRi*JFWKqdEb-zgL#g8E*l*bdP=9i?{0mv)vVBP>fX~kBh@ZdKYSZk z={z$1z&+)N#yc+Ux38Z|C`uNJ4i-K9+sEEkkE3O6)AN(v{7nn>bGjGg={~)$on4;( zDC8Eg|GK?VqpN3cKOz2P-ulHW@Wi_KASnq=ix)2`UwYRT#r9}Pb_7c;Zmy~o=7E#wE{lMogFm3&O=tCYrMp3 zVpizNMn?6%6n2;G+$zFu$tv{{9KAl+TCZ?)U03^E)vcxX9-Z)brA+JIdh^1%8`+~W z=v(_Iwo2i?F-J$tB?$fxehv-FuajO1Yqq6pwq>=I#PFx& zaB6$-ZL5}^fc>o2MR{I1pL{XELbE@wKsdX-DmuL4=P6mc^$nSmN-mW95v@_wvvDHf6XYSpQ6zr#2c*n)`xo-~VNtPEwJ}_%ZG$b+S&XVT}z{_98uJ_>5owWUqGCK6qDC zzK{~$Jl9L@T$KCalZ9ggbJk?$H9eS`xkWE?kB(8dN@B?G zc%5zU%Z&sf^>EAy+M_A`J_UzEHN&E3xbI6zEqN6Gl+a{DS{rBihR_`0b7N7LQ zpx37&&IN>F+jUlZM$iiSmd`Y|_grhg;;p!;{IYjrRYD%QCQk56)qPFywZAMqJydeK zJy$k9I2@O0xYmq2tZfcaCx{d5i+2?zHm@14s#=;ZRFs^ab}06Jx$t`p58_GA-Mh^* z^5|o_8x;igt{$nLx3%=`$GG#o$_Ecc>wQQl&v(yCZwa~o=+gPo`vu=sJyAbgsg#jA z*5cj?e?6LJa<$rqLdWV5gZFys;!Fd#_WNmJr7|sX8w~j*LOw?8nJ4dl9g(49pY=2L z%%7u^H#&P-PRyiHE`i&Pw%yvI@lox}`I2a*W;ey$Q-N)o{=36osn5I1-ke(Icee3X zKv2on8#9HoU0wXI9CoF#8^pE;{n5OjQpWwL;r8RS7far#PCAoT9lds>OMHs(K$Er3 zZsF{0Rq+nv-Iru02hE*i@YaBs(eWzT?D}l=rJj7vg73uZa~mq@%hE6NNltFqA>8b1 z-g43^v)JU|p(k$?OnLTx9c{@fqGQS|d|S=BDYdYl$eb8cg2RvFZDI4L+~5#%HZGi9 zTR#=Yqw7|0fc-1C`gGPPVx?E{%xU$)*&N39J(3u^BQB0rH`XUG1op2MxXT&iJjVwg zcflnW^o8Pn!0^gqH{m?JiUPH=?8otQMtZltH#)v2F?LVFo>_j~Vcp}~>*i``Y`@XI zpzK9`OsqDRKFHVTx_#aNQE#5UI`9}XtHR!0b$+zl(io@ekTHoLjw$qKZclsUTI`=| zm3U{%qQ&F74W_#%<%FfXn#C*;$!4`y8ff^(?1)cNcZ;x6J^P#ZUSY)<$0tvUwJ+PZ zGeRUHx83+nT)9SRsj6RkMIz>xF=Fzofg+Ws<&&2dYFPv>U9w=I^^O(RD>NnBvR*EE zQ_ZiBC+h2FoFEACRNpF2XG6P-yKUrjvxJwA{8{$dqY{ffHrNF<#z)B7`OgZiPUYOi z)1>W8_7Q{N?taIT>c~mf+nX4)joyJ%o8z8_1iqMf^Rd1_`r116 z=g#Jn-p{)<{`oQGa&zlh>Zg8MzIxW2JjcMtF%7J+^NTLw@cudbmpV0AKEst?>m|VN zA4`pC_P*F7!Kb6NL?`>Cm8!1^>w?{75mVMutJQL5u%Dka`%RQxv5MfP<63Sn@r)Oy zZiXkvEZ1cH621GXD?z00*ncW*=o$m(AM13~8Gb!JBJ>o|vD+h5usUkx{M-nU+$k#A zZ)EneuJ1JVwk3#T7U@SihYBa@gu5QP|N5-Oih~IaAO}oP^ zIogwX*A>@QZqh4?nN+F=?#OQ{PUOx$EfUm+kS1 z+4}?=dsJWN+h=Z%eKjfEA>5^Lz+nyZsm{3>g)dvvp7;h|T5{+~th`Q>`~4KvqTkYw z-Xkgk+!yJ%-i|)&zfb!2wK58+_C=%e3jMC>i49ScI*9UgDZXM7c*m2d)AKX z&g&=|`D$9u6KA{1`l4eaHvBqr!}Ia;j#lnkns>%GMAG!likJ@E|5E!lh$luIjH|j+ zR#9WVeK2NPSy-a9{ULobM*(}z^c@W@`qlASX+sWAXUpH}s(;l_sAVx~j7GV~#^px| zH^-ZAF11N{Y+R_`9cg-s)oMO}0M^fr#bB)^_PDUxvIC~nafsTbogA9ho`V*7-Y#9~ zLEf+YWp)k-UtjKXuEi`@b(6N8-QOJgT8MwNIr(Mb3n%NPZ!1+jQaW{M(shMrFP1l}wGKVp`mFNR(yWl2 zEkSQabfwL=jrJRK5V?bgX!f8BjssmqhZRD5VK zOIc>x`a-w$g=e#mg%8Em3DC?l10r50#7q_O(KFM0?j;_o>YkSsR;X-#`a@?CpR$Bt zZtpn_>AY-{)NI{rbEEZ*w~h**p?3V0?$!|z-KuYIq_;f`%1!ra5wHzK7x@n!?{qngE*Ol!%bppCf4xSU(;a$Al;pGS&$AQzE z2D0R;lY-?^hGfE6JAd0Y0^*sCu2V|4J{)NA-`TUNP@19HY|o6jdN1)=|ML@p&dqW4 zA;B9jH_6lv(Kd8jPFdr&VT76GRM`(%6$QlfJ-`1pW6$(e_XQj`MV?QW5K5g8R&vku z+&$&NCWt9At@Uy*S+pI{Bj#AGOsUnG?6vFlFVS1lLZyv{nD;7VS~i-i|LoFs+J3@| ztPDdV-1$aWusw(1@6mo$*C+YWJ#3_h)#1>Uhxd7_yu-#=vwmJVD{&d@?ibiO<#PkQFl9u*7cl=XJTnw&2-Ntro?rErGjw8XqH z0{X)SFvYTy2YRbjRjO4+7SSIj3Laj2^M=&;ww|l1z31)IPl&nb-{qevP4~O0aBNBY zbC=m`Pp@XIwzn4vm#^L@y~Ta!Osxy?`m9a5m(%nKVl$y#g-^Yjpf{r|Xj|AWRaHA< zgQiwZ37__s7}Y~zPJ-@H_%Xw40S3A8q3&+}lbhEr92yFQZE>1PqZR$b>^{0(o#0pe z_F!SQYM#odB!z>1a!(m@1_3@Q0sCIe^G_&VyOB_qWM%sF&a3tumFIjgSRK21qqXea z+sFB>(-M+4@A9jd`_|Z_KTV|C@yWRp6E!DqBrf>}BaY@U;Yoc2Hg$FsT`wxE62YWqlb_d~dlC}tc;QcTJ`hWP1 z-VE(IWVITPiy7IFMi66Zo%x$%BxmBk-k-CLgIUm7aR;-X7ppaym!H)u0{uQOl{3%V$ha~5y5ipJaUyxzjBK`q@Rr@O-n(U2 zYYusx7Jf3F6Xhwnbdb?Yt9H*0D@+QX?`+VZ7tv@QzqT|)@$P8*=htZLi5`kFE>BLH zMJ${a-!vecHX&<^bh<)pAa2n#p=YAK>%=0JQCV!C;J}g2+y1bm)qlJ%JF2cOy)w}2 zPU@;v#va1ioiZ1)UZ*R@Rdm$fGgb~LJ(qCZWoeF@huaOLu!@b)DO+D>yAa}LDJyxAjRjJF(KT2{^>e$VAI>NRV+-6J$D zF|y?R?L+6BUQFqhtD&8>vQ+!YHFLjf`c_R$r5|5DbMu@H1X1$z?dU?0$_}6QEqe|L?v z_Q~aU{JI>f$m)u%80^nXR88twZpmQByDmPK;a+Y@3?Bx!b05Hba_t@w1~f556K%xI z2NGYL_$xeWH}x5`{3vxw;Wv-ZjVHbvr@U9ChKoXD8<2GQ$c%Uk^t#Vfu{{{2*s{mx z*)MY3%Ue$LROEg_uSh9Z{k-21+Hd{6%}=m$o#H~d>~V`7Q(gY|Y;x9L|8(gvLb8o6 z40Y@6dC)IcCvJRh&Y_L#mUYb6+4%Tv_Y;#4uf*}%7rkqE#iac=(ESg}ZreTEgUxod z+5G4r-7qDntAOPefgyXmEN$$6b?o!Xo4LV`fxger&Et0rf0ZD)UepwA#;3AwI&s)r zxnUCMphPMB??Y_y^$5zSB9~JkpMrsgt0OM_$;2A{=JPreax*?O03j0$eZ$aGlLPqV6 zjKsW1?1=@q_cJRidH`Uc1eSp#94vT~myQ$oGbiU*H*iEr3IABD6AIP`QqDn%T>u9H z^-)G1H5Kr*#T*6>uFTF;&2!icC=m)}~EP)9}!0`FHGZCZt zYf14pTCALl^2+=N$$|%&P6aXXJ?7rJQKx%DNa*K~EmH>{YS9xKAq8KzMs#o^KSL&IL2$?I*JD%DYsKBE)38^}R3Vkm zw|vg@ZX@#p)?CDvQP2&wbJHV%6mOz}{lH|JQ$=r9m5;y-gT?!iO86bJvpayo&P#AR zxMnDc1r{JaXlMJ+&t@SvyWn$1wwkZv%I0`fKYn>i23JAGyo@VOIO}yHKJ0lzw)Ijr ztw%ul<9hG=xQngPc9+H8zo9M9o=^IucEDTCKhT1D%5Z|YXQK&;rjS+*2mj>w6GDUo z1aN?w$Z^rxybnaK;M#=QkxkotAzyPU+L|t7Gx7Y0lN4)|S;0(b7W1p9Yj?3!{ zX7hSzgdDgeJ|HkgYSmQfBj+0ZxNFfO%TMDN3>vt+eq@=a&EEDeca$}G>we`&=2 z8GdUt@-At$k2jPz<;?ianWXo`>4*w`$RnP}<4m(L3sb1Cf0-P@xJ@q&-R`sPiBz<>ot;dGdJ@3>aL+xc>V;W`$%aP%>VkDhYd2+U=TUm# zdnncPlaApMe!05;<`F4_%sWN>?1sv2J#t>wt%58yHo*}T5xg#g50CTLulsi8Keotm z(1f_xzZ`Ly>2x9NcP1QPX4-yj`xS0_o>K+Jt2N9754|jvfMUoc*aH0pRh{h+cYqTD zzC5F$*A4*G%2bdeGz~|*y}K#je|`e@es4=LXjkR-ON0xFj~bW^exN0t3Ks(DJxcoYW}spB}i|pid+>wiM@Mzh|(QB(MGnB^)!o zNcUgS-uWgGY$9b5OhaGrhKrz|8X#p`2wPr$J)bYs%P|QoHK!wM>edD7mn$^~88?oM zQ44a2?)93{c?ZjWYTJKQbZg>=4bz?tlXZ{VqxU-GVniMOk`!z#j1;iq@Q?&q)7TVRkX=p}jmG2HD-a zfKbd7(@#{rjC)Q5m|wenno{b98!%#*8Gf2|jYHY}5&^zc_;U8c8C%d6*3N8{#9EG3 z{|(2KPcAc`WOoRNe+0dym20MCYPMU)1VF1HlZ2fG$pd*>Usg|! z0_}_CwyR5pWZGOVKVbpDcGU97q81ZZp;~Ey=#P!6(Rw1J%}p7vdsmfPOGK756-0$% zOye2a&EP~w>=f7IG?PruipX?L*59u9jE!OUQDO)qer@-TPFG+EJ8;kG6!{yed=)sY}(5Jr{iIo`l13Cf8!co-ShK2X#2yA5Bv&6LF0?&{nPl>_;$>b z?)=1V*Xva~(<`}yyABASEkcDY<~j$G;=(jEQN==KN(3%}c%U2>dTeyb7=fbk&pib3J}jEi`y~KDi%2 zvAW#74{8P9Dx~GA8|Tr(#a{E}{K3houRR0KZqjN~M%)VqH)ZQMOVHMY&M2QpuM5{P z0IlDnFWkrR-;`~&)wn_}3j|^Rgh{FDPo__ zRB>pXNNuXmGmm9gR@hukunkd;c}@5P=V`q@yJ?<;B}X=Lmd4?}!->U*gV=COf8H#3 z^4zNV7_H^tgvqY0wYeRj?`j*7?ER$}i=vrUGYtkJ>&NUkSKf^PaG}AJ$rucvIu*dR zeaZ0&z6B{LQ@})qKjT!z!;XDItk=bvEhI<93?rra^d|5`jFbWp0k7)mO|ODgPnnU@X~&R)EKzz6^IiNLl21pxI(vgn0b^ z0{h8$x?C+w%bw0)y>R=lub6o?Zn(^Wp_2Y#BzMX zjhou-+(IqDT7awwww$|bgKJQ;qgjJ*sWrcdRM!8sfq|mZ!PNo;7^6-1a7Q&&2 z4C}BGP~aKMTyu}1XY_ioaS?v@s@J-=KL1HphNV}g$mVUgCba3uul9o%8?oufo6^~5 z+<)gwfSmB|M~qUT^oVa(!Ib*i98Js|96vpMN(c4^x2k9!H>ra{lR^bvfRu6t&Gt? z;0>Dp83Ew?po~Dy-zlk#ADT6YUw@GmoP5$*INba9n5^@kqQtq$xv$hJ|ECQ;2No5C zwUe6>w_ci912?*W*li}?jlO~xmiyAz)bjve&1JTF@2a{gA?@0rKcMGQ{0Lfy)@l^E z&ii=U$4!*T#bedRd)H={oHr7(pTWwla3DuD-%V?`Libtn-L9cZx$$#L#%rNE691(TWoXu0HdZt6`o+rm+;O7(J zf=YwYZh`qxVhN7XU)DcTUe&4LvhHRUu3?QW8ebxVFNf@m7yX|N3LM31pAgsw@M1xg15GCq?Cg#R0itZx%PRx3N4t#y2nJ?Wm+TS^T7C0Z?cjsaF0 zxo@~;AGML2GG(k5G%!Pxem@J9ykAG8|4mFC zBGO?@b1_zOC1!#trPn6ZC)n_Y*M&47Un3}iHs6)wP*r&GBF{6ZY+t=-$PD|(FKJ?m zI?lJuVEl`tW`Zf|ee|l#$PYiu)xU?v?w>ytBZmE@F5dTV-*;?D?K;t1VI89mAeHK* zl*-e)#19{SOq9usSK5g9?*kvo=1(tbrG<8_(Gc&qDb8ejmT3qom2$6olcD(yFd9-E1?JQM#pax)l?ZL_Bn=m0mVDAYgrB6SJ>Rt=;hi2m=mFIwMGCB zgXapHhFfGbvSc&Eb}PGRDz*Fsu!!`Mn|4Cd1%#MLy7}_wbFWsTXpJ^mXaV5G0iMKi z=+QFV%`Mpw#2;bUAx3v{{w3yd_Vgc8zi&Bl9A;>SpwHv#UYKi6CU{;l>`ZCStr%&Q0n_8;QZm` zzSrbXcikIQH>58m>zXm)Fvr1{ebf46$PK}^RemtQsFg>{ZqJxbaldjonKzX#@kV$f z{c0vxJuUQlAmDr3d)4T~-=7F6P70n;1&chS-xbcr-7dHK3xjZWH1l6|x?FvM)Y{w^ z;lr6O>GAUcfS%)pN&OwR67_O-X9Y_?ulxvvV-jOagtQWPRj!#^5mM1N+e;sFGJ73k zUvO$6s$6@xp?8k`@#615^R8LHhs&hG_S1HDEO_y>U|KckU)qUHxqKgQjxV~?EH2l3 zsnxG{c!4v^hj>Qsoq6d+nqwGe5EBg2rPK^rCOgF$rpYZ6O~Z{O4UfXx^a}z-+X7bI z+G}=HQ9MnIJjh?K?t$p`klTy=g+K4E6LI{}C4>RrjhBc6 z3%>h37TPCRrX{j`!`MwnJe0mW5~G3zJ+_6XO#K@G`7XyJ`y)S4O$Xn;nMalbfa#*x z0sKJh{(WuRocHqs+KRRr&%DI9e~^vyb$14o*d=V>+#H)D4xI{M?X9CPImq{JY4~OX}HmV|EkIc^reEBAZ5C6mCZA zI{8k%4{$70;#((ou263;k;o(fm~*X~Gu8j+dGS-LVea+|O?}%;h~xFcay|C}*h79J zPd>#AAMFBm_;_u#_z}T#&r3ZjMtEiaiW{QJ%Hz`CZN@+7^}35r3faSEE#KeZaZ5+> zj+>^V-cD9IgZM548{27V35`bmKK&yF;4J zG)$R*elH#?{8FA%#_m(buC4#LZ=yOB^zBWJQ<28Hrp6+Wk1=U?#l+5+QSTXwTm{Mw z>o63!05uU|RBBQwsb!_$G2K?laC{+=C!1E>5@RsUCeoYcJG?S!*AT=*G))FesIeD6 z#4pd0b;~Grj$c~Qo$J{UQ!Au2$ z8D+!it~^L~5VMg{sP)?S>q!{kF*e47XF!IMt4b=N3NNyx62Yi3ukfF+h^_ps0`uJ6 z#c8Q?AABI+rdg}R>pE>a&@b$gbc6`YVV`AYtGjoZC?DGe=na0JbHskTJ+WN)ur$jMNe#LMupFOId;$q{b zx)-oxIeRNOi9``|wFVx~7+m!6?|vEH(J%RtUQoFEr)6?7J^h2nUM2kvYK9-eFfw35 z)Jw+DI7!m(Kq3v9oh@S_Ze(L>$MqQThWDe6gzp~XfIB%&Bq5l_E={nhjpYc>~n-e3mR->$dar7IWidiIctlvk|r%MV#jiOqI2! zX)mw8VQC2n=8IUiG|F|!a@4b=+`?*Ia~enL(DRKCqHQSHP6h>oa~kFwkMEmkHOU5KGKusP%S<`jZNrUBPUds?0r-GkujGnY`8-o`=Yel z&*321sy-x|i%VMh0JT-Xd~07(t{3*^f*

zn$}8ho9{tnv=WHarVw2Un+Hq1#+9N|$U)hSgZ7NwpIquvDom3jWXnsv-dv$0#k^TZ& z4t4C&ejO<_$TM<^%PssbliBitOp-}Zw1K*3m+eN>R$rz`VD!6$=qxqg`vr6_6AH3| zXIKm~%%fF)^-$z0#*27~NR;EbTuBIQXzh>)w^7e_6_3Z4uPU95W(;qw#3`P^F}Nrz z`gvA#dgW z_pi}i5j(!%vuxpxJkxtA8Md%0l*zQC6rw`PuVllX>IB!TBpF3_*2V+&cOr1KyZs%V zm=IVc?f*u<_dN{UKDx}Jj0djVgFe4FqSKB5klEmFChh`YjS*PLoj0@Z&{&WruI>$~ z^zgFY)1?**djo$?*GD|~-)=+4i|CZxj@}p{o1=>+m-sif8wlU^aQq>y-usp9i;|9d zg#@DTgC{n3kUsSGUFz3=OmFx#W;hhgzXGi#J>H6DM4^4sL!W!M*butwWy~X{iB)zI zcbpXQ-r z%3io|!2N|2c&Er7tjH{VPui>yL7oJw#-DVT9`qn6r>eSDU&p`%J>&NO%Y%L>+f|Hp z=}mw&CYE{4HoIST0!#&%K2}=oCTi`IKLRHg+>a}P=Nix=&2Wx_?8VTGW5tExC=%Gt zg|UdaCz{?h^INa(?25t$O>}K`s(Vg!l#|D80ed{>lzFvjzz$t>R^`KIk;L(A0~*c* z!P^~CufFxI*ia8Sk&@I3izEoU^DEze!eIGkq{sd~O>F(B#`GN^>7{t_;&XPx=iH=s z^kuruZWt847=O8VP>Z)(H3w@}*&&^c%WTmH*}9#pqJU7YgflPzDm|z}F~IXJrz_KK z=|_5+HE>{6K?JMtP+x+*Oob4jv!a4^-~zq~8_AUBNxJ>en+d zk?Q|Jsoq#{q(X$&9Yk?oiJT!9lz`}9TAR&wC3iv_S3*;4-JSt@ML0D%%%2FvbFgH& zBa=rhVSjK;-o^QvIlG#0KKA~WLQYd5W{In3!J{>Ra~4Z>d0KkPxJ4sgeI`bINak`$ zswqzHYAot*EG%SsSNi6!xS!KihLC0*RI^%6o-aw%{_r26v=o^%&Hg8s!QQL)337}{ zN@jVQ`s(uiK*LdU3=?OzW<{GOtG|5f(_0ySx0XLH;qo zGT1g#*+g^YV4^yxUb!&EBvKz>NT7*m*u*zGDtu>(3#YGs;fIREez`9N&EJaAzZJ=g zJ;46C0=1l!T3lBx=$FS^y=be&g%tcXVZn7MI5UYmiBNw`h+5$nIQ0ejlRZ<3`4SG7 zqi6dzzv>XFnl{vJMn~bSP@4N&lY^BE%RWW2R`2tl;^-9s>U~+UIQe(~cX_8Y6o|xp zwq5sO?ac@VLJ`>ap{=K8tt~Ve=}uRND`$LK?-5_NOsY5^q#8y3JO0!*4tPAMDI;tJz|Es2DT<_11JL56Q_r4gOM?A z0wg_bita$j?$$@Xu0C^k*DaB~{5>t>T=S<)vNit5opxg)cYSRwJJZFiZ||XGBsn6^QmJV(zQ}g3rW%U zOG>^caP(rvg5hTvKKt!8w&r#pNqR(`2pgPCBIPZHJgCmBJgf+(fyGPa{mf}dK{Vr} zAKaHfP>HQhhsL0ufwS`fJ*MJxNqghxXf`O!SJz1CEkbzA zWK*f)h-XonC6i?-JzEoiGLq)g1MBCi{h#=&eQQVfP@b(1h`n08qoF@DLxNazngRr#5v10ALgClQFLJ3!P8P+^`Tj@J_o29E+oh z{0Vn`f4km}0UXAS#{F`l`XKYF_6{}CoHfDC%GJy9;dB|a){HNLXAr*81JV->Byd_a zvylu?Z$lsJe;luhg;A}tN}2W{Z1;)EJU=gGM&Z)#e!{-f?=DDJ=?Y>TMRKp&~$i0(M1K)(YrF5Sj?0WF&EQ(!dy}$J)`%~N+n$(AAF8I$E+EM6r#f7h^t#kfvvHOmptw- zU~J(m9#?Ni#y1gI?5W_UuhoUP|e(;b4t&x?WU})Vs%%~ zO32AF^{T6i{>Ek7JO~yKL2qivo_ZxF)DuKjnb>qJZ<`SFlQRJ`=J&1oA(0hO?)EkB zJw;%pucuo^C?Q39)H$&V39d;#iMZrjnr@MH-fG5EP*fO-D5#be2xgXx9{;B(2wbuh zoy{`Tej-1QdwQp?#q;JB8<70R%4?Y8@jG=J;{Upr+Hb~=t{M#2=#SuJ_;y=yyqBrY z1Udh8|0Qr}K{a_D65+3*g!4F6YCbGzV z?+q4Z{UT~N>G|Ig3{a!CQIAbM@hPp^U#orqA@s-8I(GwWwWONosWLR%R47*M6FU`r zG8d&o6L$_a0b-#3sYm^V35xPxm8w11&txA>a6G8`f!eECSkDLhKdx#wv?ataLeSq& zWH5gJlV40HhL~eoI*Cbrj816Z<^4-&KhDKt;)DPh3Vhu(heyDR#-HmvZf5iC&q?~| zn47-5`J!3agR-45$BzMk(%$ADEtpBnkw^)~Yx!t5bt^si{@cvgh@Shew?F!VKD562 zS-LnF$rMS3zGc~#U7W}HcYEQ-Ok5vSTA_)KA0?o9yWWcdNc1m=47N-S zA%@9(=A#N`-B97}ExV{LxDe67DcBFwIImE4CUYYof`js+0+yl-hV$O4$4~iTg(kt9 z(@7hH84FC|4^=deXmPEr;(ZGPd`G@Q70KS>(-~P7y+yKChB(Vk)5x&jkZUw&=5_w* z?8N;qIzQwVsz;Rb8`}O>u7$#8w;nlb!=9JNH`-9qRr_`PLi?E!U5N;$=Bqt_v10|@HvMCxsq7Wl@kMLEEI%%@E$C_*lnO0dzOJdHuI zs|)(&U;s0aVK6lgH>rEoj;+oQ8Ip!5>a*zaUc)pmWJfP#Q>Nu9r_dEPL<)|oNM|W8 zCtA06c+K&b_-u{2m=6g@E`ifWeQd84u;ms>ZkC{X`GS*=iZQx)8c^VK>f_<;^UoOx z9zBt_tWfv&VYT0*YQBf9&tTALB=Gft_e3@ze(2zTv)_|qdO;%XtF7*<$!D(+^{YGd zR@uTVz}zf=7T^BwnbrOonQ7^!lI^A{(_67Vk%Ojl?7iG~A*MkQ`gDjFn_R&kg&@nJ zTg=-1#&by!YZ?Wmd}O2$ribX_d~j}srgRQ~D_r#59zOi*VAD?JS_!VsK>`Q2S2ru7 z?>m+aN#8{hl$6}s+I9k6ez&$q5lLV0pRKg8FZdL^B@Iv~^A5v#Om0nf!}8N`Ifci_C|8fhQ@5>eYST|&JgM6e_Es@-HQP<1rERu&I zgn#nW&2|I;X-+hH^am9s{FI_046N^|sNVhP1SqMIXwquG@Lpu&2)4-17p4>VzmH^9 z;O7O6I)m>Iu_;Y`Wyd88=4BT5y6g+S1B&>PM1e4&H;?af%EEp}zF`*QPmvIEr`qN7 zB2!22Q>6DiG^Q+L;kMd|SVZBD~<^}%K)fiJr1wG+w(5jqL> zyHC|_!>O_2I^nws`y*id_W_AglyQs8C`13j|KATbS?C^WEXP~O0dKU=i)im02H5iW z5n)_^?MdvGL~2sAKsvD5ZF36G@1V~C^Z#1oN$P6qNtn%g{Rq$E^xEh+j{LN}>?K?% z{BbU*uTac=aD&H*4oVCCP%j(SihN$e?Sx^QDNzgR`g+mzwKcfOcKVD^q=k5;%g^aH zd5t(hv9hv+H5pyg~sj2B(O|~uPrE~ih&&39c zh?of}zUe^a=>!E^Y^&qsyNhKbJ&TKc$b@yIV0ysi&p}z|gd5u5CGbg?av2oPM z78z;#kkqg@5$;7mY&!HuC6!lI`~1lV8eG3j9HwL^;b(0!y4f<)xBWg;D7FMA#?fp} zVkA|iyhy25&&BdzS$jOSGCVy3I`Q3y0E#@_ScMD#=#=_sr#{#PDx1GjV)>@Y{z5vOA0oax`{NTP`Km+b!n8?KtWLGc5wJ4d_Jx2IT7sW~xs^TEY zeICR|PwS@mHyMwK9W2H-Iwze+mKkD+Bbfi79OXtDLH`a-9*ze~=pbh>|JPN6^?-+o zF*dN2ln3YNtSue^pC@2R9Abj!;T0h_)AK;2XCkG-tYpY_Y0_Nq=Ht}Nj|Q9h{te|+;K0BAX^+7T2O|C1oTCXZ#j-(z=1*n2#kNm`HSbF;o~Tp?<; zn5)AX@4sK1*5mqb+!>7j?vc{Mb-AKjSUvgj2eL`%;czwWHNnN!sdsYIL(BSl*#Zu# zC!Xj$JxR-~6W82_g0C20V6lcFc7BoPGF!Vn)G%i!{fwOewY@7h3dTRp*>~!gS*(8O zTUW37t{A4#c&%k@l6xd-doARb8i+qw;7E9jnOO2w{*!k#HFZN8`tVo2CGA5Qr_y}_ z;|o{a$o9tL{d3f`!1jC(s2A##?Z)dcEx{+Hqy4QeNV7vI!tHWvT}P04s?kDCmQI~| zf_0;DP{L=XV#?fR$y#L?fTgRks*2A^B+k!l$TzCWWaxOW08u9TZ1eiN*Z0q+IKL1S z|80(&m+P?nwOfKXe~(~HDC36V#G1!$xG3X#)`vyk53(WuiluHlMvWKj~IanbZtgXYyF00TP()9|KTQ zT{&PuGY{BeTgp*Rnf%_gHW{$SxyPXtq|UUS3a~j3VA*s{veqW%Jc8z{`xgZIFurhU zdJTAc5y4rMIzEbE07|EqBb^0JID4tmjA>@Pqo%B9m!@mKNB5>qQBcc~q3Pf!@tl2G=ce!tHdI#GQ6yG$!eOc{& zz1xFdwjD;zYs16}4w=nHXO0oSLIK==@F7T7l4+Z{Vg0#$QTTAuzpoIw`I#Ko?)0I| zTf(ESrZU7b;mDV-llR>sVp_V2j`rE|XR~btt67PPI~}CDZoi`#=;^D=0AM2T;xKv% zhZDIq*uS?u?n%EwZ&zR*`4Uxp{n=(5tnVS+d6=W8&P=QOY1J%EwbtEH|D~~QEWt{X zduwh->U9=`){wjGjkFy$}LYtBK$3uk_M6b6Vdwg!ixE)`KEl~4$e9b z4hseWtm1YFm%xeF%}M&~iB5ae^dmWiFv!ZLAUOE!L3j z#Ke^cXZbg^`~7P7`t_C}i1PTFK}z$I*qw3Tjj?_;f)npx3HAE|519%?V(TBR*wZSh zy!4T5$~0|GH*JpIMl%ZSZD!}-mti2!0fAloImOlI_ zG1XHhqlYykf7R1XzpKG)zw<$8!*2StVjL%G;%ytRGl+){0b$gX*@uvQRaem$0U~_% zPXP*dc)UW^wl&>%SA#&M*_0TSJtnvH>pE`YR*u~|VESRPy)t~mB1tq80A}`^7ou+L zE@2(eE`Mm($#pYklvL)1U5&%4k;|&sPAHQQQ%54uD*EusS6Cwk%I}q;pI`2HhvHF{ zD+l#GM3hDkuwFz`-ed*aEOhzJk04nis5Q6hQBH1h^`QRpSETCT^^ZG9$2Y@2Xr?h@q|pUmgI9iUf)o;G7KgSMySIFdQ|K6`{|Gz} zE=z=vM3u3Z{N!#HfxVcHd5@pVhnLk)Pn53H^c!F5P(XT2K$^r}Q9Tsr?v(oWgDFW{ z?(L_jgUG`zQ3_pONza#(o<_%1Kwh)6nIMsnu8_zxAqo+7<)BmkjXlrUGViKdVb!u1 z-Drw%4*1gdg!OE&J{PBUqJE@*4Y$QLKQRFHOEeIUr!}_`uo4_94i!#z9@ZF-1F30# zw)u}?R%=lOs35sa1r6HEY31CY*qNaIL7y0pJ6lm=zG@u4 zfaA%B!yfK5FlkAwR&cF3lOU34^6w!93&ee^V^^xg(b#Kf9|q2oqJhh$m9v7#vB3TV z%W4V{{uSUbzo%X@(0SF10rPJt20#OfUYBz9%N3EQaut#>eLpaYa(giKFM;pL6gFNV z3V+vHR^(+;2S-tLJv|(S&n``odxG=U*VH>WGvY%SDS^}zjU=c{_`2ee5e0!BkBX_i zzZ;7aqJ|E>i2)QG&h=mLnIPyaUTa`^6+DAiZ8MdF?~68)hodeKgNEfZ2D51e7rA&w zf3uBb)5OcrseOB-)&82)TmjxUp-#Q?Dslgt&j(yxCe)_qjk*-=OC9M{t;aQL4jB4z zDlDTmx`Ynca4u{#-1naPT9|m*!?G~U@Xup~LC`*Y*8{GAn_>!BB*EBmqs3sl>2q(w}!^wW;bypEFkL-+zuB*j$X%Du{Enc#De>b+!=O& z-#^ve@{hWw&hSHmFeWgb!CB|~U<9>MydY!TzGn83Y>|#l)Q`2z6OEU|?%Aa7xujTo zx?HCSl-_R@UTaV2jeE;*q+mSEWmW*;(l^&8v<|(@ImXJzwv@uONxv zB*}E#j~irf$sl4P-_UO2)oJ=GRDY*TZ$y0w51{fCCIEn_!q=7-s&xRWwz2NjGzRcY zqU<~?dToJk>gHvh9Fy%D^TZ*d;=g`T>wen5+46_^Fy$fb+Ts?iZu^J4rU2vAWfp_BH3u@vMs6!hZ0) zof%>fKcJXTE}65=QHPI@{&@=SQDY7uzIZa^W(LuzDxt}3GUl)8!D$Yu54;g^?K%$b zKho^FqA>4g45C9McQ1V=Vs`vhH5}Z)EnEN`2-%5Z^Nu2(olSXCl z=C7*t>ETUH?JZ5sHu>*g%v;xiNm>`F>_FKJm|*c|nYEse`TVNNyi&>>8-hA(cyFZS zCyo_P+&Rjs&3J(o}PV!$jp+J-V+&}2<&=2%d9uVJqqS1oEt_S?4n*BU_yHfy-fB!VP^ z>9sF3PM`SW8ZTXvP4rfL>>z?C7G6qGrI6YofwUOOW0}vJAI@I!?m3{O2JuxF(PRKf zxOmiM80wG*7x((`7tGg6`NLLP0b{o)7q@x8aty#^PdRnDSY#@?TIo}M9$WS&$}3p| z!$vomFfB_gphhU5Tz9_ZD4AfYVa_#?2P4ZDZ_dq%IsT%RDuewykI^8XmZG;nKevL; zvAU{`psSXkH|vLVpD&87+)-)!lZn89$v2w!l$@Y`IqC9r00S6uL>l!vh7x}RW+SYDtly3nlAEP@DtqroMw=}*lm0O9iujxNSJ`86-SbFui`SAoJ^FFe z(;0>?&c6Et(H{mZK9=79%sG!RN=9UZV%`Ho>Pyz8zj?=6E!Ti5G_;nutU6SKK2>_| zr`q+gz)T~46GZv1yf11AE9i<90~porp_gTFIN$?lP7hp=uMyL~!xV))*wgtZPmZrf zdLb`@TgOIyk6ecM*m6fLQ%B83Qkt>HifZOkj&(5T3{l3v>$Am^My+7K-vNIgRczG< zKu&{dk0dvYY$l3yIw%f|nj|=iW5aA%7o7h{y4G9frT5$WF=e4QZJ=GHT158vDND%Y zvrAe1ZB5-l`9DRS$6*4fl*{sP&>u-VDa+h72LEg>2t8qXSpP3sUHP$6FRH{1(bu+; zT136?f4qfwaiO#el-w^^%mtja5I~P-!Y8OfpJ(~;zZW+xWyTeq7}#kK9q=i8YwLH9 z+{W)upA#G&$oCqOw5PnwZcs?XJH~}nJ9IcH*RZa|KZXi@+{QGn-$gjyML6t-WbUC} zT=xtq`}}nB`l;-pzh#YM!_|$<6%S)@44N^tf?MW)52zp~j`ANXwbMygiLsNLc=-;Z z<&#cU7DQHNJ~9@GCW51s^nG<60g+PFSXfG~S!miu4rraSaH8@LBDAjZ9z-sR7e3%< ztmO&a$DtApF?E}QGguXY9j zMnVHS!mJq5jPj#Lx=)(7kMw@8mL5T>z(Y!T>6fr!4f`_SpLpp*wBti9{U7jr_5#l1 z|24p08R1WZS_rgw`EoKodGdYZ&+p@BPi1uOM^Dw`^K=vP#sZ@k1F<2+-$o|QOx~&o z81lzWMAFGhDIslJIjz4+E%b|J=|p6~Z-`lpB=Z~}E{E?gHQK>)ve??;8@fP-6G_v77WNn$yNOf9<&$39Y(p6NRp{OqYU9G>(!y59UN-2s~x zi-et>v5TFg({TNoo6lc(1?POJD9D%3#>ecaid~|fiEZkr0>D|J{vh;xeICyJy)y>n z|B_Y=mVFAc>QI!Pcpf1CpG`PO*vp%CCYr&gGPC0}``Lq1#L?njEmc@=&QO2O@R3>j z6BJRAO_v{jo1c2mNB*{({KGeV!U_y^mPMm(&-WdsBQf@uge0`PdeUSD(xb;73=0{* zsfkNJl{s8An@u-EDZ<{Q$sZFzuluAR(WOf&tgd1!M&#>NE-$Kmss~-(B0v2C5@Ej* z?S3VBQ04oyNVH!mW20=i-eJKokwge&{pc9xNLOUf>mjMZ!w(;+c0&S*4KU2u)XP*{ zY}j1o8RwBNl`dYXhN+oGshY+zWBuw4rO-;8AtWZm8`{P?01)4OUZDz6W;;X}AaDfJ z(DVS}2aodnNU{pX^Y!jTpNyn4oJ0BQN({V@XWw;C@Jkz_VnmytE@C1jZ{{cZaOvz( z){X?V;B)(>J#7yT$R#;mt~^1mswz?zeYRW!c{52ktqPU|32&8r{<9Z<=Y%58H$;Md z31T;~X|u8gF*Amjed4Vp<9g5O$V#mbsog?cll-LvYQO2cWNxx&H;3Fdjeb8#f{WtL5bkl9o5S(g5la}+F-tt}1Oq)y@uFSWrbXD89WlFxgpVvM>O4OINbyvxR6=?(Dcm_F2>Oi*@ z^yjd4F6g>np~AXg^+K!^d#$ecDPVo}4qiZRS?jq*ST>Y6;M)Dg@xDZ5gi3jz1Cic= z5G-RP<_P?IIKU3bA13(}yvsu`LsxfmHkc~jKX0PWKYWHqh4ONFZP<#5bp4Iufi@Ewf8IqX-JkAAgF0_{CooCrCcAySYj zvXsdiFOIdS*b|WaI_C9<^4A~wN$(g#IJJ7s)*hIjvb^i(V6=M&XmTQd^U@k41b^oU z(qr|^@vM9V3QQQ{K1d7AvPTzjy9z6eetYm5Pb>vb5b_@{e}Doq>k+4!vf!*3uLL>P z!83sEbsiIWotjRU9A4>z`{Jl9J&!kOUKw6q&3xC^;zz8f#%}(!w^5Vzv9E?luS#!~ z&!WXdH%vS$RONT2b#kMZlQq1v2qWKiMATxf7nY&hogKAYoNWZ89H3N?M$`*KW{a9- z%~m$zzRQdQx^_loz{Oj}HS)wf#fm*W?}|;K(tp`GV4TmSi-(=M4ENOAMf6B(`(4 zq5*-CJmSi7)XVhK`f}s!O^khkdN)OSy-R$(+lI9>9Q1OJ^mFM?!iI_OA734Q-Jx(N zUUF%%acwC|>YY7#9|4vt0F#vVql9K%;&mK7SsnZ`C1`s^Qic~=)<24y-w zOYMxBt+)mPaYTBdX$Frd3}n)MpR9CSt(8#g;XQp6XY^#q5zguaoRg&v4#sE@R9Gdp zT0vpquw#5`O>8P{PO2YbjAV9>Ju7tZ;&p-(+5hi@`g{Zw%VCKR>HCu#gpE9k`dU$T zKVyY@s<-y|9|q_?TCo84tok7rDDAApf>=5I)fK7+|>g5MR(<>~@m2Mary2 zDm$7Ni%9_(Aofc_q`pR^CaH#FR@SJw)=?jny7VC!pcWq-DA+AWLpV-nShMPBe|ahV zm3jSD4&VQLugQO~x~1rZ4d$TkRSev{i0pcHcOi;H5OEUfii%u`U=spBzs|VogDWW> zVhpf}EE(9nOE-9dy$xDuj{mQt>x^ouYr07Y5PAr`6FNvQ0TJO6dNovON=HCIItn7a z_a;qx5fM;CMC3stO%UmVqSC8Uf*>F*`R@Czb^hFy{7UZ3nLV@j9*LAuj*y{`mapkp z_dlMUHDc>L1qxPQ23t;04*M%p+!-qOcv~*i@Obg8pHMk~R~B#SO#=CzuA%1_T?(ks z>Pt-_b@O#;-J(j8(Gm$iWH0!1AKV}OpR87L5n&In_)!Y{ew4lY?xlw{1d2hgcDoA= zZd!cLt=v5yfhMwl=iuhT@H$9H;V-@Ua`4CpbslrzeGd&wCd{J+4p9*Zr84T96{!hI z1hDIZe?z{|83y8yKnOB4nYbvSFZDY0Rs%vk|JE{FC^j^0P5;@NZc4xQW(fP~e=*llHG?J>-egfNYM7C8G;2*8snBzK3rR`27r=FNBQ>Tu{pSK``+H-I<%+MqfV| zV=uo}#NivErKcdC%py>n)ejZ~yzhSnn7G>-C|dPwRqZlNsgqlwGiS}x26bNJm5mO0 zbjcBS80JEae}x}IC#Z3x5QSt{i&QU*#?54sjGsLeQ26!P5cE?eg>u9?c%Y#eqo)hZ zohw;y_n4&qTJ^v%Sw6pQB7aZ&9zb$-39Pmr;x%?p`~7OxJX+bN6$T3V#LbWt;NEp`}$2!NK)64a$C&8a%kUz*(jHC^Ht?6#1VkvIb+&qB8e4=d!cnzNdQP}oJ{=>d_U0&md<1Xr@#=1be<5S(AsEsb0HZE zKsN~*n-2O_5&k1j@+>Y!@%8bb;quN*68w*k2q$E~pft)2LL!MlISbrPL`5=li}M4w0)W1K|7F+@)pO2G!LUQm{75d}DwSc|~VfwS%O z3>37sB)*<1ewIpW5Gtp3$6&KqJ`*o&x-6cCN54F+P}S6YQ*>a(erR=b%77*`?G8NwG1tm>67r&BQJBRgN5B?xIf%6@!&_>@4s`82*Wn7`GbqZr;dz&>$cHdok1H zV_8vC9jZ*$v`=%-TI0puL$$%b4LO>bn<`!v2c%unh0&_wgEy4;2fKSb)Fi&|UEQ6s zXVJ^=-R`dGq83Su_8A@1bFnen)zg9x86y9l-v8S!pPNN}A&S*>JX?~~bK1c=rb*MK ztg&6xCAvS#sc*^g$jW$8>2jCE1t=wFlz{WIipHV_h};DUudmEhUkU0-7YqJd_>AU< z)dh}jwawk_JBp^u&R)=yHl{ITl(fzR4~hiRl|X=je>x4aM$-`uKhk<3%L>7x)PEf*%O^>VvniTQwxUew@HH<{MRL1Rvj zk=@AXl|@FbMVj&MR_y(1KA(F1lqPe|=B%?K4j}{V!C9+Y`t(rCOnplTZI^x988c=; z=iZoZ+Gp#uf}6k|$^e7P9-ONhv#pvMi@;)|5crpdv|Jy-e%N1cJw{0A!$jkUGptQ> zGo{Owlf--G*iaYZOg-;x1Ml~|KN~jf3;4J9q+f&j2~q8L-=bt)6Y*_Vu3P-i?@Tat zG0eh7$HPZBYDXukNhgABy$f$~4gY2)qpKD##D4=IPDaRaD@x4a^h`U-+Z|W@(;3BBQ`-D|80@=D)FOd zjY_WqrEttK%D2(5gcbRWHEi(yg`^i#+8L1#BRqcrd;TiNw1Pp~!rE z@3ueJHzFwreD;B$T=3s3Z%%;t;2GgZWSWu^>`b3Eghu&6sz{{5{N8T3K%jlK&Cgd3 z->V$RgyG|IYw}~F4x@Swrj$7I!y6?CGg!Z_tgfysjDh4>siw!`LW(T9&+WJ2`w|+L=V50f&l9sgx>SU2 zaKm~ozec3J2pS6Q;8l=w5ygVGlP~o4&~DvrKjd!wrG@=GTjcO+%Xu75J1L4u0E)B--6di&+oCs;_Qo(kZ9|0UhMs)yy#`~`(#pK*Uz6Bmc|?3Zg(pSwyCcE zMhu2ilv_P;8cgAzPrsLh#TT_Q#~R{mFJf*DaiPsV@m%@Cpy$&y-GL3y&BQgCb4Myn9+?q0Ov6s_ z#*H-@M{0f8vl+U^b-L{%mLmc6W!0~Eft6&OJTdBaObx{%@8=eLGy^|3*blm6BOgX} zx}7UVAiLuBA$MTsv0(0rz!SKB{Taml|U; zcYm?8tm<nX%zv22)VPc<^<}>bZ~c^XLLUtC)Kjlsm=zF zAIK@L5lQ+;B$TladWO-la!J;?-MeZwPJXDgJiTj2npgZ$xc4|<_1+_MzC2sA?W+vG z4jU8GMjrK${!pr)zG#2pSS8r~(CixN>N1Y-V{tpI^cka->$%F~w1ZbL4Z1Pa=g*|J z{FgbuI?Lw6N-jEX8Wv7@c$e3`EANwqMK+Dfg*UH>JRn@*+t+?aU6ku*6?;3!Sr_Z< z?zve3+!ynE7e|cBc=oOlDe(OgpVP!Ib@#$e z-Jkpv=ANtFULxcaK4Uloa6jry>%jj@zH^84zRP!7)_z<5eeW0d zQ8t{n=C`S-@hoJb?f(yx!_dV!pDsq4Wx-E)#ZPI4sg^j{t_Bp|@^}j3g;eo@p6(Q< ztN3y?{mmH?DdyeW!p&rV0!Em}NJQ$U`*nNTp^IN!u6F<2Cc_J6!s})@Ra`rNe7v0< z5s_OOWnP+u)xTUdAp#vVym{dLow0My3M=~E0=&)0d=B~z-D*Ty6kZ5;;NR`-O`j7< zlmSJ=hZm?kqQLI3E6$<)76>S&ui@0O>U`jcsekHM?iExR<^T zgC*BioGm{IbM20+9nHFbTVAxa9|)86d%_3u_ssUT9r+V~0bHsdXFEas)CH3ZA=I+s zgC#EQdWfXbN6+qM-*_I0vE04=_0pS_{%-c?D7gwQ{QY~9W)vY;Q6D_^nX-wrOi5V122nhqldzd`a#o5FvQ9xB zyMNUfQXQzV8QXZ6Lz#eHJk~IoNX*NzEGG*=3q`W8Rp^uA0`0KC{3}cD8cS&825qKA zV1kX4QLztoi9aV~sP^;>pv=S8`Gb2=LqfmLEiTvrg6^HgejuM43%niNBSlEhnJYdC z0_FuJdr9T2b_|$Ye*Q1AV$A%sF~|$P{+RDjIc6~YKmDFX+Z=ZH)KPqz@1u|H$NjzVPcN>83{eLSHb^I~F-x`1wd#1^ zAify8Td0>DYDT}RlDP`g$m%`juJ`ux4UqdRER^iMOTY?esN4LK;{Ul6_sM}GDzaDU zTFafGf-<+II?8JlIUJ1)zv@OlR^|1)EWIa=`PSvYm1aB_0Z=#Z3@AupHuLm7>VU0a zH4RKa)Y zf(60&OD^6@d>A#c1dOMiA7kgx>Dr%zU#_mOavoSw6TPU*&(-$cEEPLCzjIywbcbAr zpZKp32$L$W*(QS6?=2BxmG}=((4F^+AV%FPW(npGZW;%S3Yj+2!F?mpj?15AOu7QJ z)Qpo7Xxurx@$}o3=uUI5mD|tB?h(*I2<=~Ct1`e&BMlkokA6!}8jUpHA>vW?s%SozrH6*}Y3%!^*O-n$6u)8h*-+X#nG|yty)c$PPUVv^FHQHl@E0U%b*{_9wF|2LP+@#RL zs>E3USb0SCXc9uSG{|BD23pW~FLENRfgNsSi@OJ9*=C$+w)?q@o*swnv1QR>{C^%5@gzU~a z*>#6iY5R^>GVT7L9_B7WL*@pEA6TB8=bg=dW~-mpQqtah0VDFgQ5-Aq(z<6v#_+#m2O$hcQB31w!h(p^-ukJBNE zZ6?fc8JgOyC(ORN%%4yK9Fs5>hACzme|UAoV&Eq?+>o0wrKNG-F5j=&yD-;lRW4n1 zRn+zWXd=z7U|<#;vpk%$mDE!@hf>n&TfG4`&8;@iUfR@!@7DRh`U8}d%+|MpdTDuW zs!PdO0pP~@ArVg^1vfYmf(ipah05Oj5EwDgmiA$Ixd#Zx<0GUOr$PLj_N=0VLNjT2 zaHZ*Z{Xe3}C^06nTa?*2bVH}N!DDbc@zdhTvpAS45G3Yn{*)NycmnNvJS=k5xmVP@ zSMp-7Wb*09X*$}-H$k1|N2hinoc74w6Ijpu}MyBA+`LJT^DCg~m~2$Ci`tlb6S%i_;?>PS8(3N6ao#AdfdYy)vG60T0(p*~QE)g4kLAMJ45Q&u1GvdL&v zr0-G@d(*?Xgu@hbqD#5ZJZj=UyT}sfUJ!hY{u&zm6}xj#&%PJIB93P`nhK^RkvhDy zcbGQ#tabA~IhYMQ(SP%d(_arWXtT3+Nhx%Rv1hk}BM$@nfC())8l1mhzUUq8cl`y0@vpnBvP5o*B$(;DExw?~--L+n zgZN&gJ(k!{0^9-eqZoJa{(!1&j2@&Tk)J|qRX*2?pbp=nPneT>?0ELHdy@!c>L z=aRdfOTVU*g%a{r?Y%vnz0KJI9xHcNxC!llUBy2kvZUuJl>^n9XeeciKJ~jwqA14H zb;GJOMDrl6drfl|W^uYY8P+j65IUmOexHFLat`Y3q6p9b`$S2+oS)K%k;*rT7oYx8 zRkvQb?)J2YLgT^=(`c~8DwU-5Eg>I@?W(1dji;+rfKvEzIaWw56NJ-VkkQ;&-V$=M z&(g?_xZ!Iob$18hWnA^5C51#uZDnpei?v~uxB0N$}VoTZ7hxD%@ee6xL=3GVMC@$Ow?l`ZDv9rF!hcm$q%ZT72ebh`LIRkU(9K)#0J z1r-d?bO%-)yuv{Io1l8pqzH?-3KW&kzBmQ>sEZFgkYV*>Q?jgS`b$twx^YwS51qA6dQ$CZB54GOPjn5!>E8JAhNaXAAqoXbu&J36D;)av+glGJ`dQ4f69vIv5I(F<89&)}f zyma#@9|vjAg|r7&4xF$}Jbt7=r7uR8DnXM<%l{uGk%hwalRq*mo7qGb=^;;uVDPdg z!>W)4s`UcEUCO65?7XPdefCs{FvSw>Rp}f?B9kWemA1f?5HPkPg0>;OhwtTHKqtO) zj3$W(JnuSu=+7q+D<`3CCIRdD#MUwLdY~;CLPis3im+42KP{=iXW!xN@zF zzT-_xX0ZO?>jM>2%6&Zb%}Lsk2_(%QcFo84xYm9ir!%UUGOCTsX&ZL0<|9(@3DlE; zco6TrCD6?-KK+edcEMGdSvp5Mql!*P@my~bi;j>= zKmR0>;KSxl7GDxi)o%?K|18S*b~6%`3=*c<#__-iUf>yiN%u^23zjA-js57R_0a)( z(~<#fwGy=s$gGsK60YO(ujcId-gaUHSeC5rgTJS$AWq?{$SKueAwj*#EP;e*ziJ%S*3qRI~?+BO27l9#ez}PB}R*+ zO2~f_ab~poG0gq`kkJ5`+OJg@g&99pfY?U>DO+UvXqQSZwQ1XOvuJ_{ZcP#qPu?qsEdHVlyJo z+{iLuB>n+iK_tIlj?mXL)3w*3A|c*)gzX-}U+9)_l>K~DBI%XM0oxFA_gaCJ{@SnWMSa=jN!I5NTWfR_ z--93IF`3|c)ubH^Jyuxv{iXg!y%thx4%V@ok~%$yEeg6>GxUcn!vT;dHU+ZAZ>*>;c85_mDHV|L|Po+mypmndL_E{$r!elebyEq7l4G zcGXu-d&Olbh%ZLkrQ5`OQlaYG4>x$rAD`Z|F1nHXMt5^&pSw^4vmQ)bW?taVZ%%#B ztbL%Yb+Bi$-(oUmYQndJaW3qJiCVhdv9uH=yA zg)fJu3dRxFOC47|sQrO7K2HO^V>A};`!d$VA6Wg}%{PZ}#_HF`+SJE5Alsk5-=(u2 zn?T+$<1q2(aOHYK7>VNZmPe#pQa7ojZ}VNru6(jY-M6|=5_Q0-y{XNuuDjfemqU`v zbH~atkNjqHaPK|S(&W}Q7a}u54v_E1JZGfWmKUOMRfNkii}I}mW8+{na{4*WPQ$Wp zH1D>1cESD;Blj#;lATE?z*%sSd=i7ygntd7EzyLa5+50<19mg!@Sl0Ke3p&0r#_0H^+`_5lL70p*VrRI&*-IZ)?Op_>3+?Hi@vDNypdNl7I0{{Njtc<>Ee% z!gH-rLJf(exKYA(V33oCybbI@2`r{mAmKgRScf!}Iw!@+Jk=+!(I@i{Q4941C{676 z12SbP?pWUM`uURxWMl_j2!SNFYhGGt6x19rHn!LX@rQllZ?Cm`WCEtvaiF`T8T)Hn zBk7{Jornx`L_zM>Yt)wc8D;TUImFP>N1W%m4yPgX(*6xq1Plnw+0I)aMzJBWg4Utq z@L{A^Ws^vB69s;xN5vAN!ALpO4{z#)r(-G12xQ=WzTkGdxMYw#t5wDO|8htAgh&vZSt|@@w>h z^H=z4s7m(5sT*iQ7KUtY7V5X=eEGRW3FZsRxMCYBIeVEPy=IB}+eQN>=~`D)Vd4+Z zdkBVNu%B4Ee0dfI7~?O6lL;9a7z)-?C{FWZ?jmz`Ok!e=;Qgis%eV#+G zpI2JfUC61L2RUnfc)ew~A}v%S4fVrgZzldxe8op^5|MP@VRO6az4wfs(!R)`%q#i^ zb9zS#h0y~6jM->v9b&(@jx&fDtzlYr$aEtfXeBrCwZ+!bUhh< zmFAiWcx=*=5O}b~#Cv^wu)#hX#Ee_otlI&Du0rl=f>>s@WD9t3jr6gYMQ(}Ir|{HG z+M!K^<4OF7%UCQ#1lsGtqWP4C;UZ<;j@p|IX3VAN#!coath)X(t~98BFmY|jQ|Mzu zh&1D{aN@CH-!j`dkdBq3 zjdNj+kw9m`OFNi&rYA!8)GJiM6yB$qzbQ+yD^I-vLfO+5k)b< z@cASrLn;a%DMY%|5d8c~?UCj9Z1u|V8U=^eUBo3F`q#87MVtxM+ATe}Ulq}#%L}rS z)^Ys?!Y+s1W3y&`kW<5h<)dvOo5vhwAm>&-vEnbDXGs*vN+dn^8XPxEf;j`9cFd>2 zdeEZBtjDrQfgebh==%-M)6v?y-dS!ddO(^C*$xEUcV%Qgj>@|O1CsWZOF=wvq61b2QRbyyfPB8k7zg2k|$kuX~W(&59x$BB@g+ zijBMxqf?e$vsgtfu|3lksMXf4_khH@|MYzI#L?PBhcQ!!XCj0=3(jD;uPja7?qD?I z@VVVn&DVwOmpX)emSVL*q^|xhcv8skx{)r$(aJ{$x_uXrOICa_pBy%h~G#k#2 z4S%xX`56 z;zWw_p=`TFF%|r*Pm9&e@g=Eeo%y>|+;g^!`cY1Y71hPBIzL41>_n~Q-zS@XPf+Eb zGguV2zRh7L=Qwd+-%ysM-03u6Dp*x@tU+qbXuWsOo6$&YR}3+-B_J_5pn;h|+GL_F zQyFnpA>D0Ef-pTmMPF7@PgcmhY@GCk9ESU&M8ACXbEVX$E8&IPVzx~%qox)8#%hsG z*};C`YZPbjNY$J=bC5-%8Sz>^c8J1*BS&8y3FH zJfZHqIFW7qx;)#M`}L)?z*FoSt-S5SPyq$IUi-4YOd`UUC+wXwU(DP5J~T%KcK0ur z26h+XY;)+Rzj(Cm&uauSH>Auw&9DrMK@4?}vU*JU>!AGYbc?m*2%4P<(bMqM4W|(b zUusG(Of*Q>pAD*%fNnM9TZwRh#Ja5!Jy05np|8XNZ zb3R7n%DY@LD;2Ut3*h`G@41YF89Cb%dpQlWfPR9DHAP<+7czJ;o{z0wY zcEy3IE?`dSFhl7Hul)OKkLD#QORv&d*y^%f+x*o^M!JNdi2As-x%pGO-m&Aw5-wPF z(RHgi+nc|gEC!|o9J zFvMgSM5$bJHn8I?whB(R!i7N#w$41t11KAmdsgVoP*3i_92U6+#Qm2$duM zSt0!BCKMabFO+~((}Dn$9E+Is4FnpQsztV}&rM4UqpbgHK>^Q|ji0uJt~oKIw*>EY z^QPt-WUf2oX1%JulkPsJODvhV&xehy!p3x~v!j+4RO54e24N523Nf;LcBJ!i*vAl? z@kD=IVXz#j!=xF7x!nhGW+r#GBGt$5`gxHBEzqLMtk z=^c}|Z<8>FNoe}(wrToC>o*Iv!=qaJ%%+l-m%)dAXb1pgyD7@uB1VDL4nZo9Bii&+ zu;fUvBIwug3HhJQ>*j-GOR=GNF+*|{a6dxMF>5AGD{~qYjRYW?_QYQrq$PUogg+!o z6-n5aM=bj9>YQ0jNn$Vs<**Q{ruDr_i~Vwg4;bJo`Jo+>_?BPNFfQiXBL)CdN9oHs>oj-d_UJ|fDVHj-&N7+P&C^EFUvc?G)U|B+PJ4YTp&@ch|Na)D zQ7lZd9LV2uOB!b~qj`@#By8^9`dD3(`xmc4zcuY~{9nC^-s>3WqC9H2K63_K$Sts2; z0Eo?Ws(x9yg6c%R0Qin6Kbb=I{9(vE?6Q#8zkkQPlnv>IRAp&~t#%R?dYfYUjlcjf{0PjfZ|WYh5ki}FLGkV=GykI zxa?=7vMp9D-7MT(kDMi$oWNDocH|*38*Kcf-L3qMzc6p*iKBiCQ@*~o=%m?@x2WVE?IYY)GN(DS(+;q z*JwJtHZe|x5=IuK^H8$v@hYXSweyyaw%!YK=EkzHuF7%3Rf|jgV&?JQvC~K@a^X)( zQVieW&vy@DG-ghW=5FSJo{%~==UWuOG@#=N>eIT*aN#*WF81&VN$7A8&-;=r?wASj zTLT$!hyV16QFpaU?owL*7)Md(yBXEmjP@I=n?)3G$F!t+Jxf(7HaLkU&rCS(S~ORA z8?5-zpRn#}V|E<69Cdr-zN&GNdgdM7zY!|${LbHe3vB%n+wG9o%)ay^CYsMQPfqrW znW8mJEl8Fwqf1K-NneVpLeq2nz zrzD|z!T!JIS?7)(ES^B|S=h>0_g1F0?W~@D+bwY4+=sk>NO1ll`{4H_VQ&|UGea0T zBj_~YC_3VKdHvv!Kl4W?=6EMs&DTWYcH0)eK4p?XoF^?NA#v+NN~0GcO%i1JSCbjjf^mq|6C%fT|<^h>b$zK0}n$el>M333GlT6V?^v&=KV5<__Q! zhOmw(TuoEdOjp#jc(Xbl$^}ac)-(<_FmW@8won{BV4e7M`GUROAjraMuB_m9!)t4X zGLsvn2B!C~Qihmy)1P@A--iF|bx+THOJ-JSv7|$fL&CkL zc~_@!dquVaGUA)t{w-gP$1_CZ1g7BWO^Nm_8l zG{%P?X8l6gT#|^7o)8m*dTd(y>+yeK_qTHXY^FS|fACP+ahb=|+-26;dBc}y&{hrk z=bKDappf?Zs^V@f^ZMsRf!{J)y&Ox-PXL}We-5dG35_IKd_|>=6IgHZU)pO6@oN^| z)_v@W_!ynKnV;+QAHj0XIfJg=%GXRtLU7|xFcTf( zvzym#RSaL#{SNzZiJ6yIU&^ztTJso34bl88$ItS2ATVJ4o5K0d?`V@OGg-%Y%-Ri? z^e~$C--NFgMUJN8>?@pIjm^aZ$753Fo48WPDtO zmPy!+C=+jU6GPd%5}Apc5s}ok>XafmNvtu$$36}f!sKgQz$_U>3Wx{L3EQNmkq09# z`mRDlu9DR8igfWu%P#UH`->WXLU!kc($$!f6qZm(>j_m{a zw`>s25Y3y8a)8km_InY?AMPA|D9)_)51{!K_`Md)Z^aO(dGX~Ah-X$;!1)4MuFg*C IMef!A1E{f6)c^nh diff --git a/Tests/test_file_dds.py b/Tests/test_file_dds.py index 58447122e..0d95d3a65 100644 --- a/Tests/test_file_dds.py +++ b/Tests/test_file_dds.py @@ -14,6 +14,8 @@ TEST_FILE_DX10_BC5_TYPELESS = "Tests/images/bc5_typeless.dds" TEST_FILE_DX10_BC5_UNORM = "Tests/images/bc5_unorm.dds" TEST_FILE_DX10_BC5_SNORM = "Tests/images/bc5_snorm.dds" TEST_FILE_BC5S = "Tests/images/bc5s.dds" +TEST_FILE_BC6 = "Tests/images/bc6h.dds" +TEST_FILE_BC6S = "Tests/images/bc6h_sf.dds" TEST_FILE_DX10_BC7 = "Tests/images/bc7-argb-8bpp_MipMaps-1.dds" TEST_FILE_DX10_BC7_UNORM_SRGB = "Tests/images/DXGI_FORMAT_BC7_UNORM_SRGB.dds" TEST_FILE_DX10_R8G8B8A8 = "Tests/images/argb-32bpp_MipMaps-1.dds" @@ -86,6 +88,26 @@ def test_dx10_bc5(image_path, expected_path): assert_image_equal_tofile(im, expected_path.replace(".dds", ".png")) +@pytest.mark.parametrize( + ("image_path", "expected_path"), + ( + (TEST_FILE_BC6, TEST_FILE_BC6), + (TEST_FILE_BC6S, TEST_FILE_BC6S), + ), +) +def test_dx10_bc6(image_path, expected_path): + """Check DX10 BC6/BC6S images can be opened""" + + with Image.open(image_path) as im: + im.load() + + assert im.format == "DDS" + assert im.mode == "RGB" + assert im.size == (256, 256) + + assert_image_equal_tofile(im, expected_path.replace(".dds", ".png")) + + def test_dx10_bc7(): """Check DX10 images can be opened""" @@ -144,12 +166,6 @@ def test_dx10_r8g8b8a8_unorm_srgb(): ) -def test_unimplemented_dxgi_format(): - with pytest.raises(NotImplementedError): - with Image.open("Tests/images/unimplemented_dxgi_format.dds"): - pass - - def test_uncompressed_rgb(): """Check uncompressed RGB images can be opened""" From 0d0cf6374c0272901cce04ec7c268cefe30bdf89 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 19 Jul 2022 07:18:48 +1000 Subject: [PATCH 009/239] Restored unimplemented DXGI format test --- Tests/images/unimplemented_dxgi_format.dds | Bin 0 -> 65684 bytes Tests/test_file_dds.py | 6 ++++++ 2 files changed, 6 insertions(+) create mode 100644 Tests/images/unimplemented_dxgi_format.dds diff --git a/Tests/images/unimplemented_dxgi_format.dds b/Tests/images/unimplemented_dxgi_format.dds new file mode 100644 index 0000000000000000000000000000000000000000..70860f2fcc459053a873b7d6198d8cc296892088 GIT binary patch literal 65684 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

1AU*Lo5$}M{whIoy{IYLj8A3VbmFkL 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 literal 0 HcmV?d00001 diff --git a/Tests/test_file_dds.py b/Tests/test_file_dds.py index 0d95d3a65..d10fe2730 100644 --- a/Tests/test_file_dds.py +++ b/Tests/test_file_dds.py @@ -166,6 +166,12 @@ def test_dx10_r8g8b8a8_unorm_srgb(): ) +def test_unimplemented_dxgi_format(): + with pytest.raises(NotImplementedError): + with Image.open("Tests/images/unimplemented_dxgi_format.dds"): + pass + + def test_uncompressed_rgb(): """Check uncompressed RGB images can be opened""" From 4b97f88ef81935d451ebd5830849541cd433d8b3 Mon Sep 17 00:00:00 2001 From: ShadelessFox Date: Tue, 19 Jul 2022 02:19:21 +0300 Subject: [PATCH 010/239] Code cleanup --- src/decode.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/decode.c b/src/decode.c index d3bc2b14e..7a9b956c5 100644 --- a/src/decode.c +++ b/src/decode.c @@ -376,8 +376,6 @@ PyImaging_BcnDecoderNew(PyObject *self, PyObject *args) { actual = "L"; break; case 5: /* BC5: 2-channel 8-bit via 2 BC3 alpha blocks */ - actual = "RGB"; - break; case 6: /* BC6: 3-channel 16-bit float */ actual = "RGB"; break; From 5bd893f760f1e413292654a96ea329935e878744 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 23 Jul 2022 14:16:40 +1000 Subject: [PATCH 011/239] Rename format to BC6H and BC6HS --- Tests/test_file_dds.py | 12 ++++++------ src/PIL/DdsImagePlugin.py | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Tests/test_file_dds.py b/Tests/test_file_dds.py index d10fe2730..f9a9a8b51 100644 --- a/Tests/test_file_dds.py +++ b/Tests/test_file_dds.py @@ -14,8 +14,8 @@ TEST_FILE_DX10_BC5_TYPELESS = "Tests/images/bc5_typeless.dds" TEST_FILE_DX10_BC5_UNORM = "Tests/images/bc5_unorm.dds" TEST_FILE_DX10_BC5_SNORM = "Tests/images/bc5_snorm.dds" TEST_FILE_BC5S = "Tests/images/bc5s.dds" -TEST_FILE_BC6 = "Tests/images/bc6h.dds" -TEST_FILE_BC6S = "Tests/images/bc6h_sf.dds" +TEST_FILE_BC6H = "Tests/images/bc6h.dds" +TEST_FILE_BC6HS = "Tests/images/bc6h_sf.dds" TEST_FILE_DX10_BC7 = "Tests/images/bc7-argb-8bpp_MipMaps-1.dds" TEST_FILE_DX10_BC7_UNORM_SRGB = "Tests/images/DXGI_FORMAT_BC7_UNORM_SRGB.dds" TEST_FILE_DX10_R8G8B8A8 = "Tests/images/argb-32bpp_MipMaps-1.dds" @@ -91,12 +91,12 @@ def test_dx10_bc5(image_path, expected_path): @pytest.mark.parametrize( ("image_path", "expected_path"), ( - (TEST_FILE_BC6, TEST_FILE_BC6), - (TEST_FILE_BC6S, TEST_FILE_BC6S), + (TEST_FILE_BC6H, TEST_FILE_BC6H), + (TEST_FILE_BC6HS, TEST_FILE_BC6HS), ), ) -def test_dx10_bc6(image_path, expected_path): - """Check DX10 BC6/BC6S images can be opened""" +def test_dx10_bc6h(image_path, expected_path): + """Check DX10 BC6H/BC6HS images can be opened""" with Image.open(image_path) as im: im.load() diff --git a/src/PIL/DdsImagePlugin.py b/src/PIL/DdsImagePlugin.py index 1db7aec8e..47db9294f 100644 --- a/src/PIL/DdsImagePlugin.py +++ b/src/PIL/DdsImagePlugin.py @@ -176,11 +176,11 @@ class DdsImageFile(ImageFile.ImageFile): n = 5 self.mode = "RGB" elif dxgi_format == DXGI_FORMAT_BC6H_UF16: - self.pixel_format = "BC6" + self.pixel_format = "BC6H" n = 6 self.mode = "RGB" elif dxgi_format == DXGI_FORMAT_BC6H_SF16: - self.pixel_format = "BC6S" + self.pixel_format = "BC6HS" n = 6 self.mode = "RGB" elif dxgi_format in (DXGI_FORMAT_BC7_TYPELESS, DXGI_FORMAT_BC7_UNORM): From 806f43f0b740ea46be564b7238093734da0849b1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 23 Jul 2022 14:19:28 +1000 Subject: [PATCH 012/239] Simplified code --- Tests/test_file_dds.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/Tests/test_file_dds.py b/Tests/test_file_dds.py index f9a9a8b51..490cb9f48 100644 --- a/Tests/test_file_dds.py +++ b/Tests/test_file_dds.py @@ -88,14 +88,8 @@ def test_dx10_bc5(image_path, expected_path): assert_image_equal_tofile(im, expected_path.replace(".dds", ".png")) -@pytest.mark.parametrize( - ("image_path", "expected_path"), - ( - (TEST_FILE_BC6H, TEST_FILE_BC6H), - (TEST_FILE_BC6HS, TEST_FILE_BC6HS), - ), -) -def test_dx10_bc6h(image_path, expected_path): +@pytest.mark.parametrize("image_path", (TEST_FILE_BC6H, TEST_FILE_BC6HS)) +def test_dx10_bc6h(image_path): """Check DX10 BC6H/BC6HS images can be opened""" with Image.open(image_path) as im: @@ -105,7 +99,7 @@ def test_dx10_bc6h(image_path, expected_path): assert im.mode == "RGB" assert im.size == (256, 256) - assert_image_equal_tofile(im, expected_path.replace(".dds", ".png")) + assert_image_equal_tofile(im, image_path.replace(".dds", ".png")) def test_dx10_bc7(): From 3bec5999e044c30c2bcea5f6a20308d44df3f10a Mon Sep 17 00:00:00 2001 From: ShadelessFox Date: Sat, 23 Jul 2022 15:20:35 +0300 Subject: [PATCH 013/239] Rename remaining occurrences of BC6S -> BC6HS --- src/libImaging/BcnDecode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libImaging/BcnDecode.c b/src/libImaging/BcnDecode.c index aa4cbf647..2870cef85 100644 --- a/src/libImaging/BcnDecode.c +++ b/src/libImaging/BcnDecode.c @@ -870,7 +870,7 @@ decode_bcn( case 6: while (bytes >= 16) { rgba col[16]; - decode_bc6_block(col, ptr, strcmp(pixel_format, "BC6S") == 0 ? 1 : 0); + decode_bc6_block(col, ptr, strcmp(pixel_format, "BC6HS") == 0 ? 1 : 0); put_block(im, state, (const char *)col, sizeof(col[0]), C); ptr += 16; bytes -= 16; From 952237d37390c6d4cae90609302f1ae4cb9028e8 Mon Sep 17 00:00:00 2001 From: ShadelessFox Date: Sun, 31 Jul 2022 19:16:25 +0300 Subject: [PATCH 014/239] Minimize unnecessary code changes --- src/libImaging/BcnDecode.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/libImaging/BcnDecode.c b/src/libImaging/BcnDecode.c index 2870cef85..0058205f1 100644 --- a/src/libImaging/BcnDecode.c +++ b/src/libImaging/BcnDecode.c @@ -537,21 +537,21 @@ static const UINT8 bc6_bit_packings[][75] = { 39, 40, 41, 48, 49, 50, 51, 52, 164, 112, 113, 114, 115, 64, 65, 66, 67, 68, 176, 160, 161, 162, 163, 80, 81, 82, 83, 84, 177, 128, 129, 130, 131, 96, 97, 98, 99, 100, 178, 144, 145, 146, 147, 148, 179}, - {117, 164, 165, 0, 1, 2, 3, 4, 5, 6, 176, 177, 132, 16, 17, - 18, 19, 20, 21, 22, 133, 178, 116, 32, 33, 34, 35, 36, 37, 38, - 179, 181, 180, 48, 49, 50, 51, 52, 53, 112, 113, 114, 115, 64, 65, - 66, 67, 68, 69, 160, 161, 162, 163, 80, 81, 82, 83, 84, 85, 128, - 129, 130, 131, 96, 97, 98, 99, 100, 101, 144, 145, 146, 147, 148, 149}, + {117, 164, 165, 0, 1, 2, 3, 4, 5, 6, 176, 177, 132, 16, 17, + 18, 19, 20, 21, 22, 133, 178, 116, 32, 33, 34, 35, 36, 37, 38, + 179, 181, 180, 48, 49, 50, 51, 52, 53, 112, 113, 114, 115, 64, 65, + 66, 67, 68, 69, 160, 161, 162, 163, 80, 81, 82, 83, 84, 85, 128, + 129, 130, 131, 96, 97, 98, 99, 100, 101, 144, 145, 146, 147, 148, 149}, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 52, 10, 112, 113, 114, 115, 64, 65, 66, 67, 26, 176, 160, 161, 162, 163, 80, 81, 82, 83, 42, 177, 128, 129, 130, 131, - 96, 97, 98, 99, 100, 178, 144, 145, 146, 147, 148, 179}, - {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, - 21, 22, 23, 24, 25, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, - 48, 49, 50, 51, 10, 164, 112, 113, 114, 115, 64, 65, 66, 67, 68, - 26, 160, 161, 162, 163, 80, 81, 82, 83, 42, 177, 128, 129, 130, 131, - 96, 97, 98, 99, 176, 178, 144, 145, 146, 147, 116, 179}, + 96, 97, 98, 99, 100, 178, 144, 145, 146, 147, 148, 179}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 48, 49, 50, 51, 10, 164, 112, 113, 114, 115, 64, 65, 66, 67, 68, + 26, 160, 161, 162, 163, 80, 81, 82, 83, 42, 177, 128, 129, 130, 131, + 96, 97, 98, 99, 176, 178, 144, 145, 146, 147, 116, 179}, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 10, 132, 112, 113, 114, 115, 64, 65, 66, 67, 26, @@ -567,11 +567,11 @@ static const UINT8 bc6_bit_packings[][75] = { 48, 49, 50, 51, 52, 53, 112, 113, 114, 115, 64, 65, 66, 67, 68, 176, 160, 161, 162, 163, 80, 81, 82, 83, 84, 177, 128, 129, 130, 131, 96, 97, 98, 99, 100, 101, 144, 145, 146, 147, 148, 149}, - {0, 1, 2, 3, 4, 5, 6, 7, 176, 132, 16, 17, 18, 19, 20, - 21, 22, 23, 117, 116, 32, 33, 34, 35, 36, 37, 38, 39, 165, 180, - 48, 49, 50, 51, 52, 164, 112, 113, 114, 115, 64, 65, 66, 67, 68, - 69, 160, 161, 162, 163, 80, 81, 82, 83, 84, 177, 128, 129, 130, 131, - 96, 97, 98, 99, 100, 178, 144, 145, 146, 147, 148, 179}, + {0, 1, 2, 3, 4, 5, 6, 7, 176, 132, 16, 17, 18, 19, 20, + 21, 22, 23, 117, 116, 32, 33, 34, 35, 36, 37, 38, 39, 165, 180, + 48, 49, 50, 51, 52, 164, 112, 113, 114, 115, 64, 65, 66, 67, 68, + 69, 160, 161, 162, 163, 80, 81, 82, 83, 84, 177, 128, 129, 130, 131, + 96, 97, 98, 99, 100, 178, 144, 145, 146, 147, 148, 179}, {0, 1, 2, 3, 4, 5, 6, 7, 177, 132, 16, 17, 18, 19, 20, 21, 22, 23, 133, 116, 32, 33, 34, 35, 36, 37, 38, 39, 181, 180, 48, 49, 50, 51, 52, 164, 112, 113, 114, 115, 64, 65, 66, 67, 68, From 54b01f55f80ba0b8233fb0ecae54dd3118ac0051 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 22 Aug 2022 12:14:54 +1000 Subject: [PATCH 015/239] Round box position to integer when pasting embedded color --- .../standard_embedded_multiline_centered.png | Bin 0 -> 2951 bytes Tests/test_imagefont.py | 15 +++++++++++++++ src/PIL/ImageDraw.py | 1 + 3 files changed, 16 insertions(+) create mode 100644 Tests/images/standard_embedded_multiline_centered.png diff --git a/Tests/images/standard_embedded_multiline_centered.png b/Tests/images/standard_embedded_multiline_centered.png new file mode 100644 index 0000000000000000000000000000000000000000..3aebe37790d7d53c29743f3e05b1f319aea817ef GIT binary patch literal 2951 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 24) & 0xFF) + coord = tuple(int(c) for c in coord) coord2 = coord[0] + mask.size[0], coord[1] + mask.size[1] self.im.paste(color, coord + coord2, mask) else: From b330ff910d4925a5a420dd4b71c7db3792327319 Mon Sep 17 00:00:00 2001 From: nulano Date: Wed, 24 Aug 2022 22:04:43 +0200 Subject: [PATCH 016/239] upload fribidi.dll to GHA --- .github/workflows/test-windows.yml | 12 +++++++++++- winbuild/build_prepare.py | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index b9accfdf9..d8a1f23fe 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -181,16 +181,26 @@ jobs: id: wheel if: "github.event_name != 'pull_request'" run: | + mkdir fribidi\${{ matrix.architecture }} + copy winbuild\build\bin\fribidi* fribidi\${{ matrix.architecture }} for /f "tokens=3 delims=/" %%a in ("${{ github.ref }}") do echo ::set-output name=dist::dist-%%a winbuild\\build\\build_pillow.cmd --disable-imagequant bdist_wheel shell: cmd - - uses: actions/upload-artifact@v3 + - name: Upload wheel + uses: actions/upload-artifact@v3 if: "github.event_name != 'pull_request'" with: name: ${{ steps.wheel.outputs.dist }} path: dist\*.whl + - name: Upload fribidi.dll + if: "github.event_name != 'pull_request' && matrix.python-version == 3.10" + uses: actions/upload-artifact@v3 + with: + name: fribidi + path: fribidi\* + success: permissions: contents: none diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 94e5dd871..567ca4f7e 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -297,6 +297,7 @@ deps = { "filename": "fribidi-1.0.12.zip", "dir": "fribidi-1.0.12", "build": [ + cmd_copy(r"COPYING", r"{bin_dir}\fribidi-1.0.12-COPYING"), cmd_copy(r"{winbuild_dir}\fribidi.cmake", r"CMakeLists.txt"), cmd_cmake(), cmd_nmake(target="clean"), From ced381edaa4f4217b3e8f575f3ed1dfe6f2784f2 Mon Sep 17 00:00:00 2001 From: nulano Date: Thu, 25 Aug 2022 00:21:55 +0200 Subject: [PATCH 017/239] Document ImageDraw attributes --- docs/reference/ImageDraw.rst | 43 +++++++++++++++++++++++++++++++----- src/PIL/ImageDraw.py | 10 ++++++++- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index 1ef9079fb..ec21898e1 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -139,17 +139,50 @@ Functions must be the same as the image mode. If omitted, the mode defaults to the mode of the image. +Attributes +---------- + +.. py:attribute:: ImageDraw.fill + :type: bool + :value: False + + Selects whether :py:attr:`ImageDraw.ink` should be used as a fill or outline color. + +.. py:attribute:: ImageDraw.font + + The current default font. + + Can be set per instance:: + + from PIL import ImageDraw, ImageFont + draw = ImageDraw.Draw(image) + draw.font = ImageFont.truetype("Tests/fonts/FreeMono.ttf") + + Or globally for all future ImageDraw instances:: + + from PIL import ImageDraw, ImageFont + ImageDraw.ImageDraw.font = ImageFont.truetype("Tests/fonts/FreeMono.ttf") + +.. py:attribute:: ImageDraw.fontmode + + The current font drawing mode. + + Set to ``"1"`` to disable antialiasing or ``"L"`` to enable it. + +.. py:attribute:: ImageDraw.ink + :type: int + + The internal representation of the current default color. + Methods ------- .. py:method:: ImageDraw.getfont() - Get the current default font. + Get the current default font, :py:attr:`ImageDraw.font`. - To set the default font for all future ImageDraw instances:: - - from PIL import ImageDraw, ImageFont - ImageDraw.ImageDraw.font = ImageFont.truetype("Tests/fonts/FreeMono.ttf") + If the current default font is ``None``, + it is initialized with :py:func:`.ImageFont.load_default`. :returns: An image font. diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index e84dafb12..7ca03e875 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -87,17 +87,25 @@ class ImageDraw: self.fontmode = "1" else: self.fontmode = "L" # aliasing is okay for other modes - self.fill = 0 + self.fill = False def getfont(self): """ Get the current default font. + To set the default font for this ImageDraw instance:: + + from PIL import ImageDraw, ImageFont + draw.font = ImageFont.truetype("Tests/fonts/FreeMono.ttf") + To set the default font for all future ImageDraw instances:: from PIL import ImageDraw, ImageFont ImageDraw.ImageDraw.font = ImageFont.truetype("Tests/fonts/FreeMono.ttf") + If the current default font is ``None``, + it is initialized with ``ImageFont.load_default()``. + :returns: An image font.""" if not self.font: # FIXME: should add a font repository From 18bd77bbc041a5a6393503db9b0930c971c03c65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ondrej=20Baranovi=C4=8D?= Date: Thu, 25 Aug 2022 11:45:33 +0200 Subject: [PATCH 018/239] simplify code, make test more generic --- .../standard_embedded_multiline_centered.png | Bin 2951 -> 0 bytes Tests/images/text_float_coord.png | Bin 0 -> 2877 bytes Tests/images/text_float_coord_1_alt.png | Bin 0 -> 807 bytes Tests/test_imagefont.py | 29 +++++++++++------- src/PIL/ImageDraw.py | 5 ++- 5 files changed, 20 insertions(+), 14 deletions(-) delete mode 100644 Tests/images/standard_embedded_multiline_centered.png create mode 100644 Tests/images/text_float_coord.png create mode 100644 Tests/images/text_float_coord_1_alt.png diff --git a/Tests/images/standard_embedded_multiline_centered.png b/Tests/images/standard_embedded_multiline_centered.png deleted file mode 100644 index 3aebe37790d7d53c29743f3e05b1f319aea817ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2951 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 literal 0 HcmV?d00001 diff --git a/Tests/images/text_float_coord_1_alt.png b/Tests/images/text_float_coord_1_alt.png new file mode 100644 index 0000000000000000000000000000000000000000..50bdac3d8f39aa492bf41ba3e49c787031d07628 GIT binary patch literal 807 zcmeAS@N?(olHy`uVBq!ia0y~yVAKJ!9XObPq<-Ap?Ft=`Ln`LHy?wjzwSq`% zqUB_<*ye06UcI17g+vMN4{kAHC;pp1KY83O$D5mDQvUCK`3xoduE;Xj$ttoO;ZRg? z<6ty&;^+``a_A9YN@@`hP;P0Ez$SJ76<e?Tv6Qf;qK0-UYqm(hEAJy z-*x(%jm!J&mN4%UNb~#oD%#q3df3jB_jYed{(bmcSJ2Y0?;A8)em>{_^Y^T1@eIwg z|F5eoom#dyH1i*CV)I4UPaCuqH@no^Rq@a5>YcgaK}O=guTS62c<#*|FEVY~uUnsY zO`CSAAv@Sayxfo5+V?-U+PCHM^L5#ow^Vm6HL2H} zx9!8`#jB6#r^v)V3n@we|D`&rc9r69p0YQ3T7|f@+@fS)hnWP|6i8n7%Mj9?Qmrz hB6Z> 24) & 0xFF) - coord = tuple(int(c) for c in coord) - coord2 = coord[0] + mask.size[0], coord[1] + mask.size[1] - self.im.paste(color, coord + coord2, mask) + x, y = (int(c) for c in coord) + self.im.paste(color, (x, y, x + mask.size[0], y + mask.size[1]), mask) else: self.draw.draw_bitmap(coord, mask, ink) From 41a7bfe1c188bda64c5fcc84d6518084d742de9f Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 5 Sep 2022 06:49:48 +0200 Subject: [PATCH 019/239] append dependency licenses to windows wheels on GHA --- .github/workflows/test-windows.yml | 16 +++++++++++++ winbuild/build_prepare.py | 37 +++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index b9accfdf9..ba72a7018 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -181,6 +181,22 @@ jobs: id: wheel if: "github.event_name != 'pull_request'" run: | + setlocal EnableDelayedExpansion + for %%f in (winbuild\build\license\*) do ( + set x=%%~nf + rem Skip FriBiDi license, it is not included in the wheel. + set fribidi=!x:~0,7! + if NOT !fribidi!==fribidi ( + rem Skip imagequant license, it is not included in the wheel. + set libimagequant=!x:~0,13! + if NOT !libimagequant!==libimagequant ( + echo. >> LICENSE + echo ===== %%~nf ===== >> LICENSE + echo. >> LICENSE + type %%f >> LICENSE + ) + ) + ) for /f "tokens=3 delims=/" %%a in ("${{ github.ref }}") do echo ::set-output name=dist::dist-%%a winbuild\\build\\build_pillow.cmd --disable-imagequant bdist_wheel shell: cmd diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 94e5dd871..e878f4c8e 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -1,5 +1,6 @@ import os import platform +import re import shutil import struct import subprocess @@ -111,6 +112,11 @@ deps = { + "/libjpeg-turbo/files/2.1.4/libjpeg-turbo-2.1.4.tar.gz/download", "filename": "libjpeg-turbo-2.1.4.tar.gz", "dir": "libjpeg-turbo-2.1.4", + "license": ["README.ijg", "LICENSE.md"], + "license_pattern": ( + "(LEGAL ISSUES\n============\n\n.+?)\n\nREFERENCES\n==========" + ".+(libjpeg-turbo Licenses\n======================\n\n.+)$" + ), "build": [ cmd_cmake( [ @@ -135,6 +141,8 @@ deps = { "url": "https://zlib.net/zlib1212.zip", "filename": "zlib1212.zip", "dir": "zlib-1.2.12", + "license": "README", + "license_pattern": "Copyright notice:\n\n(.+)$", "build": [ cmd_nmake(r"win32\Makefile.msc", "clean"), cmd_nmake(r"win32\Makefile.msc", "zlib.lib"), @@ -147,6 +155,7 @@ deps = { "url": "https://download.osgeo.org/libtiff/tiff-4.4.0.tar.gz", "filename": "tiff-4.4.0.tar.gz", "dir": "tiff-4.4.0", + "license": "COPYRIGHT", "build": [ cmd_cmake("-DBUILD_SHARED_LIBS:BOOL=OFF"), cmd_nmake(target="clean"), @@ -160,6 +169,7 @@ deps = { "url": "http://downloads.webmproject.org/releases/webp/libwebp-1.2.4.tar.gz", "filename": "libwebp-1.2.4.tar.gz", "dir": "libwebp-1.2.4", + "license": "COPYING", "build": [ cmd_rmdir(r"output\release-static"), # clean cmd_nmake( @@ -176,6 +186,7 @@ deps = { "url": SF_PROJECTS + "/libpng/files/libpng16/1.6.37/lpng1637.zip/download", "filename": "lpng1637.zip", "dir": "lpng1637", + "license": "LICENSE", "build": [ # lint: do not inline cmd_cmake(("-DPNG_SHARED:BOOL=OFF", "-DPNG_TESTS:BOOL=OFF")), @@ -190,6 +201,7 @@ deps = { "url": "https://download.savannah.gnu.org/releases/freetype/freetype-2.12.1.tar.gz", # noqa: E501 "filename": "freetype-2.12.1.tar.gz", "dir": "freetype-2.12.1", + "license": ["LICENSE.TXT", r"docs\FTL.TXT", r"docs\GPLv2.TXT"], "patch": { r"builds\windows\vc2010\freetype.vcxproj": { # freetype setting is /MD for .dll and /MT for .lib, we need /MD @@ -225,6 +237,7 @@ deps = { "url": SF_PROJECTS + "/lcms/files/lcms/2.13/lcms2-2.13.1.tar.gz/download", "filename": "lcms2-2.13.1.tar.gz", "dir": "lcms2-2.13.1", + "license": "COPYING", "patch": { r"Projects\VC2022\lcms2_static\lcms2_static.vcxproj": { # default is /MD for x86 and /MT for x64, we need /MD always @@ -250,6 +263,7 @@ deps = { "url": "https://github.com/uclouvain/openjpeg/archive/v2.5.0.tar.gz", "filename": "openjpeg-2.5.0.tar.gz", "dir": "openjpeg-2.5.0", + "license": "LICENSE", "build": [ cmd_cmake(("-DBUILD_THIRDPARTY:BOOL=OFF", "-DBUILD_SHARED_LIBS:BOOL=OFF")), cmd_nmake(target="clean"), @@ -264,6 +278,7 @@ deps = { "url": "https://github.com/ImageOptim/libimagequant/archive/e4c1334be0eff290af5e2b4155057c2953a313ab.zip", # noqa: E501 "filename": "libimagequant-e4c1334be0eff290af5e2b4155057c2953a313ab.zip", "dir": "libimagequant-e4c1334be0eff290af5e2b4155057c2953a313ab", + "license": "COPYRIGHT", "patch": { "CMakeLists.txt": { "if(OPENMP_FOUND)": "if(false)", @@ -284,6 +299,7 @@ deps = { "url": "https://github.com/harfbuzz/harfbuzz/archive/5.1.0.zip", "filename": "harfbuzz-5.1.0.zip", "dir": "harfbuzz-5.1.0", + "license": "COPYING", "build": [ cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"), cmd_nmake(target="clean"), @@ -296,6 +312,7 @@ deps = { "url": "https://github.com/fribidi/fribidi/archive/v1.0.12.zip", "filename": "fribidi-1.0.12.zip", "dir": "fribidi-1.0.12", + "license": "COPYING", "build": [ cmd_copy(r"{winbuild_dir}\fribidi.cmake", r"CMakeLists.txt"), cmd_cmake(), @@ -431,6 +448,21 @@ def build_dep(name): extract_dep(dep["url"], dep["filename"]) + licenses = dep["license"] + if isinstance(licenses, str): + licenses = [licenses] + license_text = "" + for license_file in licenses: + with open(os.path.join(sources_dir, dir, license_file)) as f: + license_text += f.read() + if "license_pattern" in dep: + match = re.search(dep["license_pattern"], license_text, re.DOTALL) + license_text = "\n".join(match.groups()) + assert len(license_text) > 50 + with open(os.path.join(license_dir, f"{dir}.txt"), "w") as f: + print(f"Writing license {dir}.txt") + f.write(license_text) + for patch_file, patch_list in dep.get("patch", {}).items(): patch_file = os.path.join(sources_dir, dir, patch_file.format(**prefs)) with open(patch_file) as f: @@ -551,10 +583,12 @@ if __name__ == "__main__": bin_dir = os.path.join(build_dir, "bin") # directory for storing project files sources_dir = build_dir + sources_dir + # copy dependency licenses to this directory + license_dir = os.path.join(build_dir, "license") shutil.rmtree(build_dir, ignore_errors=True) os.makedirs(build_dir, exist_ok=False) - for path in [inc_dir, lib_dir, bin_dir, sources_dir]: + for path in [inc_dir, lib_dir, bin_dir, sources_dir, license_dir]: os.makedirs(path, exist_ok=True) prefs = { @@ -572,6 +606,7 @@ if __name__ == "__main__": "lib_dir": lib_dir, "bin_dir": bin_dir, "src_dir": sources_dir, + "license_dir": license_dir, # Compilers / Tools **msvs, "cmake": "cmake.exe", # TODO find CMAKE automatically From 9699a0e1d6ee4859f1b9da0e9e7a45082f9f4c69 Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 5 Sep 2022 10:13:52 +0200 Subject: [PATCH 020/239] test libtiff with lzma --- Tests/images/hopper_lzma.tif | Bin 0 -> 30789 bytes Tests/test_file_libtiff.py | 14 ++++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 Tests/images/hopper_lzma.tif diff --git a/Tests/images/hopper_lzma.tif b/Tests/images/hopper_lzma.tif new file mode 100644 index 0000000000000000000000000000000000000000..d7ca089fc40949c731fdc7f354ff1072f026039a GIT binary patch literal 30789 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

yOGTie&81$CleX~bmQxxgb>64rCPjpf6|)jF zgj>`uFz9dhSaQszt%mZwF&r&hSS&W|Ui5c9FC?M)H+OA{VYUK`oms_=fC@U`NI#@HdBeKTZ}oU z*T+zoX+)Jj^4Fe){AbP0aZ@~BJ0+LbbP09ps>6E}3(UNn9CgjHo!%1W!D9GGS)T|C zkuuan1m5|YQB0O5Md2q>OV|IlmlHF7k9+^>(O}GpbJY)avd_>tv46}yJ7YrgsrEL$ z7UHApf6I-7bwpoLif;ZRTumB&x4T7yLt3KVk=Dw|%ghRZd?}4b5rk7Niv@Lv%;Z}2 zN(9#H;c+#EL%Ej7G5Dm{gNdHrmjjwg0+K>JOy?+fobS&#W6F$1QUk~3##Kv}{3GIB z>}^EpQLZ8$X+yhex;&#Aho`!BU6LUtzW;+6fy+(Sz9VIg$_U6Clc#izJtOym0MeT6 zIF94WCYpnDay(xDMy~ep{pUlpOdT}QKteqB;-P2jQXV$|^_jfuOT2c;a@dCuYh6Zs zh@a9~L8W#hW6O0l548u{YNk9k6uW*!SpGyHvcm>&3n@_zQ~6UCz9?RFGjb}#m5Y(q zMtA!;VkfbvBSG3HEqt-|*g*UxoAdRe?3ayRIYz>$aP+_1@%LEp)DKGsYQk7}T7DvR zL~Sj;hW6$N)1coOF8N;kZy`c%{);_RwphFKs`U~(r6}0mFpLcS1H>QT?m|5QSf$Ai z0aII2Hh{V}ufAvzbSx6-2qKwm-^J=FDIJZwM2*dX?P%2<$&|VCG!yHcjmfh0=$|<2^QmfvrKD4onGU!6qFbb}&R44rp5)}Vx(K#!5sXs4P2_Y1 zOXi5%BLYh1qyiK9vj^85J_W;=Xgpfb>t>qW;J(IUq`WW#f4+l4 zw&30|L*8SriL7CTp(8@l$D7CtcS&JR|RQj}_zBSv(j5&I6S zx=6t;%{ZHL|ENsrz7i>15>_Ly5Iu&0GqMW18x>o<;MZWzV2biA+ad>cSh7s#L$#OQ zYzl5f^SN3VEqlQ%Vy11_Sw5l80yh-&vY?CY3&O*SyyZ*TtV}^l)qnrglY~EpBE%## zNJvBu8^i}!e#iAwR2}=;_RDqkgH82Hx}n&iYut4q|1>vk+N4Q14_Y^%JV} zxIYVMTWH8B>!}fS#ern7uyCE#H5hRp{PUB=h*+k+B5u7u=ts`8b5(ia#PTfLiLfp1 zFqng{A47~MA1sp!=vXrsh=N7yzqT~1*J+NJ#hng6h7u!UPfPa8;KVQjDi9OO**B6= zkR$24lYGgKZpGLDMoO4YwDQ-uOu{MWyd)z?Q&+nXEPX)~SD;W@I$ z(Vhj&!U?+87AE-IoW|`0VQUFH+R!8LR@$M7ImoNMF zxil#BJs`W)TKS4ECcYvVz?!Q3j-@gx+-`!8TGT3ql-P^*G%x(@?Le1M88&3OD$`&E zrellrs^aydI$0i1kR-r~XcTX+gJ=~W)eS!C+Jq4pL~9!cN#@S7g5Ka0Y-=yx5@KY- zG(VPeJzQ~I~jbcBAn6hd;hy%n0~+ZM;ZH;b|=cV_9Zf+BlFC|F=B2hC@1hjwXB#`zmMa<2~gFqOj6iUHy>FqGU(2iz%DS z(Q;0;>SZ{2-rS6Br*BTzPsN!j^>DbdhEGBYJHb|BOzK2eDB;a9UMFPmj8qD{JwxoXEFb_TS_O%R zR#quUtuM&UHO_ffm7=mQ|MgTsnW{iotmLK@oLo5kX==!KzhQB?#qD!3_=IHQN#%Xu zC7FhoI~w{8n%uLeTCNbCu~1Q1uh+dIKiP}ekC#RRH+CLPeD3nhbvC{@&Sav;X$~S< z?AGO{CTA{Q0w{x|=F9Kf&n!VtvM=Fp=QcgH%X48fqb%^0Vu2BDc@GI9>G-h*@b`98 zO{)3X%i~wba1SC;GBFT9NR0<;jU^`YtH--mae6<4E_L=mX2EgyGs{n+8}>gMF47f< zqQ72;P>D11QeUTSl0@XyS+k0F8NOUm?`6OJ@;Gx5J}0Eq4PFMjF=gHZzCQ~VMP(Fn z=Nir*aCn4Z=HY-&_l}Jgg+-f4A?-aBs%yI_l!yG>q#+vXmrWEmazlzWHf~m}qiNpmZA=(x{@E9Re<9`P^f(0P**c2^{8z(J53Kc}tUo?(5#ivMcx)9-V z|0comb&uu1{AuL5PZpY!YM?&E=YjBt$e5_?(4+{=>*Eg)AG-LPd4K9fhxVPf9J(%Y{ynJz<_=7&)*L3zAKjuCOr$$o)nuyMudvTj zQUr_J=Z9%Pe4@zY=r-^q#V?qScqESa>kZ-w2*5T<&+hMNT?2Y!lXB$p#B_exDOgmT z=!PoY3j{>fmMlMjoVLTHIG;iWr$HmD3yBJ-P=EIS&W%P8{;lwM$74)ZZ@gaEMrghK zgr*lurx8WO;c)+mHo(;k|0_%gLciJaXLVhi&e>8v@JtrG@8m?#9MJ%GYISdg(SX|U zM(8}@h57t;s>u;|NU=;7t3vQgflmBH@iXY*-{*^)BA3H(?69i;$@PBh$^1uc{@?9eW`X)!4gaXBD9SFs6XLv`n6JF zXp>jr>7mBNux33Xs0RU@LjP%3qLftjE4Fmo#8x99gior!WhNX_G`17HT={{QB=$4`-AC|lbiSEfJ{1R%6WNg8^tn?;FP z13lr&{3F#G1&%97nIZq7gfuS-%Vb1U+ zAoR2A&Rzg!m9w^=u`rs>H*d{U<=!KMs*L^*?QRY+g}(OHbx9tmo^-T)VF}BoCQ}!T z^T0131#+3{!3Mz@tDlbHuEsHs=A`C?a)YruDPVZRqV>8IQf)TY6g4}VwAU}2Ccdzk zFtCj6^IQ#@aB~mZzHThubFlJ*xZ|M1dd<4<`YVQ)=jg$BI_b8tQjN@nr<}h@k=io| z&#DNx?O1=+Aj-HjKNGlGoQ2x;D;%Tab<|=L{`klk@m1@gWZKJGua>R-l1iHV`NOmV z9d{o#v#~7ou$0&| z`zzgfG<=&iFO!LsOkSgN;edu7jjeI!i}?qMiqTg6%zrIgjRiHc=k1%*Qu2hgMX5L7RJAo*AvUq(iRIlyvkgM?cYf4ORoMQn9mYlmL+xv76o2JC=(wC=vbo8qmrOGnXwq8r3fsR3lnY&ZtIOOD z_2;pAku}=yZ(r;e{B{($8wIkKmF%8e!xF#OWfqz{sMBG6J6q5i*eDr47F z<3d7Ed-p$%+(}hYn@9KPVx8R{z-f3l`M%;isf?uIe=KxCjx$W|b}|}~Q&ZGwSU4I+ z@b~juJtseom<`YmMY;%EAk=(nGDVGL$RBwa{{w7yw2%j@Y+C2y*S1K-zJ7Zd&;PB>HmUgNA78n zd&Y1pmJ5yHA|wL-!tti$uMFKleQj)%I+7jeN@2+d&j9ku3J>*amxDzzx_6R~pwy42 z#uo4Jr|s|LRxLw=6+pe1Zs2?i$_Q?Ot=?rZ7-ljKIIlk=Kh0R$^`W!Rk#Q?)n3vn< z<1rOBFO#~};G7NQ&I`yMqr0mk6iT+369E#y>Ka4pTcfp)-lF=*VhkQdWAc-GQDmf_ zQgy0Nnu=z6^skNvotTw&_sP<;-m_@eBSX{Oi?0CiaDdH@j8_?|HeSVOceAYqNmL>g z(gy9b;VgnAM+J3z9wvqD9Ts3_ny|87Is_VTVAHX)<27z1ln9fy)ypxX$TX`PKZnTOX8nBMEb}OA36W z_!Q!SP=PnSxh|H6^YxOEp1v{$a2G4K*u$aeaCZYijRi@zZc;fFnms-3?-(l@=k_FQ zsNK`2m|v3Kw9o*Jpm&AaZ$|a|p77EgN=nXz&peL&fV_}HnCT8p8=wP(T$yuL4J57I zD83ia+{LihKLqLZ;5%bef}lJE@Z9-s6d12lu1Q&^WK2lT(#EM)ljon;R+_RiBf_xE zp9e3UX3mdyK5#_7T0K_0k{FPuu*|g4PICT9dn6~D4CxUPbU>%ZkkW&0P4Fj<`LSRce@*#9r_Sf;@U}C$u4m+XZ@=9Lf|k5<6R6ha zPujdnhQO1Ge-uxwcyki8?*PlVl zr8yNKXy%`dR~^aUdrO2fp9jYbR~!Fh=(7B`5}_)tWtpu59(5&6-55x^YIx4t#l0|_ z(IHr6wy_lj^8}>v@L+D!UT-!YLLAG~{qi!Hxii3IL63{y)LjI&SNk}cBbcyVl`1V z0L^JB=idgJt&kUG#8bX1Q3c$g;vmN5?2$nCeA6%DFp)qVxar`Q`oN?G2pI{BudOgi z8ek=e)z|^N)pBJ!N992=dCW~3G&%Q_U9X`gun~O;SF`-IXG z#D7p)m|P{9OW%)9Pg9e)<+&<#!pTdl>X@i;{Wl3sj>ZHUO2^m+C*FI%rVq`VBdgt9fqqqpQ0gEU96Fk!&# z_s!1Mpi8xRX{#OkHR$y%2O-5+JcqYYc`1xcpYvc-(UDvdy7{wQK^(l-h-%f)gVe3C zPx*POc&XXw2nkc}VGOV|4y{xXyz1hR&~;guOMJ$0mxw6yv=1@=dYHKn#REyvFajd{dM?cE=7f{2*#H*PzUJjf5QGZpFs+Giet#hW#xzn;f7TjUEBJGnhsJ za)(F`Cs z9^3<9FomKx5v<3dS{k%ilkpjsu>8EA-fmx} z^1(~Jl+fB$Yc3d-#9|_+h(vDf{r_!bCIJ0cp3WSe3A8+ygT;k3b^{5W1TjjFZA&Z_{c!bl&-`iAaYjl%Kp zGZ-b!$fQQjLrYqyt7w9``nlLSh+1dwMwm$xCCT|fGB_xPWfrtc=fDn+uHNJf_#Od8 z?MVdsyuN2trRt|9(hWEsUiehWaWpt!=}5fe1WZ~BYp&=sXI$f|EZj-7r7IdMN4HEO z#4)sj57~J|zpdRJLv$9k^daP(HeJ=i%`RjJpbUVO4voB^yLbnCD#!RbZ0Q()Ty0U; zKz1|BT=}&qe$rVkxma1jNs5Qv1Uqjm4ohh5{-T&EiVU)Mr^^QnCj%DkUpo?98C0&K zxaq+?@5;(`@?|zXs|lBZ;O``eYoNmneDt%%LRyj;g-PhMQxf_=@>>+wSF#(2{(wThnQ@OGbJx|5&?*`<^v)P3`N)^H0% zWlZam;MgI0E=9I~N`bt#lA6uY`?^w(N%&w1^Z#(kF5y8%gBgwGsi6Cp;CP8?c%eOq zK4i76V(UVMc;Q$*24LOh8Z$z3$80iKaA#sE(g!nJ^|)tJ8|hjjoUlWZ4rmiIUOjHx zA)C}G9qn7MYjZ8OYoQ<#uXT7)tO5_Rc0eV?2m-d%c9(rLH0#)vdLCNKLb6x}zat>y zP5BX9Lri{xzJAZ=(pABy3q`pRL9#vz$IMiYDIAc@$MR(U zvhpf!QMKCDITkIx=;+A3W4tly0O#Aw*cr>K|K16YLeHu4Gl?1H(3<>LpGe*p2hMZU zm3ClNR?Au!RvI|XmNMbz#@e>)F6K;=MSD7TSYrJ7q3|eu#@RTr@Ibgdgk-z>HKv)K zgmOK0!(&Evm;>a$LZ98``#x!!#;bRuT3Fv$vIr3_WpJmRhCdpbzTWPh zG(UCIhoPF&=?IDnoDNf^50V<=zEtYEH|=x)$6K|ZGm;-!bqyGJn-sa4ReQpmA#u}d zLW{Aa%N>=SPP#5D23Aigk6%Et_8!k%;9pq1>b{69KhJngV?~7bp3I4>s(&YOJRZ)!aVs7TOzhB?fK8F362lAG; z%A=iV+Z{&Ny2LFR_`NgQT~%2F9*VMQKQ%oLD$E#*8q1jSojkB?SD*ivSdIZQHhO+qP}nwr$(iUe;d5yPxx& zuj))Hbzjrz=_G%WR841kr|MO|qF}E9sn-W6#au}rp~zWtr`QCotcsHgpLN0Vn))a-7^bUZlVCpt4e z2Zl|h8Li~LuFl9TfTg*K>}(+s^Aj#(%8)*1<=EQORPCOZOKd2A@f9W!~Q_p9`f&rsyzI|O7*H>+tPdv^NE)`hYhzo8rps~ zB`YBkDE}>=N2jnJj@gQg4shsg^XLqO{baQ3(FJpfhHB|6e&VQ6@g-i{#b1n0{%exR z4ZJazTTS*Kyb!^WZy}F!1U_AAsKq}f&dVp@6~_W77BEg~mnwO|+kDnKDvZyuHXGxJ znhQ5d?3zDP6g8D2mYghdcSs;I``a*eGT68)HN;jvK z_lIdks7=Ydw`8Dz7~%Bo9Kuq2*l=aAO8%w}8Xzf&o6ew91h)U(w^h8p9Wrx}%C##H zo14xBxr_923G6xwXgk-Sd>tUzOvoY;)Dm1ug46x`Fyd=Q&CXf)&B?D6V9IkCrur`- zJ)-%>S{s?su!iL*e^U;*6t%o&kp)!1D^0LeMrn+Dq|3<9!muK*g;}iggJ{JQT{Pv6 z9E(P|4Fmwjl1v|M;qK7@gXw@c)&cH_mBJbsVfjf;7#qi!{9-)O%*wh&BalPUJAYML z{?(5JKro!R>d~6S;bzo2N0@0VSbRr()7_@}aC^~4!h7$XQ_w?|*0;|m_se~JhLM)| z^eT0~90fM)Hj}z*LEHWoNn<~Zfz3}caADV1awosY;V>UI1R1s)-i~+VxshGgzP15^ z+6k}m6hN=iyf+-L-YhTR-6QZ+2wsE#ySgb-?lx#sKqYC2eUS&6i6u8W_Q;c?=ND*1 z4O(KN9+!#f+V#G36MlD?-BGgtz;{%}O)V^F#0%V`X}`LPS%<25s->%4nF-Qd#=6?| zTXP62j((GwU5MlN9jeiaa>cP_8C_&fr+%fWu~#z2zi zt6!YFDH-33&BNjpub@jD2r>T9IA|{`ej|a6#E1@3_gQ4+Eb)v&(x7&Dk2(cVEn*MW zSqVK@Sy;7cqN^o!S)~g0285P*tY+Y0sS-F))d80b>p>1;w0%2#o^Za>Oi@&Uqz zb1QnchZ+sebK!L5VkpB55z@@_#JBcAT=Q_*_eULi9TxeBs{~USvo8dfC`cg}Y&Jc! zHPJkxXOj)=X7s5gN5GARH@8#ombyQoEz(X&95nC&mq)nbE3fs z({_1gu8$u4uXQ>9D+wIk#VOy~lgid0)W@7}va?!hXbBy6eF99m$h(|GN|m!f=lx=D zxGVJ72qa;KLqeaAPe;$ebQXwn=zu3i_8OgcY>^nay{%7eibPuP!al|maAg8i*%IYd zYVBiw?`h@mVD3KHU*PDo!-80<%#dE{6}n2BN*At8O5D zDb7|`Nd!pL;x55V^yLayVy>t=>ya+}#e$po^Y=)TSi9Jlm7ykL6M%)cCDv_CL(0ta zmsOk&f$XS*XYNkls|&O_)j1&-om5!O7IXatE^XnwX6vc@Y$@2)7HRkMp(E(3>*95m zvG0R)Gfj?{I{vG$O}Cij2PJUto}~G`Rf*C9Doe;darKQ!3t{5i3bv8(C;&&5njJ72{CRrTp7{fMt%pD~#%icli|6VkE6Kh> zLyMVR{}Mk9G%IkwD@wBf$yD9y99p$E)buFoH48&7ij~vK`jSV(lYlbhoaRPN2$EN> z^$Y^x#5Go9C@%KBMoO^q66i^d8f;cB1_!D_7*+-SeRo=R8DQ4XY0G}NeSr?X0h@P1 zDN&wc33QWym6Pb2iZ$#wm1G#IfsV(1MG|fV_7k(?iYbB}mRFQ{At0a}_<0nCuH!`> zRw(h$9AOVGIqwSx$$>u#I>q<5Jts?75uNnwuw<8IIZXyd>E(2eh5kuLm$PaIFprV9 z@r#)@ffXPqkH@J*!=4wun+m>2TNdWqF;db`+In4u|n$;{HDm6A-A ztr4<4-a1COY@_&}v6u8*gyqM%W3)|I$X2vtm!I-oGk6cTYy!IN+hg%px^~zr%LBnb zWNR#$@yn$5F}M?kBVpIE$3<6^L_KNzT{s^j(QU0M{3(_fsQZQ1vLFZQ0qL2|6o6Nn zDK8Yz8XS-(^KeSX4|l;6W?)v~a4Q)xaZ#(l>9^ip0_JnM*=Z{UjEpE23Ft!j_IX`q z;XV!xI}BYO&qhb9T!fHz0hLy-@S`H7vH-o@KrLASHM9FcC;Gu>*`#4`29n@=VZ+kS!L_~DC> zY7cv#b`|RQ>1Ha8bfak%@X2pUo2{1v;`G)Xo{@39f{Hp_cMCm#*ts2g&}q+(LoeP` z4wre2Ku4=Cs+idgy`t3o024w#PSL;(!?!*A`Z4%>&kE!v{?HFe(j7BAds>y5=Cw{V4RiCYo zmHoYrP!9Wvjl`UZee}R+kUC<`!d|6w6W|c1*s=8()b5Mv$SnkM-2Ijb-~VbjodW+*IuLx-Ru3sM@wr?~w#T zi8Mzoh{A}kzo9WiLB3z(7#?Qgkj=vjh9R>m(`L7@!z`R)gzC5Q@%q%zYJn~{Z2B=?tS!De5F8J%piUtUG!Ma9bF%0tk9n|sU+Hm zpH36weS0mHHHMX*-_R*!{wDv*^-~a>U&gC@9SkHYwuFmv${5I+uXJnxOw#M>d1-yL z<$}4$3Y)&*|DF2@=AzVkR|n8Xt(D<)m$sS|vz`c)3ud-S+v-yR;Td67*35isO@z ztRMX8hImkN5?-fZ5lfD(N{TreV#M}Ou|Lh@#1j=L9&~F$`o;V18%YG67s@wr65r(6 zYDbkB><9i~Shyb$%b5v^4y06P0xIy7k^r+oqBG6|F-^3lz4q`Oli9VnJhG9WK9|(e zIPR_oog$*zQ#>$2Z8gXk4rt7#v$K|t*Y7D!x0u{PnwP#TiU*Ltw57qVjzD=dylR}^ z1B2VE<+%>dZ&~Ak`J>`8?UiDo#TF(bAT$gJEtxOA@se_1zQjgEp^?aM@Wgf2f8t6S z!7l&q3#b#Vd8oJPCa8-hw62mYE4^VUHMBF9f&g1FoB+1)E{bEtJ^`8%c-?F~ zNrEAag8jPH!Zcc3haN{3817W0!Atv;jkuWdJv+rjT(XGeHtc`~9RHLjb?|>A6_#c# z%zEcUdmXP*__+1va!2yIrG(33mGrRHV|f5rba8^Rp+EFT%aQP@0G11<&Rl*I&Q9{9 z$rJpI6KIHM*;JE7)1^r^EJCPRkSiT1oB~n!58E{R76;F{O!{%%*B& z$jI$Upat?BU3K~_@fE?k1h!O~^lk95h+UZVKy*ad2y(&q%LQQWDDak>XrW#0; z)iE=!8O!zOuQb1NrXx8Jlt(tnln80U0){o{LH+yLPk4U4quIruWGvrH5eKj*s@Cai z!VE?E7@-Se3pz0LxloSlZ(st2x6?fj?#n9zG7 z4Nfu3amIDN0!7F9SHVVlKRg*Y?}Qs6Bgdrg(2s!-qz(&j=hIH04;I7=TS>+>D_!(I zv3P7J?PB6r>|O~~S;zHUc)z$mptX@+l36toyb1$y)p{%rz2N3-9JiDbE>V@#{Ij!$lY0YE7L1%yiBX7-kZ8?uv$0b?V&Ge69#Z=0KN)9!I72Q;+3ZWEUM2sbBXu+ zk8g6Z{rr%~-x<+k97d84nTs}C_FXZe_?{)}JE2xtzmpi;KJKnhfZ@WwLq3B2GksKQ zSN&ARW-3IPM^?2*v!)v^f~Xc+HB1MAj0LJzX5nWm;VTK(5*h4E=qW<;znKWaHg%jQ zrbM};$!#3S%%12k@_hV4pIma%&UCo)Zmxf3`2{G<LtA2xFwe6~ymylkM5&Q!1ChQ;FO9a1nig~v zT=W_N`c^$aft6-?e{uG(+yeA>UC;r_15bP77R#^M^r;hECs6qzMd&@Iyy6`0=EeuO zpwB^plrsg9-y|oVA9yGODJYQ?3?PEGZ@!CKw((tA} z8_1o9znv2K^?M;k78Fx7oB&+q?2y(}*;$fmbX)Jn88X8>>YGZkJifW(u50ZV1J%t% zs^rBYE@#y)Aj|+?V;KjUdNG2?TFL@1D>FT-N9a`44aCUVlB51M z?V6+2z;6De+Y;9b{<`NoVgv9|8CWk{y4(hl>380!TzTELV2U?5_bJ~5+Y;--TU?-LJLY9b}o|J2$cQmbDVUmCT%b#IVsEjX2UjdB86 z-$&oG=+9nu(u&I~t%G3JTTr~e+G>ly;Uqaw8V;b2wx?aUl0h6FvLS!Rkj_>A#vDdv z!LHKFt!L!OG71Em+B`jebHCbpS6+~4T~;+3D#-v=L6HR9AgYmN$BCUS*TkP@4m1e? zsS>^H_83AK^MXNTP#Qg~4;tMettry^S3?;##AbOXd$1Q-xKa^2I&0&wdT6vl3eY#Z z{3$kYMBJ5Xg)-(m&0j)2^=-_9_vJMv$!x#_xQ-HY@@l&8xJ%vBKpHTR4Gj>_Q=UY5 z7%r3;Ex``+Kjv??A0f?(0jVw*WZ3OHT3n1Kz)AHu2@(79!f7gS$wjE!iSN9W1hoE_ z7X+ReBh3}U@j>kwI1&Y{F-E0C=X)yU!23seqs(8SuThT!&V@~8>G9_)vGB-gs!&Vs znH1QI8oAgf_%$OAJ6RZpLcZdS)|=PV`zW1Mye?GA%ITlz&nsDjls8G75U)yw(3cI%*QEZght=MvUHd6}oi@1ev9$U=FMC4(&1)PgY<4)8N zD}ALvb%^ua#h}EL(UG+ zhJ*bDq0vax95Ddbfm`jDXLSPDxH>4RWe$AG#8nJgGaNYGP6x`mOKTybul@lug3t)u z@b>rL3U&c|Nch^a?)wqFQ$YT-DA4>)&91X-y3gJ4H&gZLH_vR;rm&iRixls)`~f^! z8$9z-`x4vNdoRB4-*Dg>PUCNii{w2hwiG}WgIt9B`a;#*=sPejyl@6 zKMv*$P!9o2#sMvRZc3s!B%iVyT@soUv31O@GYbN2WSd5ijAyl&-W6i)o2gna2K8?3 zi5q7Qf5B2Cfn0krB(KO9`oN;=^x37u=Z zW7Pgyl;Zx4-sk7nI4&N`9xm(HRb+X@zUAq5E!$-32ccp;Wsub$i8r6m)8Z(jU4=tY zNj!S1jfG_)y5cNRqV=ku2unB2nsKHkMB)x8PC_Y`kX##qlZKa?yB`iT@|Hbiwh=}Q z(5F<7kBL>H=x@?bY$>;~&?1{4mSya%kghq!J%LWhzwaH8 zjZqYo!IpOA5VwqoBMJW!y{*iNxKbC9&Bkiw==95+Kblb1cv>6ix8Bu*^v!B|6jY}% zuuP3{Hhdp05nSY2mQXvR-#sgE>?jyirL&P3)V});&L&zORghN;_h9t) zxRv>dt3$~86_SmNA-R1VBed4;zP=qqcOcE1U6Gq&;h-o=ZF$5=fF##Rx$wGYdE~_0 z0@c2kDVR<;mNAH_bQ&b~mZ2Uzg0tFgtcgb)dOK!s_Dfuh6s*AaZu z+(ukLE3I%o0mQ;{M)#47Tmm8z1^UYoA&d2OJ;(X@87Cn^m}#8VmLH!FKeA!h@-!jS z#r{q{mZk}9dm0Mi1hmsoWlC?wV*W@GRHP8KWRR+sW&_91_A=Z8+0n<{Lt<)4fdzFH z*$#L{&PqakKG@meq^5%_XNHI0PxB~YulcU9vqE$7%%!zwLkZVezi;rhZIVS8rGe+6x3Lb*R1?Us$09|ey@ zHUv$>Gs9B6s8f)l{+vENKqm8nC}&w43_vw?oiY!2n3k3&c=`-o%5GMdDPQ?(U`bTt z*^muX);G$-vfO*)Q%BMAM#zK0DMikKgb@J*$9DzW*pU-U;%qNOZJP%RNEy40lZ zLVEn`$r_y)js8J7D!D^s!Y#7npjtz1&IgC;z=m~0=fIVjiHom)<1gv%kvVGbzf!DoaL0a1X)H@z7iiN=1zOJ7=*i;s!75=J%lv4h^xkG+Ag%?WGE@x>~fo330cr@b9Hw}>r^euF7ltVF`dh;P`i##ve^*j z;gNz4I(~9=TCxz@+!Y1!iM@mkTJT6hxZ4t~Yl*uRy{A#N?(lxW_tDxF`a@| z^@-t^gGm*(Pa)`KuJWT%yv9RQ@Zv!NijdY^eZ8kCwm^V@?9*Xvd>3%`*k8~&rlvq! z%pW=mS!-d$NDI*zqdyYt*48wFpLYGZa4+9!(}f*c>d9c?qr3AhX&yO{K#K#4y3O34 zAPJTT0^?#=pJ>6N20WR-6-bi?B$o~8xx9^}esQQ&N))u+A3Gaa?ip!SE0!*>j7r^7 zklE#G&l{>_+aKYdlXYWjJK|j@%5Lo>-TaRNN^OAKHi|Tp(!u#D8$A1AzQAdJItU|| zy3m2F?98V#MXZOgyu??c^)>)V<)@g&(iS--mBf`Zh1tU~%oEKp#4ATDKbaKiH2+uBzu zvil`On_4+phu<4@EIS+KT0!8UvzhJy~H;HOx_{n|xg5-+}=MD%nRBpL`0!BZ4(!vI-B(RzACe0GENhPg@TNVV@^ zvh^rW5>$~)52f8!4@i{R%;c5Y2bBh3!RRCw^zTIfNq@bMFSpzD^AHDVt*+|g=Sz1> z*GJ4DgxTyI&+*+xI>e19{4TZ6TI6@0S8iF- z9b1+GDLr$u?HHG!;Gyoq7T@f}$3B-gO(_r? z$R5;KEv%?V-5Lguo9u^8M=00FmT&yd9AT*>Jm;%Vi%r&j5wfF8Z4zn{7!J2%*aP?- z_0uWP@$Ns=OEeux_yV0ZcK&67)x!%PU*C55vnn7mhGaPXta^c4(N( zn@i@zEz#9&=Ok{b7WIub7{jZNm~Qv0X0A@_jp)+NL*@=QJF-`x?T@r_JR;X!~M}$p3l89s;MWHtLqA zCT!P#O+vS7{2t!e3Lgk;YWFpfDLK?|F{@~&y>4i~8ru*{MOfYjfa)nN_<^BkmePZ; z29!uXi$_Kz;sMB$L9BUZ94tLT-+&yGaknkny2}H}PcYTQwInX1yD%{%3&abu@Jc1P zUe$;sfF83O(@I7=M;P^Bu8GI42<3XtsO)P}N2m1VDWDKZt<7u2H6mfQ3cxz@);m3@X$ng}EuKqq#N@=rz1$p1#N9 zykQvSe9Fmjd)z!=Db}FBF83n%O{p?r>|HQFa>%4ob4)%8;Hj9>b@@Q`)M z@``eJ7kxMb*`R%;w`rLt9!7n_NYA<;E{dAy)|>jn7IhPYv7;^UixG!~`Z_-RPnXT# z2wUi2GP?bWU3-glmPNBxK7Y>M?2pyTR;q_MT46$3TvP0z{lZNj*B|&Kw=;dBs+ioI zhqUj;Q0oJ%fSolM26#G>u*8vokx#eLF-3gx2<&(yy_!Ga$D}0;ADG~kzA#$nWCA=* zouSLB)Ey7UfBMg4WBw(qPuS;@%z$5KTgBCwsv}{2)Iv?!Q8}~wftJg2l;?6|aL2~! zWPTQZA@|#VNu;d-au=%Wfyf#h34mzFBSv@^@vzA=1r1)}jGCHUGQ{mfP`~m50IWmy z@mraEXNL(pM$JFfzSq*9i2|q7)DG*r)+7heBXL^TKFYhgxxs)R@O}(-w zIc+PlYy+B`v;?gfTTtfKC7CzKLt$i!5+#EDt?A!mTCKvKuEiH+BLY*Ud{KC|7AcLo zhtWK`!P*-r@%d`t)u7G18cRaeF6?irqgJkj4yvdBxg`ug%hs6T#3hhW;dvyOW!4J+ zlxIYZd?8E9(RuA`012ubI&9lEMLj~)_OJRvD|(19=#CD+ueAnmy6;30VQR0PjZ0-& zi80?v6HjguSTH@UWOg&QjPpl;uL#iKwJXUU6F-5wAO&mxef9zQ>8Q`DQo8nW#I%_qiK>&fpcY%Ta5 zo!%n9%Es8{II<%IaYA=~A=qdYVm(PVPIx8Tt3A}!=X}`OpAOLDe*C>w&O@qVtlj@k zqEBaet7K=cAV1#K~ZE}EDHu-9X8!xm`T0mq= zY%6}h=*;!1PqG7}mgjc6p^#a@eMkD0`pm^{;lOQN#|W&bAfvYm;7QYr%>hJG&?ezo zn0nSNiSMGhnab#caUsDP?q=`zYC%sBu|P_a4(1xey#tJMXJRMWY7`$DI4hGUo20C0 zMYzV5=}d*6u^i9-t&(hOuZ>O)s^}?Bqwu=#UgFm5?;miJX7YnI{t6e0rKRLY94SSH z`Z#RIUzjb7ka^9fy~_36NoU0SY&~01jCezxmD#v-O}bA!O{A>}%qvxQZLBKNX6_HA zmq$UEC|N?21}*#S=lnE8vTK_m;>q}6ugXCth}{}Q#BkU7BE&A+4a6!PC0#*6mONJ^$$5gg23NR*Qm0 zyz|(+PEqB+B^|oiYtx-FB#{Fj8k9qdAH7(IG=X4O=`V=;f}>li$pb_63;+XVOUx?+ zLmuFdl3R_oz)a!0D`A%|8{MtARUqf71Y(r2V|`jt+vOId_IwRg@x1bL;B%#lH{qNnLSq2@KLJSjHYO#h)LK{XKb%}w zX`bL;FSXhH$DuKfS#W6my2nu}in2GN9J!W;G-!q?q_h{jCd}lZO6X8Va-wj+fr;gq ziL=nXX%p+W!We^H*gEWtQAjkmQz8s=s~gO`vY9k>aoc3W--5;K3_V&%Jl-RGBQkHF zpAvG1-EaSrJqbhJ0&4{DC>=`cq6oouQIZ3P@l0fl@6Pg+fj3?;L4* zB`QvD4iR{@j|DU5Vmv2S>wIkphW zjZtJbX1boQlNX^YvZ-Jup0*wlv7rAQ?s6lJ!K&7mR4)5yo4QeM+_q{&&1yv<11H-d zy?8NgAn6Lqj%aLJE%+h|UDGT-r(fa7C|zOhU5t_*Ojoha$oV$`e5S4CNPQ(`do0EC z1>_x(hj=||g{)&voNIxLQqO}3KQ7^%x;$rRjT$+Y)_=HZ5DB9VW5;WiQsVgDHX@Sr zSdBIEf{mzdZnvWt`_201Xmz$w<9umXCTgbR@L8UHB2{-_p6l3 z9=cIm5wp)p6}&Gtr#lpF#qr>K3)p<@TJlO#bda}Bul@-}Z~7T2L3f2&+nC9~I=TRcbZY0^ya z(&*6Hu|{Ns@E3L_q=;VeBD?k>ZNTwtVShSZxyCF@LLs@Xz3q@1#W%wU5Ep=cR*8Qh z6$LH2>`YJ2hMeUEBzYaFJ!2Ab@}nIV^{=>*?1*>;Ud~80FIbvC7`RB==2T_BhUE^p z&S!=3E>1Zj1DD0Rq;p2!0!LI?AKKDQnXq?30! ze|Q(zz2;xoj-zJpn;e#IOo*~+CIy!tmAu=7ZG?%G5zEg>dj;A)n_OZ8F@f-S7T3zDa6rtosKOWCID2s zHmGJY3S&K!2BUl2uUVL@T#g8I4S12_s}!|kEdo<&vTcAnf|y27@goo`%K-d~-tbu$netG;*UcCldZ~PZi^i_~Kn08}Uw=4J zIk#_3K(N_c5!?15pT))d*?B`vh!WF}VYoP^kAznS#DWFgg?u#_540#wnEf?XE%k?S$X&V6QYY^2p{rkM} z46@B$45p)3G0OmM_wNs%j*Rr(P|=v{isTGJ(a&kHqbk!XJ4g0ZpK4f_T5Ow_RF|M+ zSFd|jz%7Lepj4QFMVgMZ5buRO~gXBqz zkqGu;-Sh@nmmpG2`MczJpc^bc{-3u}P;Zbr!EN$UYc*5#rA@KS62y72H~e^M)h81V zxxfn|Jm7h~{`9(G#z#i;aI2id-!kmKS2-ZI_{SbSnG=RJgDA?^W;lJz)@ffAMW4T* zF-l5d)kS9+PJOMFRw>SJnw={6;Xb7}amp`eJv3F@0J&VE%v4u(jsi1yu-)xaE~|ZF zpeYjbm8KLS&6XM7zks1*m)iloBXjCPMkAp9UngXYRB$o@QHsDW0?JQCjCE~8lZWLijWEgSXD(^Ehlc{Wxpeh^He8__r%RweB*xLJEyO_!8yLm8BG*&& zL%iiQTuQ#wf7EjN{QQv9q}5XsjHQXG^%3osCd<4Y$;(9uPp_6xxJ{|lb*&P?@B|i> zJM4q-3jj~&t+x5KnKowbpccS&ij+htr2YHr@qO_ID~8VzT(Z~_nLW7*vbG(PP`X;W z#UI~lxvFlKJa)k*BFiJ12 zgu;YZm+;5wpt>xDyQBKFIf&Lu>K3J?r{kL_fw8#~4fpdH>E&r3k{48OFAyacO$II- z+ArIAZ)VIWcbrhHOoJo18B^0_ATXb#k_%LZHeY?T|69xK#18Fzu}x(h8lGRa@~(;S zW;QM1A`}b1DQ*&r^x3qJ!#D{aOi(9fzfY_LA|)qfdy2b(!Y$;vFo5H&as87%GNbgD zfv&gx1orND0unte5s~f><-y6cwfy_h_9c>ypn_q=3Tca`c%7jvg3k>kfW$butujd1 zZZ^;Mt4P@jXuEf2z4pgmVcUXrC+>}Z<-4;AO#?js)}%rCf^m|0>;&#J?-e!qF_fR# zG1&+v&s_w3KQqL?40O0}0&lV!nw`O>Dloa{GOYEDm7fJ+n)WWHt8Ar{f8`icX%gjY z;;qzCls=HVv!O#ecl)W9A4A1xnvrM$9HP1(CKm& zPVO#9DG`BI zE)(56J!+$+HpET&QtN9y&2Y(}W(CaqhmeAoj*k}Cbs}}R#$Enb=-d@FUDHec-@H|Y zAml?g_<6_3DGoZjg7ZhHxqcH&7VvRw@>vb%EkUQ=Pf+Z97=e_7F5FsXRP}kvqtjztF^$`jM%-{h{<#5BR~R=qVR0w z+R*0)5gVEUtyT%+5B|VFUqN+a7bc23ROe3hm_^2e>xO_OqaFd6wCz+~dJwC-Q=ZsqkrCXgnbnbQY{1R< zuWl^>VABm?KmhRO0z!WU0Mr-Ie>MP3E#!YkfB^vialpSF@c;0?Dz^Z@|7=_C006-M zYqyO6008@6{x1U5|MGw1;Q)aENdGB;2LJ#fAjtp3g!tD-`aeFX|I7cyMf+b4_{Zq~ z>%Z}@(k>Pt=>LqJ`gdOJ|K+8BjPrluQ2d`bl>hkuyBk0NzJLDzF~YxV;Qz;9|M=kF z*#Fw^zedPD2KN6e1i*jWFXVdhe-{5kFO~oFcV5{Q^FQ@BGb8&?? Date: Mon, 5 Sep 2022 11:05:18 +0200 Subject: [PATCH 021/239] compile libtiff with liblzma on windows --- .github/workflows/test-windows.yml | 4 +++ winbuild/build_prepare.py | 41 +++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index ba72a7018..23d907806 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -86,6 +86,10 @@ jobs: if: steps.build-cache.outputs.cache-hit != 'true' run: "& winbuild\\build\\build_dep_zlib.cmd" + - name: Build dependencies / xz + if: steps.build-cache.outputs.cache-hit != 'true' + run: "& winbuild\\build\\build_dep_xz.cmd" + - name: Build dependencies / LibTiff if: steps.build-cache.outputs.cache-hit != 'true' run: "& winbuild\\build\\build_dep_libtiff.cmd" diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index e878f4c8e..0ac9d3c81 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -151,11 +151,46 @@ deps = { "headers": [r"z*.h"], "libs": [r"*.lib"], }, + "xz": { + "url": SF_PROJECTS + "/lzmautils/files/xz-5.2.6.tar.gz/download", + "filename": "xz-5.2.6.tar.gz", + "dir": "xz-5.2.6", + "license": "COPYING", + "patch": { + r"src\liblzma\api\lzma.h": { + "#ifndef LZMA_API_IMPORT": "#ifndef LZMA_API_IMPORT\n#define LZMA_API_STATIC", # noqa: E501 + }, + r"windows\vs2019\liblzma.vcxproj": { + # retarget to default toolset (selected by vcvarsall.bat) + "v142": "$(DefaultPlatformToolset)", # noqa: E501 + # retarget to latest (selected by vcvarsall.bat) + "10.0": "$(WindowsSDKVersion)", # noqa: E501 + }, + }, + "build": [ + cmd_msbuild(r"windows\vs2019\liblzma.vcxproj", "Release", "Clean"), + cmd_msbuild(r"windows\vs2019\liblzma.vcxproj", "Release", "Build"), + cmd_mkdir(r"{inc_dir}\lzma"), + cmd_copy(r"src\liblzma\api\lzma\*.h", r"{inc_dir}\lzma"), + ], + "headers": [r"src\liblzma\api\lzma.h"], + "libs": [r"windows\vs2019\Release\{msbuild_arch}\liblzma\liblzma.lib"], + }, "libtiff": { "url": "https://download.osgeo.org/libtiff/tiff-4.4.0.tar.gz", "filename": "tiff-4.4.0.tar.gz", "dir": "tiff-4.4.0", "license": "COPYRIGHT", + "patch": { + r"cmake\LZMACodec.cmake": { + # fix typo + "${{LZMA_FOUND}}": "${{LIBLZMA_FOUND}}", + }, + r"libtiff\tif_lzma.c": { + # link against liblzma.lib + "#ifdef LZMA_SUPPORT": '#ifdef LZMA_SUPPORT\n#pragma comment(lib, "liblzma.lib")', # noqa: E501 + }, + }, "build": [ cmd_cmake("-DBUILD_SHARED_LIBS:BOOL=OFF"), cmd_nmake(target="clean"), @@ -216,7 +251,7 @@ deps = { "": "zlib.lib;libpng16.lib", # noqa: E501 }, r"src/autofit/afshaper.c": { - # link against harfbuzz.lib once it becomes available + # link against harfbuzz.lib "#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ": '#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ\n#pragma comment(lib, "harfbuzz.lib")', # noqa: E501 }, }, @@ -423,8 +458,8 @@ def write_script(name, lines): name = os.path.join(build_dir, name) lines = [line.format(**prefs) for line in lines] print("Writing " + name) - with open(name, "w") as f: - f.write("\n\r".join(lines)) + with open(name, "w", newline="") as f: + f.write(os.linesep.join(lines)) if verbose: for line in lines: print(" " + line) From a7df33551ccffc6aea7ba4c65ccc4f769e1eaede Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 5 Sep 2022 11:58:12 +0200 Subject: [PATCH 022/239] libopenjpeg has no dependencies, skip searching for dependencies of openjpeg binaries on windows --- winbuild/build_prepare.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 0ac9d3c81..9772883d3 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -300,7 +300,7 @@ deps = { "dir": "openjpeg-2.5.0", "license": "LICENSE", "build": [ - cmd_cmake(("-DBUILD_THIRDPARTY:BOOL=OFF", "-DBUILD_SHARED_LIBS:BOOL=OFF")), + cmd_cmake(("-DBUILD_CODEC:BOOL=OFF", "-DBUILD_SHARED_LIBS:BOOL=OFF")), cmd_nmake(target="clean"), cmd_nmake(target="openjp2"), cmd_mkdir(r"{inc_dir}\openjpeg-2.5.0"), From ffedfe034a50318e579a5d389537b65f91f3b209 Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 5 Sep 2022 11:58:42 +0200 Subject: [PATCH 023/239] test libtiff with webp compression --- Tests/images/hopper_webp.png | Bin 0 -> 27598 bytes Tests/images/hopper_webp.tif | Bin 0 -> 3651 bytes Tests/test_file_libtiff.py | 13 +++++++++++++ 3 files changed, 13 insertions(+) create mode 100644 Tests/images/hopper_webp.png create mode 100644 Tests/images/hopper_webp.tif diff --git a/Tests/images/hopper_webp.png b/Tests/images/hopper_webp.png new file mode 100644 index 0000000000000000000000000000000000000000..94b927ac24a337fd22d74cededf764dfeed026ef GIT binary patch literal 27598 zcmV*bKvchpP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 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 Date: Mon, 5 Sep 2022 12:48:42 +0200 Subject: [PATCH 024/239] compile libtiff with webp on windows --- .github/workflows/test-windows.yml | 8 +++--- winbuild/build_prepare.py | 43 ++++++++++++++++++------------ 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 23d907806..e6452ceb4 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -90,14 +90,14 @@ jobs: if: steps.build-cache.outputs.cache-hit != 'true' run: "& winbuild\\build\\build_dep_xz.cmd" - - name: Build dependencies / LibTiff - if: steps.build-cache.outputs.cache-hit != 'true' - run: "& winbuild\\build\\build_dep_libtiff.cmd" - - name: Build dependencies / WebP if: steps.build-cache.outputs.cache-hit != 'true' run: "& winbuild\\build\\build_dep_libwebp.cmd" + - name: Build dependencies / LibTiff + if: steps.build-cache.outputs.cache-hit != 'true' + run: "& winbuild\\build\\build_dep_libtiff.cmd" + # for FreeType CBDT/SBIX font support - name: Build dependencies / libpng if: steps.build-cache.outputs.cache-hit != 'true' diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 9772883d3..dfcbbb979 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -176,6 +176,28 @@ deps = { "headers": [r"src\liblzma\api\lzma.h"], "libs": [r"windows\vs2019\Release\{msbuild_arch}\liblzma\liblzma.lib"], }, + "libwebp": { + "url": "http://downloads.webmproject.org/releases/webp/libwebp-1.2.4.tar.gz", + "filename": "libwebp-1.2.4.tar.gz", + "dir": "libwebp-1.2.4", + "license": "COPYING", + "build": [ + cmd_rmdir(r"output\release-static"), # clean + cmd_nmake( + "Makefile.vc", + "all", + [ + "CFG=release-static", + "OBJDIR=output", + "ARCH={architecture}", + "LIBWEBP_BASENAME=webp", + ], + ), + cmd_mkdir(r"{inc_dir}\webp"), + cmd_copy(r"src\webp\*.h", r"{inc_dir}\webp"), + ], + "libs": [r"output\release-static\{architecture}\lib\*.lib"], + }, "libtiff": { "url": "https://download.osgeo.org/libtiff/tiff-4.4.0.tar.gz", "filename": "tiff-4.4.0.tar.gz", @@ -190,6 +212,10 @@ deps = { # link against liblzma.lib "#ifdef LZMA_SUPPORT": '#ifdef LZMA_SUPPORT\n#pragma comment(lib, "liblzma.lib")', # noqa: E501 }, + r"libtiff\tif_webp.c": { + # link against webp.lib + "#ifdef WEBP_SUPPORT": '#ifdef WEBP_SUPPORT\n#pragma comment(lib, "webp.lib")', # noqa: E501 + }, }, "build": [ cmd_cmake("-DBUILD_SHARED_LIBS:BOOL=OFF"), @@ -200,23 +226,6 @@ deps = { "libs": [r"libtiff\*.lib"], # "bins": [r"libtiff\*.dll"], }, - "libwebp": { - "url": "http://downloads.webmproject.org/releases/webp/libwebp-1.2.4.tar.gz", - "filename": "libwebp-1.2.4.tar.gz", - "dir": "libwebp-1.2.4", - "license": "COPYING", - "build": [ - cmd_rmdir(r"output\release-static"), # clean - cmd_nmake( - "Makefile.vc", - "all", - ["CFG=release-static", "OBJDIR=output", "ARCH={architecture}"], - ), - cmd_mkdir(r"{inc_dir}\webp"), - cmd_copy(r"src\webp\*.h", r"{inc_dir}\webp"), - ], - "libs": [r"output\release-static\{architecture}\lib\*.lib"], - }, "libpng": { "url": SF_PROJECTS + "/libpng/files/libpng16/1.6.37/lpng1637.zip/download", "filename": "lpng1637.zip", From b22c66eeb80309bd14c5f3b2d802ab7a8b66e874 Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 5 Sep 2022 13:44:12 +0200 Subject: [PATCH 025/239] skip libtiif webp test when libtiff is too old --- Tests/test_file_libtiff.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index e32ce87af..f1d290a32 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -3,6 +3,7 @@ import io import itertools import os import re +import sys from collections import namedtuple import pytest @@ -844,6 +845,8 @@ class TestFileLibTiff(LibTiffTestCase): captured = capfd.readouterr() if "LZMA compression support is not configured" in captured.err: pytest.skip("LZMA compression support is not configured") + sys.stdout.write(captured.out) + sys.stderr.write(captured.err) raise def test_webp(self, capfd): @@ -857,6 +860,15 @@ class TestFileLibTiff(LibTiffTestCase): captured = capfd.readouterr() if "WEBP compression support is not configured" in captured.err: pytest.skip("WEBP compression support is not configured") + if ( + "Compression scheme 50001 strip decoding is not implemented" + in captured.err + ): + pytest.skip( + "Compression scheme 50001 strip decoding is not implemented" + ) + sys.stdout.write(captured.out) + sys.stderr.write(captured.err) raise def test_lzw(self): From 8b1f92a7567a9c8a55b0c6809e072c277b869f5f Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 5 Sep 2022 14:58:41 +0200 Subject: [PATCH 026/239] restore py_vcruntime_redist --- winbuild/build_prepare.py | 1 + 1 file changed, 1 insertion(+) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index dfcbbb979..2dafb3d18 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -553,6 +553,7 @@ def build_pillow(): cmd_cd("{pillow_dir}"), *prefs["header"], cmd_set("DISTUTILS_USE_SDK", "1"), # use same compiler to build Pillow + cmd_set("py_vcruntime_redist", "true"), # always use /MD, never /MT r'"{python_dir}\{python_exe}" setup.py build_ext --vendor-raqm --vendor-fribidi %*', # noqa: E501 ] From 17e5f1eb3b6b68a1775064194a85f3a562c5faca Mon Sep 17 00:00:00 2001 From: nulano Date: Wed, 7 Sep 2022 19:31:31 +0200 Subject: [PATCH 027/239] add recommended build flag for webp set to the default value --- winbuild/build_prepare.py | 1 + 1 file changed, 1 insertion(+) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 2dafb3d18..104d52ac5 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -188,6 +188,7 @@ deps = { "all", [ "CFG=release-static", + "RTLIBCFG=dynamic", "OBJDIR=output", "ARCH={architecture}", "LIBWEBP_BASENAME=webp", From 2f95e49b3659203c4e8d166280f5807f51a64306 Mon Sep 17 00:00:00 2001 From: nulano Date: Wed, 7 Sep 2022 19:34:34 +0200 Subject: [PATCH 028/239] add test using woff2 font with freetype --- Tests/fonts/LICENSE.txt | 1 + Tests/fonts/OpenSans.woff2 | Bin 0 -> 16740 bytes Tests/images/test_woff2.png | Bin 0 -> 6724 bytes Tests/test_imagefont.py | 18 ++++++++++++++++++ 4 files changed, 19 insertions(+) create mode 100644 Tests/fonts/OpenSans.woff2 create mode 100644 Tests/images/test_woff2.png diff --git a/Tests/fonts/LICENSE.txt b/Tests/fonts/LICENSE.txt index 104ff677c..da559b3d3 100644 --- a/Tests/fonts/LICENSE.txt +++ b/Tests/fonts/LICENSE.txt @@ -8,6 +8,7 @@ TINY5x3GX.ttf, from http://velvetyne.fr/fonts/tiny ArefRuqaa-Regular.ttf, from https://github.com/google/fonts/tree/master/ofl/arefruqaa ter-x20b.pcf, from http://terminus-font.sourceforge.net/ BungeeColor-Regular_colr_Windows.ttf, from https://github.com/djrrb/bungee +OpenSans.woff2, from https://fonts.googleapis.com/css?family=Open+Sans All of the above fonts are published under the SIL Open Font License (OFL) v1.1 (http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL), which allows you to copy, modify, and redistribute them if you need to. diff --git a/Tests/fonts/OpenSans.woff2 b/Tests/fonts/OpenSans.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..15339ea9ccdd3bc54b25dfe54800f1c3bb730d26 GIT binary patch literal 16740 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 Qc*TNHRn$@_xohhGKUSIoPyhe` literal 0 HcmV?d00001 diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 16da87d46..b9a0f6db5 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -1064,6 +1064,24 @@ class TestImageFont: assert_image_similar_tofile(im, "Tests/images/colr_bungee_mask.png", 22) + def test_woff2(self): + try: + font = ImageFont.truetype( + "Tests/fonts/OpenSans.woff2", + size=64, + layout_engine=self.LAYOUT_ENGINE, + ) + except OSError as e: + assert str(e) in ("unimplemented feature", "unknown file format") + pytest.skip("FreeType compiled without brotli or WOFF2 support") + + im = Image.new("RGB", (350, 100), "white") + d = ImageDraw.Draw(im) + + d.text((15, 5), "OpenSans", "black", font=font) + + assert_image_similar_tofile(im, "Tests/images/test_woff2.png", 5) + def test_fill_deprecation(self): font = self.get_font() with pytest.warns(DeprecationWarning): From e9af622a2bb67548172dea903f9708e4ffd7f0fb Mon Sep 17 00:00:00 2001 From: nulano Date: Wed, 7 Sep 2022 19:59:55 +0200 Subject: [PATCH 029/239] build brotli on Windows --- .github/workflows/test-windows.yml | 5 +++++ winbuild/build_prepare.py | 18 ++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index e6452ceb4..a231bb78c 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -103,6 +103,11 @@ jobs: if: steps.build-cache.outputs.cache-hit != 'true' run: "& winbuild\\build\\build_dep_libpng.cmd" + # for FreeType WOFF2 font support + - name: Build dependencies / brotli + if: steps.build-cache.outputs.cache-hit != 'true' + run: "& winbuild\\build\\build_dep_brotli.cmd" + - name: Build dependencies / FreeType if: steps.build-cache.outputs.cache-hit != 'true' run: "& winbuild\\build\\build_dep_freetype.cmd" diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 104d52ac5..e289027fe 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -242,6 +242,20 @@ deps = { "headers": [r"png*.h"], "libs": [r"libpng16.lib"], }, + "brotli": { + "url": "https://github.com/google/brotli/archive/refs/tags/v1.0.9.tar.gz", + "filename": "brotli-1.0.9.tar.gz", + "dir": "brotli-1.0.9", + "license": "LICENSE", + "build": [ + cmd_cmake(), + cmd_nmake(target="clean"), + cmd_nmake(target="brotlicommon-static"), + cmd_nmake(target="brotlidec-static"), + cmd_xcopy(r"c\include", "{inc_dir}"), + ], + "libs": ["*.lib"], + }, "freetype": { "url": "https://download.savannah.gnu.org/releases/freetype/freetype-2.12.1.tar.gz", # noqa: E501 "filename": "freetype-2.12.1.tar.gz", @@ -255,10 +269,10 @@ deps = { '': '\n $(WindowsSDKVersion)', # noqa: E501 }, r"builds\windows\vc2010\freetype.user.props": { - "": "FT_CONFIG_OPTION_SYSTEM_ZLIB;FT_CONFIG_OPTION_USE_PNG;FT_CONFIG_OPTION_USE_HARFBUZZ", # noqa: E501 + "": "FT_CONFIG_OPTION_SYSTEM_ZLIB;FT_CONFIG_OPTION_USE_PNG;FT_CONFIG_OPTION_USE_HARFBUZZ;FT_CONFIG_OPTION_USE_BROTLI", # noqa: E501 "": r"{dir_harfbuzz}\src;{inc_dir}", # noqa: E501 "": "{lib_dir}", # noqa: E501 - "": "zlib.lib;libpng16.lib", # noqa: E501 + "": "zlib.lib;libpng16.lib;brotlicommon-static.lib;brotlidec-static.lib", # noqa: E501 }, r"src/autofit/afshaper.c": { # link against harfbuzz.lib From ae6520ccd638045d6b627264c5c85e004fc7adb5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 14 Sep 2022 00:05:23 +1000 Subject: [PATCH 030/239] Fixed pasting an L frame onto an RGB(A) GIF --- Tests/images/no_palette_after_rgb.gif | Bin 0 -> 101 bytes Tests/test_file_gif.py | 9 +++++++++ src/PIL/GifImagePlugin.py | 15 ++++++--------- 3 files changed, 15 insertions(+), 9 deletions(-) create mode 100644 Tests/images/no_palette_after_rgb.gif diff --git a/Tests/images/no_palette_after_rgb.gif b/Tests/images/no_palette_after_rgb.gif new file mode 100644 index 0000000000000000000000000000000000000000..8704c464cc4c794e9b25002569b6381c99026148 GIT binary patch literal 101 wcmZ?wbhEHbWMlwA2F0H&3}1k>4g(N?L>d_xfm|ryU}0cnVZl_yKuE$G05SRrZ2$lO literal 0 HcmV?d00001 diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 68cb8a36e..887b6b018 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -83,6 +83,15 @@ def test_l_mode_transparency(): assert im.load()[0, 0] == 128 +def test_l_mode_after_rgb(): + with Image.open("Tests/images/no_palette_after_rgb.gif") as im: + im.seek(1) + assert im.mode == "RGB" + + im.seek(2) + assert im.mode == "RGB" + + def test_strategy(): with Image.open("Tests/images/chi.gif") as im: expected_zero = im.convert("RGB") diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 2e11df54c..1b40b9ad7 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -432,16 +432,13 @@ class GifImageFile(ImageFile.ImageFile): self.mode = "RGB" self.im = self.im.convert("RGB", Image.Dither.FLOYDSTEINBERG) return - if self.mode == "P" and self._prev_im: - if self._frame_transparency is not None: - self.im.putpalettealpha(self._frame_transparency, 0) - frame_im = self.im.convert("RGBA") - else: - frame_im = self.im.convert("RGB") + if not self._prev_im: + return + if self._frame_transparency is not None: + self.im.putpalettealpha(self._frame_transparency, 0) + frame_im = self.im.convert("RGBA") else: - if not self._prev_im: - return - frame_im = self.im + frame_im = self.im.convert("RGB") frame_im = self._crop(frame_im, self.dispose_extent) self.im = self._prev_im From 50ba43ac4f13257aacf153462c7a9e49f2135220 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 14 Sep 2022 21:01:58 +1000 Subject: [PATCH 031/239] Pad IM palette to 768 bytes when saving --- Tests/test_file_im.py | 12 ++++++++++++ src/PIL/ImImagePlugin.py | 8 +++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_im.py b/Tests/test_file_im.py index e458a197c..5cf93713b 100644 --- a/Tests/test_file_im.py +++ b/Tests/test_file_im.py @@ -86,6 +86,18 @@ def test_roundtrip(mode, tmp_path): assert_image_equal_tofile(im, out) +def test_small_palette(tmp_path): + im = Image.new("P", (1, 1)) + colors = [0, 1, 2] + im.putpalette(colors) + + out = str(tmp_path / "temp.im") + im.save(out) + + with Image.open(out) as reloaded: + assert reloaded.getpalette() == colors + [0] * 765 + + def test_save_unsupported_mode(tmp_path): out = str(tmp_path / "temp.im") im = hopper("HSV") diff --git a/src/PIL/ImImagePlugin.py b/src/PIL/ImImagePlugin.py index 78ccfb9cf..31b0ff469 100644 --- a/src/PIL/ImImagePlugin.py +++ b/src/PIL/ImImagePlugin.py @@ -352,7 +352,13 @@ def _save(im, fp, filename): fp.write(b"Lut: 1\r\n") fp.write(b"\000" * (511 - fp.tell()) + b"\032") if im.mode in ["P", "PA"]: - fp.write(im.im.getpalette("RGB", "RGB;L")) # 768 bytes + im_palette = im.im.getpalette("RGB", "RGB;L") + colors = len(im_palette) // 3 + palette = b"" + for i in range(3): + palette += im_palette[colors * i : colors * (i + 1)] + palette += b"\x00" * (256 - colors) + fp.write(palette) # 768 bytes ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, -1))]) From 16d04f4a49b7f4c9d297e12a7fd41b7f5780850a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 15 Sep 2022 21:25:40 +1000 Subject: [PATCH 032/239] Removed EXIF prefix when saving --- Tests/test_file_webp_metadata.py | 4 +--- src/PIL/WebPImagePlugin.py | 4 +++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/test_file_webp_metadata.py b/Tests/test_file_webp_metadata.py index e6d6fc63f..f77a245c0 100644 --- a/Tests/test_file_webp_metadata.py +++ b/Tests/test_file_webp_metadata.py @@ -55,9 +55,7 @@ def test_write_exif_metadata(): test_buffer.seek(0) with Image.open(test_buffer) as webp_image: webp_exif = webp_image.info.get("exif", None) - assert webp_exif - if webp_exif: - assert webp_exif == expected_exif, "WebP EXIF didn't match" + assert webp_exif == expected_exif[6:], "WebP EXIF didn't match" def test_read_icc_profile(): diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index c1f4b730f..5eaeb10cc 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -311,9 +311,11 @@ def _save(im, fp, filename): lossless = im.encoderinfo.get("lossless", False) quality = im.encoderinfo.get("quality", 80) icc_profile = im.encoderinfo.get("icc_profile") or "" - exif = im.encoderinfo.get("exif", "") + exif = im.encoderinfo.get("exif", b"") if isinstance(exif, Image.Exif): exif = exif.tobytes() + if exif.startswith(b"Exif\x00\x00"): + exif = exif[6:] xmp = im.encoderinfo.get("xmp", "") method = im.encoderinfo.get("method", 4) From 6663ed929b41c3c89ec1469b78fbf6c36af487cc Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 17 Sep 2022 17:56:36 +1000 Subject: [PATCH 033/239] If first frame has transparency for RGB_ALWAYS, use RGBA --- Tests/test_file_gif.py | 17 ++++++++++++----- src/PIL/GifImagePlugin.py | 24 +++++++++++++++--------- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 68cb8a36e..0f7f3814b 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -84,17 +84,24 @@ def test_l_mode_transparency(): def test_strategy(): + with Image.open("Tests/images/iss634.gif") as im: + expected_rgb_always = im.convert("RGB") + with Image.open("Tests/images/chi.gif") as im: - expected_zero = im.convert("RGB") + expected_rgb_always_rgba = im.convert("RGBA") im.seek(1) - expected_one = im.convert("RGB") + expected_different = im.convert("RGB") try: GifImagePlugin.LOADING_STRATEGY = GifImagePlugin.LoadingStrategy.RGB_ALWAYS - with Image.open("Tests/images/chi.gif") as im: + with Image.open("Tests/images/iss634.gif") as im: assert im.mode == "RGB" - assert_image_equal(im, expected_zero) + assert_image_equal(im, expected_rgb_always) + + with Image.open("Tests/images/chi.gif") as im: + assert im.mode == "RGBA" + assert_image_equal(im, expected_rgb_always_rgba) GifImagePlugin.LOADING_STRATEGY = ( GifImagePlugin.LoadingStrategy.RGB_AFTER_DIFFERENT_PALETTE_ONLY @@ -105,7 +112,7 @@ def test_strategy(): im.seek(1) assert im.mode == "P" - assert_image_equal(im.convert("RGB"), expected_one) + assert_image_equal(im.convert("RGB"), expected_different) # Change to RGB mode when a frame has an individual palette with Image.open("Tests/images/iss634.gif") as im: diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 2e11df54c..ab165dd51 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -299,11 +299,13 @@ class GifImageFile(ImageFile.ImageFile): self.im.paste(self.dispose, self.dispose_extent) self._frame_palette = palette or self.global_palette + self._frame_transparency = frame_transparency if frame == 0: if self._frame_palette: - self.mode = ( - "RGB" if LOADING_STRATEGY == LoadingStrategy.RGB_ALWAYS else "P" - ) + if LOADING_STRATEGY == LoadingStrategy.RGB_ALWAYS: + self.mode = "RGBA" if frame_transparency is not None else "RGB" + else: + self.mode = "P" else: self.mode = "L" @@ -313,7 +315,6 @@ class GifImageFile(ImageFile.ImageFile): palette = copy(self.global_palette) self.palette = palette else: - self._frame_transparency = frame_transparency if self.mode == "P": if ( LOADING_STRATEGY != LoadingStrategy.RGB_AFTER_DIFFERENT_PALETTE_ONLY @@ -386,7 +387,8 @@ class GifImageFile(ImageFile.ImageFile): transparency = -1 if frame_transparency is not None: if frame == 0: - self.info["transparency"] = frame_transparency + if LOADING_STRATEGY != LoadingStrategy.RGB_ALWAYS: + self.info["transparency"] = frame_transparency elif self.mode not in ("RGB", "RGBA"): transparency = frame_transparency self.tile = [ @@ -410,9 +412,9 @@ class GifImageFile(ImageFile.ImageFile): temp_mode = "P" if self._frame_palette else "L" self._prev_im = None if self.__frame == 0: - if "transparency" in self.info: + if self._frame_transparency is not None: self.im = Image.core.fill( - temp_mode, self.size, self.info["transparency"] + temp_mode, self.size, self._frame_transparency ) elif self.mode in ("RGB", "RGBA"): self._prev_im = self.im @@ -429,8 +431,12 @@ class GifImageFile(ImageFile.ImageFile): def load_end(self): if self.__frame == 0: if self.mode == "P" and LOADING_STRATEGY == LoadingStrategy.RGB_ALWAYS: - self.mode = "RGB" - self.im = self.im.convert("RGB", Image.Dither.FLOYDSTEINBERG) + if self._frame_transparency is not None: + self.im.putpalettealpha(self._frame_transparency, 0) + self.mode = "RGBA" + else: + self.mode = "RGB" + self.im = self.im.convert(self.mode, Image.Dither.FLOYDSTEINBERG) return if self.mode == "P" and self._prev_im: if self._frame_transparency is not None: From d02f91c6da0a7d0d7c5020b636f4c423cb6e38cd Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 17 Sep 2022 20:11:55 +1000 Subject: [PATCH 034/239] Raise a warning if NumPy will not raise an error during conversion --- Tests/test_image_array.py | 7 +++++-- src/PIL/Image.py | 24 ++++++++++++++++++------ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/Tests/test_image_array.py b/Tests/test_image_array.py index 7e5fd6fe1..ae3518e44 100644 --- a/Tests/test_image_array.py +++ b/Tests/test_image_array.py @@ -35,10 +35,13 @@ def test_toarray(): test_with_dtype(numpy.float64) test_with_dtype(numpy.uint8) - if parse_version(numpy.__version__) >= parse_version("1.23"): - with Image.open("Tests/images/truncated_jpeg.jpg") as im_truncated: + with Image.open("Tests/images/truncated_jpeg.jpg") as im_truncated: + if parse_version(numpy.__version__) >= parse_version("1.23"): with pytest.raises(OSError): numpy.array(im_truncated) + else: + with pytest.warns(UserWarning): + numpy.array(im_truncated) def test_fromarray(): diff --git a/src/PIL/Image.py b/src/PIL/Image.py index d2819e076..b2f4a3530 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -679,12 +679,24 @@ class Image: new["shape"] = shape new["typestr"] = typestr new["version"] = 3 - if self.mode == "1": - # Binary images need to be extended from bits to bytes - # See: https://github.com/python-pillow/Pillow/issues/350 - new["data"] = self.tobytes("raw", "L") - else: - new["data"] = self.tobytes() + try: + if self.mode == "1": + # Binary images need to be extended from bits to bytes + # See: https://github.com/python-pillow/Pillow/issues/350 + new["data"] = self.tobytes("raw", "L") + else: + new["data"] = self.tobytes() + except Exception as e: + if not isinstance(e, (MemoryError, RecursionError)): + try: + import numpy + from packaging.version import parse as parse_version + except ImportError: + pass + else: + if parse_version(numpy.__version__) < parse_version("1.23"): + warnings.warn(e) + raise return new def __getstate__(self): From b2b3b62be7248043da7716c0ea5e6b56f58e6c90 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 23 Sep 2022 20:06:08 +1000 Subject: [PATCH 035/239] Consider all frames when selecting mode for PNG save_all --- Tests/test_file_apng.py | 10 ++++++++++ src/PIL/PngImagePlugin.py | 41 ++++++++++++++++++++++++++++----------- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/Tests/test_file_apng.py b/Tests/test_file_apng.py index 0ff05f608..bc9b91619 100644 --- a/Tests/test_file_apng.py +++ b/Tests/test_file_apng.py @@ -648,6 +648,16 @@ def test_seek_after_close(): im.seek(0) +@pytest.mark.parametrize("mode", ("RGBA", "RGB", "P")) +def test_different_modes_in_later_frames(mode, tmp_path): + test_file = str(tmp_path / "temp.png") + + im = Image.new("L", (1, 1)) + im.save(test_file, save_all=True, append_images=[Image.new(mode, (1, 1))]) + with Image.open(test_file) as reloaded: + assert reloaded.mode == mode + + def test_constants_deprecation(): for enum, prefix in { PngImagePlugin.Disposal: "APNG_DISPOSE_", diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 442c65e6f..181002422 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -1089,28 +1089,28 @@ class _fdat: self.seq_num += 1 -def _write_multiple_frames(im, fp, chunk, rawmode): - default_image = im.encoderinfo.get("default_image", im.info.get("default_image")) +def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images): duration = im.encoderinfo.get("duration", im.info.get("duration", 0)) loop = im.encoderinfo.get("loop", im.info.get("loop", 0)) disposal = im.encoderinfo.get("disposal", im.info.get("disposal", Disposal.OP_NONE)) blend = im.encoderinfo.get("blend", im.info.get("blend", Blend.OP_SOURCE)) if default_image: - chain = itertools.chain(im.encoderinfo.get("append_images", [])) + chain = itertools.chain(append_images) else: - chain = itertools.chain([im], im.encoderinfo.get("append_images", [])) + chain = itertools.chain([im], append_images) im_frames = [] frame_count = 0 for im_seq in chain: for im_frame in ImageSequence.Iterator(im_seq): - im_frame = im_frame.copy() - if im_frame.mode != im.mode: - if im.mode == "P": - im_frame = im_frame.convert(im.mode, palette=im.palette) + if im_frame.mode == rawmode: + im_frame = im_frame.copy() + else: + if rawmode == "P": + im_frame = im_frame.convert(rawmode, palette=im.palette) else: - im_frame = im_frame.convert(im.mode) + im_frame = im_frame.convert(rawmode) encoderinfo = im.encoderinfo.copy() if isinstance(duration, (list, tuple)): encoderinfo["duration"] = duration[frame_count] @@ -1221,7 +1221,26 @@ def _save_all(im, fp, filename): def _save(im, fp, filename, chunk=putchunk, save_all=False): # save an image to disk (called by the save method) - mode = im.mode + if save_all: + default_image = im.encoderinfo.get( + "default_image", im.info.get("default_image") + ) + modes = set() + append_images = im.encoderinfo.get("append_images", []) + if default_image: + chain = itertools.chain(append_images) + else: + chain = itertools.chain([im], append_images) + for im_seq in chain: + for im_frame in ImageSequence.Iterator(im_seq): + modes.add(im_frame.mode) + for mode in ("RGBA", "RGB", "P"): + if mode in modes: + break + else: + mode = modes.pop() + else: + mode = im.mode if mode == "P": @@ -1373,7 +1392,7 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False): chunk(fp, b"eXIf", exif) if save_all: - _write_multiple_frames(im, fp, chunk, rawmode) + _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images) else: ImageFile._save(im, _idat(fp, chunk), [("zip", (0, 0) + im.size, 0, rawmode)]) From aabb964de192e9db7096ae6fdb5d3ae2883397f4 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 23 Sep 2022 20:14:05 +1000 Subject: [PATCH 036/239] Show all frames in ImageShow --- src/PIL/ImageShow.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index 9f9a551fb..76f42a307 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -136,7 +136,7 @@ class WindowsViewer(Viewer): """The default viewer on Windows is the default system application for PNG files.""" format = "PNG" - options = {"compress_level": 1} + options = {"compress_level": 1, "save_all": True} def get_command(self, file, **options): return ( @@ -154,7 +154,7 @@ class MacViewer(Viewer): """The default viewer on macOS using ``Preview.app``.""" format = "PNG" - options = {"compress_level": 1} + options = {"compress_level": 1, "save_all": True} def get_command(self, file, **options): # on darwin open returns immediately resulting in the temp @@ -197,7 +197,7 @@ if sys.platform == "darwin": class UnixViewer(Viewer): format = "PNG" - options = {"compress_level": 1} + options = {"compress_level": 1, "save_all": True} def get_command(self, file, **options): command = self.get_command_ex(file, **options)[0] From d402fe0b17e75ca6f6869dbf356200754c6d21c1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 29 Sep 2022 08:22:01 +1000 Subject: [PATCH 037/239] Added IMT tests --- Tests/images/bw_gradient.imt | Bin 0 -> 2599 bytes Tests/test_file_imt.py | 19 +++++++++++++++++++ src/PIL/ImtImagePlugin.py | 6 +++--- 3 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 Tests/images/bw_gradient.imt create mode 100644 Tests/test_file_imt.py diff --git a/Tests/images/bw_gradient.imt b/Tests/images/bw_gradient.imt new file mode 100644 index 0000000000000000000000000000000000000000..d765cf95facfcb7cb2dbd63b61a6e48d213c31b5 GIT binary patch literal 2599 zcmdN&&d<$F%`4$5&rB)FP%ttz+{(>E|QGBzQN=`{lOV7y6%FfBn%P%M_ zDlRE4E3c@ms;;T6t8Zv*YHn$5Ywzgn>h9_7>z^=j(&Q;qr%j(RbJpxRbLY)puyE1h zB}NRWEt>3V5)8;K(w{73CbJy-Yd-v@>aPZLKBS()NKXLNZ=`&}~oxgDL z(&Z~xuU)@!^VaPo;%Tz5np>)8{W=zkUDl^Vjb`fB*d- c_5W!6kEZ|8{6AX$jh6qT_1|dyKhoMi07e@JTmS$7 literal 0 HcmV?d00001 diff --git a/Tests/test_file_imt.py b/Tests/test_file_imt.py new file mode 100644 index 000000000..f56acc429 --- /dev/null +++ b/Tests/test_file_imt.py @@ -0,0 +1,19 @@ +import io + +import pytest + +from PIL import Image, ImtImagePlugin + +from .helper import assert_image_equal_tofile + + +def test_sanity(): + with Image.open("Tests/images/bw_gradient.imt") as im: + assert_image_equal_tofile(im, "Tests/images/bw_gradient.png") + + +@pytest.mark.parametrize("data", (b"\n", b"\n-", b"width 1\n")) +def test_invalid_file(data): + with io.BytesIO(data) as fp: + with pytest.raises(SyntaxError): + ImtImagePlugin.ImtImageFile(fp) diff --git a/src/PIL/ImtImagePlugin.py b/src/PIL/ImtImagePlugin.py index 5790acdaf..5bcb959f1 100644 --- a/src/PIL/ImtImagePlugin.py +++ b/src/PIL/ImtImagePlugin.py @@ -74,13 +74,13 @@ class ImtImageFile(ImageFile.ImageFile): if not m: break k, v = m.group(1, 2) - if k == "width": + if k == b"width": xsize = int(v) self._size = xsize, ysize - elif k == "height": + elif k == b"height": ysize = int(v) self._size = xsize, ysize - elif k == "pixel" and v == "n8": + elif k == b"pixel" and v == b"n8": self.mode = "L" From cb2243713c5241f49336e8892b354bccf250e586 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 28 Sep 2022 21:36:07 +1000 Subject: [PATCH 038/239] Only read a maximum of 100 bytes at a time --- src/PIL/ImtImagePlugin.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/PIL/ImtImagePlugin.py b/src/PIL/ImtImagePlugin.py index 5bcb959f1..dc7078012 100644 --- a/src/PIL/ImtImagePlugin.py +++ b/src/PIL/ImtImagePlugin.py @@ -39,15 +39,19 @@ class ImtImageFile(ImageFile.ImageFile): # Quick rejection: if there's not a LF among the first # 100 bytes, this is (probably) not a text header. - if b"\n" not in self.fp.read(100): + buffer = self.fp.read(100) + if b"\n" not in buffer: raise SyntaxError("not an IM file") - self.fp.seek(0) xsize = ysize = 0 while True: - s = self.fp.read(1) + if buffer: + s = buffer[:1] + buffer = buffer[1:] + else: + s = self.fp.read(1) if not s: break @@ -55,7 +59,12 @@ class ImtImageFile(ImageFile.ImageFile): # image data begins self.tile = [ - ("raw", (0, 0) + self.size, self.fp.tell(), (self.mode, 0, 1)) + ( + "raw", + (0, 0) + self.size, + self.fp.tell() - len(buffer), + (self.mode, 0, 1), + ) ] break @@ -63,8 +72,11 @@ class ImtImageFile(ImageFile.ImageFile): else: # read key/value pair - # FIXME: dangerous, may read whole file - s = s + self.fp.readline() + if b"\n" not in buffer: + buffer += self.fp.read(100) + lines = buffer.split(b"\n") + s += lines.pop(0) + buffer = b"\n".join(lines) if len(s) == 1 or len(s) > 100: break if s[0] == ord(b"*"): From ea3b66d8efdae18b2de923e0f7832191fb0cd5e1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 29 Sep 2022 21:29:16 +1000 Subject: [PATCH 039/239] Allow palette chunk to not be first --- Tests/images/hopper_palette_chunk_second.fli | Bin 0 -> 16909 bytes Tests/test_file_fli.py | 8 +++++++- src/PIL/FliImagePlugin.py | 19 ++++++++++++++----- 3 files changed, 21 insertions(+), 6 deletions(-) create mode 100644 Tests/images/hopper_palette_chunk_second.fli diff --git a/Tests/images/hopper_palette_chunk_second.fli b/Tests/images/hopper_palette_chunk_second.fli new file mode 100644 index 0000000000000000000000000000000000000000..54447de0af71d9ae5de4b149bbe86f032a28e9e7 GIT binary patch literal 16909 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 literal 0 HcmV?d00001 diff --git a/Tests/test_file_fli.py b/Tests/test_file_fli.py index a7d43d2e9..b8b999d70 100644 --- a/Tests/test_file_fli.py +++ b/Tests/test_file_fli.py @@ -4,7 +4,7 @@ import pytest from PIL import FliImagePlugin, Image -from .helper import assert_image_equal_tofile, is_pypy +from .helper import assert_image_equal, assert_image_equal_tofile, is_pypy # created as an export of a palette image from Gimp2.6 # save as...-> hopper.fli, default options. @@ -79,6 +79,12 @@ def test_invalid_file(): FliImagePlugin.FliImageFile(invalid_file) +def test_palette_chunk_second(): + with Image.open("Tests/images/hopper_palette_chunk_second.fli") as im: + with Image.open(static_test_file) as expected: + assert_image_equal(im.convert("RGB"), expected.convert("RGB")) + + def test_n_frames(): with Image.open(static_test_file) as im: assert im.n_frames == 1 diff --git a/src/PIL/FliImagePlugin.py b/src/PIL/FliImagePlugin.py index e13b1779c..908bed9f4 100644 --- a/src/PIL/FliImagePlugin.py +++ b/src/PIL/FliImagePlugin.py @@ -15,6 +15,7 @@ # See the README file for information on usage and redistribution. # +import os from . import Image, ImageFile, ImagePalette from ._binary import i16le as i16 @@ -80,11 +81,19 @@ class FliImageFile(ImageFile.ImageFile): if i16(s, 4) == 0xF1FA: # look for palette chunk - s = self.fp.read(6) - if i16(s, 4) == 11: - self._palette(palette, 2) - elif i16(s, 4) == 4: - self._palette(palette, 0) + number_of_subchunks = i16(s, 6) + chunk_size = None + for _ in range(number_of_subchunks): + if chunk_size is not None: + self.fp.seek(chunk_size - 6, os.SEEK_CUR) + s = self.fp.read(6) + chunk_type = i16(s, 4) + if chunk_type in (4, 11): + self._palette(palette, 2 if chunk_type == 11 else 0) + break + chunk_size = i32(s) + if not chunk_size: + break palette = [o8(r) + o8(g) + o8(b) for (r, g, b) in palette] self.palette = ImagePalette.raw("RGB", b"".join(palette)) From db1b74198bd8ecc70ecf99702c5b4a90c257f038 Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Wed, 3 Jan 2018 10:56:17 +0000 Subject: [PATCH 040/239] Don't reassign crc on close --- src/PIL/PngImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 442c65e6f..5a991b6e8 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -189,7 +189,7 @@ class ChunkStream: self.close() def close(self): - self.queue = self.crc = self.fp = None + self.queue = self.fp = None def push(self, cid, pos, length): From b34307b9d29968a03824f4aa510f57a62d302f4f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 30 Sep 2022 21:57:54 +1000 Subject: [PATCH 041/239] Corrected docstring --- src/PIL/PngImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 5a991b6e8..abcffe926 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -224,7 +224,7 @@ class ChunkStream: ) from e def crc_skip(self, cid, data): - """Read checksum. Used if the C module is not present""" + """Read checksum""" self.fp.read(4) From d03f35b5bb75cd991187945d1a4a0050b680f04c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 1 Oct 2022 17:24:35 +1000 Subject: [PATCH 042/239] Added enums --- docs/reference/ExifTags.rst | 25 +- src/PIL/ExifTags.py | 619 ++++++++++++++++++------------------ 2 files changed, 336 insertions(+), 308 deletions(-) diff --git a/docs/reference/ExifTags.rst b/docs/reference/ExifTags.rst index 794fa238f..ff5788524 100644 --- a/docs/reference/ExifTags.rst +++ b/docs/reference/ExifTags.rst @@ -11,7 +11,7 @@ provide constants and clear-text names for various well-known EXIF tags. :type: dict The TAGS dictionary maps 16-bit integer EXIF tag enumerations to - descriptive string names. For instance: + descriptive string names. For instance: >>> from PIL.ExifTags import TAGS >>> TAGS[0x010e] @@ -20,9 +20,28 @@ provide constants and clear-text names for various well-known EXIF tags. .. py:data:: GPSTAGS :type: dict - The GPSTAGS dictionary maps 8-bit integer EXIF gps enumerations to - descriptive string names. For instance: + The GPSTAGS dictionary maps 8-bit integer EXIF GPS enumerations to + descriptive string names. For instance: >>> from PIL.ExifTags import GPSTAGS >>> GPSTAGS[20] 'GPSDestLatitude' + + +These values are also exposed as ``enum.IntEnum`` classes. + +.. py:data:: Base + + >>> from PIL.ExifTags import Base + >>> Base.ImageDescription.value + 270 + >>> Base(270).name + 'ImageDescription' + +.. py:data:: GPS + + >>> from PIL.ExifTags import GPS + >>> GPS.GPSDestLatitude.value + 20 + >>> GPS(20).name + 'GPSDestLatitude' diff --git a/src/PIL/ExifTags.py b/src/PIL/ExifTags.py index 7da2ddae5..f3a73bf1a 100644 --- a/src/PIL/ExifTags.py +++ b/src/PIL/ExifTags.py @@ -14,318 +14,327 @@ This module provides constants and clear-text names for various well-known EXIF tags. """ +from enum import IntEnum -TAGS = { + +class Base(IntEnum): # possibly incomplete - 0x0001: "InteropIndex", - 0x000B: "ProcessingSoftware", - 0x00FE: "NewSubfileType", - 0x00FF: "SubfileType", - 0x0100: "ImageWidth", - 0x0101: "ImageLength", - 0x0102: "BitsPerSample", - 0x0103: "Compression", - 0x0106: "PhotometricInterpretation", - 0x0107: "Thresholding", - 0x0108: "CellWidth", - 0x0109: "CellLength", - 0x010A: "FillOrder", - 0x010D: "DocumentName", - 0x010E: "ImageDescription", - 0x010F: "Make", - 0x0110: "Model", - 0x0111: "StripOffsets", - 0x0112: "Orientation", - 0x0115: "SamplesPerPixel", - 0x0116: "RowsPerStrip", - 0x0117: "StripByteCounts", - 0x0118: "MinSampleValue", - 0x0119: "MaxSampleValue", - 0x011A: "XResolution", - 0x011B: "YResolution", - 0x011C: "PlanarConfiguration", - 0x011D: "PageName", - 0x0120: "FreeOffsets", - 0x0121: "FreeByteCounts", - 0x0122: "GrayResponseUnit", - 0x0123: "GrayResponseCurve", - 0x0124: "T4Options", - 0x0125: "T6Options", - 0x0128: "ResolutionUnit", - 0x0129: "PageNumber", - 0x012D: "TransferFunction", - 0x0131: "Software", - 0x0132: "DateTime", - 0x013B: "Artist", - 0x013C: "HostComputer", - 0x013D: "Predictor", - 0x013E: "WhitePoint", - 0x013F: "PrimaryChromaticities", - 0x0140: "ColorMap", - 0x0141: "HalftoneHints", - 0x0142: "TileWidth", - 0x0143: "TileLength", - 0x0144: "TileOffsets", - 0x0145: "TileByteCounts", - 0x014A: "SubIFDs", - 0x014C: "InkSet", - 0x014D: "InkNames", - 0x014E: "NumberOfInks", - 0x0150: "DotRange", - 0x0151: "TargetPrinter", - 0x0152: "ExtraSamples", - 0x0153: "SampleFormat", - 0x0154: "SMinSampleValue", - 0x0155: "SMaxSampleValue", - 0x0156: "TransferRange", - 0x0157: "ClipPath", - 0x0158: "XClipPathUnits", - 0x0159: "YClipPathUnits", - 0x015A: "Indexed", - 0x015B: "JPEGTables", - 0x015F: "OPIProxy", - 0x0200: "JPEGProc", - 0x0201: "JpegIFOffset", - 0x0202: "JpegIFByteCount", - 0x0203: "JpegRestartInterval", - 0x0205: "JpegLosslessPredictors", - 0x0206: "JpegPointTransforms", - 0x0207: "JpegQTables", - 0x0208: "JpegDCTables", - 0x0209: "JpegACTables", - 0x0211: "YCbCrCoefficients", - 0x0212: "YCbCrSubSampling", - 0x0213: "YCbCrPositioning", - 0x0214: "ReferenceBlackWhite", - 0x02BC: "XMLPacket", - 0x1000: "RelatedImageFileFormat", - 0x1001: "RelatedImageWidth", - 0x1002: "RelatedImageLength", - 0x4746: "Rating", - 0x4749: "RatingPercent", - 0x800D: "ImageID", - 0x828D: "CFARepeatPatternDim", - 0x828E: "CFAPattern", - 0x828F: "BatteryLevel", - 0x8298: "Copyright", - 0x829A: "ExposureTime", - 0x829D: "FNumber", - 0x83BB: "IPTCNAA", - 0x8649: "ImageResources", - 0x8769: "ExifOffset", - 0x8773: "InterColorProfile", - 0x8822: "ExposureProgram", - 0x8824: "SpectralSensitivity", - 0x8825: "GPSInfo", - 0x8827: "ISOSpeedRatings", - 0x8828: "OECF", - 0x8829: "Interlace", - 0x882A: "TimeZoneOffset", - 0x882B: "SelfTimerMode", - 0x8830: "SensitivityType", - 0x8831: "StandardOutputSensitivity", - 0x8832: "RecommendedExposureIndex", - 0x8833: "ISOSpeed", - 0x8834: "ISOSpeedLatitudeyyy", - 0x8835: "ISOSpeedLatitudezzz", - 0x9000: "ExifVersion", - 0x9003: "DateTimeOriginal", - 0x9004: "DateTimeDigitized", - 0x9010: "OffsetTime", - 0x9011: "OffsetTimeOriginal", - 0x9012: "OffsetTimeDigitized", - 0x9101: "ComponentsConfiguration", - 0x9102: "CompressedBitsPerPixel", - 0x9201: "ShutterSpeedValue", - 0x9202: "ApertureValue", - 0x9203: "BrightnessValue", - 0x9204: "ExposureBiasValue", - 0x9205: "MaxApertureValue", - 0x9206: "SubjectDistance", - 0x9207: "MeteringMode", - 0x9208: "LightSource", - 0x9209: "Flash", - 0x920A: "FocalLength", - 0x920B: "FlashEnergy", + InteropIndex = 0x0001 + ProcessingSoftware = 0x000B + NewSubfileType = 0x00FE + SubfileType = 0x00FF + ImageWidth = 0x0100 + ImageLength = 0x0101 + BitsPerSample = 0x0102 + Compression = 0x0103 + PhotometricInterpretation = 0x0106 + Thresholding = 0x0107 + CellWidth = 0x0108 + CellLength = 0x0109 + FillOrder = 0x010A + DocumentName = 0x010D + ImageDescription = 0x010E + Make = 0x010F + Model = 0x0110 + StripOffsets = 0x0111 + Orientation = 0x0112 + SamplesPerPixel = 0x0115 + RowsPerStrip = 0x0116 + StripByteCounts = 0x0117 + MinSampleValue = 0x0118 + MaxSampleValue = 0x0119 + XResolution = 0x011A + YResolution = 0x011B + PlanarConfiguration = 0x011C + PageName = 0x011D + FreeOffsets = 0x0120 + FreeByteCounts = 0x0121 + GrayResponseUnit = 0x0122 + GrayResponseCurve = 0x0123 + T4Options = 0x0124 + T6Options = 0x0125 + ResolutionUnit = 0x0128 + PageNumber = 0x0129 + TransferFunction = 0x012D + Software = 0x0131 + DateTime = 0x0132 + Artist = 0x013B + HostComputer = 0x013C + Predictor = 0x013D + WhitePoint = 0x013E + PrimaryChromaticities = 0x013F + ColorMap = 0x0140 + HalftoneHints = 0x0141 + TileWidth = 0x0142 + TileLength = 0x0143 + TileOffsets = 0x0144 + TileByteCounts = 0x0145 + SubIFDs = 0x014A + InkSet = 0x014C + InkNames = 0x014D + NumberOfInks = 0x014E + DotRange = 0x0150 + TargetPrinter = 0x0151 + ExtraSamples = 0x0152 + SampleFormat = 0x0153 + SMinSampleValue = 0x0154 + SMaxSampleValue = 0x0155 + TransferRange = 0x0156 + ClipPath = 0x0157 + XClipPathUnits = 0x0158 + YClipPathUnits = 0x0159 + Indexed = 0x015A + JPEGTables = 0x015B + OPIProxy = 0x015F + JPEGProc = 0x0200 + JpegIFOffset = 0x0201 + JpegIFByteCount = 0x0202 + JpegRestartInterval = 0x0203 + JpegLosslessPredictors = 0x0205 + JpegPointTransforms = 0x0206 + JpegQTables = 0x0207 + JpegDCTables = 0x0208 + JpegACTables = 0x0209 + YCbCrCoefficients = 0x0211 + YCbCrSubSampling = 0x0212 + YCbCrPositioning = 0x0213 + ReferenceBlackWhite = 0x0214 + XMLPacket = 0x02BC + RelatedImageFileFormat = 0x1000 + RelatedImageWidth = 0x1001 + RelatedImageLength = 0x1002 + Rating = 0x4746 + RatingPercent = 0x4749 + ImageID = 0x800D + CFARepeatPatternDim = 0x828D + BatteryLevel = 0x828F + Copyright = 0x8298 + ExposureTime = 0x829A + FNumber = 0x829D + IPTCNAA = 0x83BB + ImageResources = 0x8649 + ExifOffset = 0x8769 + InterColorProfile = 0x8773 + ExposureProgram = 0x8822 + SpectralSensitivity = 0x8824 + GPSInfo = 0x8825 + ISOSpeedRatings = 0x8827 + OECF = 0x8828 + Interlace = 0x8829 + TimeZoneOffset = 0x882A + SelfTimerMode = 0x882B + SensitivityType = 0x8830 + StandardOutputSensitivity = 0x8831 + RecommendedExposureIndex = 0x8832 + ISOSpeed = 0x8833 + ISOSpeedLatitudeyyy = 0x8834 + ISOSpeedLatitudezzz = 0x8835 + ExifVersion = 0x9000 + DateTimeOriginal = 0x9003 + DateTimeDigitized = 0x9004 + OffsetTime = 0x9010 + OffsetTimeOriginal = 0x9011 + OffsetTimeDigitized = 0x9012 + ComponentsConfiguration = 0x9101 + CompressedBitsPerPixel = 0x9102 + ShutterSpeedValue = 0x9201 + ApertureValue = 0x9202 + BrightnessValue = 0x9203 + ExposureBiasValue = 0x9204 + MaxApertureValue = 0x9205 + SubjectDistance = 0x9206 + MeteringMode = 0x9207 + LightSource = 0x9208 + Flash = 0x9209 + FocalLength = 0x920A + Noise = 0x920D + ImageNumber = 0x9211 + SecurityClassification = 0x9212 + ImageHistory = 0x9213 + TIFFEPStandardID = 0x9216 + MakerNote = 0x927C + UserComment = 0x9286 + SubsecTime = 0x9290 + SubsecTimeOriginal = 0x9291 + SubsecTimeDigitized = 0x9292 + AmbientTemperature = 0x9400 + Humidity = 0x9401 + Pressure = 0x9402 + WaterDepth = 0x9403 + Acceleration = 0x9404 + CameraElevationAngle = 0x9405 + XPTitle = 0x9C9B + XPComment = 0x9C9C + XPAuthor = 0x9C9D + XPKeywords = 0x9C9E + XPSubject = 0x9C9F + FlashPixVersion = 0xA000 + ColorSpace = 0xA001 + ExifImageWidth = 0xA002 + ExifImageHeight = 0xA003 + RelatedSoundFile = 0xA004 + ExifInteroperabilityOffset = 0xA005 + FlashEnergy = 0xA20B + SpatialFrequencyResponse = 0xA20C + FocalPlaneXResolution = 0xA20E + FocalPlaneYResolution = 0xA20F + FocalPlaneResolutionUnit = 0xA210 + SubjectLocation = 0xA214 + ExposureIndex = 0xA215 + SensingMethod = 0xA217 + FileSource = 0xA300 + SceneType = 0xA301 + CFAPattern = 0xA302 + CustomRendered = 0xA401 + ExposureMode = 0xA402 + WhiteBalance = 0xA403 + DigitalZoomRatio = 0xA404 + FocalLengthIn35mmFilm = 0xA405 + SceneCaptureType = 0xA406 + GainControl = 0xA407 + Contrast = 0xA408 + Saturation = 0xA409 + Sharpness = 0xA40A + DeviceSettingDescription = 0xA40B + SubjectDistanceRange = 0xA40C + ImageUniqueID = 0xA420 + CameraOwnerName = 0xA430 + BodySerialNumber = 0xA431 + LensSpecification = 0xA432 + LensMake = 0xA433 + LensModel = 0xA434 + LensSerialNumber = 0xA435 + CompositeImage = 0xA460 + CompositeImageCount = 0xA461 + CompositeImageExposureTimes = 0xA462 + Gamma = 0xA500 + PrintImageMatching = 0xC4A5 + DNGVersion = 0xC612 + DNGBackwardVersion = 0xC613 + UniqueCameraModel = 0xC614 + LocalizedCameraModel = 0xC615 + CFAPlaneColor = 0xC616 + CFALayout = 0xC617 + LinearizationTable = 0xC618 + BlackLevelRepeatDim = 0xC619 + BlackLevel = 0xC61A + BlackLevelDeltaH = 0xC61B + BlackLevelDeltaV = 0xC61C + WhiteLevel = 0xC61D + DefaultScale = 0xC61E + DefaultCropOrigin = 0xC61F + DefaultCropSize = 0xC620 + ColorMatrix1 = 0xC621 + ColorMatrix2 = 0xC622 + CameraCalibration1 = 0xC623 + CameraCalibration2 = 0xC624 + ReductionMatrix1 = 0xC625 + ReductionMatrix2 = 0xC626 + AnalogBalance = 0xC627 + AsShotNeutral = 0xC628 + AsShotWhiteXY = 0xC629 + BaselineExposure = 0xC62A + BaselineNoise = 0xC62B + BaselineSharpness = 0xC62C + BayerGreenSplit = 0xC62D + LinearResponseLimit = 0xC62E + CameraSerialNumber = 0xC62F + LensInfo = 0xC630 + ChromaBlurRadius = 0xC631 + AntiAliasStrength = 0xC632 + ShadowScale = 0xC633 + DNGPrivateData = 0xC634 + MakerNoteSafety = 0xC635 + CalibrationIlluminant1 = 0xC65A + CalibrationIlluminant2 = 0xC65B + BestQualityScale = 0xC65C + RawDataUniqueID = 0xC65D + OriginalRawFileName = 0xC68B + OriginalRawFileData = 0xC68C + ActiveArea = 0xC68D + MaskedAreas = 0xC68E + AsShotICCProfile = 0xC68F + AsShotPreProfileMatrix = 0xC690 + CurrentICCProfile = 0xC691 + CurrentPreProfileMatrix = 0xC692 + ColorimetricReference = 0xC6BF + CameraCalibrationSignature = 0xC6F3 + ProfileCalibrationSignature = 0xC6F4 + AsShotProfileName = 0xC6F6 + NoiseReductionApplied = 0xC6F7 + ProfileName = 0xC6F8 + ProfileHueSatMapDims = 0xC6F9 + ProfileHueSatMapData1 = 0xC6FA + ProfileHueSatMapData2 = 0xC6FB + ProfileToneCurve = 0xC6FC + ProfileEmbedPolicy = 0xC6FD + ProfileCopyright = 0xC6FE + ForwardMatrix1 = 0xC714 + ForwardMatrix2 = 0xC715 + PreviewApplicationName = 0xC716 + PreviewApplicationVersion = 0xC717 + PreviewSettingsName = 0xC718 + PreviewSettingsDigest = 0xC719 + PreviewColorSpace = 0xC71A + PreviewDateTime = 0xC71B + RawImageDigest = 0xC71C + OriginalRawFileDigest = 0xC71D + SubTileBlockSize = 0xC71E + RowInterleaveFactor = 0xC71F + ProfileLookTableDims = 0xC725 + ProfileLookTableData = 0xC726 + OpcodeList1 = 0xC740 + OpcodeList2 = 0xC741 + OpcodeList3 = 0xC74E + NoiseProfile = 0xC761 + + +"""Maps EXIF tags to tag names.""" +TAGS = { + **{i.value: i.name for i in Base}, 0x920C: "SpatialFrequencyResponse", - 0x920D: "Noise", - 0x9211: "ImageNumber", - 0x9212: "SecurityClassification", - 0x9213: "ImageHistory", 0x9214: "SubjectLocation", 0x9215: "ExposureIndex", + 0x828E: "CFAPattern", + 0x920B: "FlashEnergy", 0x9216: "TIFF/EPStandardID", - 0x927C: "MakerNote", - 0x9286: "UserComment", - 0x9290: "SubsecTime", - 0x9291: "SubsecTimeOriginal", - 0x9292: "SubsecTimeDigitized", - 0x9400: "AmbientTemperature", - 0x9401: "Humidity", - 0x9402: "Pressure", - 0x9403: "WaterDepth", - 0x9404: "Acceleration", - 0x9405: "CameraElevationAngle", - 0x9C9B: "XPTitle", - 0x9C9C: "XPComment", - 0x9C9D: "XPAuthor", - 0x9C9E: "XPKeywords", - 0x9C9F: "XPSubject", - 0xA000: "FlashPixVersion", - 0xA001: "ColorSpace", - 0xA002: "ExifImageWidth", - 0xA003: "ExifImageHeight", - 0xA004: "RelatedSoundFile", - 0xA005: "ExifInteroperabilityOffset", - 0xA20B: "FlashEnergy", - 0xA20C: "SpatialFrequencyResponse", - 0xA20E: "FocalPlaneXResolution", - 0xA20F: "FocalPlaneYResolution", - 0xA210: "FocalPlaneResolutionUnit", - 0xA214: "SubjectLocation", - 0xA215: "ExposureIndex", - 0xA217: "SensingMethod", - 0xA300: "FileSource", - 0xA301: "SceneType", - 0xA302: "CFAPattern", - 0xA401: "CustomRendered", - 0xA402: "ExposureMode", - 0xA403: "WhiteBalance", - 0xA404: "DigitalZoomRatio", - 0xA405: "FocalLengthIn35mmFilm", - 0xA406: "SceneCaptureType", - 0xA407: "GainControl", - 0xA408: "Contrast", - 0xA409: "Saturation", - 0xA40A: "Sharpness", - 0xA40B: "DeviceSettingDescription", - 0xA40C: "SubjectDistanceRange", - 0xA420: "ImageUniqueID", - 0xA430: "CameraOwnerName", - 0xA431: "BodySerialNumber", - 0xA432: "LensSpecification", - 0xA433: "LensMake", - 0xA434: "LensModel", - 0xA435: "LensSerialNumber", - 0xA460: "CompositeImage", - 0xA461: "CompositeImageCount", - 0xA462: "CompositeImageExposureTimes", - 0xA500: "Gamma", - 0xC4A5: "PrintImageMatching", - 0xC612: "DNGVersion", - 0xC613: "DNGBackwardVersion", - 0xC614: "UniqueCameraModel", - 0xC615: "LocalizedCameraModel", - 0xC616: "CFAPlaneColor", - 0xC617: "CFALayout", - 0xC618: "LinearizationTable", - 0xC619: "BlackLevelRepeatDim", - 0xC61A: "BlackLevel", - 0xC61B: "BlackLevelDeltaH", - 0xC61C: "BlackLevelDeltaV", - 0xC61D: "WhiteLevel", - 0xC61E: "DefaultScale", - 0xC61F: "DefaultCropOrigin", - 0xC620: "DefaultCropSize", - 0xC621: "ColorMatrix1", - 0xC622: "ColorMatrix2", - 0xC623: "CameraCalibration1", - 0xC624: "CameraCalibration2", - 0xC625: "ReductionMatrix1", - 0xC626: "ReductionMatrix2", - 0xC627: "AnalogBalance", - 0xC628: "AsShotNeutral", - 0xC629: "AsShotWhiteXY", - 0xC62A: "BaselineExposure", - 0xC62B: "BaselineNoise", - 0xC62C: "BaselineSharpness", - 0xC62D: "BayerGreenSplit", - 0xC62E: "LinearResponseLimit", - 0xC62F: "CameraSerialNumber", - 0xC630: "LensInfo", - 0xC631: "ChromaBlurRadius", - 0xC632: "AntiAliasStrength", - 0xC633: "ShadowScale", - 0xC634: "DNGPrivateData", - 0xC635: "MakerNoteSafety", - 0xC65A: "CalibrationIlluminant1", - 0xC65B: "CalibrationIlluminant2", - 0xC65C: "BestQualityScale", - 0xC65D: "RawDataUniqueID", - 0xC68B: "OriginalRawFileName", - 0xC68C: "OriginalRawFileData", - 0xC68D: "ActiveArea", - 0xC68E: "MaskedAreas", - 0xC68F: "AsShotICCProfile", - 0xC690: "AsShotPreProfileMatrix", - 0xC691: "CurrentICCProfile", - 0xC692: "CurrentPreProfileMatrix", - 0xC6BF: "ColorimetricReference", - 0xC6F3: "CameraCalibrationSignature", - 0xC6F4: "ProfileCalibrationSignature", - 0xC6F6: "AsShotProfileName", - 0xC6F7: "NoiseReductionApplied", - 0xC6F8: "ProfileName", - 0xC6F9: "ProfileHueSatMapDims", - 0xC6FA: "ProfileHueSatMapData1", - 0xC6FB: "ProfileHueSatMapData2", - 0xC6FC: "ProfileToneCurve", - 0xC6FD: "ProfileEmbedPolicy", - 0xC6FE: "ProfileCopyright", - 0xC714: "ForwardMatrix1", - 0xC715: "ForwardMatrix2", - 0xC716: "PreviewApplicationName", - 0xC717: "PreviewApplicationVersion", - 0xC718: "PreviewSettingsName", - 0xC719: "PreviewSettingsDigest", - 0xC71A: "PreviewColorSpace", - 0xC71B: "PreviewDateTime", - 0xC71C: "RawImageDigest", - 0xC71D: "OriginalRawFileDigest", - 0xC71E: "SubTileBlockSize", - 0xC71F: "RowInterleaveFactor", - 0xC725: "ProfileLookTableDims", - 0xC726: "ProfileLookTableData", - 0xC740: "OpcodeList1", - 0xC741: "OpcodeList2", - 0xC74E: "OpcodeList3", - 0xC761: "NoiseProfile", } -"""Maps EXIF tags to tag names.""" -GPSTAGS = { - 0: "GPSVersionID", - 1: "GPSLatitudeRef", - 2: "GPSLatitude", - 3: "GPSLongitudeRef", - 4: "GPSLongitude", - 5: "GPSAltitudeRef", - 6: "GPSAltitude", - 7: "GPSTimeStamp", - 8: "GPSSatellites", - 9: "GPSStatus", - 10: "GPSMeasureMode", - 11: "GPSDOP", - 12: "GPSSpeedRef", - 13: "GPSSpeed", - 14: "GPSTrackRef", - 15: "GPSTrack", - 16: "GPSImgDirectionRef", - 17: "GPSImgDirection", - 18: "GPSMapDatum", - 19: "GPSDestLatitudeRef", - 20: "GPSDestLatitude", - 21: "GPSDestLongitudeRef", - 22: "GPSDestLongitude", - 23: "GPSDestBearingRef", - 24: "GPSDestBearing", - 25: "GPSDestDistanceRef", - 26: "GPSDestDistance", - 27: "GPSProcessingMethod", - 28: "GPSAreaInformation", - 29: "GPSDateStamp", - 30: "GPSDifferential", - 31: "GPSHPositioningError", -} +class GPS(IntEnum): + GPSVersionID = 0 + GPSLatitudeRef = 1 + GPSLatitude = 2 + GPSLongitudeRef = 3 + GPSLongitude = 4 + GPSAltitudeRef = 5 + GPSAltitude = 6 + GPSTimeStamp = 7 + GPSSatellites = 8 + GPSStatus = 9 + GPSMeasureMode = 10 + GPSDOP = 11 + GPSSpeedRef = 12 + GPSSpeed = 13 + GPSTrackRef = 14 + GPSTrack = 15 + GPSImgDirectionRef = 16 + GPSImgDirection = 17 + GPSMapDatum = 18 + GPSDestLatitudeRef = 19 + GPSDestLatitude = 20 + GPSDestLongitudeRef = 21 + GPSDestLongitude = 22 + GPSDestBearingRef = 23 + GPSDestBearing = 24 + GPSDestDistanceRef = 25 + GPSDestDistance = 26 + GPSProcessingMethod = 27 + GPSAreaInformation = 28 + GPSDateStamp = 29 + GPSDifferential = 30 + GPSHPositioningError = 31 + + """Maps EXIF GPS tags to tag names.""" +GPSTAGS = {i.value: i.name for i in GPS} From ed990abed4fe1c73d13fb4c17a8ca07af00718cf Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 2 Oct 2022 07:05:18 +0100 Subject: [PATCH 043/239] windows: update xz to 5.2.7, update libpng to 1.6.38 --- winbuild/build_prepare.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 414857fc2..573020ca3 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -152,9 +152,9 @@ deps = { "libs": [r"*.lib"], }, "xz": { - "url": SF_PROJECTS + "/lzmautils/files/xz-5.2.6.tar.gz/download", - "filename": "xz-5.2.6.tar.gz", - "dir": "xz-5.2.6", + "url": SF_PROJECTS + "/lzmautils/files/xz-5.2.7.tar.gz/download", + "filename": "xz-5.2.7.tar.gz", + "dir": "xz-5.2.7", "license": "COPYING", "patch": { r"src\liblzma\api\lzma.h": { @@ -228,9 +228,9 @@ deps = { # "bins": [r"libtiff\*.dll"], }, "libpng": { - "url": SF_PROJECTS + "/libpng/files/libpng16/1.6.37/lpng1637.zip/download", - "filename": "lpng1637.zip", - "dir": "lpng1637", + "url": SF_PROJECTS + "/libpng/files/libpng16/1.6.38/lpng1638.zip/download", + "filename": "lpng1638.zip", + "dir": "lpng1638", "license": "LICENSE", "build": [ # lint: do not inline From ba78f5d0dad12a5fb54930a220f8b0dd2ffe64d7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 3 Oct 2022 09:39:37 +1100 Subject: [PATCH 044/239] Document the default layout engine --- src/PIL/ImageFont.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 310072dfc..6c747fc3e 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -945,6 +945,8 @@ def truetype(font=None, size=10, index=0, encoding="", layout_engine=None): encoding of any text provided in subsequent operations. :param layout_engine: Which layout engine to use, if available: :data:`.ImageFont.Layout.BASIC` or :data:`.ImageFont.Layout.RAQM`. + If it is available, Raqm layout will be used by default. + Otherwise, basic layout will be used. You can check support for Raqm layout using :py:func:`PIL.features.check_feature` with ``feature="raqm"``. From 74f47d8c1a6152a4dd05ccf4a74079fc438673e0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 3 Oct 2022 10:03:13 +1100 Subject: [PATCH 045/239] Document that basic layout is faster than raqm --- src/PIL/ImageFont.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 6c747fc3e..6a66f8a71 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -948,6 +948,9 @@ def truetype(font=None, size=10, index=0, encoding="", layout_engine=None): If it is available, Raqm layout will be used by default. Otherwise, basic layout will be used. + If complex text layout is not required, basic layout will have + better performance. + You can check support for Raqm layout using :py:func:`PIL.features.check_feature` with ``feature="raqm"``. From 00a716c421947bd9ccd0235f80ad39ab68896d55 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 4 Oct 2022 12:54:48 +1100 Subject: [PATCH 046/239] High bit depth multichannel images are not yet supported --- docs/handbook/concepts.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/handbook/concepts.rst b/docs/handbook/concepts.rst index 66eeaf6f8..a9b33e437 100644 --- a/docs/handbook/concepts.rst +++ b/docs/handbook/concepts.rst @@ -60,7 +60,10 @@ Pillow also provides limited support for a few additional modes, including: * ``BGR;24`` (24-bit reversed true colour) * ``BGR;32`` (32-bit reversed true colour) -However, Pillow doesn’t support user-defined modes; if you need to handle band +Apart from these additional modes, Pillow doesn't yet support multichannel +images with a depth of more than 8 bits per channel. + +Pillow also doesn’t support user-defined modes; if you need to handle band combinations that are not listed above, use a sequence of Image objects. You can read the mode of an image through the :py:attr:`~PIL.Image.Image.mode` From 802a1430fe17f7380a6fea0e31a2fa4a2af5d343 Mon Sep 17 00:00:00 2001 From: Jay-Jay Aslan Date: Wed, 5 Oct 2022 11:03:51 +0200 Subject: [PATCH 047/239] added support for 16 bit integer tiffs --- src/PIL/TiffImagePlugin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 766d46ffb..3ea3d971a 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -169,6 +169,7 @@ OPEN_INFO = { (II, 0, (1,), 2, (8,), ()): ("L", "L;IR"), (MM, 0, (1,), 2, (8,), ()): ("L", "L;IR"), (II, 1, (1,), 1, (8,), ()): ("L", "L"), + (II, 0, (1,), 1, (16,), ()): ("I", "I;16"), (MM, 1, (1,), 1, (8,), ()): ("L", "L"), (II, 1, (1,), 2, (8,), ()): ("L", "L;R"), (MM, 1, (1,), 2, (8,), ()): ("L", "L;R"), From 0fd110864ace00482280d81acd1120a7684e6812 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 5 Oct 2022 21:15:45 +1100 Subject: [PATCH 048/239] Changed mode to I;16 --- src/PIL/TiffImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 3ea3d971a..04a63bd2b 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -169,11 +169,11 @@ OPEN_INFO = { (II, 0, (1,), 2, (8,), ()): ("L", "L;IR"), (MM, 0, (1,), 2, (8,), ()): ("L", "L;IR"), (II, 1, (1,), 1, (8,), ()): ("L", "L"), - (II, 0, (1,), 1, (16,), ()): ("I", "I;16"), (MM, 1, (1,), 1, (8,), ()): ("L", "L"), (II, 1, (1,), 2, (8,), ()): ("L", "L;R"), (MM, 1, (1,), 2, (8,), ()): ("L", "L;R"), (II, 1, (1,), 1, (12,), ()): ("I;16", "I;12"), + (II, 0, (1,), 1, (16,), ()): ("I;16", "I;16"), (II, 1, (1,), 1, (16,), ()): ("I;16", "I;16"), (MM, 1, (1,), 1, (16,), ()): ("I;16B", "I;16B"), (II, 1, (1,), 2, (16,), ()): ("I;16", "I;16R"), From e6ffbfd8df075b805550b0d3115192882b1e6d6f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 6 Oct 2022 08:46:31 +1100 Subject: [PATCH 049/239] If palette is present but not needed, do not use global palette --- .../palette_not_needed_for_second_frame.gif | Bin 0 -> 29116 bytes Tests/test_file_gif.py | 6 ++++++ src/PIL/GifImagePlugin.py | 4 +++- 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 Tests/images/palette_not_needed_for_second_frame.gif diff --git a/Tests/images/palette_not_needed_for_second_frame.gif b/Tests/images/palette_not_needed_for_second_frame.gif new file mode 100644 index 0000000000000000000000000000000000000000..0617291d152975acfb63c18e742398bca34e7afd GIT binary patch literal 29116 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�Ad|KobbD>_=amU(R;%* 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 Date: Fri, 7 Oct 2022 09:48:56 +1100 Subject: [PATCH 050/239] Do not attempt normalization if image is already normal --- Tests/test_image_convert.py | 6 ++++++ src/PIL/Image.py | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index 1a78f8b4c..e573a06a2 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -38,6 +38,12 @@ def test_sanity(): convert(im, output_mode) +def test_unsupported_conversion(): + im = hopper() + with pytest.raises(ValueError): + im.convert("INVALID") + + def test_default(): im = hopper("P") diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 6611ceb3c..cbf3ff865 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1036,7 +1036,10 @@ class Image: except ValueError: try: # normalize source image and try again - im = self.im.convert(getmodebase(self.mode)) + modebase = getmodebase(self.mode) + if modebase == self.mode: + raise + im = self.im.convert(modebase) im = im.convert(mode, dither) except KeyError as e: raise ValueError("illegal conversion") from e From fcd3eef594bb8eadce8333236440e2f9e9ccbe5f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 7 Oct 2022 22:33:45 +1100 Subject: [PATCH 051/239] Added conversion between RGB/RGBA/RGBX and LAB --- Tests/test_image_convert.py | 11 +++++++++++ src/PIL/Image.py | 13 +++++++++++++ 2 files changed, 24 insertions(+) diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index 1a78f8b4c..adbd83733 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -242,6 +242,17 @@ def test_p2pa_palette(): assert im_pa.getpalette() == im.getpalette() +@pytest.mark.parametrize("mode", ("RGB", "RGBA", "RGBX")) +def test_rgb_lab(mode): + im = Image.new(mode, (1, 1)) + converted_im = im.convert("LAB") + assert converted_im.getpixel((0, 0)) == (0, 128, 128) + + im = Image.new("LAB", (1, 1), (255, 0, 0)) + converted_im = im.convert(mode) + assert converted_im.getpixel((0, 0))[:3] == (0, 255, 255) + + def test_matrix_illegal_conversion(): # Arrange im = hopper("CMYK") diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 6611ceb3c..5641879ac 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1027,6 +1027,19 @@ class Image: warnings.warn("Couldn't allocate palette entry for transparency") return new + if "LAB" in (self.mode, mode): + other_mode = mode if self.mode == "LAB" else self.mode + if other_mode in ("RGB", "RGBA", "RGBX"): + from . import ImageCms + + srgb = ImageCms.createProfile("sRGB") + lab = ImageCms.createProfile("LAB") + profiles = [lab, srgb] if self.mode == "LAB" else [srgb, lab] + transform = ImageCms.buildTransform( + profiles[0], profiles[1], self.mode, mode + ) + return transform.apply(self) + # colorspace conversion if dither is None: dither = Dither.FLOYDSTEINBERG From 5911b8c30b72d43a17a3456cc513ff4cbffea66a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 8 Oct 2022 21:20:31 +1100 Subject: [PATCH 052/239] Updated BC6H test images --- Tests/images/bc6h.dds | Bin 65684 -> 22020 bytes Tests/images/bc6h.png | Bin 7171 -> 28685 bytes Tests/images/bc6h_sf.dds | Bin 65684 -> 22020 bytes Tests/images/bc6h_sf.png | Bin 6378 -> 28732 bytes Tests/test_file_dds.py | 2 +- 5 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/images/bc6h.dds b/Tests/images/bc6h.dds index 92c4b35a5f67ea0e935180f3fe7d628205b999fa..77993a0c1ea1909f05d622e81503e1fdbab63635 100644 GIT binary patch literal 22020 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 AXaE2J literal 65684 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 literal 7171 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 X83;_UN6XkhxCbb|)R3>1g@^nHyZ}oM diff --git a/Tests/images/bc6h_sf.dds b/Tests/images/bc6h_sf.dds index 95b085ad16df9d3da52fb3210bbc06f86c7f2acc..2ab1b195b19056df32cba742c29b7ec69b89c79d 100644 GIT binary patch literal 22020 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 literal 6378 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 Date: Sun, 9 Oct 2022 08:13:35 +1100 Subject: [PATCH 053/239] Updated harfbuzz to 5.3.0 --- winbuild/build_prepare.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index f4515468f..ad7b1ddb6 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -281,9 +281,9 @@ deps = { "libs": [r"imagequant.lib"], }, "harfbuzz": { - "url": "https://github.com/harfbuzz/harfbuzz/archive/5.2.0.zip", - "filename": "harfbuzz-5.2.0.zip", - "dir": "harfbuzz-5.2.0", + "url": "https://github.com/harfbuzz/harfbuzz/archive/5.3.0.zip", + "filename": "harfbuzz-5.3.0.zip", + "dir": "harfbuzz-5.3.0", "build": [ cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"), cmd_nmake(target="clean"), From eef4d1ced12119af1a8137adb1d5c1ce4d95da0f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 9 Oct 2022 08:48:07 +1100 Subject: [PATCH 054/239] Moved mode check outside of loops --- src/libImaging/Paste.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libImaging/Paste.c b/src/libImaging/Paste.c index fafd8141e..acf5202e5 100644 --- a/src/libImaging/Paste.c +++ b/src/libImaging/Paste.c @@ -432,18 +432,18 @@ fill_mask_L( } } else { + int alpha_channel = strcmp(imOut->mode, "RGBa") == 0 || + strcmp(imOut->mode, "RGBA") == 0 || + strcmp(imOut->mode, "La") == 0 || + strcmp(imOut->mode, "LA") == 0 || + strcmp(imOut->mode, "PA") == 0; for (y = 0; y < ysize; y++) { UINT8 *out = (UINT8 *)imOut->image[y + dy] + dx * pixelsize; UINT8 *mask = (UINT8 *)imMask->image[y + sy] + sx; for (x = 0; x < xsize; x++) { for (i = 0; i < pixelsize; i++) { UINT8 channel_mask = *mask; - if ((strcmp(imOut->mode, "RGBa") == 0 || - strcmp(imOut->mode, "RGBA") == 0 || - strcmp(imOut->mode, "La") == 0 || - strcmp(imOut->mode, "LA") == 0 || - strcmp(imOut->mode, "PA") == 0) && - i != 3 && channel_mask != 0) { + if (alpha_channel && i != 3 && channel_mask != 0) { channel_mask = 255 - (255 - channel_mask) * (1 - (255 - out[3]) / 255); } From f9a3178bb34e6b28bc46d42ef88f5069ebabde32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20S=2E=20O=2E=20Bueno?= Date: Sun, 9 Oct 2022 11:47:24 -0300 Subject: [PATCH 055/239] Fix #6652: Handle translucent color used in RGB ImagePallete --- Tests/test_imagepalette.py | 9 +++++++++ src/PIL/ImagePalette.py | 8 ++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Tests/test_imagepalette.py b/Tests/test_imagepalette.py index 475d249ed..0583154f7 100644 --- a/Tests/test_imagepalette.py +++ b/Tests/test_imagepalette.py @@ -50,6 +50,15 @@ def test_getcolor(): palette.getcolor("unknown") +def test_getcolor_raises_on_incompatible_color(): + palette = ImagePalette.ImagePalette(mode="RGB") + # Opaque RGBA colors should work + palette.getcolor((0, 0, 0, 255)) + assert palette.getcolor((0, 0, 0)) == palette.getcolor((0, 0, 0, 255)) + with pytest.raises(ValueError): + palette.getcolor((0, 0, 0, 128)) + + @pytest.mark.parametrize( "index, palette", [ diff --git a/src/PIL/ImagePalette.py b/src/PIL/ImagePalette.py index b73b2cd9d..e407bbcd1 100644 --- a/src/PIL/ImagePalette.py +++ b/src/PIL/ImagePalette.py @@ -114,9 +114,13 @@ class ImagePalette: if self.rawmode: raise ValueError("palette contains raw palette data") if isinstance(color, tuple): - if self.mode == "RGB": - if len(color) == 4 and color[3] == 255: + if self.mode == "RGB" and len(color) == 4: + if color[3] == 255: color = color[:3] + else: + raise ValueError( + "RGB ImagePalette can't handle non-opaque RGBA colors" + ) elif self.mode == "RGBA": if len(color) == 3: color += (255,) From 397167569a3ec91a787ffc3c43ab6cec62193e18 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Mon, 10 Oct 2022 09:11:41 +1100 Subject: [PATCH 056/239] Recommend raqm for non-English text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ondrej Baranovič --- src/PIL/ImageFont.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 6a66f8a71..60d4ca97f 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -948,8 +948,8 @@ def truetype(font=None, size=10, index=0, encoding="", layout_engine=None): If it is available, Raqm layout will be used by default. Otherwise, basic layout will be used. - If complex text layout is not required, basic layout will have - better performance. + Raqm layout is recommended for all non-English text. If Raqm layout + is not required, basic layout will have better performance. You can check support for Raqm layout using :py:func:`PIL.features.check_feature` with ``feature="raqm"``. From 982d7c49e318339444fb53d29ac04d19f4a68691 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 10 Oct 2022 11:46:33 +1100 Subject: [PATCH 057/239] Corrected test --- Tests/test_image_access.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index bb09a7708..a000cb64c 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -345,13 +345,14 @@ class TestCffi(AccessTest): @pytest.mark.parametrize("mode", ("P", "PA")) def test_p_putpixel_rgb_rgba(self, mode): - for color in [(255, 0, 0), (255, 0, 0, 127)]: + for color in ((255, 0, 0), (255, 0, 0, 127 if mode == "PA" else 255)): im = Image.new(mode, (1, 1)) access = PyAccess.new(im, False) access.putpixel((0, 0), color) - alpha = color[3] if len(color) == 4 and mode == "PA" else 255 - assert im.convert("RGBA").getpixel((0, 0)) == (255, 0, 0, alpha) + if len(color) == 3: + color += (255,) + assert im.convert("RGBA").getpixel((0, 0)) == color class TestImagePutPixelError(AccessTest): From 0b2cef5b03fed477f6988ee60ef5d4b7a6084a38 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 10 Oct 2022 12:02:10 +1100 Subject: [PATCH 058/239] Updated error message --- Tests/test_imagepalette.py | 11 ++++++----- src/PIL/ImagePalette.py | 12 ++++++------ 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Tests/test_imagepalette.py b/Tests/test_imagepalette.py index 0583154f7..5bda28117 100644 --- a/Tests/test_imagepalette.py +++ b/Tests/test_imagepalette.py @@ -50,11 +50,12 @@ def test_getcolor(): palette.getcolor("unknown") -def test_getcolor_raises_on_incompatible_color(): - palette = ImagePalette.ImagePalette(mode="RGB") - # Opaque RGBA colors should work - palette.getcolor((0, 0, 0, 255)) - assert palette.getcolor((0, 0, 0)) == palette.getcolor((0, 0, 0, 255)) +def test_getcolor_rgba_color_rgb_palette(): + palette = ImagePalette.ImagePalette("RGB") + + # Opaque RGBA colors are converted + assert palette.getcolor((0, 0, 0, 255)) == palette.getcolor((0, 0, 0)) + with pytest.raises(ValueError): palette.getcolor((0, 0, 0, 128)) diff --git a/src/PIL/ImagePalette.py b/src/PIL/ImagePalette.py index e407bbcd1..fe76c86f4 100644 --- a/src/PIL/ImagePalette.py +++ b/src/PIL/ImagePalette.py @@ -114,13 +114,13 @@ class ImagePalette: if self.rawmode: raise ValueError("palette contains raw palette data") if isinstance(color, tuple): - if self.mode == "RGB" and len(color) == 4: - if color[3] == 255: + if self.mode == "RGB": + if len(color) == 4: + if color[3] != 255: + raise ValueError( + "cannot add non-opaque RGBA color to RGB palette" + ) color = color[:3] - else: - raise ValueError( - "RGB ImagePalette can't handle non-opaque RGBA colors" - ) elif self.mode == "RGBA": if len(color) == 3: color += (255,) From 995625325d03a3737164e0454e6792b1d70a7d57 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 10 Oct 2022 19:24:41 +1100 Subject: [PATCH 059/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index c3e60acff..55eabefd4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Raise an error when allocating translucent color to RGB palette #6654 + [jsbueno, radarhere] + - Added reading of TIFF child images #6569 [radarhere] From 78d258f24d1b6a605754af9f8ac57b665543e8b9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 10 Oct 2022 20:48:18 +1100 Subject: [PATCH 060/239] Removed gamma correction --- Tests/images/bc6h.png | Bin 28685 -> 25223 bytes Tests/images/bc6h_sf.png | Bin 28732 -> 25201 bytes src/libImaging/BcnDecode.c | 11 +++-------- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/Tests/images/bc6h.png b/Tests/images/bc6h.png index 422d47ff2a84b340229108495b20b74ceebc0a57..609f114890c9136de1af6353190e607c0703e90a 100644 GIT binary patch literal 25223 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 diff --git a/Tests/images/bc6h_sf.png b/Tests/images/bc6h_sf.png index 14fb4cae1da24365ac921df516892ff4c98f5fcd..6a3b73d5fc97d185e83e9075122d35e9ef6e755e 100644 GIT binary patch literal 25201 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 literal 28732 zcmV)tK$pLXP)qY-F!PY8bCL4AV3f#0VXh05~Y!31>3SlBiWv3k3WacZ{Fy$BnR1} z=g(mjY|FAmiLxolBTb?(IXNx?GYJuPk%*5G1GC%SUGss+ztxG&FAdod$N>7R=hgX=fHbdib z#sKbns>b12>vT4*N1%N3;~#tMul`Tp^ZIMg%rDyh_F8-XwkMu^={NrHrDn5P=)#R& zzjtNyKmYjO{4Zbn#*;5@)LSbb`QT%p{K$Jc^EEGUm>dPs=9SH#|Cyh!NA>3MPi)x& zT!4v&@{K8iS{*<-mL?m@plPz9^sI@x&N%=U#yJ3B42;E`abcRulg<@HjvN8tOn@

GVgO~*ah~St51Hc#qz_51^0HX2|j4^;Q0I(n;&cQi+C8Q8sfN?Mo07nc2 zLm&qN7-JFugJsO(x^p{%0T_`ZA_7e=!yOqg5DG`Ox&tETjMYy0fs>v!MJ=c;ADBPA zb^5tyzxuIHyzfsx_dhP0)ni8wAHDg&wJTfkC~gMz&@aksMWHtw?f&tX|M>FN^*e66 z^})NA4&HJI*B@KETXM9u&{zPl74K}tu^&YMr1TmXApp=rfXr|4A{`RB0yJ0E{z6tYl6}IL;U`syI**lfXDQ=gv)OO%)&Zm{Ig0s#s z#sp^o11EqpI1mRTPQ*9^B;%HWF~%8Z0IW04YAys|2ym1+GDO4}0}&v}8OO_;G>oC* zEZ|3xE{t=Q2>@8XyuE&Tdu{y+fLhI8KesiE$6Mo05C%&tOY!zbqZR-dCMHRXu+~-5 zfBDtt&YXMwn}25h!9V<4Qy$dY^&kB74}RuzpIJNggbE^^B}T{Xx%u9OVGn3Go0l`& z=&VG|LP5*hT#lg(wghm{_MfPuiqB(t)F0w z%8kOgAeWP~<;72(IyW2+jvl!mKpf|QO0ran1yh*uc+7xNoR#O0O6Vd7prl_E#ct9s z-+$^W&#Y~1oVf4A7r*?)G*3g>PDfk1$oJp+p7-9h2;hn5Urp24%36Ok?2m?*F21;W z(*ppSwR%6vRCSAvfh3o{5E+T_I0+)fIPr4Lg8^_rMLtdd7&$No4lt@7lE8UM;yqF0 z9I=T&Vyxr=061HUEDmynN`eqEM^v7VK)@b5B`9M|a?Ut$29T4S1Y;cBF%YpYV= z+jndZz}800dBDV+u}+D)L=x3})vT|r?|kOJe>NTV|J#30YhmaC9u0o{ul*>1uYdik zYHr^LKls5oj=%V)U;M65f7i`7-~5%Ye&yW7tJ&FaIWKJA_E@_;KgeQ8zobuMX{@F2 zSk$VK(bcM9jEDmS5Mv-`IhzRE@_!|PiOMC#z$SzvB5-m2;+%Zk8OJ!l85kU3$Z=;# zb4QLbhEhJ1JRmSIIprbeoSd6ErFF0z+yP)5jF;R5vw*P9IOiOJ3rA#`<3a>naAS zR<8%+bQg>xXg4A`N^+f9>3JLsjLOYBp*9Dsb5uSn7?tb)dX)>tsvS{k1Hn0gh)Pj3 zE1*j0Hu)@2SQ1t<#vJ3E+iF)a&K(gWUVewO>n;YsT5`@fIL1Wzg~?yU^&jK_2+j+A zXPwan7u6>{DFL7d0CA=O2&MdbO?YZN%+n$pj(Px6&5hG7UjbNHTq<5rH5X6Qv=*xv<=p!o!S5Dk@{0qPRgZul~``AG!7Bt?kXPfBmbSxw(fv^2kj$-2~w2 zr=LFg!pXsCaQNV10B291z3-0uzV+<4zWn%?ANlT&+FzzWDn3n;RK)swouQ z5iv)M98H$^WU&*#0LM$+=fFzkOhlz=3nH*ea}YTqxKa*G)Bs}>YZFv$GtNO%r9U|s zQ0gE+27&|u#__1x;G>K;Yt1;%TCLVk z|MZU^J$h71X-p<%aCPTOz0NMLzkc~kum9+eed@$5hnEh22*Arv{rPRjZ|!YglPWlN za7p%h4}I$U$HR?0&97a4;j7p_pP_w83bXTCqhU7O8R-86-C0i;M~M<;&~nb&kwDs zOn5GY)f#~GDx<^cL)JVY-M7fy`2ESj)2zDtzxxlr%kzBSkE}JV)?AW~rIbO~^gMq!-0gNF=Z4O?B2P_G zNa;DUCQp1%)@y-IV^2a#;>?jtttIQI~8u&tz`B~Prq94v~-pid6tYehrRK5m~36!?QIV7G%fP+`jxAG=x4*M zNV867DXQ1IzAn!n1eL<~lz8xt#ZSEdw!`~6&>5Toxh%<>twxsQB~eid{*q{WqNcL( z@fIphnK0b~T(F5|0q2Z!R+%u2&-i^4HnTm|y5sx)cYW&Px8HWxzxe0> z))=Eb&;RI0KiF(`fwA(L^udG{!jW@En>0?Plt!oItdJgKTwm{7_@1}-V)A^U?ec{<>E)=l~!&;ig0HRu^Ja22W z*J#vpt$jZLAUxp;!v)WcDH7&qZaIAEJo#gG04z5!l562%ZF6P+O8Ec%nGZ{8jFHwR zr=R)?fYWDQKJ&uqOY2v+b`ue-NZ(I$lUni6BOg>soIQIsYD7V&+C5Bg06J%v&p&tD ziTm$==$@y(`BG8Xt|!iX`>7|DxaE#}yDR&=mHAS*1JHTVtcQ<2aO;=8`Qljc!em0i zXn<5{12H&&Gk{ZRfjgqo=W?K_eaZKnUIqe{`WeI=;9O~)qBI>#PrH-=rIS{fJq%!w zeuVG-fgfL4>6Z6buh(CD?X~r*7p;w?^nk)R=%QGd@4oY$_dNOJlTj0-@t$|TAHX-C z`lIo9yWQ#Jx%O+dZg+WY?Sk(IQe5Xk1K=)qR9Fh8Vlf#dokQ>OI|s}C_m$87B7j$3 zcgH;`2Nc9S&KNmzLRl67kWmf5cxU&UfA}k}yztVqC)ekie)*iA z{I2hm@&P>l_)Bk|dDRbOniW}|MIo0MzUy7bpLpW&Ho z9Hb?0#QDS_WCEBtJ(IAK2-^Toe&gxKzxu87YvUs;_4j}9J)6Vm;+f~3{rclS`1gM_ z|D$J*96ru1hh7_MKqcK-G9YzOxpI`#CQzwpYb!1M2a#{&R#-UrZVsG3yWZg}?U zZoAW7SiJLzC!T!wyB|Js)4~_O^wnpcfAzup?|AgF_uq8yhsyIDS#GZHZir~f_hGan z@KTISA)&=|jcTnkGVd7UfM*!oz^3LAwkU#&kzt~;&9(?eTzy8-h1z_#UZo9Fx zZ~oS`D^Cl#Bh+~Rf!qJeU-{rdr#1b~crgC_Z+}Heq`4JIFPw%%K~^uplO|M_$O>;7ZM0VMsN$Gt!O@>i~0UH|G6FGfML)u^Xg4&ceJ zKhuc(n~$u1^84TO(T{%mul>z~0M5SpstAh?|sNneCtYErTsn)!LUIc24Y^2>nv2BDT)c;Sv^tpHx_cK@ShQId1Kk&|X z-`46j^DOrRAHeSJ=nKE~g{_^<@?5>%MyIosregr(-r$}G?*E&>u{&>m@*6Mq27ML3 zC4dT$)7BN=dh*FX_|oTVK?A@}Kbva@MPdK*Z~x0rKK9)JKL6Xl|C4|Bf7yTR_?0uK z0qj3^+^FcIKm5Z0zW1+h{KcRA#pO%aUV80~<@vc2cO3or_kOIqxCFq51+aPH(oJ{V zkz@D5sk6Il8&RWP3!;Da3qL>D9X|cSzuMeRfAurJ-s-jioI7{n<(FUH-QB+B9gjgY zAS3|ixT7*6Kp+BVmDh_ZMN=(xRz>Ki0;-}qqly!gqm1*+DvYW9#2A-pYP$24{CV;D zH(oz?4M3rF=%L7qS5KYk%++tYWwkrsou2W&ci;DoXU=9i@Ar0tpk|DYqWIAM`|F+7 zxHnima;QH@zV)rA0n}@C0Ac7ciUE)_blja*TUubx$fNjwX5r&`;Fhe z{~h-Lc<_Nc0lfU&^DqC}e}DABl^XC*{?5FTb4Uv1F#u#p8eRwHID|;n+<}Kk*Ykvj6aY z0QFY=#62e-eek|Ne*7Cj5VhOwd+%Mi?Y3hN-*pdw&FyUffBvO!T-~@Ph3F0X!~Ot( zprT}e(+QcvS<@R111|(%tlr*9=jXy!!!HWE&5zu^jALf`LBHY z3t##6cR%*f+@a&Y_s9P&j>o-zp^XV^3xmN<7}f^opTY45;f1i4a|>d`Ef^RRRS1-( zI=ak%R6#pdnwRWG3u*GnnU{Ok)O8=0eq)hA>o~Y@Ve`tBb;hlAj!9V*b>pP;TDznC z?73u=slw84{o3OZ4<3E^mXH3GPrh{W;+6I9H2fgl&FVfwp1L7BytHt3ZS93uw>1{D z1yqfzv}u#R;{jKkCj|`*%O#2^LT(10WVJx$Sndxoiczekt<_cz>!cf5|C#R$qFU&j z%?7dbJ?EV7g^VlszR{U#ME!KEe1%9|7-d3mt@S7_(2pCn!JmKqh1PucgCBkDvA^*z z&ZqzWJFmR<-~asCLVvs9o?^^dX^fF#-S@Gx{U&c8?+{R_-j+Mhs}ghK>%cbchv4SVx9fYXa7uV?fZe%F#s?00Z7RMzm}wN zySsSy&CMsi{alf`FMjPW?!EK&<>l22S6^6LU%!0i@=LFr`jNl!sZYM^T>u)s|LA)j z_|l(!1Ax&QfaiNaOr(n+08ys%K>4{T(sUGc8l!Wi+nHOVyhQN^~hQ~%vdv75Zsxq*BJYj3|QeLO;vLo*(eA3m_l$u>b%}2+o-!#~=h31UHo%L^H{(iC4__ z@{s_*`Heo%Bt|jK>$6!U!Rw6>z@RtY?De~ID*&`LoO5d(6KuCPG}cC84Zxp#@wqr2 z0;tvMFTV2H$=BbILgqyVAWp|;PhQ5{+=YIc@bJSQzo$DlKT7%lbQ+hTE@Q?;F*2qQ zQWB9Tz1$SO^tkkN3LzmClFdFBB2Qv2q#xC`Hnx?g0Bl?v^m_d~F93MH&$$qsTWf{j z*4fDSBxgmDD0Gj~d zQZga96oN6ug>4p0es>2 z{^(!)+`l~i+WLn-^v*`q*0unkypSx^}_+^uDL&szA6Ix#B)ndC7s zy|6T2@<2)fz&V>`S+CbKM(aYSX`1JGZd|6#IF7SC?+=o#&AybfjQTcfwQ-s%Sy^I> z`Y0Th#WB~x-ZEF6UAc|{H>VQrx#xG${L+E5fV$GQ8B-}r);TGI z+z^qp#+8m@CdWzPlpyXSby-e%I3JE}Y@`vKbIu)SL>xj`a*o3>NLUVXf;&J-4!{@- zKoe|UD9_i941@53VX7^caLN}VCnzC}Hi2Y?^FhRcW{7ddI9OrG_qnmm^Q1P0G2$G1 zR4dnU$)}l!Zxw7P|Lh2WU?`*26N71z*kPP8IAejg03C?noVC_4oeLqA3czKWX=6rt z;e?dhIZmny@-azFGH_yiP-K7h`%e}+uZ4|4(m#K3OG;7qxYmW3B(NQkV9XIQz*?hJ zK#X+ZoF?H508Fr83O53*wdLLp!$50|Nq!2`_y9;7JC&!BPh*W_oMlWh&HzWw8DoSH zLWs;+ZH#2ra6x39bx;ya75Nwd=R!y!nV5=>(z8wq=?SSp)>@DS=_1PjaKSm}u#RyC zTdrt^i6ENIu}u7cO12YKAR|`g(J&G8tfK!kt+pqS06;QaAM&pL=7f+$q;&>x#zXE< ziAXj{tz4fge)IB~TG+^oLI@cJ((`1JkU{;`8CxiUq%4xC1y-02~?Xth0_-VR^<`=&OUoRGRJh;au9^%DI+Y zDyAoEwy-3*uwV?97*U#;rY}9lVf9f)P#9;~RzIzKXeiaGHwzUtl=PG*6=$65+>Fu+ z%ax?*gwQ}(2ha?C5~39Ia6aa&hyNeK*~E0t{ytH(yBM+=GxumIm(Oa=Y@3HXu^Qua5zkx&2TtOWzz=` zh7o{fD-1#(KZt&)=S zDmzV%7~|Gj&H*bqkpsBM%p}`q9ci5k;R(*YR@>?#Pm=jssHDpBy!woBf=}^hK&dtf z^}t`QcMiSd_IvNSd8x4%bdrSuph6FT7twfkRKA}MlRd`xzyuY*hy_sqAWO1(U0GYL zo=(u(x7?W9w|w{2^{+g8t~VYJ(!`gJPnRDTT4yxlLvd-yDk8@@-;?&vu?md|4hF{D zThaqlL-mHEObJiZnI+E{XHxM5r1rS82mc}f##&LXcL5=|F_yGuCDsRmG0rEll`{lB zv$jyOnI~5`=gRku+YS6+6psK1<^aoJnH2yG(DqcoSzY4x2ktm}+w$S|K2Ka{l|}7l znZN=df=ZhC^@h&$9;J}(F-(O3be0CSARmoG$pB=jvmnpohxa$?Rx(RL)FPg-pQT;bT zk(Qf4-hRu&XU?57rjT6cYRgj|Y2$Rxi4zIQUBIF&9eeeLCd&YlAgcio6eACeF|rKW zx^WmTrir$sy|9y~LjbAp;?x9QW2@i{Im=0K5h!7ty=iIr#G(EDI5`~fTaK-C>-Bm~ zF(FB3oG~GU;DQr_t6U^T!V5i9Xj=%z1Y^t#eX^9NEC?b1&e~L)sNV4Wj>(HK>ZIeI zkkYxhsbrdG%7-z<&Gj&R@TSAd-o;B-zI`c$gAc@U;ddHbg*FFbh{z)%#yNv6mtCSk# zJlDPN)|KOFFV`?}3$6okrYW3xzyLaO;O~DEq8Ny8=X2oer);B!Tl$W)O-$~1AvsmIZOa> z1WjaJiJ+dM-m2gOuL3)agO?Q^<->Af6HF=NrC$s_4O3L#<%~1N90SO~k(0-6S^mi% z`<`F@%pbH{-5>bApITUG{+oaIi+LV5>ow1lj@1BgQl1QRlUidO*NhoLG?7$_G0T_$ zV2s<_gA77}6_#_B7X<+6HB6SaqS{=ub<=_UX_hBi-`w&$tF7|8e!wx2=E92rSncw- zUx^n#WRjOiGUv!yV&urVIF3ssnH%MfTs|0Q@gPkS)(!y#Q3N1P$3iIWBk;Vu&_Z}l zn41oE2Zrxld;M~Xt`8elSXQf{xaXMVTre(;vtO)?8`B#7K4|S0Vp^hJ4I)41N-}~HCzUMQ> zw8=RWj;wV?a7hx(mT4mgCz%qZc?v)(ZgquBnmvfzFp1~tO{GMdW(Lj!`Hj)IH;Nb2G+ylN8zp@wayH3rS!ZNvl{ymKgVSUjBsWO4E0iPUa_3Ut zt9Js?%Um<63Bk$PK$_KgTMPZdFFYoh=n7S{LIUtRg-vr?XjpARB|RkqCPHL`P3hW@ ziU3l?xnPWeCAFxflVLGVN`U!zJlfn`1E8I(^~d=jZ!a#%xo~Xq#^UPk)hjmLiNYXi z*Dqb$%0lIW`Gq-6{bAh~p_AYk1CBANJbuS9j0{FpCf9%&j`+o!Yzyt4p?1T6I z+yC@CS&;xBm zl<$XGuCrWADSYJdRxjV~pDRWkuc0QW)BXDwrSzmy0BovN7zyRi?yw>or)iNUm8m)! zjW1nUi~K8vG3$bRo&?Zr)JXZ=W&|McRN>d8C$qwYzG_6F5-5#o=Lu4b2u=uQX`1_; zq}&DFD9mwstq9kmF$r;NcHGaAj@l zwukOMzHs~tpMPnT^!HphvZ2(emT(EkN;VFHC1cEZoI0LOcVeT~D9um+agn#8aQ{-P zSquC9abYZg451I?MWKW&tdo?ln*i-)Y>tHGF0}xfbBkNoHn56Dqg(O-&Hzw;B<0Y= zgDl?8be;}#09V&HuWnqK>l|2FUTC+Qy3p;&-`?q+yS%>LAHDzGpBN2$FTM8S;@q5A z3L8-ncrt+OHd{c&0P7sIG1f{UL=}LeStJh*(*on|crpp+cN`S#Ax-x};BgJS0zfZaH`jLneD@E1 zM1-sV>EHZ|j)$!QQ_3Twlu&l)thPcmLND)h^zFB{m%4oW+IoL09*%q4+uJHyulIuTmW$CRa9{f~bCV`ooY z(s_31rke--?NJf?8N2<+{v=E9nJ-@0z2$sJ5{OgeG~-pJF&GnU znhL$7n{hG*;GC60mB<+-1b_$LcW2aU%&)F~`SV{(vm`HaKUj6PtW97-ah+*x z?Y;XJYF&1y{^TeE^p>hhFFv7tSoL&i&-~{s4fpub#PZ=JNS#FOA2s(>%`g zJ+~dc>pk!AmzLtus5ajKFxp<{1^`C8JtMPhJnS|@QtHZUuTsDNweP%o{P4l=ymWqk zt}z&BZK_w>PeCZf$_ez&qZo2e%$MRA1QlDDaK7A><-+ z8Q_Fe9_JZVK$AUB48WugU}9&o>;12({F$JMW~wSHn`%;{DFO8R>D8-O0DSE$Z#?y_ z7XYN`_=TsRe&mC9`hMh`t*_t%?|ciYiH!h56a19#jr9*zL4 zUt3=vygnYs0Ak6I1ly&m3rsMg=M8=Kj_`l9seM-Q~SP5S1k)3@$! zE#7)>`FWWeCwZWJ?QCV>&?Kofv2ZHj=ugVRl&riE1m0-r!qi3r<6ff~a@QUw z$(gHHj_uoj=IToT>h;>|=hk&x%pG3bzp~KoHeY$+wPqsMzSr0p{cDLh&;gMSo^>%x^TSad; z1h94XwdAyYxV?BE#|zZtMC3?tHqHw#oGEUYO>AAa8%l==Y=Q>bTkJDIBsOstXkwre zNWSs>+9=WW=0b0GM7|88W#F1I$*|W2@buGXf9Jn`>E`?2^`S@Zc<{Y>yC$3zU$DTl|#$r6D~9tPCU}}f&hS!^5mJ< z7ET=G%_abrE$*Iwqb&hk*iH`Iq2g=TimX`N zx8g-L1FMZbcz9o+{J4KLTx#{tZaQPrG+z$=#ZGgyw_}H zTNNzTX0Zrresg|h-w6O=YblS{035#Om`U^QzMAkuo2S{%=AhTF&39xN~dOmxN`B4?@eeW&~> zbwGa*D>b>QiH%JGa4y0yID2aCTTgv+t{ufdmZjG&U(EBkR-fl2h=^Irb#dX!*6Ax} z1K)q}9gnW;oAq|?jzvb+p=?A>%*V;>qfkYsqxq6;t z*7N4~uQuB4xw%6C{6-Xvl5w$>k4NqO2UJ+^kI!~j7W_tA1T~w*KooT6Hr_nF|EAlT zHy;rf*RP$vxOiw8K;GXm$+*;270+tQ6B8hQI6r2&?}yC;tJ#%8g&_dPrIEG5WK4SG z98KbPv+flp2L8;S2ooNFXqqsda;W;s>?tPalu8M$tx_UO2QrEzLTzfC;xx_v@b|tL z2K6L^VR;aQ+8Q4jJ=&Pd*!lmFvK{&{}iO8Q7?AI&Az9`^tgSs{27Z}(b9 zSA6az!vuh)2+lP+t*dM2ZB42u0^qflU`vuY#(CMKgY6+Lu3OR9uP#>Ms2xq})c4wV zlv=wCSJ0$#0~6a-W*Ea$7D5Ge&YiKEfmsAQNxFUEDra?%`$(akaRkLfPVg*Jmm1+> z#-KxpFwsVUGFxyFKnYuDsRHMml-4i$XmzqeH28LB>F(`|vc6I)(lp)ZaV}e`PQz41 zb9t6T^=6W;X@5sYDS%#bMTogZqrH0UL~U`O7Xw-59&?Mq&2BWOk2(Du69MbIa)h)&KJ$jkwb@X zyKTrQ@Ks>@+bl$3tmiQ<06P)#6Ujb}Lx*+{ACdtakvEE-Rl+ z4m&c=8FGRgcfvWcj+YoOXUQq3y|b3_L(Y9C3u{$SBV&=9sMaoulylCQSi1QLfXvvD zY5|yEoD-ghG#d;D0E#?k3IcvqZvw~)+nnzzFKEm!hKnmwbl``O9JvOd>h1K*>HW9d zEPS7Yqe8RLhXe8=Twd~bdil8L*E@DLr!tb(s6ol&i0ZA*Q4gjEio~Nraf0CYybMqv}a3M7;BAl z( zK?RC&xmOslM_e}bzGjWmD&aDDiza|YfX}w#5P-Pun-Wx(PC9@yVc-g|oC{0>r`1o+ z1(@StmMn-ISqB4W;9LMj&M_$|Gdxn{s3=k)1Ls_x54Ns`tM@(j=^s7TU0MOKbj!_w zNVm_PJ$&du=g=Vl@tddeWETJnMHGe6iDU6#R19|g<^m7`z$ihUCAN3=h3A~MR%@Fl zT>6X~iYP0l!~N)4}r<5h}xMh};&cSb{SklV{mVS*r<{&kiCL0LKH z;4E1ON8AC(Q3&n}aBz?VF*k8Dm_>=Ba75fW=7=%J;R@!#W3r&cqfr!uyx)4+*G)zPgp!R7bf%f(ey!D7%11^2$_CS{K7Rn+0-tP-0w4oxplY|=^Wd2mU);EG4Y-yiLja4*D=YgB>5c_J zjloZ69fq@C4Ze6P1abv6MI!IcqJ23{5~a4F$X8V^Ev+GdP# z5PHL2r`h?QkKeI+xTA~A6uFRFVJbWy|uNnZ~qzqAYq>4Wzz^^jBzHoC_zwD#KUzwzzt%k z{NfG8GH(Y_nfG00Z9( z-gV#I_uX^9GM50jiAosuc6pX|mlm9MMV4?On3MzHapcuu@ zX`>A}MDaS~yG0SVv_h=Pk6ubph&GnzCt;>mFa; zN_R{$Y7))@1mfV>YoNs?$P&{;);U3NAOal7fy25o@-b1uoPohuErk-wYNHi!`>~rp z@V@&2Xp_%gJ{b+`LS#u2H9J{6Hbw4=0zeRWOnCr|)_D^9Q7vqDf}jJyY;A5_xsq)4 zPCxhU-N8=i`?dYsDhQj+CIBUBSvuZ+@oJIfmrkEvS=x8*$^~S_!a}=+Wt$hq6uhp4 zawaci7|6m%sf^KQPriI?|LswyGuXJgu(a>RVVfqh*;k^pCaaz_Y?g-Ai_!|B%B0*& zjs0as#q2Fxq1pku$XfNF(1|H*z0)4L9l-Ylm((7Q#`UI>QfZSBktqu4hsqk|@f65R zMx2BY4oE2txYG_E7n0-tdv5!{Bk%G8=9xkmO+4pZ`pzavCLs#x3FU3CT>$XL$(I+F zms+CX3g-o$s(S^+niUet76oT2>uq0s{p5IS>%NbEa{bD=wX@^y;D59*H>r6;VjC0E(Rg1G&1<|#wGkh4d z4jkSxe!z6@gs3o6&KUw{f^$>Vd10F6t?a_G(`8w~T2)iRIq(X6T(0B2#Xc*VSQUt1 z0A#BkBxwPl(+c~89ge!M#_PS|a=lS+)Sc~RIuuGdYZ&KN=Ymn58^baHk{LJyCZf3DqigYOS&wegkhbbHeo>x>Iq z?R3y*s!dvFz3p@~*tooT;+^*f&DQ=Sw=M5KN&+@d27}#~Uw-Mr#mh@`OLyOOXJ>io zRw1o&KznYfwQr^N`m3+rrc@mezMrQFamNLB)>v!8urarKC>iv1UFEyGp5M;K+z~Im z*J`P9$H2?>S#P_fE3yMq>Z4Q-lWkEFewu)p?vm-(%iy*ww3SjcYTjIHew6BAe?9D| z{NsP^@ZqKY)wRWLsMvL~kX#Bb zWi5*1#LJ6jy(v8}h$5ksT3WKjz!}RW=RykU<%6O0ylgZ$|DD&C4lH)&_L(#{I+H3e zI;(eA06g-@2ksv1!lApoKZ%C`fHr{U%AJSq!;@eB`uR6rKd7||YhJ4zv?{+r2x*Pc zc^b9an_HKomDMC>{ajd@K41y?sf_q3B{5_9l8 z`cMAdpE`Va2|&ADE8L8WXZLQ<5B%}&*4kEIDphN>07w}EC@4^MpZOdB=fV_4G8nAA zadu~CXZ7Hr^1D(6GW4Yi0MxrH_3lcm)!lc~?Es=$bN0!aD|a3}a?93MueUYQX{>yY zah}Do$+Eo2gp_{N(s}B4yGhK}Qk72HS?9s8F&KLBEfm~%hPwUA0GuP_>LN;qjnWYgRf zzULoa+5e%Be6ViFvrZRADJwY7pt#$Olyu2hh&uD6NSsJ$)b9gG7M8>A3RswBIQ_;+ zzZQmb3tMa32luIQYGpgvJbmWe_SM734mvNik~87_u;Hu;nl*A-)*~4-YDREG~>2`*d&R@+uaDj`js86wIhmZo;F%Z zHP>Cb`;NOC^_G;<_dRRkDFnU>LQIg%-Ddk;cinxcT~~nuP^$%2n<6hfC8a2vq)ZxF zdH|5*X>YK5`n8v~FP<81Ts`&8Kl_c({lWRu7kAgTyDRemN-Sw!gaMK^TZI46Gd&857C_z=WV82Zt}cX01L4 zhXvdfn)M78LJ7heNVq&a}3Ka!yh z!kUy>v(X9m`9dn8q#lnGm)Tt!ZS6F+)1dEAi62GjBnK5W=K(~u z#`5a2iu|BmpFaMe-@Cf8d39rRVPNih-^1&L+RlO$K_CD#AuUY_404qsC`J7ggmOcM z;BDWb33UJM7+CHalI?EP-%Yyh=H>OZQ9dqfo$HNgJkFnb`e`X;z21t)ItWyrn_A?@ zg_T?Z&SWtn9(aChXZ`Y-pcbAe`XjI1D10fEEb<~wvu5BU0T&X0wMEH*TCEPC*BeI5 z!Q9;9ZTBAE$n?pJ7jM1ymZ;s3b_Af+ULLh};kT^OLdn6_#>La;x3+s>5CCsRN@#88 z<}L_d?QCsmW0e%z*fdK*-viL@wwueVQMY~Q#4Rcc!ggoa-yMy{N{CjwGfs=e`Szhh zH;t^?>5mviKn+|{*=T+OXkd(iapLyvIzdc}^!D;CGmMzVt7niCf5{$F&b70YWWEt`|2y9vXMg(gxpqy~B7dA}H{5`iQ_x~0 ztfZwQw``ty8Nk&yF7$SWahxu6yFt(pepqXJrZCCQc)XqXjkzNS4=%1A%(Jm|yRu$$ z#^y_4J z88+)XI~$8Di~UhN8jTu_hAxUCFMQuC3!p)c;0TU!C0ym@22RJ9uX_8JCY*PSaW;up z(67v`Jy0O`?mQvCgkW zVYgLg-A$qWT7A5|BP5UGEY8#Q@nuspalm}|Hf2})+zOwV;*-z{IpdfESZQ>WtChWl z2iBGm({h6l5zSO|&yaqi6H2f6tpN#n?#lVV3%>T`030@7_*de)hT37qYtG!skEv z=}*=ht?ixISQjcUHMtVRL}8o(AOPh|Ll;ZT^J+jG8_3ZcXXj3wP>qHQm;jSb8fRqF zP|`yJ?&i3J7EB7_l0=&7!RSc0cI1QiZLXzX_|hBk`K!A}k9w-8c|04A122qsM_xTJ z8swe*lfpeS;0VI|-!9hHrua-(5l-rtJ- zZim7n{S5uEQLqrTK#IUv0=HZ`Fk%242a#~RasF28i`yH56HfPcnBdKJ{lkwQeeu;103GLObn`UpZ*Pr9qs66F&PAFG z;?Yq0H2^}$)kFKjdaDFl8Olsi07$dcj0WS;(DQw-)d3)bAk{^;5p7%cZ zF2Xqg2p3@i5#*&76=iURKn$E$woeIn1C=6ZR(^2KO1(N;6->1EnVGo7^iS;i_ZR>v zxX5+Nc~JL#Bf@L})GsU0oDTcr&RqTc#j|03Sqg5PV^%vN!C;Dldy4CP(de%7_LFXK zp4+0do%1}u@Z{5nKlWkng-i&NmYj7q6C7nxJGT~qCkTKUS3RrAy5ZQQV++6=U#%Uz zd(;~OP|bR0zUCkpSB>tnBjJj|THTyq3Oj9~0;@A00GKfafEK_h2Vk-cz8VhFY&4Y0 z>#y(DLeU>0ny>l32f2cv3t5LBB0~-$0+-g6(;J-7O!`ht;G2`?p}>R(dy9Rq_0ojc ze1c(_?wBcKFeUlKyDrr+Pm7{ms{_b%kqpKFI&*aZ^{8Pj0g$}vA_#yn&TS#G1i-SC zt0e};m}%FRi&w5-Fpx<;xv(LVdJ^RVc9Z z63SC!h%xB_)))y0qdiYq%5&1hIadrwb6bZH#ugUb!Ft@vB3%r^+dMVTc{t7hkZ^{% z8sJ%57vb9T-&y(Y2ZSvsmBm)!1v)qs_P1k?znNuiFJw|m4A#A-WkOqPJueuoc~Lu} z(b_k5w@;*|xwyEszV3xS23Mk}K2CO{s2;TglDRW3Psapn(yVF6(oIm4tN<~b%L ziW_TVu(y`qCdf=|udtkT)u02TkHpXa3!C5S3`HL8q*(YX*ldjC%sib-Pja}-de{rWx%wn@k|9Dh%-2A@)U*7oK?~W z%tdfZN-Q!{Qos5>hXEraq|YT2hOCrw{mQkaaURvto%1XJljQlNr-v@!G?dRJiey){ z8fk9grN-m`s!Uv2Azfxm3>RG)Z;` zV*pX$H5#tJ+tb==Yn2-T=#9pOHtD!`d8=28TK7KuF2B(#ubvkA^11KqZe4!xLw~is z|FA1^?)fH9MG!)dEEhhrVq)$PV9C*6LbzEnQ`Ju8?S<9kr-a}-Guwms(zNHJ%4iN16Ublr%@>k?m8z58`V zde=9qK@~r(Fk64g4gh0@F#z1La)=7!1R&I08WIy@{692K(i`s zY8_LOa?PZ|;Ox0*WjPtFb(TD!88)+P7q+!&k+TREu3p`8J&J0+8fUqbzO(k3ulE?L*lk8Rf8pdrI_oz~4FhmV5I!x9j@5mx3$((D> z(BK3J%dK`~ft(>xuFe@IeFRI7RA(cHIsm^J=9x|=4VdDz*h26hzqET{I}MZwRRCb| z-~xc{t83z6C6$bOBLMpkEG9_~z)3&Naz8dMwDW0EI2sl%&I`_1Ckh&oANX=Uum9n%UfsC-(FY&3X2E!wl!0J^z%4Xc zmQMhQZwVz_M`%{{lDlr+}w%+ zp>O~)Ku*pj01L{&G6rMa*b7u@S^>otySsxUfEcy>=3b3e29@P(b94Lr!2dVDd9`{| z+eie%Y`n1C1TY$)~=6RCjb91fCpiv8wOvhQVmiG!rO^F?K%W1DyzU{X*fxD;FrhIvp+w6$}-E)_xyJLYfr>=Pq4B<4uo}R@KL9Rv24x(2PQ-TzC^W4nWWe~b})Rz)Lt*#|Axi(?9zW?UDUccbW z4bv2ioXHq>QbG(aSmM?s&wu4}&*aI{f#bK_`M@2w-`NP;$!Gu|YBa~zmd$x2z&Ljf zWjJ1OSG5^)L?wK&%EV6{FFr|8q8gM-Zqi~jbvowfT{R)6q^ zKYYjHO?kg>i{jShmG#S)c4P=Z+hhP2zyr{P0#Ja)FxI%5yiQ(j0SA_sFI-u_aOLvz z-+uGkr?!Tu;GrWX-DLBX)0!~dQ^5AdPWRxl-Re=2`C-IG-W$hW$OMebikk8q7z!Xn z;HkE+0k@!KsK`g04nU=HUmT*G7%Pn9!w#9^7s9n;JU z``cHadHm{2PkrTWjsB)t&8%P*gP`Y)cm{ap{g=kBAm;7BRO0>F7N z22K}77gng!PvDG!SB-0g;3n6TJOWSw${i7LAwZ6tW7C{70Yp-8>6o$304y&GE=1r5 z_rK%z)=(5E(N2i=tt{VtXZr1@i!29FaKQ)}gC(H}8S#QB)f#>!f`4Ua9Xr(nFE{pd zz^RmTtrh2-F=DJtDqUwg`&e$(`=b#6uZRYE%L{7O8Gs}g$`dM7O7b#stsv*4&?oYg z1mJm!3(;tV)+!ZveY?OBAtdPC+iSS2r0q_fFg?#c7s;;?8(z- zPo7?EweNoCg9neSSilY$_d+f`XNph?U5VvW9sA~vo_OpdAGmt?a@6cVXwHQsndY(Z z>ZY(^=%;BO_#QV!E%c-iq%-JTNKyCeTDfeH24N_rzq_`+aPtYjwUnihjp)>=Gii~9 zUNF1*W-UW!osvRIWr;+dN^(*tC@&`*sJWuZp8e|YKKH~|_8&QM(=8`fSN65q3j=E* z1gtIF@`_UGvohrZ!dtAOQfJUa0ueDlYU6}pj0Jd8)I}RN=vW ztIu7!G=d$I9m8@{BuY3biThj%N*w^QE-e`2x##=Nxi}t1kpKeDy~xXnG*J;qpqgXN zERoeYfXkOI6-9w4xaVU(aPnXM^v<=bM;D{q?dJJdH5x+892$Vut{zG~87K-sNWqol zo(Iaxc8kUsztP-(&pQA>DJ01o-#J-PACoGU34E=}>k>KZEVD{7CnT(av(N>1JD7Ba z%Cqe3sTZ%DKLsFcF0bsr`R3d2s0XsqY)@~dV3vq;2F9Fqgh@mDvj3=at`dQh<{pwU zI7cFf1$k0tMFC*2J6JljoTq6JHjFXCuL0QH*nIWOne&txP)-hjnG^;CfCCxFe8GL` z8)b}oI_`y}ol!2vIeJ2P`0fK=}a5fvtXX?zX${I{ET1L0MJQdV63%U+7(;9cf9|8zqOPhgao9#c0 zrmA?fV9q&1lVF@H=P5ZSD$cepd(J(3o&wKB`{ zW~;#&znmvfe7v=O=O;cgfAa$n5wru4C;8K_zT^i%ZVcFDBnhXo;XE{C8Fq{h} zRM~xlbAqQp1p_?A9Wl~FB7g}`aKQ>H`&I(!+Uw7*U3l4#>aEs;?|lE!6UR!jjF<-{ zL{SLObE&PSepcX@%1f3+hI2tgiZc+bqcm}VFxo|3f9s9jzy8I49}f*AcGr8|Vekw%ElV}KomUil5L=fg)&9D}te^+ZuHD%*-qA&#t6!rWOcS;2G+KuRV! z(}sX{b158dr2qOC|9zepc~+R*c9%OQx5_EcDbGm>SxwFbBhDuPO9wj-5>M;V6x3yD>r@j-}?Kz@!0c~=PMaS0Hl!q zq<@U%BC6E^?DR+T3tgRLMUw5m^~i%C{@d_8?oaXrTL1AMd?C#<07^>1dD&@rl67Qd zM8^tYIJ3M8e>xJDxC3)wmRN;jVAJo|fE(_Num!VPa8?fQV0q6v6OVgOe*KGo{>Oia z(V*1QrPP}u)m$~CmTMW(r_~YO2_lHvxD#rBxLt zhhwnS81`Ay1_TLs1VvCpoJiX1ErV&=BTbXnUOI^^!&LJxEI;yzAA0@L)tcvZm-c&M zsKTgLYZgTTxJJ99i##S5bXzuR2xTpXHN!#w)zhz4%rJ&w($p4U zoVaA16XQfq&vYa!6T-p~Ct_5wp+Xr>?^_5bB;(F#>x?5ISm?3+6`OVM# z;+KB!chDg%q+JYgCSWdFGoh0JySwZNA<0eQ!950Wc;?$hCv5Ryq;8 zJ`BsNi54)7TgJ-@@N!-?V+CUcgn=+hFvgf8vW^(z1?Lom&%S(eb90lNGgZfX4-os` z_1NmoC$4O6OX)ScOZ8@_v$C?fy5u*Ss|WVq*EyKzY`qx#)KC8cZ!ZC;8XN!sMx)U; zzxhojd@eYLoAiAalN`#lj~Jw4QkIY)CK#A=tO}}$a!h%dC$UVpvS%z{hzyY>;^3S! zZCzUEB1@COb|X@ik!H5n&-}&3%^hBa;cg5{^LO8WZ?jf2#u{s;({={CgD0MOQArgA zVQ<(6;QJoqyhsOtq3WMwA6SGAU4#DFpiShcbia1vS}SI8X|j&ettqs#@l3v%P9 zS|yot06ZlNZC*e1`V_8G${~Oqf9w;@xuvafvOP=zgtdCmZUYd05DG84Ir!L5{cTxJ zrj#BgQ8NJN&Ye4T>J&=+fhap@3snEg0bs1WZz0P0@7h}Dtme!z=h*dQGAav}V8INL zC0K$5cK{Kyju~r$S`7eMi+pTNAw6G6@0GJJOi5n>B~XS0nSAKscfRoI$zdFqK5)4S zu3o&nzP*tbc|EEtsWMwmFmlq1bZJ5Zz}o`nCy+^+RQj;J5g;I%)GQ!R3#q)S!G#5Y zLK{!XQ?H)-=!ZYxw1!{0KLmIP*ohB)*Qr1M!?4+in){nkON0>snHq+* z%1oym{MNU=6-7~zJL_2BbFB?A@48!^*yv?-qjLo0+{qG~OEkp=F)BYXnWo_!IOhZq zLE8$T$c2~1u~J?Z$Bgq@Yd#)q`%&Za8!zDD52HHQ)CDI1vQck*`nx{<+5hr?0oW-5 za4voI(&yw~GJp8=qTSUpspE z`JemeKhvD+%%*4i{lTAqK4m zBgViuSlPSXImVcsgrIqa1tROL;l#;uRW@|r^cdywIEU3~f`(#%@H`!BDY!OPmbEMM zi;H*OcKk1%d5KM&^^L7+S%7*EZpKWmQwk7?}xiYPK{@nTUo$ILo zTfhCqFMjcfC%$>=)X8&K&kf^YVG3i7*4h}|Y&QGD-Y@+8FKlmb12D$)`-881dp+P>H?r4F+Ox zXHLC_Fq*R7I#PmLM;t(w=Z6m;zVkgF^TgW^Kff`dJkw17hF@msKUw%}_AaMC%NAU- zTqr-Ltv+?~^bI%gE5Gs|fA@EPH;&_OcMgCQGVlXqj5Rh4qcl%<`@3nLCcWh6e(vXX zc6I=KV3V zYyhrj!i}P-+E6s91iFDP#9)~?bLC=^kFz2z{k>oQm0$k-&wqh2=KFq86obJa*F|o$ z)LXfpRVE^(%UdB$<+UX3yPtOg{k3ET{?{ zJpC?Bw{4QdPd)WiDKUQemw)+B{^ZNGnkP6JIbj_K&r$=%JYo|!hExFLeUST((&{QWvt zck-ZAt}Aiw^b2K&7cL|hs>qF}JT6=UKwE2!+jn3efST`{w;2;RvrcCu$sT3h9tCGt z{VZiMQ%tf4;GV%&a?Xn)2k`v!uR7;G``OQa@r!?wrp0)iTW5(#c~S^&tszH@v)mXi zgb>2_eE^-#T(7sIlzQWh(?SZ)nUJz{po9?4Q9118ZCA=HmcP=cM9w+Jrwe6f>CAX4 zH)K`Gz1``_NvXEi&Rx265oK83RWnGrn$(pyTRwo&D^Bv9fHI6p*RRl7g1%K1Pp>$$ z>TmooD$tT6(6sVkI&H}F{LyH5;ljoL^q>CAXFvNn0KPAsBXYz!H^ypXITy}3qmA;U zqiWhG=X^LEbS8E5S(+*3r%3`pYXi=-F$~O=y8ikH0WjSk6NQ3=VF8rlktPYC@=l2$M5*(t{ac-oh=`<8X+Hka zm;RJ9=1D)#b>J)IdA{#UsQ}be1Vc3yHCxTF-i#XEsJY6O5K6^q;s6T*XO=7xh@9{H zAXZL=o*gQMQh+d&4e+cZTTY^3TuLc~l2UQT86$#qPJ?WT808#-GtR*nv9ga9xw2Z7 zmF9byh5-W;jB~*u>kSQe@!Top$04_h*%BTjF&Sw(JuH!Nw(1R6>2+T2NM^4S`q%4# zw`M?R*)VOUKb1VQ8`M!*UP8n;K+IXkIM+J2E-j%P`D7mA-~5|Das1fvMyD}*{^4+V z@#4k*@?SqY9*+bUh0e>l-2~#qXQc#9ccrUB@-uDOrt7$RE^_}*PsHdtz#&y#4a&S9 z2$LuoV+3b(ZjFhv-sVQHUJD^SI14T$kz>4!fDnK*FT_-$RqCa*svHOodydR3^=EEk zPoUqRq8Ve8R+xX;LxG5lHcClr9cN5Q8TbJZW_eah`44~iL-*f*|KY=jZ}?)CWvi>J zufF>FlTSUBrlk`@02pVk?0Ry2y|CS23AMO3oEVm#30azVe+{b*?2>S{u$qB_`M;mOz|U z^H0lI5HEp(WmlhBW>i|%I)W2>Gr}egDV3fVcQ;x*1P12}m@8c~B7q90NWLACGoc^s zuB~gM;VI%0RB!-}s1)^^n>#Zny8#$a6$}27W7dtFrSD&MxM@;u#{P4lvhBK*QtJYM z?*+!1EYA-gI()|+chu{307Q;TLwQOsYcHI;^yZnf0D>?qgJqmCURjYhUcK6X)j!S{ zD{X=q>RSgpD}!!0$y=yjJws_c&d7tQS!Kvs0P$#CmQs}m2Vjil3`CT~8OkYIldp1c z!9?X`TR13#$fE6SCQsoQ!(%&Owk%qTo^)>%tbF1WohoYyOe zy&Ep=bd+~9LAtQF^bQ5UxNz2h?DptY9b{7 zD&6&JkE4Y7U8i+{8*3JByrGF!n5sX)IAbi!QX;C=YJu-tYgSkG{n%gokvs3ab8&I8 z{Gv0aLWFYxpx5hdtZkh>eNsxPJe6e`7aWW+4$dY+byykm0T+S`QJRp`t8+eyliM;v zMGoa2;+#S7$;c$m!ONeV-H`dNtmwTCO`MgI$^m4FF)kUG&PV{+XcS4#oB_ZoOFhBK zImX#|JZ6C9-l!nX%jTt}FJLQqEf`VmT&@(-bQ}a8fTGA4W3Di!D4emLkX9E;a3wfd zl=+RqlZa3S$=a-QgF6A__ zKHeO}TZ7;EjnCBr9|F0~nE)~39%qb^b<8@DbJjR(h^#d>%@Rhi)*9nDm~lm6atCdS z#6eS*hn{lGF<#ok&{-xewAGFiDd9L-=136Z!~}Sm0Dxn#f`Ktf;DRYmyi7ea&Pj5{ zB4;G$LNSc{o7_2{2c8prcI;A?7nN7ZSQTa>x3|(c09KlVd7jU8JHz3yD2isYgegFB zDS7EwR2#_meJ*%1O02PrF)s{jVK|QCS{P0xQDvArQw9x>BUzrU%8xQsnJFVQI1}Rqq4TGaELJgSZlN~^{}4jx$paVo~OMrnt`XhI8E8K6;*ks&P~hRgpf&+ zC{LBE;mtSC-F^3oR=uXRmQqd$IUEdM`OYibSGFGi#y8?LRzf(=gb>zR#-|wZvSAG4 zTu_-*(kfE5LKlT5D-8fsKxBE%3SUTodqQ5qpe;iHhTPS(-kmiM$^tg47SS8|tvR#p50G1XP zcXoFG+;#h1$L_cdK$c}+{pwe1wF(H;>2zws2e8}g1Gv0-6~NlnwVmyqZ$I(OXlK}6 z>}ExNL!5o;2UNB{V^S(>DNU2tUq89M{hm&}QLERKQbeS+9u9_Yy#B_ilc)1hy0Ntm zKq*gKvKj&k*1@4v!Hj{}6p_G;)qxjEApuB5+PFHg`|f=}T<*84EHoaOkqSoDj^4KK z-+lRu+8IAH0ALE`*K(bLfip%*qU@II3CUmzH#7Qt?;s-1IFWOc9l+oKAq$w4oRTaZ z780ozb>ocN>w!Qt#r!Z3VYD&cH@CkzH;=$0O8`bY!+3iX807`NH8w+HJsm|+v(c2C z*Xj{~qbmm)W36uM&%XF)y?##$$vqEK+pfITvd9v`6ophC=Rz=8>(V5C^W-c0=DI~M z_TUv+*2@PMPF*EW~O!Y={+T#6ER6Ns+K?D6~V|E2Hzsn{_GV-G;$2r0=n@}kI% z@kx%Q@P~qFZ|sdYR~+O@m?IJ6WZ_t4kCdi28)xHq*hixUk2nA&7=Vq94JN#4(=)yo zaixh{0RY;XFbq4LPL^fc>sv{j} zGri>I#ulp}E_U-PyO6XLI`x4(w}{4DbXS!{6QQ zgH=gL27rOwUM6hn6Basewb~ahTmW$V=HtruUVi4qjjfG(tu`Kx(>N7U2q8S*bIy65 z2Ts;h;q%1g?RNWp?|a|b(`S3V9&?WPjn1_-Mo4Lm32Qa0oz_|iKM4FMpMLVhP5ZNK zytQ!+z-TZkMDd+hUg65uc>#FNSqloJ%B?B6h^jCVqTIpG31>4hoiN_-wT~aW^`TFE z>ZW^6M03qkPrPvGo;%jwJb(CIcV2kvm6d&~Yj0dS^3aKNoZWu-*yo@88i3mmAKTgi zfMb_WH5*Z9khJP8@0NW4F2)y~e%a|1kTapoy#-dfX%k8U!0z@|`BEw^w}2Y#&c;Qu zmU|wIvqWSaV~}MSg)qs@AQiNkZr3he91I4}fAe_&o%wdV-S$jiQ?ii(jN(DAvwA($ zu;>9`#!`vI2GI2DKJ%$e3rzC_M9zW8DXAPeCS;c79E@|W3oYQDfBv~2{=x49@WOZA z>~HrTcYp{+HNvoN1Z1+Qh}{VjFdf#ynMyfR|a z$dwW6Z(1yjuGP%_?Rs&bmP(!9q;$jg~ zb`ax?F>Q)B-Z*pa%?pJtoYtK4T3D+^HK9B&45KItYRyKY)^2wjjQ~KSp+XUQ!UNFs z>HvD%y)4gA78#UYz(fK~oDwQyZ6)M@Gv?rwQeXU|FCV}4*qICGU1nc<_4L}-h7{6p z5U217LXJjbzXtZ>^vI;^T7{t~8fANE3_(XmxAPo$sB$6yAN?^KZW5 zQt%Kw02L+Gz!{qcktYZQN5q7X1~5s#Vn7aX;PRQ%D5d{I*_H!{X_85wSSuu@2cXae zfG`LF`a?TlN3LJnGyiu!h5h_oGURX|VPo@ zM+9=(+NYjdOFbdH0F&k%ju|{{Q;f|Mr1L9@%%`(2-ki`ZquGC(Fw#ez!XZ z6!Ab`UT@kOt`$S!H@(VWwOIimH%7E4t*5~%4oqdvG&lx0A}K{-^4|94iU9^FH|WkV zBlv8OE{+I-IU)j+j;L%nVXfvfsvD)Woshngew@>^EX~@&7*ERHaFiB10DR>GrBiWz zUW_KaEyztlcT1o+r_0MLQN1<$q?TC=n{&MtL=+0k zS*b*QDPc5OAS)q%qKwA_e0HJ_pK&jWyo$$39tR5lymTVo!;edVFGL zSnE=o5>*Ld$4Yoi>4Pw$5>^SvNP0?mGS3U9WTRHg@~o@~$4rcuh+v5TQ8ofBVS$rj zz7FIouC>l|y1unGPEry1eVzEeDpl>I7dbKK7=b$&ZefX>6P$6$^P(6Fdsb)LFAnP$ z&yKbdXI!$8M&Jotn(X4jb0!MjxP0L+o_n$mv$${n+Qkb(%J8KZ^P=!~HUNYs15lP{ z25tmkOV<{9n-}`V8tWP!mx^hEBM>KYZ6V^Inl%|=shL>sRw*VB0 z%JA1Dld=~y76w$J=eKuua!d6nEQ&OriD`2{8VR2X&N*k+ni88(Fi5xoFi4$Bhgsh- z&YdyRi~Na|$J0Tq2ijN;WXPOlyKkH`&PndP;kLCF+?(_0TAIz=Y&%2_-<>+&6WwMB zp)CgwF~(;m223sNauYCPtQ61;n_C-;3riBfaMVll6kIXlhNznOSssiWxw0cu*-J=D zrM1>Ncg}Gxr7Y8Fe3Fx#cs)dAR#g|2XL+;JRGue+DwCh+!yv^k>f`r5AARO`xS=nRsSwOTDne zrqD+)$VW)$lXNX-VG$5CNWf;wLDXsJ5)pymjt9nuDLU4)ig@pKy)w)8yl!UR0KF`^ zwpPl~)V|re_w&AQg6V*6bg7BGgBVHCw*7O2?~kiUwC24w04z4%Mf6hFMG#Ss5Q2vb z;DKMiZ$IDOrz&mx{uP6-;1~uuLNp2+aIF=9YD#G_6Pk9-mt!&7>`crg%97Wdmki+8 zj|exjby?2ZUd)WAkMUn(f~WLl%VjU&5u#_SO*tYKTasE5Q%C|2p1t;7x7N=dKDiK# zDduNZWRY<1Z64J&VgThlANx^T|9-hB%b#O=)FW%cGs)dQ?*LTc9sm3Upp?^+u!!b7 r!JDZB({%_B5Y?B}L)CG+eE|3i<`dR?W7om700000NkvXXu0mjf=-nRa diff --git a/src/libImaging/BcnDecode.c b/src/libImaging/BcnDecode.c index 0058205f1..a57b74b61 100644 --- a/src/libImaging/BcnDecode.c +++ b/src/libImaging/BcnDecode.c @@ -688,11 +688,6 @@ bc6_clamp(float value) { } } -static float -bc6_gamma_correct(float value, float gamma) { - return powf(1.0f - expf(-value), 1.0f / gamma); -} - static void bc6_lerp(rgba *col, int *e0, int *e1, int s, int sign) { int r, g, b; @@ -700,9 +695,9 @@ bc6_lerp(rgba *col, int *e0, int *e1, int s, int sign) { r = (e0[0] * t + e1[0] * s) >> 6; g = (e0[1] * t + e1[1] * s) >> 6; b = (e0[2] * t + e1[2] * s) >> 6; - col->r = bc6_clamp(bc6_gamma_correct(bc6_finalize(r, sign), 2.2f)); - col->g = bc6_clamp(bc6_gamma_correct(bc6_finalize(g, sign), 2.2f)); - col->b = bc6_clamp(bc6_gamma_correct(bc6_finalize(b, sign), 2.2f)); + col->r = bc6_clamp(bc6_finalize(r, sign)); + col->g = bc6_clamp(bc6_finalize(g, sign)); + col->b = bc6_clamp(bc6_finalize(b, sign)); } static void From 13be5f2bb03fe91f50ceb15a9d392c2e8d03b1dd Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 11 Oct 2022 10:37:16 +1100 Subject: [PATCH 061/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 55eabefd4..9c49079fe 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Added support for opening WhiteIsZero 16-bit integer TIFF images #6642 + [JayWiz, radarhere] + - Raise an error when allocating translucent color to RGB palette #6654 [jsbueno, radarhere] From e0c30d2fcd90ed9cfb1bdf2d27f6170bc9e762ad Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 11 Oct 2022 13:00:23 +1100 Subject: [PATCH 062/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 9c49079fe..63a96ec7f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Added DDS BC6 reading #6449 + [ShadelessFox, REDxEYE, radarhere] + - Added support for opening WhiteIsZero 16-bit integer TIFF images #6642 [JayWiz, radarhere] From ce2dc184240ea1345f13ebf224bc26f6f6cd145a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 12 Oct 2022 09:04:45 +1100 Subject: [PATCH 063/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 63a96ec7f..f189cf9dd 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,15 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Round box position to integer when pasting embedded color #6517 + [radarhere, nulano] + +- Removed EXIF prefix when saving WebP #6582 + [radarhere] + +- Pad IM palette to 768 bytes when saving #6579 + [radarhere] + - Added DDS BC6 reading #6449 [ShadelessFox, REDxEYE, radarhere] From f63cc582b7d305780762f786cf61e602f7398b0d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 12 Oct 2022 22:12:12 +1100 Subject: [PATCH 064/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index f189cf9dd..c3502881c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,18 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Raise a warning if NumPy failed to raise an error during conversion #6594 + [radarhere] + +- Show all frames in ImageShow #6611 + [radarhere] + +- Allow FLI palette chunk to not be first #6626 + [radarhere] + +- If first GIF frame has transparency for RGB_ALWAYS loading strategy, use RGBA mode #6592 + [radarhere] + - Round box position to integer when pasting embedded color #6517 [radarhere, nulano] From 69baeccf2ee7850ccfb9b2b05ab584b87ad50fe1 Mon Sep 17 00:00:00 2001 From: Mark Mayo Date: Thu, 13 Oct 2022 15:20:11 +1300 Subject: [PATCH 065/239] some pylint warnings Fixed some pylint issues --- Tests/helper.py | 13 ++++++------- Tests/test_file_jpeg.py | 2 +- Tests/test_file_libtiff.py | 2 +- Tests/test_file_png.py | 2 +- Tests/test_file_ppm.py | 8 ++++---- Tests/test_file_tiff.py | 2 +- Tests/test_image.py | 7 +++---- Tests/test_image_access.py | 5 ++--- Tests/test_imagemath.py | 7 +++---- setup.py | 2 +- 10 files changed, 23 insertions(+), 27 deletions(-) diff --git a/Tests/helper.py b/Tests/helper.py index 13c6955e4..0d1d03ac8 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -208,12 +208,11 @@ class PillowLeakTestCase: # ru_maxrss # This is the maximum resident set size utilized (in bytes). return mem / 1024 # Kb - else: - # linux - # man 2 getrusage - # ru_maxrss (since Linux 2.6.32) - # This is the maximum resident set size used (in kilobytes). - return mem # Kb + # linux + # man 2 getrusage + # ru_maxrss (since Linux 2.6.32) + # This is the maximum resident set size used (in kilobytes). + return mem # Kb def _test_leak(self, core): start_mem = self._get_mem_usage() @@ -285,7 +284,7 @@ def magick_command(): if imagemagick and shutil.which(imagemagick[0]): return imagemagick - elif graphicsmagick and shutil.which(graphicsmagick[0]): + if graphicsmagick and shutil.which(graphicsmagick[0]): return graphicsmagick diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index adbb72aa5..fa96e425b 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -30,7 +30,7 @@ from .helper import ( ) try: - import defusedxml.ElementTree as ElementTree + from defusedxml import ElementTree except ImportError: ElementTree = None diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index d9066c589..58d3aac06 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -934,7 +934,7 @@ class TestFileLibTiff(LibTiffTestCase): im.save(out, exif=tags, compression=compression) with Image.open(out) as reloaded: - for tag in tags.keys(): + for tag in tags: assert tag not in reloaded.getexif() def test_old_style_jpeg(self): diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 1af0223eb..37235fe6f 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -20,7 +20,7 @@ from .helper import ( ) try: - import defusedxml.ElementTree as ElementTree + from defusedxml import ElementTree except ImportError: ElementTree = None diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 5c6376caf..72711de77 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -240,7 +240,7 @@ def test_header_token_too_long(tmp_path): def test_truncated_file(tmp_path): # Test EOF in header path = str(tmp_path / "temp.pgm") - with open(path, "w") as f: + with open(path, "w", encoding='utf-8') as f: f.write("P6") with pytest.raises(ValueError) as e: @@ -259,7 +259,7 @@ def test_truncated_file(tmp_path): @pytest.mark.parametrize("maxval", (0, 65536)) def test_invalid_maxval(maxval, tmp_path): path = str(tmp_path / "temp.ppm") - with open(path, "w") as f: + with open(path, "w", encoding='utf-8') as f: f.write("P6\n3 1 " + str(maxval)) with pytest.raises(ValueError) as e: @@ -283,12 +283,12 @@ def test_neg_ppm(): def test_mimetypes(tmp_path): path = str(tmp_path / "temp.pgm") - with open(path, "w") as f: + with open(path, "w", encoding='utf-8') as f: f.write("P4\n128 128\n255") with Image.open(path) as im: assert im.get_format_mimetype() == "image/x-portable-bitmap" - with open(path, "w") as f: + with open(path, "w", encoding='utf-8') as f: f.write("PyCMYK\n128 128\n255") with Image.open(path) as im: assert im.get_format_mimetype() == "image/x-portable-anymap" diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 1a5ba594f..a32e6a005 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -18,7 +18,7 @@ from .helper import ( ) try: - import defusedxml.ElementTree as ElementTree + from defusedxml import ElementTree except ImportError: ElementTree = None diff --git a/Tests/test_image.py b/Tests/test_image.py index ab945e946..d847d0617 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -129,7 +129,6 @@ class TestImage: im.size = (3, 4) def test_invalid_image(self): - import io im = io.BytesIO(b"") with pytest.raises(UnidentifiedImageError): @@ -699,15 +698,15 @@ class TestImage: def test_empty_exif(self): with Image.open("Tests/images/exif.png") as im: exif = im.getexif() - assert dict(exif) != {} + assert dict(exif) # Test that exif data is cleared after another load exif.load(None) - assert dict(exif) == {} + assert not dict(exif) # Test loading just the EXIF header exif.load(b"Exif\x00\x00") - assert dict(exif) == {} + assert not dict(exif) @mark_if_feature_version( pytest.mark.valgrind_known_error, "libjpeg_turbo", "2.0", reason="Known Failing" diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index a000cb64c..5a6d1c754 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -131,8 +131,7 @@ class TestImageGetPixel(AccessTest): bands = Image.getmodebands(mode) if bands == 1: return 1 - else: - return tuple(range(1, bands + 1)) + return tuple(range(1, bands + 1)) def check(self, mode, c=None): if not c: @@ -415,7 +414,7 @@ class TestEmbeddable: def test_embeddable(self): import ctypes - with open("embed_pil.c", "w") as fh: + with open("embed_pil.c", "w", encoding='utf-8') as fh: fh.write( """ #include "Python.h" diff --git a/Tests/test_imagemath.py b/Tests/test_imagemath.py index 39d91eade..a5414a050 100644 --- a/Tests/test_imagemath.py +++ b/Tests/test_imagemath.py @@ -6,10 +6,9 @@ from PIL import Image, ImageMath def pixel(im): if hasattr(im, "im"): return f"{im.mode} {repr(im.getpixel((0, 0)))}" - else: - if isinstance(im, int): - return int(im) # hack to deal with booleans - print(im) + if isinstance(im, int): + return int(im) # hack to deal with booleans + print(im) A = Image.new("L", (1, 1), 1) diff --git a/setup.py b/setup.py index aa3168aa5..14c404752 100755 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ from setuptools.command.build_ext import build_ext def get_version(): version_file = "src/PIL/_version.py" - with open(version_file) as f: + with open(version_file, encoding="utf-8") as f: exec(compile(f.read(), version_file, "exec")) return locals()["__version__"] From c93413835d46d0090b4c0e519d5bc0beef7ae90c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 02:22:49 +0000 Subject: [PATCH 066/239] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- Tests/test_file_ppm.py | 8 ++++---- Tests/test_image_access.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 72711de77..471da9a79 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -240,7 +240,7 @@ def test_header_token_too_long(tmp_path): def test_truncated_file(tmp_path): # Test EOF in header path = str(tmp_path / "temp.pgm") - with open(path, "w", encoding='utf-8') as f: + with open(path, "w", encoding="utf-8") as f: f.write("P6") with pytest.raises(ValueError) as e: @@ -259,7 +259,7 @@ def test_truncated_file(tmp_path): @pytest.mark.parametrize("maxval", (0, 65536)) def test_invalid_maxval(maxval, tmp_path): path = str(tmp_path / "temp.ppm") - with open(path, "w", encoding='utf-8') as f: + with open(path, "w", encoding="utf-8") as f: f.write("P6\n3 1 " + str(maxval)) with pytest.raises(ValueError) as e: @@ -283,12 +283,12 @@ def test_neg_ppm(): def test_mimetypes(tmp_path): path = str(tmp_path / "temp.pgm") - with open(path, "w", encoding='utf-8') as f: + with open(path, "w", encoding="utf-8") as f: f.write("P4\n128 128\n255") with Image.open(path) as im: assert im.get_format_mimetype() == "image/x-portable-bitmap" - with open(path, "w", encoding='utf-8') as f: + with open(path, "w", encoding="utf-8") as f: f.write("PyCMYK\n128 128\n255") with Image.open(path) as im: assert im.get_format_mimetype() == "image/x-portable-anymap" diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index 5a6d1c754..955740b95 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -414,7 +414,7 @@ class TestEmbeddable: def test_embeddable(self): import ctypes - with open("embed_pil.c", "w", encoding='utf-8') as fh: + with open("embed_pil.c", "w", encoding="utf-8") as fh: fh.write( """ #include "Python.h" From 9602908f7a6fa8ee44cef88961472bf8ea5187a1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 13 Oct 2022 14:31:26 +1100 Subject: [PATCH 067/239] Removed print statement --- Tests/test_imagemath.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Tests/test_imagemath.py b/Tests/test_imagemath.py index 39d91eade..fca5cffaf 100644 --- a/Tests/test_imagemath.py +++ b/Tests/test_imagemath.py @@ -6,10 +6,8 @@ from PIL import Image, ImageMath def pixel(im): if hasattr(im, "im"): return f"{im.mode} {repr(im.getpixel((0, 0)))}" - else: - if isinstance(im, int): - return int(im) # hack to deal with booleans - print(im) + elif isinstance(im, int): + return int(im) # hack to deal with booleans A = Image.new("L", (1, 1), 1) From eccf9e87cf170e5ceb97054ff2c9ecc084c93ed8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 13 Oct 2022 19:28:39 +1100 Subject: [PATCH 068/239] Added GPS tags --- src/PIL/TiffTags.py | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/PIL/TiffTags.py b/src/PIL/TiffTags.py index 3f3a1ccd2..9b5277138 100644 --- a/src/PIL/TiffTags.py +++ b/src/PIL/TiffTags.py @@ -232,7 +232,39 @@ TAGS_V2_GROUPS = { 41730: ("CFAPattern", UNDEFINED, 1), }, # GPSInfoIFD - 34853: {}, + 34853: { + 0: ("GPSVersionID", BYTE, 4), + 1: ("GPSLatitudeRef", ASCII, 2), + 2: ("GPSLatitude", RATIONAL, 3), + 3: ("GPSLongitudeRef", ASCII, 2), + 4: ("GPSLongitude", RATIONAL, 3), + 5: ("GPSAltitudeRef", BYTE, 1), + 6: ("GPSAltitude", RATIONAL, 1), + 7: ("GPSTimeStamp", RATIONAL, 3), + 8: ("GPSSatellites", ASCII, 0), + 9: ("GPSStatus", ASCII, 2), + 10: ("GPSMeasureMode", ASCII, 2), + 11: ("GPSDOP", RATIONAL, 1), + 12: ("GPSSpeedRef", ASCII, 2), + 13: ("GPSSpeed", RATIONAL, 1), + 14: ("GPSTrackRef", ASCII, 2), + 15: ("GPSTrack", RATIONAL, 1), + 16: ("GPSImgDirectionRef", ASCII, 2), + 17: ("GPSImgDirection", RATIONAL, 1), + 18: ("GPSMapDatum", ASCII, 0), + 19: ("GPSDestLatitudeRef", ASCII, 2), + 20: ("GPSDestLatitude", RATIONAL, 3), + 21: ("GPSDestLongitudeRef", ASCII, 2), + 22: ("GPSDestLongitude", RATIONAL, 3), + 23: ("GPSDestBearingRef", ASCII, 2), + 24: ("GPSDestBearing", RATIONAL, 1), + 25: ("GPSDestDistanceRef", ASCII, 2), + 26: ("GPSDestDistance", RATIONAL, 1), + 27: ("GPSProcessingMethod", UNDEFINED, 0), + 28: ("GPSAreaInformation", UNDEFINED, 0), + 29: ("GPSDateStamp", ASCII, 11), + 30: ("GPSDifferential", SHORT, 1), + }, # InteroperabilityIFD 40965: {1: ("InteropIndex", ASCII, 1), 2: ("InteropVersion", UNDEFINED, 1)}, } From a4b257269e3abaea822405f18f64c3a4634dc61c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 13 Oct 2022 20:21:39 +1100 Subject: [PATCH 069/239] Image channel is used when converting PA with an RGBA palette --- src/PIL/Image.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index d5080a05c..6ba8e11be 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -880,7 +880,7 @@ class Image: and the palette can be represented without a palette. The current version supports all possible conversions between - "L", "RGB" and "CMYK." The ``matrix`` argument only supports "L" + "L", "RGB" and "CMYK". The ``matrix`` argument only supports "L" and "RGB". When translating a color image to greyscale (mode "L"), @@ -899,6 +899,9 @@ class Image: this passes the operation to :py:meth:`~PIL.Image.Image.quantize`, and ``dither`` and ``palette`` are ignored. + When converting from "PA", if an "RGBA" palette is present, the alpha + channel from the image will be used instead of the values from the palette. + :param mode: The requested mode. See: :ref:`concept-modes`. :param matrix: An optional conversion matrix. If given, this should be 4- or 12-tuple containing floating point values. From 5b5a784f826d402cedb983e7bc86f15b64faced4 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 13 Oct 2022 20:30:11 +1100 Subject: [PATCH 070/239] Updated zlib to 1.2.13 --- winbuild/build_prepare.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index ad7b1ddb6..39b9bcc2c 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -132,9 +132,9 @@ deps = { "bins": ["cjpeg.exe", "djpeg.exe"], }, "zlib": { - "url": "https://zlib.net/zlib1212.zip", - "filename": "zlib1212.zip", - "dir": "zlib-1.2.12", + "url": "https://zlib.net/zlib1213.zip", + "filename": "zlib1213.zip", + "dir": "zlib-1.2.13", "build": [ cmd_nmake(r"win32\Makefile.msc", "clean"), cmd_nmake(r"win32\Makefile.msc", "zlib.lib"), From 8e17c626abd3ca1dd171afcd9ed283072e7f21ac Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 14 Oct 2022 08:23:02 +1100 Subject: [PATCH 071/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index c3502881c..eeb733283 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Don't reassign crc on ChunkStream close #6627 + [wiredfool, radarhere] + - Raise a warning if NumPy failed to raise an error during conversion #6594 [radarhere] From 340f247672ce4b44cae43b3e566ed3236dd1a506 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 19 Oct 2022 08:23:58 +1100 Subject: [PATCH 072/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index eeb733283..ffa0ad9ef 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Fixed seeking to an L frame in a GIF #6576 + [radarhere] + +- Consider all frames when selecting mode for PNG save_all #6610 + [radarhere] + - Don't reassign crc on ChunkStream close #6627 [wiredfool, radarhere] From b2e2559c83550f503dd06791bb60506af5ac9e24 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 19 Oct 2022 19:35:52 +1100 Subject: [PATCH 073/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index ffa0ad9ef..5a1095b61 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Added conversion between RGB/RGBA/RGBX and LAB #6647 + [radarhere] + +- Do not attempt normalization if mode is already normal #6644 + [radarhere] + - Fixed seeking to an L frame in a GIF #6576 [radarhere] From a91b1fe415fdf60310603e727f01bccde7613670 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 19 Oct 2022 19:56:11 +1100 Subject: [PATCH 074/239] Added release notes for #6449 [ci skip] --- docs/releasenotes/9.1.0.rst | 2 +- docs/releasenotes/9.3.0.rst | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/releasenotes/9.1.0.rst b/docs/releasenotes/9.1.0.rst index 57646e558..48ce6fef7 100644 --- a/docs/releasenotes/9.1.0.rst +++ b/docs/releasenotes/9.1.0.rst @@ -202,7 +202,7 @@ Pillow now builds binary wheels for musllinux, suitable for Linux distributions (rather than the glibc library used by manylinux wheels). See :pep:`656`. ImageShow temporary files on Unix -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ When calling :py:meth:`~PIL.Image.Image.show` or using :py:mod:`~PIL.ImageShow`, a temporary file is created from the image. On Unix, Pillow will no longer delete these diff --git a/docs/releasenotes/9.3.0.rst b/docs/releasenotes/9.3.0.rst index 7109a09f2..d3be270cd 100644 --- a/docs/releasenotes/9.3.0.rst +++ b/docs/releasenotes/9.3.0.rst @@ -63,7 +63,7 @@ TODO Other Changes ============= -Added DDS ATI1 and ATI2 reading -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Added DDS ATI1, ATI2 and BC6H reading +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Support has been added to read the ATI1 and ATI2 formats of DDS images. +Support has been added to read the ATI1, ATI2 and BC6H formats of DDS images. From 1e4b0609c937e07e5a546f14836c24c28d7c3a05 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 19 Oct 2022 19:58:59 +1100 Subject: [PATCH 075/239] Added release notes for #6611 [ci skip] --- docs/releasenotes/9.3.0.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/releasenotes/9.3.0.rst b/docs/releasenotes/9.3.0.rst index d3be270cd..0b8696040 100644 --- a/docs/releasenotes/9.3.0.rst +++ b/docs/releasenotes/9.3.0.rst @@ -67,3 +67,9 @@ Added DDS ATI1, ATI2 and BC6H reading ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Support has been added to read the ATI1, ATI2 and BC6H formats of DDS images. + +Show all frames with ImageShow +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When calling :py:meth:`~PIL.Image.Image.show` or using +:py:mod:`~PIL.ImageShow`, all frames will now be shown. From b67806ac943b06be44fc1be31ac02c38453be224 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 20 Oct 2022 13:12:32 +1100 Subject: [PATCH 076/239] Updated harfbuzz to 5.3.1 --- winbuild/build_prepare.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 39b9bcc2c..f4858b630 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -281,9 +281,9 @@ deps = { "libs": [r"imagequant.lib"], }, "harfbuzz": { - "url": "https://github.com/harfbuzz/harfbuzz/archive/5.3.0.zip", - "filename": "harfbuzz-5.3.0.zip", - "dir": "harfbuzz-5.3.0", + "url": "https://github.com/harfbuzz/harfbuzz/archive/5.3.1.zip", + "filename": "harfbuzz-5.3.1.zip", + "dir": "harfbuzz-5.3.1", "build": [ cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"), cmd_nmake(target="clean"), From 70e3e4fb10d6495e01084ef6abe8902ce464d6c7 Mon Sep 17 00:00:00 2001 From: Nathanael Gentry Date: Fri, 21 Oct 2022 19:44:48 -0400 Subject: [PATCH 077/239] BMP: Add 4-bit RLE decoder --- src/PIL/BmpImagePlugin.py | 73 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 4 deletions(-) diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index 1041ab763..107eb344d 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -212,7 +212,9 @@ class BmpImageFile(ImageFile.ImageFile): if file_info["bits"] == 32 and header == 22: # 32-bit .cur offset raw_mode, self.mode = "BGRA", "RGBA" elif file_info["compression"] == self.RLE8: - decoder_name = "bmp_rle" + decoder_name = "bmp_rle8" + elif file_info["compression"] == self.RLE4: + decoder_name = "bmp_rle4" else: raise OSError(f"Unsupported BMP compression ({file_info['compression']})") @@ -227,7 +229,7 @@ class BmpImageFile(ImageFile.ImageFile): palette = read(padding * file_info["colors"]) greyscale = True indices = ( - (0, 255) + (0, file_info["colors"]) if file_info["colors"] == 2 else list(range(file_info["colors"])) ) @@ -276,7 +278,7 @@ class BmpImageFile(ImageFile.ImageFile): self._bitmap(offset=offset) -class BmpRleDecoder(ImageFile.PyDecoder): +class BmpRle8Decoder(ImageFile.PyDecoder): _pulls_fd = True def decode(self, buffer): @@ -328,6 +330,68 @@ class BmpRleDecoder(ImageFile.PyDecoder): return -1, 0 +class BmpRle4Decoder(ImageFile.PyDecoder): + _pulls_fd = True + + def decode(self, buffer): + data = bytearray() + x = 0 + while len(data) < self.state.xsize * self.state.ysize: + pixels = self.fd.read(1) + byte = self.fd.read(1) + if not pixels or not byte: + break + num_pixels = pixels[0] + if num_pixels: + # encoded mode + if x + num_pixels > self.state.xsize: + # Too much data for row + num_pixels = max(0, self.state.xsize - x) + first_pixel = o8(byte[0] >> 4) + second_pixel = o8(byte[0] & 0x0f) + for index in range(num_pixels): + if index % 2 == 0: + data += first_pixel + else: + data += second_pixel + x += num_pixels + else: + if byte[0] == 0: + # end of line + while len(data) % self.state.xsize != 0: + data += b"\x00" + x = 0 + elif byte[0] == 1: + # end of bitmap + break + elif byte[0] == 2: + # delta + bytes_read = self.fd.read(2) + if len(bytes_read) < 2: + break + right, up = self.fd.read(2) + data += b"\x00" * (right + up * self.state.xsize) + x = len(data) % self.state.xsize + else: + # absolute mode (2 pixels per byte) + total_bytes_to_read = byte[0] // 2 + bytes_read = self.fd.read(total_bytes_to_read) + for byte_read in bytes_read: + first_pixel = o8(byte_read >> 4) + data += first_pixel + second_pixel = o8(byte_read & 0x0f) + data += second_pixel + if len(bytes_read) < total_bytes_to_read: + break + x += byte[0] + + # align to 16-bit word boundary + if self.fd.tell() % 2 != 0: + self.fd.seek(1, os.SEEK_CUR) + rawmode = "L" if self.mode == "L" else "P" + self.set_as_raw(bytes(data), (rawmode, 0, self.args[-1])) + return -1, 0 + # ============================================================================= # Image plugin for the DIB format (BMP alias) # ============================================================================= @@ -433,7 +497,8 @@ Image.register_extension(BmpImageFile.format, ".bmp") Image.register_mime(BmpImageFile.format, "image/bmp") -Image.register_decoder("bmp_rle", BmpRleDecoder) +Image.register_decoder("bmp_rle8", BmpRle8Decoder) +Image.register_decoder("bmp_rle4", BmpRle4Decoder) Image.register_open(DibImageFile.format, DibImageFile, _dib_accept) Image.register_save(DibImageFile.format, _dib_save) From c2e9c66fcd0020826d708c6cca43c1cabcd8a44d Mon Sep 17 00:00:00 2001 From: Nathanael Gentry Date: Fri, 21 Oct 2022 19:45:05 -0400 Subject: [PATCH 078/239] Add tests for 4-bit RLE decoder --- Tests/images/hopper_4bit.bmp | Bin 0 -> 8310 bytes Tests/images/hopper_rle4.bmp | Bin 0 -> 8140 bytes Tests/test_file_bmp.py | 7 +++++++ 3 files changed, 7 insertions(+) create mode 100644 Tests/images/hopper_4bit.bmp create mode 100644 Tests/images/hopper_rle4.bmp diff --git a/Tests/images/hopper_4bit.bmp b/Tests/images/hopper_4bit.bmp new file mode 100644 index 0000000000000000000000000000000000000000..8a69b349d30c43ec2af8f94991d5adde83184d88 GIT binary patch literal 8310 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 literal 0 HcmV?d00001 diff --git a/Tests/images/hopper_rle4.bmp b/Tests/images/hopper_rle4.bmp new file mode 100644 index 0000000000000000000000000000000000000000..3c53cc1dd38aa9689c62fd3712958397825d5901 GIT binary patch literal 8140 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 j%rvW>i?-RoB->7MuGY80H#@ literal 0 HcmV?d00001 diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py index f6860a9a4..ec852c905 100644 --- a/Tests/test_file_bmp.py +++ b/Tests/test_file_bmp.py @@ -175,6 +175,13 @@ def test_rle8(): with pytest.raises(ValueError): im.load() +def test_rle4(): + with Image.open("Tests/images/hopper_rle4.bmp") as im: + assert_image_similar_tofile(im, "Tests/images/hopper_4bit.bmp", 12) + + with Image.open("Tests/images/bmp/g/pal4rle.bmp") as im: + assert_image_similar_tofile(im, "Tests/images/bmp/g/pal4.bmp", 12) + @pytest.mark.parametrize( "file_name,length", From 455ffff735795462f81d75b3a101ee422ea172fa Mon Sep 17 00:00:00 2001 From: Nathanael Gentry Date: Fri, 21 Oct 2022 19:45:18 -0400 Subject: [PATCH 079/239] Update documentation for 4-bit RLE decoder --- docs/handbook/image-file-formats.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index dc629666c..e365aad56 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -45,9 +45,9 @@ BMP ^^^ Pillow reads and writes Windows and OS/2 BMP files containing ``1``, ``L``, ``P``, -or ``RGB`` data. 16-colour images are read as ``P`` images. 4-bit run-length encoding -is not supported. Support for reading 8-bit run-length encoding was added in Pillow -9.1.0. +or ``RGB`` data. 16-colour images are read as ``P`` images. +Support for reading 8-bit run-length encoding was added in Pillow 9.1.0. +Support for reading 4-bit run-length encoding was added in Pillow 9.3.0. Opening ~~~~~~~ @@ -56,7 +56,8 @@ The :py:meth:`~PIL.Image.open` method sets the following :py:attr:`~PIL.Image.Image.info` properties: **compression** - Set to ``bmp_rle`` if the file is run-length encoded. + Set to ``bmp_rle8`` if the file is a 256-color run-length encoded image. + Set to ``bmp_rle4`` if the file is a 16-color run-length encoded image. DDS ^^^ From f2dfd0bfb30b5741589fc124227b4052564ed7dd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 21 Oct 2022 23:41:26 +0000 Subject: [PATCH 080/239] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- Tests/test_file_bmp.py | 1 + src/PIL/BmpImagePlugin.py | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py index ec852c905..e857f881c 100644 --- a/Tests/test_file_bmp.py +++ b/Tests/test_file_bmp.py @@ -175,6 +175,7 @@ def test_rle8(): with pytest.raises(ValueError): im.load() + def test_rle4(): with Image.open("Tests/images/hopper_rle4.bmp") as im: assert_image_similar_tofile(im, "Tests/images/hopper_4bit.bmp", 12) diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index 107eb344d..d1517df27 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -348,7 +348,7 @@ class BmpRle4Decoder(ImageFile.PyDecoder): # Too much data for row num_pixels = max(0, self.state.xsize - x) first_pixel = o8(byte[0] >> 4) - second_pixel = o8(byte[0] & 0x0f) + second_pixel = o8(byte[0] & 0x0F) for index in range(num_pixels): if index % 2 == 0: data += first_pixel @@ -379,11 +379,11 @@ class BmpRle4Decoder(ImageFile.PyDecoder): for byte_read in bytes_read: first_pixel = o8(byte_read >> 4) data += first_pixel - second_pixel = o8(byte_read & 0x0f) + second_pixel = o8(byte_read & 0x0F) data += second_pixel if len(bytes_read) < total_bytes_to_read: break - x += byte[0] + x += byte[0] # align to 16-bit word boundary if self.fd.tell() % 2 != 0: @@ -392,6 +392,7 @@ class BmpRle4Decoder(ImageFile.PyDecoder): self.set_as_raw(bytes(data), (rawmode, 0, self.args[-1])) return -1, 0 + # ============================================================================= # Image plugin for the DIB format (BMP alias) # ============================================================================= From cc45886bc3821b60ec911f90dfd77381f7a25f1b Mon Sep 17 00:00:00 2001 From: Nathanael Gentry Date: Fri, 21 Oct 2022 20:59:02 -0400 Subject: [PATCH 081/239] Revert unintentional change --- src/PIL/BmpImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index d1517df27..9d780bcca 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -229,7 +229,7 @@ class BmpImageFile(ImageFile.ImageFile): palette = read(padding * file_info["colors"]) greyscale = True indices = ( - (0, file_info["colors"]) + (0, 255) if file_info["colors"] == 2 else list(range(file_info["colors"])) ) From 78430b954956313795a2e76c9398264de3316e0e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 22 Oct 2022 18:50:17 +1100 Subject: [PATCH 082/239] Corrected BMP compression setting [ci skip] --- docs/handbook/image-file-formats.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index e365aad56..1e79db68b 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -56,8 +56,8 @@ The :py:meth:`~PIL.Image.open` method sets the following :py:attr:`~PIL.Image.Image.info` properties: **compression** - Set to ``bmp_rle8`` if the file is a 256-color run-length encoded image. - Set to ``bmp_rle4`` if the file is a 16-color run-length encoded image. + Set to 1 if the file is a 256-color run-length encoded image. + Set to 2 if the file is a 16-color run-length encoded image. DDS ^^^ From 6c8234bef3d4b3c4c1672cea1103aadfa59f0f7c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 22 Oct 2022 19:54:54 +1100 Subject: [PATCH 083/239] Combined BMP RLE decoders --- src/PIL/BmpImagePlugin.py | 113 ++++++++++++-------------------------- 1 file changed, 34 insertions(+), 79 deletions(-) diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index 9d780bcca..1eeab24a7 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -211,10 +211,8 @@ class BmpImageFile(ImageFile.ImageFile): elif file_info["compression"] == self.RAW: if file_info["bits"] == 32 and header == 22: # 32-bit .cur offset raw_mode, self.mode = "BGRA", "RGBA" - elif file_info["compression"] == self.RLE8: - decoder_name = "bmp_rle8" - elif file_info["compression"] == self.RLE4: - decoder_name = "bmp_rle4" + elif file_info["compression"] in (self.RLE8, self.RLE4): + decoder_name = "bmp_rle" else: raise OSError(f"Unsupported BMP compression ({file_info['compression']})") @@ -252,16 +250,18 @@ class BmpImageFile(ImageFile.ImageFile): # ---------------------------- Finally set the tile data for the plugin self.info["compression"] = file_info["compression"] + args = [raw_mode] + if decoder_name == "bmp_rle": + args.append(file_info["compression"] == self.RLE4) + else: + args.append(((file_info["width"] * file_info["bits"] + 31) >> 3) & (~3)) + args.append(file_info["direction"]) self.tile = [ ( decoder_name, (0, 0, file_info["width"], file_info["height"]), offset or self.fp.tell(), - ( - raw_mode, - ((file_info["width"] * file_info["bits"] + 31) >> 3) & (~3), - file_info["direction"], - ), + tuple(args), ) ] @@ -278,10 +278,11 @@ class BmpImageFile(ImageFile.ImageFile): self._bitmap(offset=offset) -class BmpRle8Decoder(ImageFile.PyDecoder): +class BmpRleDecoder(ImageFile.PyDecoder): _pulls_fd = True def decode(self, buffer): + rle4 = self.args[1] data = bytearray() x = 0 while len(data) < self.state.xsize * self.state.ysize: @@ -295,9 +296,19 @@ class BmpRle8Decoder(ImageFile.PyDecoder): if x + num_pixels > self.state.xsize: # Too much data for row num_pixels = max(0, self.state.xsize - x) - data += byte * num_pixels + if rle4: + first_pixel = o8(byte[0] >> 4) + second_pixel = o8(byte[0] & 0x0F) + for index in range(num_pixels): + if index % 2 == 0: + data += first_pixel + else: + data += second_pixel + else: + data += byte * num_pixels x += num_pixels else: + # absolute mode if byte[0] == 0: # end of line while len(data) % self.state.xsize != 0: @@ -315,73 +326,18 @@ class BmpRle8Decoder(ImageFile.PyDecoder): data += b"\x00" * (right + up * self.state.xsize) x = len(data) % self.state.xsize else: - # absolute mode - bytes_read = self.fd.read(byte[0]) - data += bytes_read - if len(bytes_read) < byte[0]: - break - x += byte[0] - - # align to 16-bit word boundary - if self.fd.tell() % 2 != 0: - self.fd.seek(1, os.SEEK_CUR) - rawmode = "L" if self.mode == "L" else "P" - self.set_as_raw(bytes(data), (rawmode, 0, self.args[-1])) - return -1, 0 - - -class BmpRle4Decoder(ImageFile.PyDecoder): - _pulls_fd = True - - def decode(self, buffer): - data = bytearray() - x = 0 - while len(data) < self.state.xsize * self.state.ysize: - pixels = self.fd.read(1) - byte = self.fd.read(1) - if not pixels or not byte: - break - num_pixels = pixels[0] - if num_pixels: - # encoded mode - if x + num_pixels > self.state.xsize: - # Too much data for row - num_pixels = max(0, self.state.xsize - x) - first_pixel = o8(byte[0] >> 4) - second_pixel = o8(byte[0] & 0x0F) - for index in range(num_pixels): - if index % 2 == 0: - data += first_pixel + if rle4: + # 2 pixels per byte + byte_count = byte[0] // 2 + bytes_read = self.fd.read(byte_count) + for byte_read in bytes_read: + data += o8(byte_read >> 4) + data += o8(byte_read & 0x0F) else: - data += second_pixel - x += num_pixels - else: - if byte[0] == 0: - # end of line - while len(data) % self.state.xsize != 0: - data += b"\x00" - x = 0 - elif byte[0] == 1: - # end of bitmap - break - elif byte[0] == 2: - # delta - bytes_read = self.fd.read(2) - if len(bytes_read) < 2: - break - right, up = self.fd.read(2) - data += b"\x00" * (right + up * self.state.xsize) - x = len(data) % self.state.xsize - else: - # absolute mode (2 pixels per byte) - total_bytes_to_read = byte[0] // 2 - bytes_read = self.fd.read(total_bytes_to_read) - for byte_read in bytes_read: - first_pixel = o8(byte_read >> 4) - data += first_pixel - second_pixel = o8(byte_read & 0x0F) - data += second_pixel - if len(bytes_read) < total_bytes_to_read: + byte_count = byte[0] + bytes_read = self.fd.read(byte_count) + data += bytes_read + if len(bytes_read) < byte_count: break x += byte[0] @@ -498,8 +454,7 @@ Image.register_extension(BmpImageFile.format, ".bmp") Image.register_mime(BmpImageFile.format, "image/bmp") -Image.register_decoder("bmp_rle8", BmpRle8Decoder) -Image.register_decoder("bmp_rle4", BmpRle4Decoder) +Image.register_decoder("bmp_rle", BmpRleDecoder) Image.register_open(DibImageFile.format, DibImageFile, _dib_accept) Image.register_save(DibImageFile.format, _dib_save) From ddede39932c65a2e618f6e71306f41036a1b1713 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 23 Oct 2022 14:24:30 +1100 Subject: [PATCH 084/239] Removed blank line --- Tests/test_image.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/test_image.py b/Tests/test_image.py index d847d0617..e57903490 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -129,7 +129,6 @@ class TestImage: im.size = (3, 4) def test_invalid_image(self): - im = io.BytesIO(b"") with pytest.raises(UnidentifiedImageError): with Image.open(im): From 64e5baaaf1e57a451a00ea24ca01e80a8bf1bc83 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 23 Oct 2022 15:17:54 +1100 Subject: [PATCH 085/239] Write in binary format --- Tests/test_file_ppm.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 471da9a79..fbcbea6c6 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -240,8 +240,8 @@ def test_header_token_too_long(tmp_path): def test_truncated_file(tmp_path): # Test EOF in header path = str(tmp_path / "temp.pgm") - with open(path, "w", encoding="utf-8") as f: - f.write("P6") + with open(path, "wb") as f: + f.write(b"P6") with pytest.raises(ValueError) as e: with Image.open(path): @@ -256,11 +256,11 @@ def test_truncated_file(tmp_path): im.load() -@pytest.mark.parametrize("maxval", (0, 65536)) +@pytest.mark.parametrize("maxval", (b"0", b"65536")) def test_invalid_maxval(maxval, tmp_path): path = str(tmp_path / "temp.ppm") - with open(path, "w", encoding="utf-8") as f: - f.write("P6\n3 1 " + str(maxval)) + with open(path, "wb") as f: + f.write(b"P6\n3 1 " + maxval) with pytest.raises(ValueError) as e: with Image.open(path): @@ -283,13 +283,13 @@ def test_neg_ppm(): def test_mimetypes(tmp_path): path = str(tmp_path / "temp.pgm") - with open(path, "w", encoding="utf-8") as f: - f.write("P4\n128 128\n255") + with open(path, "wb") as f: + f.write(b"P4\n128 128\n255") with Image.open(path) as im: assert im.get_format_mimetype() == "image/x-portable-bitmap" - with open(path, "w", encoding="utf-8") as f: - f.write("PyCMYK\n128 128\n255") + with open(path, "wb") as f: + f.write(b"PyCMYK\n128 128\n255") with Image.open(path) as im: assert im.get_format_mimetype() == "image/x-portable-anymap" From f7363c1091c70356d92e56abfca6b65bef9e7b26 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 15 Oct 2022 22:30:53 +1100 Subject: [PATCH 086/239] Decode JPEG compressed BLP1 data in original mode --- Tests/images/blp/blp1_jpeg2.blp | Bin 0 -> 2834 bytes Tests/test_file_blp.py | 3 +++ src/PIL/BlpImagePlugin.py | 4 ++-- 3 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 Tests/images/blp/blp1_jpeg2.blp diff --git a/Tests/images/blp/blp1_jpeg2.blp b/Tests/images/blp/blp1_jpeg2.blp new file mode 100644 index 0000000000000000000000000000000000000000..890180e9b479d0569213512b93d5c9caafa03be8 GIT binary patch literal 2834 zcmZ?r2{2>;ga7YVG6ESu%nA`;5MX9t_zwj@As7>+N^AMM(@<5osgLZq<%p3fU;gcH$c{=!byF0kK21FGn1%&2? zxw@rvq~?}aH#Rl;CG<}3shw6_*H{BGgi%ynTuw^PT2axuCfF^whGg*n2;(B42Ur=w z0ODdGpNW};m5rT)lZ#t`fr*isnTds&m6e4BC{qiRV_*?v6;d>GWD^cdWLGK_F>0K+ zkVDyN<3Z7&iyu^slZu)+xx~aJB&Af<)HO7>&RzGL?foE6BpCXviky7|5PjD6C}E$RXl1apA^;oXW;QA4HRi zE^>*fm^@Vd2=WrxN5pxki7X$%Jp}j9{{svn9E=Qq7+4sAt`}rr76e8B!~a_hd@PJW zKG5}o4E79pkvz+`)~d)wB8r)pbs z{~XZdU}^w~I{-4H%=PQsUO%e5v=%1_El?Y!Ervu64P zo)tzAgCsEQCEfu@hJzgiHw)y8z=G31=ig&m{djFq?ODyRZIi3Y86*EQ>~^jdcy&E& zi`-v_H2<~Qzh-;}DPz9+@%ze^<)%yHZu!2v! Date: Mon, 24 Oct 2022 09:26:37 +1100 Subject: [PATCH 087/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 5a1095b61..e897fbe77 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Decode JPEG compressed BLP1 data in original mode #6678 + [radarhere] + +- Added GPS TIFF tag info #6661 + [radarhere] + - Added conversion between RGB/RGBA/RGBX and LAB #6647 [radarhere] @@ -41,7 +47,7 @@ Changelog (Pillow) - Pad IM palette to 768 bytes when saving #6579 [radarhere] -- Added DDS BC6 reading #6449 +- Added DDS BC6H reading #6449 [ShadelessFox, REDxEYE, radarhere] - Added support for opening WhiteIsZero 16-bit integer TIFF images #6642 From 6c17f2e33c5bd2a9ae3d57f7b3bb2f011420dbb8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 24 Oct 2022 12:21:54 +1100 Subject: [PATCH 088/239] Added release notes for #6678 --- docs/releasenotes/9.3.0.rst | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/docs/releasenotes/9.3.0.rst b/docs/releasenotes/9.3.0.rst index 0b8696040..5a4086748 100644 --- a/docs/releasenotes/9.3.0.rst +++ b/docs/releasenotes/9.3.0.rst @@ -4,25 +4,6 @@ Backwards Incompatible Changes ============================== -TODO -^^^^ - -Deprecations -============ - -TODO -^^^^ - -TODO - -API Changes -=========== - -TODO -^^^^ - -TODO - API Additions ============= @@ -55,10 +36,14 @@ Additional images can also be appended when saving, by combining the Security ======== -TODO -^^^^ +Decode JPEG compressed BLP1 data in original mode +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -TODO +Within the BLP image format, BLP1 data may use JPEG compression. Instead of +telling the JPEG library that this data is in BGRX mode, Pillow will now +decode the data in its natural CMYK mode, then convert it to RGB and rearrange +the channels afterwards. Trying to load the data in an incorrect mode could +result in a segmentation fault. Other Changes ============= From d092bb7e0ff341871835d2b9cae742228efef919 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 24 Oct 2022 15:36:31 +1100 Subject: [PATCH 089/239] Only use existing test image --- Tests/images/hopper_4bit.bmp | Bin 8310 -> 0 bytes Tests/images/hopper_rle4.bmp | Bin 8140 -> 0 bytes Tests/test_file_bmp.py | 3 --- 3 files changed, 3 deletions(-) delete mode 100644 Tests/images/hopper_4bit.bmp delete mode 100644 Tests/images/hopper_rle4.bmp diff --git a/Tests/images/hopper_4bit.bmp b/Tests/images/hopper_4bit.bmp deleted file mode 100644 index 8a69b349d30c43ec2af8f94991d5adde83184d88..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8310 zcmai3-)|dNb{;Zzif)U5Gb{Fa=8EIANgvi6s7#Y$;id0zk%tuaXfB1=T>;IKGz`N+ zD@GnJBNzozl!F1WT?EYn>_X|T4V%n}(L+)>h-mAjK*f#+0{T)wTW|{$h`kZL^EdQ6 zmtri(RC*=O4|2Y9zVq{5UKZcFp(}FS!10gq`n#i7N;!oq3|$`OA%i2mUV8CG<)xRt zqh$UtqkQM3Gs??<@cT;cj|}Cibw>Hq*P6=r-x(I~09kp^{y(9^gu z=yTja4r9V#3k&&$1;K?dorQU}m^X;t9Pp!X73_3Cjf|9|F;h>`3U8&;8E6CtyhS<*ix-WhcN`^cWCdIB`f(%JeQ&q9)AJi%^MZm; zq0}D5w?R@@Ne{^+T~1mG1C!bsOwdGDTUctxtzh@s?v-)R_x+|r(&sc&jN&_a4X)Bu zLp4SDYB(4Swh;;XoV|R{qm!zc^OgtLX1LkxHgM5!()2!KrBQSn{-Z~l;U=&&wl#<& z2fey;jTBuJ%voG$>_;CThC5%agFydX`dH%QE-n%IGlOJj0v~Ko>|kPLy!ol)n69uj z)m++*KHdrcs`=Ll5U~jW-_=8p)H@toK4M zY;|El%WG8#4FA13ibD$lG}O}r;CJyixChE4UYNU}lod#~i>8}(*ekHyZwqEw`Ex1Y zHA5qO9v6mzhY!JjJQ%E|U>O`J{sfeo7li@!;-X_Z6TF*ac|%vy8cQ+umc)~!3#1?8 z*Ee*Bg2qlemtoD$2LKn5vq+(`=XtH>QEsS|a=9M?|2?Qf`fu*stqG=sO&XSI*;IA~ z`0*V9q-iqqyn`&Z%a&o2c^Txmq2;cR;wX++`3Fi= zJS?4eF6e~EfCmU4*69t@AX$-8(}so$#=}k}5zpAHMZUg*L!0-hG!VMTu4fj(9BP1A)Os10m z$b-O6hoPn+1o&_jNDu}sq#+6set{)0m{9{sU=0Dn6V_rq%HUJygp!3G>1CJnel?~rPZJV1iK9xp8G z16k0H0T03r;DQ4YRNj<$fXVw*hB!+7l09$)AtYrWTJS{k#2I*Z?ne_mWKsV+(v8U= z1^Jf@G)WL+S%fYNNz~a$JNZH*29&z9pT*-d(m6QtoXJEbq0hmBg2?Uq7)O|*y=pphKaNwy801Gfg znMv{iIUbL$fGoL?+&A%IkrnZd3ipA(lurz{wjn1RxWOj+3MCjj$Scb@jCDX=FX>FauEz^^U_J>h%KR z|0U^%a+U=eg9=%23g5`ZH22ws)A&r59(6x-QFaEXVHmbr%@(eHN1p<3W=TB;+~{&- z?`UM_*sGss(_}tHQ2GIuyqY9Wg2+e5?-2oArlg^|TtXqi<&yr0^Wv*xc!Lz+Ay`k> zzNy;9FkB|!N(vHN_ zq1PVPEv&AS=0txT3r8;q_J4((PL-b1PX0f~>?=?4@zCp9I^lZ_&s(&trA8|Vw{9VT zlZ1apJK-5SZ!b*+4C7&|+p^eTJ%sltBn9b!U+Vgc-#|`oxj4u8B9`ZJG=^=M?bq}7 zn<2ahI0Sf&u>XE~ZU%XXuz8W0+*&%qM-uN_TR}+fw7fMOy`cX~eI|SGM+3?6^9y(i zqZv-{?|;AFts`_TM8D zo8Q1qZz=vc2#^Gtaop|?CEoXk{kBAdfFFdPJeVS$J-ha~^eG}h1^^IAJQXgK_a_1( zKkROvfam-a6G&ov9s<6`BWHcIU%wv@hkg*Z{ciJ*54F)JgsA)@yzr z1cpB17xbdA*K3?WK-k4s?r;y#eJ>JD_{NKaBhM?lYKDe&#&*_uTKjBk`u!KGI*e3PA)pP)(yfgjua% zFq6RecK$_b=$xBgNO(Mox`k%hJw2Z?DvrZ$b9M%d*Z7$`B;ee_QhzXj{m9&29ge5G zMgOA?Ch_6(`Zq?ShilH6jzb1mmw>;In710OLI8XlsSh6p?Y19B-A43`-P4=#ntiH_ z>5#kgP0V9tuNMpp{kYd?(+!Pb0|5x0*M9&ZgKb{SK|3<|hZ4UPgKiK-!y|k|y8Sqe zpT`r2>Rq9@HsX&ya#`9R!*|0l@>^}GJdWUyc7U6mDd*#7yL>Lk%VfXHPfze6@WHT8 z(TjTRaM(jJKve(XaoqG8nX8j_8o4*6|NDqN4DcJMJS0*dO(>vJnu)jFD&r;2We(Wa z;&>k|paI|ahpirhhn|K>9NK&SOgz*Yl>QSAONifLKaS8mpuFLs@Tq%&eb|fJ52mD# zoqQnvpd^<)B4EhGuKWfivA&6TM`${&YAiOZY-j9cE;0X&E7-&Mo zs2>NjDwyK#+Ee@8^TB!)qv??DCzAQ(5cT@Q+4vF^+gQ($dd|-W1%KG`+Y|1jc`~A? z-D=NjVZ2m@`)qiSYrq$MtJOomdoqGUfGGxa3Neizi`(?lrL|9?ltIxiw8Cxx z=Mx3O_hGw_0!A2vDd^Z4)cIw*;=+Yy6A9oKknMfnL-oOrZHityj&R1S`C#K1a&jrk z)#7qhR9#VO-Xs2nmfxbJLk18OX^2-l3Wxr%`SEk`QLR!cE^lxBQ@OGf{tYJbpnz&` z(8|mA(RpA(kF4=SSkde}kN?;fMJ%=}B~k9?rTV6^d6f|RtMjF*z+D||62iGj<{`+4Xtc(27FL_bBLs&IlVc4!+?~0kCof{yPgit=tM&xxw=>X1*{C9v7Cbc)u7O zY;1H6pm(DK9>fLsHMa72cN-0)h&Ht7vlkkK=(QTuL(n+pI%1N26x10Ue8 z17i+=|IoR)y>xFa674=jPd z!3tP#qijP7hXKA9QUm%2(tmK!0STO80xanqBsafuui6!fx2wDu=F>~yABPyEK8}VV z?7&?&f4w8e0ogz#4mL)kae4QG5GX?EM=ccnJo_`OHzjm(h)5I;Yx%1Oq#Fz{rb4#M zrHXCar5fcSdYvt#j(+?qpw>jV>hP&tev-qAgZ#5?jVEN>t zn`gQ@3opQ*6TX5|h3rqM#=K?896~9zd~#nGo18kEg@@-4gsq{s$hvBNz&^zyTwlmnWuY@Wt{(yv<8we+A3f;4HM` zXOK)X#dB^Ktu}P=phGR%CRe)QBumu{Owwm z_z!ql^rSX5ol;XuDx;=oGb*j)O3`NHc?}cTO1WI+xsR$}*geN^@QaX|26?%wmr9*w z3f|GRnfz()5Paq5Wx?||lP~Sw0tjF$3*Y5+Y&g--@S_s8MU=T&_Lpilrr*DORQ|bb zZeh2)YZkEurJGso-CgLX7jhN30LstueKhQuO(w4x(l=!qwA_U55JvJ38v^y%0u z9||Coag~d$@wV7=%QhIi@LjLIA9*)!c$-Prw{AZCY|SXo!n-SvYNcIlcHv9V%6JTX z)jr2wm=Bvz;v~7Tw4NmUQM~_e3*)=9+}9BwC65FNfFD;ryL5%{*S4+8DBvfbhlam-ozyy`CI; z)_i_Z?j7#02@Bej-_6DgOkl!ZhV@v%+V&6k%(7_XXOot-Aoig(*{Ta{fMJT9jV~qt z^@YpduE_qwZP;J6Y0q)+<@`K+kHA<9TXdcc@7RgRVg+TF(7#X#<-KxE1b>d_A1@XV zz$DqNuYvtZ>N}-J74)x)Y@a2vANX}=g{*pA;*&K&_!HE3><>BCjx|&v`cFmT?R8P2 zgu1kU2=kL4!$l{;I~?mzK`TJ;m-@|8{Uv_s^S!sL_WGKA4UU86{|5{! fko3G(sR$$x_`i7=e^GMRSFe)eYhN4rEf@a>9bq(V diff --git a/Tests/images/hopper_rle4.bmp b/Tests/images/hopper_rle4.bmp deleted file mode 100644 index 3c53cc1dd38aa9689c62fd3712958397825d5901..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8140 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 j%rvW>i?-RoB->7MuGY80H#@ diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py index e857f881c..5f6d52355 100644 --- a/Tests/test_file_bmp.py +++ b/Tests/test_file_bmp.py @@ -177,9 +177,6 @@ def test_rle8(): def test_rle4(): - with Image.open("Tests/images/hopper_rle4.bmp") as im: - assert_image_similar_tofile(im, "Tests/images/hopper_4bit.bmp", 12) - with Image.open("Tests/images/bmp/g/pal4rle.bmp") as im: assert_image_similar_tofile(im, "Tests/images/bmp/g/pal4.bmp", 12) From cf46156345d3df5e9fcff3d03e0a67ef78d557fc Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 24 Oct 2022 17:07:57 +1100 Subject: [PATCH 090/239] Moved comment back [ci skip] --- src/PIL/BmpImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index 1eeab24a7..bdf51aa5c 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -308,7 +308,6 @@ class BmpRleDecoder(ImageFile.PyDecoder): data += byte * num_pixels x += num_pixels else: - # absolute mode if byte[0] == 0: # end of line while len(data) % self.state.xsize != 0: @@ -326,6 +325,7 @@ class BmpRleDecoder(ImageFile.PyDecoder): data += b"\x00" * (right + up * self.state.xsize) x = len(data) % self.state.xsize else: + # absolute mode if rle4: # 2 pixels per byte byte_count = byte[0] // 2 From c924fd84a39f3ee9aa9b96d8dec58ec50fdca9d6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 24 Oct 2022 17:12:38 +1100 Subject: [PATCH 091/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index e897fbe77..060569ab5 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Added support for reading BMP images with RLE4 compression #6674 + [npjg, radarhere] + - Decode JPEG compressed BLP1 data in original mode #6678 [radarhere] From 59b04644218d7c21b82e1751b704f175c69d1f92 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 24 Oct 2022 20:04:02 +1100 Subject: [PATCH 092/239] Note when the security issue was introduced --- docs/releasenotes/9.3.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/releasenotes/9.3.0.rst b/docs/releasenotes/9.3.0.rst index 5a4086748..1753901fd 100644 --- a/docs/releasenotes/9.3.0.rst +++ b/docs/releasenotes/9.3.0.rst @@ -43,7 +43,7 @@ Within the BLP image format, BLP1 data may use JPEG compression. Instead of telling the JPEG library that this data is in BGRX mode, Pillow will now decode the data in its natural CMYK mode, then convert it to RGB and rearrange the channels afterwards. Trying to load the data in an incorrect mode could -result in a segmentation fault. +result in a segmentation fault. This issue was introduced in Pillow 9.1.0. Other Changes ============= From 46b0644c4f780e8f292492b007801bf6e81664c2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 24 Oct 2022 22:19:22 +1100 Subject: [PATCH 093/239] Do not modify previous frame when calculating delta --- Tests/test_file_apng.py | 6 ++++-- src/PIL/PngImagePlugin.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Tests/test_file_apng.py b/Tests/test_file_apng.py index 1f5567163..51637c786 100644 --- a/Tests/test_file_apng.py +++ b/Tests/test_file_apng.py @@ -553,18 +553,20 @@ def test_apng_save_disposal(tmp_path): def test_apng_save_disposal_previous(tmp_path): test_file = str(tmp_path / "temp.png") size = (128, 64) - transparent = Image.new("RGBA", size, (0, 0, 0, 0)) + blue = Image.new("RGBA", size, (0, 0, 255, 255)) red = Image.new("RGBA", size, (255, 0, 0, 255)) green = Image.new("RGBA", size, (0, 255, 0, 255)) # test OP_NONE - transparent.save( + blue.save( test_file, save_all=True, append_images=[red, green], disposal=PngImagePlugin.Disposal.OP_PREVIOUS, ) with Image.open(test_file) as im: + assert im.getpixel((0, 0)) == (0, 0, 255, 255) + im.seek(2) assert im.getpixel((0, 0)) == (0, 255, 0, 255) assert im.getpixel((64, 32)) == (0, 255, 0, 255) diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 7fb468877..2c53be109 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -1128,7 +1128,7 @@ def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images) prev_disposal = Disposal.OP_BACKGROUND if prev_disposal == Disposal.OP_BACKGROUND: - base_im = previous["im"] + base_im = previous["im"].copy() dispose = Image.core.fill("RGBA", im.size, (0, 0, 0, 0)) bbox = previous["bbox"] if bbox: From d72779ac03b73f9f9538c8e7fc9f57c887452e88 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 25 Oct 2022 09:03:15 +1100 Subject: [PATCH 094/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 060569ab5..6221fe551 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Added ExifTags enums #6630 + [radarhere] + +- Do not modify previous frame when calculating delta in PNG #6683 + [radarhere] + - Added support for reading BMP images with RLE4 compression #6674 [npjg, radarhere] From 1324c55ddc7e8d7000395e5119633d1bd4aa68ce Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 25 Oct 2022 10:16:47 +1100 Subject: [PATCH 095/239] Added release notes for #6630 --- docs/releasenotes/9.3.0.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/releasenotes/9.3.0.rst b/docs/releasenotes/9.3.0.rst index 1753901fd..a20ee4da6 100644 --- a/docs/releasenotes/9.3.0.rst +++ b/docs/releasenotes/9.3.0.rst @@ -32,6 +32,13 @@ Additional images can also be appended when saving, by combining the im.save(out, save_all=True, append_images=[im1, im2, ...]) +Added ExifTags enums +^^^^^^^^^^^^^^^^^^^^ + +The data from :py:data:`~PIL.ExifTags.TAGS` and +:py:data:`~PIL.ExifTags.GPSTAGS` is now also exposed as ``enum.IntEnum`` +classes: :py:data:`~PIL.ExifTags.Base` and :py:data:`~PIL.ExifTags.GPS`. + Security ======== From 68b435ed86a9cd579cc8d0bb76831877828d990b Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 25 Oct 2022 15:34:31 +0300 Subject: [PATCH 096/239] Test Python 3.11.0 final --- .github/workflows/test-windows.yml | 4 ++-- .github/workflows/test.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 36bd03e7e..f2c35f6a3 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -5,7 +5,7 @@ on: [push, pull_request, workflow_dispatch] permissions: contents: read -concurrency: +concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true @@ -15,7 +15,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11-dev"] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] architecture: ["x86", "x64"] include: # PyPy 7.3.4+ only ships 64-bit binaries for Windows diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4c8a1b85f..645384c02 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,7 +22,7 @@ jobs: python-version: [ "pypy-3.8", "pypy-3.7", - "3.11-dev", + "3.11", "3.10", "3.9", "3.8", From 6788e8f95753be2b13b1b80458d026e6cb3eb9f0 Mon Sep 17 00:00:00 2001 From: Christoph Gohlke Date: Wed, 26 Oct 2022 11:11:30 -0700 Subject: [PATCH 097/239] Fix malloc in _imagingft.c:font_setvaraxes --- src/_imagingft.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_imagingft.c b/src/_imagingft.c index 8f19b763c..4c3a37fb2 100644 --- a/src/_imagingft.c +++ b/src/_imagingft.c @@ -1179,7 +1179,7 @@ font_setvaraxes(FontObject *self, PyObject *args) { } num_coords = PyObject_Length(axes); - coords = malloc(2 * sizeof(coords)); + coords = (FT_Fixed*)malloc(num_coords * sizeof(FT_Fixed)); if (coords == NULL) { return PyErr_NoMemory(); } From d97db54be0b874199bbf666167c9ac15cebfb58e Mon Sep 17 00:00:00 2001 From: Christoph Gohlke Date: Wed, 26 Oct 2022 11:17:28 -0700 Subject: [PATCH 098/239] Only use ASCII characters in C source file --- src/libImaging/ColorLUT.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libImaging/ColorLUT.c b/src/libImaging/ColorLUT.c index fd6e268b5..aee7cda06 100644 --- a/src/libImaging/ColorLUT.c +++ b/src/libImaging/ColorLUT.c @@ -7,8 +7,8 @@ #define PRECISION_BITS (16 - 8 - 2) #define PRECISION_ROUNDING (1 << (PRECISION_BITS - 1)) -/* 8 — scales are multiplied on byte. - 6 — max index in the table +/* 8 - scales are multiplied on byte. + 6 - max index in the table (max size is 65, but index 64 is not reachable) */ #define SCALE_BITS (32 - 8 - 6) #define SCALE_MASK ((1 << SCALE_BITS) - 1) @@ -44,14 +44,14 @@ table_index3D(int index1D, int index2D, int index3D, int size1D, int size1D_2D) Transforms colors of imIn using provided 3D lookup table and puts the result in imOut. Returns imOut on success or 0 on error. - imOut, imIn — images, should be the same size and may be the same image. + imOut, imIn - images, should be the same size and may be the same image. Should have 3 or 4 channels. - table_channels — number of channels in the lookup table, 3 or 4. + table_channels - number of channels in the lookup table, 3 or 4. Should be less or equal than number of channels in imOut image; - size1D, size_2D and size3D — dimensions of provided table; - table — flat table, - array with table_channels × size1D × size2D × size3D elements, - where channels are changed first, then 1D, then​ 2D, then 3D. + size1D, size_2D and size3D - dimensions of provided table; + table - flat table, + array with table_channels * size1D * size2D * size3D elements, + where channels are changed first, then 1D, then 2D, then 3D. Each element is signed 16-bit int where 0 is lowest output value and 255 << PRECISION_BITS (16320) is highest value. */ From 454b586f15397dc667699c50759c8cea0417b0ab Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 26 Oct 2022 22:02:13 +0300 Subject: [PATCH 099/239] Update release notes for 9.3.0 --- docs/releasenotes/9.3.0.rst | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/docs/releasenotes/9.3.0.rst b/docs/releasenotes/9.3.0.rst index a20ee4da6..c05df4a4a 100644 --- a/docs/releasenotes/9.3.0.rst +++ b/docs/releasenotes/9.3.0.rst @@ -1,9 +1,6 @@ 9.3.0 ----- -Backwards Incompatible Changes -============================== - API Additions ============= @@ -55,11 +52,28 @@ result in a segmentation fault. This issue was introduced in Pillow 9.1.0. Other Changes ============= +Windows wheels +^^^^^^^^^^^^^^ + +This release contains wheels for Windows built using GitHub Actions. + +Previously they were built by `Christoph Gohlke `_. + +A huge thanks to Christoph for building Windows binaries for us for around a decade, +plus testing, and fixing over a hundred bug fixes along the way, in addition to building +and hosting unofficial Windows binaries for hundreds of Python projects! + Added DDS ATI1, ATI2 and BC6H reading ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Support has been added to read the ATI1, ATI2 and BC6H formats of DDS images. +Release GIL when converting images using matrix operations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Python's Global Interpreter Lock is now released when converting images using matrix +operations. + Show all frames with ImageShow ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From d3b471b2ae76372d73da64364e06e22807f57369 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 27 Oct 2022 07:42:18 +1100 Subject: [PATCH 100/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 6221fe551..b1d802891 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Release Python GIL when converting images using matrix operations #6418 + [hmaarrfk] + - Added ExifTags enums #6630 [radarhere] From fb0e7cdd91ca271c48ed50c66c4d93d678d325e0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 27 Oct 2022 22:33:20 +1100 Subject: [PATCH 101/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index b1d802891..0724ced59 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Fix malloc in _imagingft.c:font_setvaraxes #6690 + [cgohlke] + - Release Python GIL when converting images using matrix operations #6418 [hmaarrfk] From 4ab80f663e879bb2b83ae2891c47125ff3c7ad6f Mon Sep 17 00:00:00 2001 From: Christoph Gohlke Date: Thu, 27 Oct 2022 08:15:36 -0700 Subject: [PATCH 102/239] Remove backup implementation of Round for Windows platforms --- src/libImaging/Convert.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/libImaging/Convert.c b/src/libImaging/Convert.c index bdc680be4..2b45d0cc4 100644 --- a/src/libImaging/Convert.c +++ b/src/libImaging/Convert.c @@ -43,13 +43,6 @@ #define L(rgb) ((INT32)(rgb)[0] * 299 + (INT32)(rgb)[1] * 587 + (INT32)(rgb)[2] * 114) #define L24(rgb) ((rgb)[0] * 19595 + (rgb)[1] * 38470 + (rgb)[2] * 7471 + 0x8000) -#ifndef round -double -round(double x) { - return floor(x + 0.5); -} -#endif - /* ------------------- */ /* 1 (bit) conversions */ /* ------------------- */ From 3ffd2b2b8f2975e3da3a76c799fc4550c2a1531f Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 28 Oct 2022 13:02:19 +0300 Subject: [PATCH 103/239] Double quotes for old CPython on Windows --- setup.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 14c404752..7a1fabe23 100755 --- a/setup.py +++ b/setup.py @@ -15,9 +15,7 @@ import subprocess import sys import warnings -from setuptools import Extension -from setuptools import __version__ as setuptools_version -from setuptools import setup +from setuptools import Extension, setup from setuptools.command.build_ext import build_ext @@ -852,7 +850,6 @@ class pil_build_ext(build_ext): sys.platform == "win32" and sys.version_info < (3, 9) and not (PLATFORM_PYPY or PLATFORM_MINGW) - and int(setuptools_version.split(".")[0]) < 60 ): defs.append(("PILLOW_VERSION", f'"\\"{PILLOW_VERSION}\\""')) else: From b4bf2885f365b23e16772380173e971f89b208bf Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 28 Oct 2022 21:23:25 +1100 Subject: [PATCH 104/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 0724ced59..74ff24f5c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Fixed set_variation_by_name offset #6445 + [radarhere] + - Fix malloc in _imagingft.c:font_setvaraxes #6690 [cgohlke] From 88ba3a0cb0f0ea3487135c157788a0fb5dd963db Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 28 Oct 2022 22:54:08 +0300 Subject: [PATCH 105/239] Document 3.11 wheels in 9.3.0 release notes --- docs/releasenotes/9.3.0.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/releasenotes/9.3.0.rst b/docs/releasenotes/9.3.0.rst index c05df4a4a..e5a68ed9e 100644 --- a/docs/releasenotes/9.3.0.rst +++ b/docs/releasenotes/9.3.0.rst @@ -52,6 +52,15 @@ result in a segmentation fault. This issue was introduced in Pillow 9.1.0. Other Changes ============= +Python 3.11 wheels +^^^^^^^^^^^^^^^^^^ + +Pillow 9.2.0 had wheels built against Python 3.11 beta, available as a preview to help +others prepare for 3.11, and ensure Pillow can be used immediately on release day of +3.11.0 final (2022-10-24, :pep:`664`). + +Pillow 9.3.0 now officially includes binary wheels for Python 3.11 final. + Windows wheels ^^^^^^^^^^^^^^ From 7ad021efb0a45ac78c3b021f799e3658ea9ece26 Mon Sep 17 00:00:00 2001 From: nulano Date: Fri, 28 Oct 2022 22:51:09 +0100 Subject: [PATCH 106/239] GHA: use GITHUB_OUTPUT instead of deprecated set-output --- .github/workflows/test-windows.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 394b67de2..6b7f62c23 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -65,7 +65,9 @@ jobs: xcopy /S /Y winbuild\depends\test_images\* Tests\images\ # make cache key depend on VS version - & "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" | find """catalog_buildVersion""" | ForEach-Object { $a = $_.split(" ")[1]; echo "::set-output name=vs::$a" } + & "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" ` + | find """catalog_buildVersion""" ` + | ForEach-Object { $a = $_.split(" ")[1]; echo "vs=$a" >> $env:GITHUB_OUTPUT } shell: pwsh - name: Cache build @@ -212,7 +214,7 @@ jobs: ) ) ) - for /f "tokens=3 delims=/" %%a in ("${{ github.ref }}") do echo ::set-output name=dist::dist-%%a + for /f "tokens=3 delims=/" %%a in ("${{ github.ref }}") do echo dist=dist-%%a >> %GITHUB_OUTPUT% winbuild\\build\\build_pillow.cmd --disable-imagequant bdist_wheel shell: cmd From c3326da8a34104a78764dc2f74bf3686b858cf02 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Sat, 29 Oct 2022 11:31:20 +1100 Subject: [PATCH 107/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 74ff24f5c..86893f4db 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Remove backup implementation of Round for Windows platforms #6693 + [cgohlke] + - Fixed set_variation_by_name offset #6445 [radarhere] From e055ef0356414ce02541faab1009f5b4befbf78f Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Sat, 29 Oct 2022 19:22:28 +1100 Subject: [PATCH 108/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 86893f4db..796ad23d6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,15 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Inline fname2char to fix memory leak #6329 + [nulano] + +- Fix memory leaks related to text features #6330 + [nulano] + +- Use double quotes for version check on old CPython on Windows #6695 + [hugovk] + - Remove backup implementation of Round for Windows platforms #6693 [cgohlke] From 93e5fd4b4035096d5e089b3bc7f474e12da84dda Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 18 Aug 2022 19:12:09 +1000 Subject: [PATCH 109/239] Initialize libtiff buffer --- src/libImaging/TiffDecode.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libImaging/TiffDecode.c b/src/libImaging/TiffDecode.c index 7663f96a9..428cd93d2 100644 --- a/src/libImaging/TiffDecode.c +++ b/src/libImaging/TiffDecode.c @@ -771,11 +771,11 @@ ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int fp) { TRACE(("Opening using fd: %d for writing \n", clientstate->fp)); clientstate->tiff = TIFFFdOpen(fd_to_tiff_fd(clientstate->fp), filename, mode); } else { - // malloc a buffer to write the tif, we're going to need to realloc or something + // calloc a buffer to write the tif, we're going to need to realloc or something // if we need bigger. TRACE(("Opening a buffer for writing \n")); - /* malloc check ok, small constant allocation */ - clientstate->data = malloc(bufsize); + /* calloc check ok, small constant allocation */ + clientstate->data = calloc(bufsize, 1); clientstate->size = bufsize; clientstate->flrealloc = 1; From fa71b1107343143c7afecdac578ec5150dd8c715 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 25 Oct 2022 20:06:41 +1100 Subject: [PATCH 110/239] Revert "Temporarily skip valgrind failure" This reverts commit a3e61c1f89ea726d011683486ce81d6c448a2374. --- Tests/test_file_pdf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index 4129e8783..9667b6a4a 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -42,7 +42,6 @@ def test_save(tmp_path, mode): helper_save_as_pdf(tmp_path, mode) -@pytest.mark.valgrind_known_error(reason="Temporary skip") def test_monochrome(tmp_path): # Arrange mode = "1" From 13f2c5ae14901c89c38f898496102afd9daeaf6d Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Fri, 28 Oct 2022 14:11:25 +0200 Subject: [PATCH 111/239] Prevent DOS with large SAMPLESPERPIXEL in Tiff IFD A large value in the SAMPLESPERPIXEL tag could lead to a memory and runtime DOS in TiffImagePlugin.py when setting up the context for image decoding. --- ...-225817ca0f8c663be7ab4b9e717b02c661e66834.tif | Bin 0 -> 88 bytes Tests/test_file_tiff.py | 15 ++++++++++++++- src/PIL/TiffImagePlugin.py | 8 ++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 Tests/images/oom-225817ca0f8c663be7ab4b9e717b02c661e66834.tif diff --git a/Tests/images/oom-225817ca0f8c663be7ab4b9e717b02c661e66834.tif b/Tests/images/oom-225817ca0f8c663be7ab4b9e717b02c661e66834.tif new file mode 100644 index 0000000000000000000000000000000000000000..01dca594f53e22fda9b11ed5b704326680af1b8c GIT binary patch literal 88 zcmebD)MDUZU|`^8U|?isU<9(bfS3`=2FWl%*#bZ|Gn5Td$A-ifWn=;Cox~s{WCDf& DV5 MAX_SAMPLESPERPIXEL: + # DOS check, samples_per_pixel can be a Long, and we extend the tuple below + logger.error("More samples per pixel than can be decoded: %s", samples_per_pixel) + raise SyntaxError("Invalid value for samples per pixel") + if samples_per_pixel < bps_actual_count: # If a file has more values in bps_tuple than expected, # remove the excess. From 05b175ef88c22f5c416bc9b8d5b897dea1abbf2c Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Fri, 28 Oct 2022 14:46:20 +0200 Subject: [PATCH 112/239] Tighter test case --- Tests/test_file_tiff.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 47c4e1b13..752c1e61e 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -868,7 +868,7 @@ class TestFileTiff: def test_oom(self, test_file): with pytest.raises(UnidentifiedImageError): with Image.open(test_file) as im: - im.load() + pass From 00b25fd3ac3648bc28eff5d4c4d816e605e3f05f Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 28 Oct 2022 18:02:24 +0300 Subject: [PATCH 113/239] Hide UserWarning in logs Tests/test_file_tiff.py::TestFileTiff::test_oom[Tests/images/oom-225817ca0f8c663be7ab4b9e717b02c661e66834.tif] PIL/TiffImagePlugin.py:850: UserWarning: Corrupt EXIF data. Expecting to read 12 bytes but only got 6. warnings.warn(str(msg)) Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- Tests/test_file_tiff.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 752c1e61e..5953dfa18 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -867,8 +867,9 @@ class TestFileTiff: @pytest.mark.timeout(2) def test_oom(self, test_file): with pytest.raises(UnidentifiedImageError): - with Image.open(test_file) as im: - pass + with pytest.warns(UserWarning): + with Image.open(test_file) as im: + pass From 799a6a01052cea3f417a571d7c64cd14acc18c64 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 28 Oct 2022 18:03:38 +0300 Subject: [PATCH 114/239] Fix linting --- Tests/test_file_tiff.py | 3 +-- src/PIL/TiffImagePlugin.py | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 5953dfa18..4f3c8e390 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -868,11 +868,10 @@ class TestFileTiff: def test_oom(self, test_file): with pytest.raises(UnidentifiedImageError): with pytest.warns(UserWarning): - with Image.open(test_file) as im: + with Image.open(test_file): pass - @pytest.mark.skipif(not is_win32(), reason="Windows only") class TestFileTiffW32: def test_fd_leak(self, tmp_path): diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 46166fc63..1dfd5275f 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1401,7 +1401,9 @@ class TiffImageFile(ImageFile.ImageFile): if samples_per_pixel > MAX_SAMPLESPERPIXEL: # DOS check, samples_per_pixel can be a Long, and we extend the tuple below - logger.error("More samples per pixel than can be decoded: %s", samples_per_pixel) + logger.error( + "More samples per pixel than can be decoded: %s", samples_per_pixel + ) raise SyntaxError("Invalid value for samples per pixel") if samples_per_pixel < bps_actual_count: From 0846bfae48513f2f51ca8547ed3b8954fa501fda Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 28 Oct 2022 18:03:50 +0300 Subject: [PATCH 115/239] Add to release notes --- docs/releasenotes/9.3.0.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/releasenotes/9.3.0.rst b/docs/releasenotes/9.3.0.rst index e5a68ed9e..410666fc0 100644 --- a/docs/releasenotes/9.3.0.rst +++ b/docs/releasenotes/9.3.0.rst @@ -49,6 +49,15 @@ decode the data in its natural CMYK mode, then convert it to RGB and rearrange the channels afterwards. Trying to load the data in an incorrect mode could result in a segmentation fault. This issue was introduced in Pillow 9.1.0. +Limit SAMPLESPERPIXEL to avoid runtime DOS +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A large value in the ``SAMPLESPERPIXEL`` tag could lead to a memory and runtime DOS in +``TiffImagePlugin.py`` when setting up the context for image decoding. +This was introduced in Pillow 9.2.0, found with `OSS-Fuzz`_ and fixed by limiting +``SAMPLESPERPIXEL`` to the number of planes that we can decode. + + Other Changes ============= @@ -88,3 +97,5 @@ Show all frames with ImageShow When calling :py:meth:`~PIL.Image.Image.show` or using :py:mod:`~PIL.ImageShow`, all frames will now be shown. + +.. _OSS-Fuzz: https://github.com/google/oss-fuzz From 744f455830871d61a8de0a5e629d4c2e33817cbb Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 25 Oct 2022 20:28:24 +1100 Subject: [PATCH 116/239] Added release notes --- docs/releasenotes/9.3.0.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/releasenotes/9.3.0.rst b/docs/releasenotes/9.3.0.rst index e5a68ed9e..a9acad6f8 100644 --- a/docs/releasenotes/9.3.0.rst +++ b/docs/releasenotes/9.3.0.rst @@ -40,6 +40,12 @@ classes: :py:data:`~PIL.ExifTags.Base` and :py:data:`~PIL.ExifTags.GPS`. Security ======== +Initialize libtiff buffer when saving +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When saving a TIFF image to a file object using libtiff, the buffer was not +initialized. This behaviour introduced in Pillow 2.0.0, and has now been fixed. + Decode JPEG compressed BLP1 data in original mode ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 909dc64ed5f676169aa3d9b0c26f132a06321b83 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 29 Oct 2022 15:21:20 +0300 Subject: [PATCH 117/239] 9.3.0 version bump --- src/PIL/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/_version.py b/src/PIL/_version.py index 8e736a432..43896fabd 100644 --- a/src/PIL/_version.py +++ b/src/PIL/_version.py @@ -1,2 +1,2 @@ # Master version for Pillow -__version__ = "9.3.0.dev0" +__version__ = "9.3.0" From d594f4cb8dc47fb0c69ae58d9fff86faae4515bd Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 29 Oct 2022 15:25:53 +0300 Subject: [PATCH 118/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 796ad23d6..6f2ba569e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,9 +2,15 @@ Changelog (Pillow) ================== -9.3.0 (unreleased) +9.3.0 (2022-10-29) ------------------ +- Limit SAMPLESPERPIXEL to avoid runtime DOS #6700 + [wiredfool] + +- Initialize libtiff buffer when saving #6699 + [radarhere] + - Inline fname2char to fix memory leak #6329 [nulano] From 4fc0a4ceb292dcb5856c0ef079c8271a324f8e70 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 29 Oct 2022 17:22:07 +0300 Subject: [PATCH 119/239] 9.4.0.dev0 version bump --- src/PIL/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/_version.py b/src/PIL/_version.py index 43896fabd..1cc1d0f1c 100644 --- a/src/PIL/_version.py +++ b/src/PIL/_version.py @@ -1,2 +1,2 @@ # Master version for Pillow -__version__ = "9.3.0" +__version__ = "9.4.0.dev0" From 76b99756e425099b650bd383e204c259b5bdd3f1 Mon Sep 17 00:00:00 2001 From: nulano Date: Sat, 29 Oct 2022 18:24:44 +0100 Subject: [PATCH 120/239] disable __CxxFrameHandler4 when compiling harfbuzz --- winbuild/build_prepare.py | 1 + 1 file changed, 1 insertion(+) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index b4b15cc1e..bc19c5fa1 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -360,6 +360,7 @@ deps = { "dir": "harfbuzz-5.3.1", "license": "COPYING", "build": [ + cmd_set("CXXFLAGS", "-d2FH4-"), cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"), cmd_nmake(target="clean"), cmd_nmake(target="harfbuzz"), From eaee7fda973d6915f7071f9b7f7edb7ad481ef7c Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 30 Oct 2022 02:56:05 +0000 Subject: [PATCH 121/239] add xfail mark to test_image_access:test_embeddable --- Tests/test_image_access.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index 955740b95..04bdfc5aa 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -406,11 +406,8 @@ class TestImagePutPixelError(AccessTest): class TestEmbeddable: - @pytest.mark.skipif( - not is_win32() or on_ci(), - reason="Failing on AppVeyor / GitHub Actions when run from subprocess, " - "not from shell", - ) + @pytest.mark.xfail(reason="failing test") + @pytest.mark.skipif(not is_win32(), reason="requires Windows") def test_embeddable(self): import ctypes From 181fad2918f6ca24f4f149ebd29af73dda34d2cb Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 30 Oct 2022 03:17:13 +0000 Subject: [PATCH 122/239] move import used only on Windows and remove unused import --- Tests/test_image_access.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index 04bdfc5aa..f19db440d 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -4,11 +4,10 @@ import sys import sysconfig import pytest -from setuptools.command.build_ext import new_compiler from PIL import Image -from .helper import assert_image_equal, hopper, is_win32, on_ci +from .helper import assert_image_equal, hopper, is_win32 # CFFI imports pycparser which doesn't support PYTHONOPTIMIZE=2 # https://github.com/eliben/pycparser/pull/198#issuecomment-317001670 @@ -410,6 +409,7 @@ class TestEmbeddable: @pytest.mark.skipif(not is_win32(), reason="requires Windows") def test_embeddable(self): import ctypes + from setuptools.command.build_ext import new_compiler with open("embed_pil.c", "w", encoding="utf-8") as fh: fh.write( From 23df3bda7f4e052c84dc71d3dbf417f0d8d03741 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 30 Oct 2022 03:17:54 +0000 Subject: [PATCH 123/239] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- Tests/test_image_access.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index f19db440d..6c4f1ceec 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -409,6 +409,7 @@ class TestEmbeddable: @pytest.mark.skipif(not is_win32(), reason="requires Windows") def test_embeddable(self): import ctypes + from setuptools.command.build_ext import new_compiler with open("embed_pil.c", "w", encoding="utf-8") as fh: From e50a3a213ed5f5296deeaefe9ec0699010545157 Mon Sep 17 00:00:00 2001 From: TrellixVulnTeam Date: Sun, 30 Oct 2022 23:44:48 +0000 Subject: [PATCH 124/239] Adding tarfile member sanitization to extractall() --- winbuild/build_prepare.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index b4b15cc1e..0e585f796 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -474,7 +474,26 @@ def extract_dep(url, filename): zf.extractall(sources_dir) elif filename.endswith(".tar.gz") or filename.endswith(".tgz"): with tarfile.open(file, "r:gz") as tgz: - tgz.extractall(sources_dir) + def is_within_directory(directory, target): + + abs_directory = os.path.abspath(directory) + abs_target = os.path.abspath(target) + + prefix = os.path.commonprefix([abs_directory, abs_target]) + + return prefix == abs_directory + + def safe_extract(tar, path=".", members=None, *, numeric_owner=False): + + for member in tar.getmembers(): + member_path = os.path.join(path, member.name) + if not is_within_directory(path, member_path): + raise Exception("Attempted Path Traversal in Tar File") + + tar.extractall(path, members, numeric_owner=numeric_owner) + + + safe_extract(tgz, sources_dir) else: raise RuntimeError("Unknown archive type: " + filename) From 74c60b47a89502adec658316b06cd0043046d36b Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 30 Oct 2022 23:45:49 +0000 Subject: [PATCH 125/239] simplify patch, also check zipfile --- winbuild/build_prepare.py | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 0e585f796..3cd841484 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -469,31 +469,23 @@ def extract_dep(url, filename): raise RuntimeError(ex) print("Extracting " + filename) + sources_dir_abs = os.path.abspath(sources_dir) if filename.endswith(".zip"): with zipfile.ZipFile(file) as zf: + for member in zf.namelist(): + member_abspath = os.path.abspath(os.path.join(sources_dir, member)) + member_prefix = os.path.commonprefix([sources_dir_abs, member_abspath]) + if sources_dir_abs != member_prefix: + raise RuntimeError("Attempted Path Traversal in Zip File") zf.extractall(sources_dir) elif filename.endswith(".tar.gz") or filename.endswith(".tgz"): with tarfile.open(file, "r:gz") as tgz: - def is_within_directory(directory, target): - - abs_directory = os.path.abspath(directory) - abs_target = os.path.abspath(target) - - prefix = os.path.commonprefix([abs_directory, abs_target]) - - return prefix == abs_directory - - def safe_extract(tar, path=".", members=None, *, numeric_owner=False): - - for member in tar.getmembers(): - member_path = os.path.join(path, member.name) - if not is_within_directory(path, member_path): - raise Exception("Attempted Path Traversal in Tar File") - - tar.extractall(path, members, numeric_owner=numeric_owner) - - - safe_extract(tgz, sources_dir) + for member in tgz.getmembers(): + member_abspath = os.path.abspath(os.path.join(sources_dir, member.name)) + member_prefix = os.path.commonprefix([sources_dir_abs, member_abspath]) + if sources_dir_abs != member_prefix: + raise RuntimeError("Attempted Path Traversal in Tar File") + tgz.extractall(sources_dir) else: raise RuntimeError("Unknown archive type: " + filename) From 7528b673fa4517604f4af91a6d061802843f3246 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 31 Oct 2022 19:36:14 +1100 Subject: [PATCH 126/239] Removed Fedora 35 --- .github/workflows/test-docker.yml | 3 +-- docs/installation.rst | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/test-docker.yml b/.github/workflows/test-docker.yml index c68d43935..1e36b3382 100644 --- a/.github/workflows/test-docker.yml +++ b/.github/workflows/test-docker.yml @@ -5,7 +5,7 @@ on: [push, pull_request, workflow_dispatch] permissions: contents: read -concurrency: +concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true @@ -30,7 +30,6 @@ jobs: centos-stream-9-amd64, debian-10-buster-x86, debian-11-bullseye-x86, - fedora-35-amd64, fedora-36-amd64, gentoo, ubuntu-18.04-bionic-amd64, diff --git a/docs/installation.rst b/docs/installation.rst index eb69d5805..4812b27cf 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -440,8 +440,6 @@ These platforms are built and tested for every change. +----------------------------------+----------------------------+---------------------+ | Debian 11 Bullseye | 3.9 | x86 | +----------------------------------+----------------------------+---------------------+ -| Fedora 35 | 3.10 | x86-64 | -+----------------------------------+----------------------------+---------------------+ | Fedora 36 | 3.10 | x86-64 | +----------------------------------+----------------------------+---------------------+ | Gentoo | 3.9 | x86-64 | From 6fd772e6694c53aeb1c9d5ff48d931a4db63fb2c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Nov 2022 00:08:29 +1100 Subject: [PATCH 127/239] Updated lcms2 to 2.14 --- docs/installation.rst | 2 +- winbuild/build_prepare.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 4812b27cf..c65095640 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -147,7 +147,7 @@ Many of Pillow's features require external libraries: * **littlecms** provides color management * Pillow version 2.2.1 and below uses liblcms1, Pillow 2.3.0 and - above uses liblcms2. Tested with **1.19** and **2.7-2.13.1**. + above uses liblcms2. Tested with **1.19** and **2.7-2.14**. * **libwebp** provides the WebP format. diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index b4b15cc1e..455481e3d 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -293,9 +293,9 @@ deps = { # "bins": [r"objs\{msbuild_arch}\Release\freetype.dll"], }, "lcms2": { - "url": SF_PROJECTS + "/lcms/files/lcms/2.13/lcms2-2.13.1.tar.gz/download", - "filename": "lcms2-2.13.1.tar.gz", - "dir": "lcms2-2.13.1", + "url": SF_PROJECTS + "/lcms/files/lcms/2.13/lcms2-2.14.tar.gz/download", + "filename": "lcms2-2.14.tar.gz", + "dir": "lcms2-2.14", "license": "COPYING", "patch": { r"Projects\VC2022\lcms2_static\lcms2_static.vcxproj": { From 6b286ed62f6cb2be447a5bb3a7f09f5edad5f3d7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Nov 2022 07:47:05 +1100 Subject: [PATCH 128/239] XCB will not be used on Linux if gnome-screenshot is present --- Tests/test_imagegrab.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py index fa2291582..5e0eca28b 100644 --- a/Tests/test_imagegrab.py +++ b/Tests/test_imagegrab.py @@ -1,4 +1,5 @@ import os +import shutil import subprocess import sys @@ -33,7 +34,9 @@ class TestImageGrab: @pytest.mark.skipif(Image.core.HAVE_XCB, reason="tests missing XCB") def test_grab_no_xcb(self): - if sys.platform not in ("win32", "darwin"): + if sys.platform not in ("win32", "darwin") and not shutil.which( + "gnome-screenshot" + ): with pytest.raises(OSError) as e: ImageGrab.grab() assert str(e.value).startswith("Pillow was built without XCB support") From c10c6bf8940c2a1bbbc1d01575489cb85e612a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ondrej=20Baranovi=C4=8D?= Date: Thu, 3 Nov 2022 20:23:59 +0100 Subject: [PATCH 129/239] use os.path.commonpath instead of os.path.commonprefix Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- winbuild/build_prepare.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 3cd841484..14f8d7ba0 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -474,7 +474,7 @@ def extract_dep(url, filename): with zipfile.ZipFile(file) as zf: for member in zf.namelist(): member_abspath = os.path.abspath(os.path.join(sources_dir, member)) - member_prefix = os.path.commonprefix([sources_dir_abs, member_abspath]) + member_prefix = os.path.commonpath([sources_dir_abs, member_abspath]) if sources_dir_abs != member_prefix: raise RuntimeError("Attempted Path Traversal in Zip File") zf.extractall(sources_dir) @@ -482,7 +482,7 @@ def extract_dep(url, filename): with tarfile.open(file, "r:gz") as tgz: for member in tgz.getmembers(): member_abspath = os.path.abspath(os.path.join(sources_dir, member.name)) - member_prefix = os.path.commonprefix([sources_dir_abs, member_abspath]) + member_prefix = os.path.commonpath([sources_dir_abs, member_abspath]) if sources_dir_abs != member_prefix: raise RuntimeError("Attempted Path Traversal in Tar File") tgz.extractall(sources_dir) From d93b9919e338f8fe253e76fe0bdc7f0994267385 Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Thu, 3 Nov 2022 21:40:08 +0100 Subject: [PATCH 130/239] Use verbose flag for pip install * Ensures when developing that compilation warnings are visible * Provides feedback that compilation has occured. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 8f2862948..7783bd96b 100644 --- a/Makefile +++ b/Makefile @@ -53,12 +53,12 @@ inplace: clean .PHONY: install install: - python3 -m pip install . + python3 -m pip -v install . python3 selftest.py .PHONY: install-coverage install-coverage: - CFLAGS="-coverage -Werror=implicit-function-declaration" python3 -m pip install --global-option="build_ext" . + CFLAGS="-coverage -Werror=implicit-function-declaration" python3 -m pip -v install --global-option="build_ext" . python3 selftest.py .PHONY: debug From 41987cffade673b46b96054f8462f3c45d410de0 Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Thu, 3 Nov 2022 22:57:39 +0100 Subject: [PATCH 131/239] Fix compiler error: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit src/_imaging.c:1842:17: warning: ‘ImagingTransform’ accessing 64 bytes in a region of size 48 [-Wstringop-overflow=] 1842 | imOut = ImagingTransform( | ^~~~~~~~~~~~~~~~~ 1843 | imOut, imIn, IMAGING_TRANSFORM_AFFINE, 0, 0, xsize, ysize, a, filter, 1); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ src/_imaging.c:1842:17: note: referencing argument 8 of type ‘double *’ --- src/_imaging.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_imaging.c b/src/_imaging.c index 0888188fb..940b5fbb3 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -1829,7 +1829,7 @@ _resize(ImagingObject *self, PyObject *args) { box[1] - (int)box[1] == 0 && box[3] - box[1] == ysize) { imOut = ImagingCrop(imIn, box[0], box[1], box[2], box[3]); } else if (filter == IMAGING_TRANSFORM_NEAREST) { - double a[6]; + double a[8]; memset(a, 0, sizeof a); a[0] = (double)(box[2] - box[0]) / xsize; From f9a2f991db4fb91a78ba1bb30bb45b9d2b235348 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 4 Nov 2022 11:48:18 +1100 Subject: [PATCH 132/239] Replaced IOError with OSError --- src/_imagingft.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_imagingft.c b/src/_imagingft.c index 7cd6dfb1d..bd4099176 100644 --- a/src/_imagingft.c +++ b/src/_imagingft.c @@ -956,7 +956,7 @@ font_render(FontObject *self, PyObject *args) { /* we didn't ask for color, fall through to default */ #endif default: - PyErr_SetString(PyExc_IOError, "unsupported bitmap pixel mode"); + PyErr_SetString(PyExc_OSError, "unsupported bitmap pixel mode"); goto glyph_error; } @@ -1023,7 +1023,7 @@ font_render(FontObject *self, PyObject *args) { } } } else { - PyErr_SetString(PyExc_IOError, "unsupported bitmap pixel mode"); + PyErr_SetString(PyExc_OSError, "unsupported bitmap pixel mode"); goto glyph_error; } } From 8947cbf4d113b112660b15db23fe84f94e128788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ondrej=20Baranovi=C4=8D?= Date: Fri, 4 Nov 2022 07:31:00 +0100 Subject: [PATCH 133/239] simplify code Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- winbuild/build_prepare.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 14f8d7ba0..872e74a20 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -480,8 +480,8 @@ def extract_dep(url, filename): zf.extractall(sources_dir) elif filename.endswith(".tar.gz") or filename.endswith(".tgz"): with tarfile.open(file, "r:gz") as tgz: - for member in tgz.getmembers(): - member_abspath = os.path.abspath(os.path.join(sources_dir, member.name)) + for member in tgz.getnames(): + member_abspath = os.path.abspath(os.path.join(sources_dir, member)) member_prefix = os.path.commonpath([sources_dir_abs, member_abspath]) if sources_dir_abs != member_prefix: raise RuntimeError("Attempted Path Traversal in Tar File") From bbe9cc6ae5f19207b0c3bf80f591016ec03019b2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 4 Nov 2022 20:26:39 +1100 Subject: [PATCH 134/239] Use verbose flag for pip install for debug target --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7783bd96b..a2545b54e 100644 --- a/Makefile +++ b/Makefile @@ -67,7 +67,7 @@ debug: # for our stuff, kills optimization, and redirects to dev null so we # see any build failures. make clean > /dev/null - CFLAGS='-g -O0' python3 -m pip install --global-option="build_ext" . > /dev/null + CFLAGS='-g -O0' python3 -m pip -v install --global-option="build_ext" . > /dev/null .PHONY: release-test release-test: From ef1eb2f3d6e7e19c251beb6eda5e9ac06e902ca8 Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Fri, 4 Nov 2022 11:16:22 +0100 Subject: [PATCH 135/239] Add oss-fuzz badge [ci skip] --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index e7c0ebc5a..7a81e0c40 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,9 @@ As of 2019, Pillow development is Tidelift Align + Fuzzing Status From b8be24850bc07c04a21635dbd8835c9e8408cf93 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 5 Nov 2022 21:54:23 +1100 Subject: [PATCH 136/239] Added file to questionable list --- Tests/test_bmp_reference.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/test_bmp_reference.py b/Tests/test_bmp_reference.py index b17aad2ea..ed9aff9cc 100644 --- a/Tests/test_bmp_reference.py +++ b/Tests/test_bmp_reference.py @@ -35,6 +35,7 @@ def test_questionable(): "pal8os2v2.bmp", "rgb24prof.bmp", "pal1p1.bmp", + "pal4rletrns.bmp", "pal8offs.bmp", "rgb24lprof.bmp", "rgb32fakealpha.bmp", From 4001a9fab471dbeaaa6f530d98208e25ef6bf912 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 6 Nov 2022 22:41:06 +1100 Subject: [PATCH 137/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 6f2ba569e..fc8d8362a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,15 @@ Changelog (Pillow) ================== +9.4.0 (unreleased) +------------------ + +- Fix compiler warning: accessing 64 bytes in a region of size 48 #6714 + [wiredfool] + +- Use verbose flag for pip install #6713 + [wiredfool, radarhere] + 9.3.0 (2022-10-29) ------------------ From 9448532f913665006b0373ae3cee61c550b34339 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 7 Nov 2022 18:03:51 +0000 Subject: [PATCH 138/239] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 22.8.0 → 22.10.0](https://github.com/psf/black/compare/22.8.0...22.10.0) - [github.com/sphinx-contrib/sphinx-lint: v0.6.1 → v0.6.7](https://github.com/sphinx-contrib/sphinx-lint/compare/v0.6.1...v0.6.7) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f81bcb956..2c13fb3b1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/psf/black - rev: 22.8.0 + rev: 22.10.0 hooks: - id: black args: ["--target-version", "py37"] @@ -44,7 +44,7 @@ repos: - id: check-yaml - repo: https://github.com/sphinx-contrib/sphinx-lint - rev: v0.6.1 + rev: v0.6.7 hooks: - id: sphinx-lint From e31ca06b7cde667aa973cfcb35166d17a5408925 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 8 Nov 2022 10:58:33 +1100 Subject: [PATCH 139/239] Updated AppVeyor to Python 3.11 --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 20908052b..b817cd9d8 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -10,7 +10,7 @@ environment: TEST_OPTIONS: DEPLOY: YES matrix: - - PYTHON: C:/Python310 + - PYTHON: C:/Python311 ARCHITECTURE: x86 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 - PYTHON: C:/Python37-x64 From 5471dc2b265d2486a9fe0f37d2d7cbfe6f4f4cd6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 8 Nov 2022 11:49:39 +1100 Subject: [PATCH 140/239] Use fractional coordinates when drawing text --- .../images/test_anchor_multiline_mm_right.png | Bin 9514 -> 9525 bytes .../test_combine_multiline_lm_center.png | Bin 4160 -> 4147 bytes .../images/test_combine_multiline_lm_left.png | Bin 4223 -> 4197 bytes .../test_combine_multiline_lm_right.png | Bin 4170 -> 4154 bytes .../test_combine_multiline_mm_center.png | Bin 4243 -> 4204 bytes .../images/test_combine_multiline_mm_left.png | Bin 4216 -> 4189 bytes .../test_combine_multiline_mm_right.png | Bin 4247 -> 4215 bytes .../test_combine_multiline_rm_center.png | Bin 4160 -> 4149 bytes .../images/test_combine_multiline_rm_left.png | Bin 4149 -> 4144 bytes .../test_combine_multiline_rm_right.png | Bin 4215 -> 4186 bytes Tests/images/text_float_coord.png | Bin 2877 -> 2875 bytes Tests/images/text_float_coord_1_alt.png | Bin 807 -> 809 bytes Tests/test_imagedraw.py | 21 ++++++++++++++++++ src/PIL/ImageDraw.py | 10 +++++++-- src/PIL/ImageFont.py | 19 ++++++++++++++-- src/_imagingft.c | 12 ++++++---- 16 files changed, 54 insertions(+), 8 deletions(-) diff --git a/Tests/images/test_anchor_multiline_mm_right.png b/Tests/images/test_anchor_multiline_mm_right.png index cf002b12cd0a89eec1f97cd1c6530b7d1007fc9e..7e98b8eac8d780876d377315b2158f203c30757c 100644 GIT binary patch literal 9525 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 TfD8N|D1`D|b%jrICcgg%hStVx diff --git a/Tests/images/test_combine_multiline_lm_center.png b/Tests/images/test_combine_multiline_lm_center.png index 7b1e9c4e42f69874b4c508d16bc85527d5d902b7..6a15130248ad8bed5e13a937d3667eb98624b5bf 100644 GIT binary patch literal 4147 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 literal 4160 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 CJA=Fc diff --git a/Tests/images/test_combine_multiline_lm_left.png b/Tests/images/test_combine_multiline_lm_left.png index a26996c2dbe70748f834607fc49ae8dd079c4e09..8eb254fdf26323e778c1ff5e73dab8c8a0b4b638 100644 GIT binary patch literal 4197 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 diff --git a/Tests/images/test_combine_multiline_lm_right.png b/Tests/images/test_combine_multiline_lm_right.png index 7caf5cb742a27f001106cc3ade926f2f177e71fb..cb640a7409fccce7b08ec23bdf728f11a78a1533 100644 GIT binary patch literal 4154 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 literal 4170 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 T-aH8YY>?KX<8lAr0RHTE diff --git a/Tests/images/test_combine_multiline_mm_left.png b/Tests/images/test_combine_multiline_mm_left.png index aadb5191f0e7df6ae1838ca6b04745268de17728..f539a8e62e66d1769e1bd692594d5938db7d1d46 100644 GIT binary patch literal 4189 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 AyZ`_I literal 4216 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 Ha8LLb^xtTU literal 4247 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 sq)Epb{z}gH3sL0nTmDag9FofH^0IWV!Z diff --git a/Tests/images/test_combine_multiline_rm_center.png b/Tests/images/test_combine_multiline_rm_center.png index 7568dd63a33181ae5ec5c8c99ac72c18859109b6..4cce8f6a00ead59f2f061bde6a6b374e9fecbfe3 100644 GIT binary patch literal 4149 zcmeHLX*85?8<(hrrd3){sZ@w)tl?kURFtx&v4m8UWEoSo5t`nTEX8Dc%J*5=RD`R?(4dK%XL3bF4>$>T)Ta( zl$4aB)!CDmrKDCQEMIG+!4n80J00AGRws|!-Ore0Jh-zNA*r8XDCKT8{$;oMczq;G zZN1#**r@muL))HhkKXr6`Q^!@$A82$smo#ysP5greT($d1IiNJkI_*+n^m_=|3$Eq znURUMk9L$lS52|t@#)NS*_XPrMIE$>l2q?to++JSSkcb9Q%feB%RVyj{QT_e*RP|h zvPIUmfe@(ycR8i?X!jK!YyK>MO7IU9oCbIy6Pmtnf7@Z+=$+}zyK;U1USHYv$p<{PE|{#<-a^Yp_n`hQ8Y zJ^l-24)AtTD1Tn!T<&ER2wA<>sx(qpMt+Uu|9kn#<#MTJ=&l2qHljwXQ=61r>0{ZL z-A*$^iCA#z)G1a_ZB5O>2*%B3Xm@|u9JR`qp4ZOG$;m-7!wsK2j`~v1{$Iri?8*(J&&<; z#&|mItygt8YZfx^1j>_dlX)4}!C}9^ zyHoVsjMwemlA?0GXxBDgX_Eg*o=#VZ8>)F9#*1H3zBJDYWWB|>-XDC_h*q5FDx$BT zU4p?#A>46C*9Rk?wz>&N)BOUfA*#m~7RRt^M{j=5hP0|G$*nK++KH>4h_?_M-TIje zf*T6+H(fkd=0=HVQbUEstD5yz`!S1Qbwfi#u)5&Rt}ae@nJ2x9ay@NQqZcS~213u=j#Zy{b723fqr<2ALlC%FCnOr=(>xxAhlit=KqFxoVd}gY<{>TK{_~RL{=K%L^CqSV1Ae zg&C?@iu6tIbjw{)h`mH!FT5^z@;1kI^94=%PU#JW>K@HW`>19yCvN}!N^;H8(h}&s z0sp|-yky^XWosOUx(b~uz}H4qPc>AQQ+F#hoa@4(#KuTY-+vxug(M}M17P&4xIr}m z$w5Z*K}Q*1z8d)ThtXijOl1E9K(fOJ4;qiZdjlE`4fyqi?nen{)FK3Rv=5Ku=hTL> zAFG-SysP%5r%0%{t!`od9BdlV*!Ak(diql1KRCqzegN8?Z*+V zhA+;J08|^jszZ)0H?!Bpp==$0}_^%MKp`fbPB@s1@T?Xb_=e6ArG2trTzDtvCP*WiaoXH*b&!8{VbzYZwn zoEitHLo3xL-C**WeydT&jAeif{!^+2D&j8W=C@Zz^05&i!!2X0Ct7qU*C-L;jo#>W zDj@)bBRBFdV#ywjX!iWaC!3am!=5koXgs8;3GtauIM8t0e^zpN>ZxY2(ZH-FTA|q@ zDcN)n8ROzddQT)CGZl?tQLf?K&QKmZ)fCCmEFmV5%0RE>w;sMjubRw;sBGNtq}>8C zHy2Jf_OWsKSt0dxx$P{W`n*fkou0AlK;$o)aqSNK!n+CygXkT12_q?e(tIXL0yG!m z-*@li8iy935-CFUA)t)s)neMr1zJcu#1 zY!KY7KAG0p8K-7p&c{`R@;L-l?MRBtYd7mu|$MI*2oY>&jb>x2UgJhnimrKLT6`qV6x z=jGGV(&AUk%CkF!=<{zH!KQLSfwbJn#2I@iBJg#jjHdshqxr&6tj@X`A%X_O)P1}O z2zR`7EW-pb4givs2`pzgN>)AqVry&b-{Xz(&eu=<79+Q5yD|4@=REbH^v1%EipsjX z3`xP0z1HalP_nRD1Dhd<`)Y4*Z^#I`vj2tgz)Iln?VH9{sj^qkLR1Ddvv_B#XA)B zj(tEN5P)Fh$tpTf27Le`H2y2agnDW#tg_ubGmHa)c`hQinFh1vb%+U>oPLr3t+<1S zL=?>oH|bohJYdQo1dhFj(|D6~B=)>&$Ea|^?Qr6SK`8Qw3yxw@1}vQe+*IA-5zRHU zXwbyqPPZla8*sHM9 zs1$$|$33X2=~8_c=I!lGD~3U8?%CS@AdF?DQ_^{05b;87c?FFSF7!2U!%aqCdCryu z)n5PfR4D&q)U=ZNuyIV?Lr*MD)P?gojAd%u+jsW9hob;Z!QAEwCK|B&kN?_W*ni(s zU7!W326|iqbPsfs2xkKI z#4}T!+w^bL7#))xQ~q;3Efvp@E^c3<~>U)S&Y{=VPu@4D{Cm#odV zZBgAKCMLG+!uhjyVq)UYH-4KXK#v0REkR6dyUc~NXBa@Zl`r8-^CrQ^X}R$p?*YS-ooLZ!xJy?Hj1{p+@HcP?T{MPuu#lR_f}L_ z+K%56^2=9od{p|GF6BSbINZ#_z)*|r!I{p;X*Q41HrqCXEgvu_YX~$zqEKo0-w8)O zEj&Fv?PKvTvvP7}#4tHha+^(y@BZUw^Amy}S@0|1%TSlQ_v0^985+Y;4mtrJ*g_i} zN0(0}`|c(xKhV0;d_Fr~7z7=Er_E@@s}k3M4} zW1a%;8dOrVc8Buzhi=kkclg}&bkL1-ajABz!)vOKtS=Awb>}%6r|9YEtgej4#)oV+ zJc(Co`&xIe>FJ($i_D`*ZIhqh!35tM>e0ByKa}7uw;NNT;dZ%QJO&Ckp5&z*yoS+y z-C%Jp&I^;5pWk6P`gD#eI+b8SLNmkq-`}Vh^tZJA{6aWjk3xy+<@5&w=~g5XDc2zn zClXq|JUrF;w&5ze-RSVnubUyMddRA_G#C|04$gJBxjf6PV1=d!qmBUvsDOv%a>B&1CE>&^rwa52iav}Yz=T5Q3N))alvx!7TVJG8 zATjFD)g-S}BOyRE5QG1F#Cb;0=XA4pEi0$0z^NT*ua)n^3a3`SKHqP;<)}{G0okeM zc4rcchLf}O%>XABIHC#3Ufzu8mdG z)lheqnb;>P`ofVX`aMLHh*L2n6fAV$1oKTAIDsu34tH|uFTXVaFyPHH$$mo=Ng4Tl zCw#9U5Qqb#n7`K?iQJ%y=eEz*Xn3&D^yE^nyNnudoJLrno($^B zv8^Pl)|j|DY1ZcJ>_BDNJzMSnCMoR$0s#|PXVbS8nB45j(TH5RO3g=wjwMt!T3A{# z3Y{6>i}kA}(+9bLtRVAj9Co5re{YQmNOyJK&iOP`(by?Wv9hjHSEgm6DWV2EHdnLE zqByUNC8D^okK2ve1yxm5lF#`3iL{QP@#@e8Ar32~e^A?bc;|?7z|N9GDXIK9oLBZl zM;g_)uXJYduDGNgRlto%Of$TF4dBl8<4?YeJ@<6Ct-XCbInyTXl#-GXlZ+)2i9@BH zt?6b@xYTS)Mux}yNaH;j1(r|i9A$0x(T-pJVPod%p&lb2AFs)~EsWE~#>S|^RiEIc zRW`8$1}EL1XC)o;I)TPoSVs8BKVW>U^UdX^_7c!lhn9U~_Z%C))KgScG|Q?9M;8J& zxa{c2Azjpp59N;~4v>5joeQvYEFkhVG^fYekEUyHXUFY^qd4TY2P&?woU=fn)b*u- zsK0`9$fKEN5(53J0f8ORTjz zFBuoW!_c^SQ-7)azN91W=Vmv1e|)sV^x@VUA$);b1dtvu9TqkzN>YXeNLbkiluSgF zX_-LLgC9~%u-CI?wy8QG5TTm)qR+}+F0*%ZOdwE3{(#6=2|!A@9Uf!tkLJeOP38)6 zaz0s$03R4G0W<(CKuWkY;OFJ%OV6Us^p!c2ZU(KGM&Fmcyif`juLQaL?Ya%{oO?xa z*4YakokSEhlKKRYfR=n5l31PqfwS%`(6EEI&CyZO>R~y+a-)sVhB{LDp$KYZgmBON znR}Zu73zSMr#O2y#i+^jhaeDrqp`45LIf~(f4@!UPj|l%4dFUZf;qY1hWfB_@EnU- zTLVW$@F;NW6imJBvFmN#D`8>Y@?cy(C@HZRIi3vJWnn5iO;1;sMjacD)@aAs%hGy| zSV6^v*NKn}9S9`WDC;)tow($te^%FmG`_@%N9ZIZ1G^1kAQ?I4QCj=2&@2baZ!LIf zUwnlbd91CBS=)QU_Y?IyG#?a!X6>}<`M>tct(<-?1a>ptAzY-}NDqb}kxWN8 zG9jGN2fShsxdDJMDcn01#5cttqJ%TrX?N^upTWM@N`ey9Y+Zq7uHI8P^=ZA)=GHCF zt0V40AOwk1`xIZh9oOd!RKPgz>HcMv`Npw>8|2gvriz4Xt*x!uHfgL9_of+LVAR#i ztZ+FV=DAdzg^Lie@Q`TqN!7^T1HJmpfYtlq_UMb)w^>=&XL?J*mu8yV<@cVLDIY*v zX4(P0c2JM90Yt9vy=@x^H)m!~i_2af;llWC4G*`;AR`4U^JZqE^~$d9Zl!N7D<2>4 zqVmTHGmF67VFBZ9gQib`B1>EKTCKf~Cgl?;#>=@^*1CI{Q4%`~KJupM+6L=@QbZ)V ze0a7@9YO6{%xpdxYJ<3JbfwhZZ^1fNs5cBn=uCuBuxvDCRn7^mpXyP^SyIJfo z@HX&=1(FHG_1ZXS+I1N{P_8SY-{4Z=2ZlhL$s!C0->9=!J_uX={`}q8^Va8#Q$*r* z4bcjy>X6&$r4OSq2)M*itYG5CLy&7^)`0 zGp~&TO6efcC{QRih&A+^!VRpDr?2ti;_&gHBhEQgSP7`d?5k*KeS_FefJm>_f6&`&*cqHR@IjeyKfT3F@<> zW4Fm%^#bs}2mlL^SDVMt%*;$qKM4YXuo=Xm;;RjMAHL!Rbl0GuAmAO9t!HhFr!u

Gc&a5H1zmVybWfk{$63_yW#%@t&MX| diff --git a/Tests/images/test_combine_multiline_rm_left.png b/Tests/images/test_combine_multiline_rm_left.png index b8c3b5b143d2b7b67116daaef4f7b3f119777494..93d8162b3bf9196b44bc38721cbc254de2854cb4 100644 GIT binary patch literal 4144 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 literal 4149 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 diff --git a/Tests/images/text_float_coord.png b/Tests/images/text_float_coord.png index 49468698cd4e749ad6a666bb253a45695a3e6839..d2270826a5bd84efa4a8dbde8a987d5db0a2dc94 100644 GIT binary patch delta 2732 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 diff --git a/Tests/images/text_float_coord_1_alt.png b/Tests/images/text_float_coord_1_alt.png index 50bdac3d8f39aa492bf41ba3e49c787031d07628..2287071ffab678232370853706c56c3c843d4fbb 100644 GIT binary patch delta 684 zcmV;d0#p5`2B`*+Bs=g)L_t(|obB6Na_b-vMNuV{DP{~FQ^w#yDj!NwB!W&O9tY&T z>VEm!h_-ZqAo*?I_YHu~ySf9wtCQ~nB9VO!f9yluffk;R#EYL_vUV`iu(K8)sI>j( z!}tg@A<)^8-sE7hZClw)_@|X*vOVua~gKmjstaQpP3L4 zE13yDzkY@RV~mVPi&ciwTk@ZX^Gw2t8;h9`5F>8du4`%9CAz1ya4BB4 z@Ux?<#4qL?U*=SFXaDkhT79Y^{}Q^jOCby^gxo(*3-?nxoDW!UCl3*N=(iE2&skcZ(Ml1*+mF8f{l?|oue1q93h7MfAAVLjug*tCR6k-c9tUy$ zTzi|fv>MJ?q2@UU^BNX~Fg~NkQ?s3)+3UhCEq-yKq7FAM?U1_~Yp%oO!?C};?fYKb z)*;!U(w~8NMi--n(!nX$RI7IUK@Cd&6#Tb*Pp|4rbAH>c+G*9`dBVK#b)`O{{ym|w z5?FLJJ~(lOnCqK z8wN6CWH?%^GL&AD{X`sR5>DK3%!Gg#Aq2$MzeRykes_VD=N!}_yzt$*5N1yK>RUTAmsYvS-n+D8`@{B%Ql<6pfGC9d=MVh-%@uQ)Db4@a zB&C&hqg84C4Pk@PMe?vKA+tn(K6($GijA4D;Amm~_2urE=SvG;6qc6y+Vi@Wmdb!@ zR+zxli*|;Om(PSv)-#sE+?~luCVveJhY1$>ar8Ivjp0@C_qpQR( zWE@}SRCH(m^m|%;sv-Llx`Xl%`V~UzpQVNCDILxSthb|w@IAD5pm%BVZ7O32y7g1B zF;jZwEwmy9%FVqTMML?qZ@E3N3sKC3Ol$Zk?=E=hEeZ^FyaBLrI`mkMHh)rjK@Z-S zuisvuwzPW2(z=aS!$Wxa(bvA;xSZ>iHpfVzn+fg1&noBD_{fOrM+}+AL5x4w-exVW zhH+M?dCuY8HLM6>ct$f%&Gvt0uf2R};fo6u> 24) & 0xFF) - x, y = (int(c) for c in coord) + x, y = coord self.im.paste(color, (x, y, x + mask.size[0], y + mask.size[1]), mask) else: self.draw.draw_bitmap(coord, mask, ink) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 457e906c8..c8de65be2 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -26,6 +26,7 @@ # import base64 +import math import os import sys import warnings @@ -588,6 +589,7 @@ class FreeTypeFont: stroke_width=0, anchor=None, ink=0, + start=None, ): """ Create a bitmap for the text. @@ -659,6 +661,7 @@ class FreeTypeFont: stroke_width=stroke_width, anchor=anchor, ink=ink, + start=start, )[0] def getmask2( @@ -672,6 +675,7 @@ class FreeTypeFont: stroke_width=0, anchor=None, ink=0, + start=None, *args, **kwargs, ): @@ -750,12 +754,23 @@ class FreeTypeFont: size, offset = self.font.getsize( text, mode, direction, features, language, anchor ) - size = size[0] + stroke_width * 2, size[1] + stroke_width * 2 + if start is None: + start = (0, 0) + size = tuple(math.ceil(size[i] + stroke_width * 2 + start[i]) for i in range(2)) offset = offset[0] - stroke_width, offset[1] - stroke_width Image._decompression_bomb_check(size) im = fill("RGBA" if mode == "RGBA" else "L", size, 0) self.font.render( - text, im.id, mode, direction, features, language, stroke_width, ink + text, + im.id, + mode, + direction, + features, + language, + stroke_width, + ink, + start[0], + start[1], ) return im, offset diff --git a/src/_imagingft.c b/src/_imagingft.c index bd4099176..b52d6353e 100644 --- a/src/_imagingft.c +++ b/src/_imagingft.c @@ -777,13 +777,15 @@ font_render(FontObject *self, PyObject *args) { const char *lang = NULL; PyObject *features = Py_None; PyObject *string; + float x_start = 0; + float y_start = 0; /* render string into given buffer (the buffer *must* have the right size, or this will crash) */ if (!PyArg_ParseTuple( args, - "On|zzOziL:render", + "On|zzOziLff:render", &string, &id, &mode, @@ -791,7 +793,9 @@ font_render(FontObject *self, PyObject *args) { &features, &lang, &stroke_width, - &foreground_ink_long)) { + &foreground_ink_long, + &x_start, + &y_start)) { return NULL; } @@ -876,8 +880,8 @@ font_render(FontObject *self, PyObject *args) { } /* set pen position to text origin */ - x = (-x_min + stroke_width) << 6; - y = (-y_max + (-stroke_width)) << 6; + x = (-x_min + stroke_width + x_start) * 64; + y = (-y_max + (-stroke_width) - y_start) * 64; if (stroker == NULL) { load_flags |= FT_LOAD_RENDER; From 97a6f651d4ddf07cbd13b2cd38b19dde381050c6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 8 Nov 2022 12:01:15 +1100 Subject: [PATCH 141/239] Added Interop tags --- docs/reference/ExifTags.rst | 50 +++++++++++++++++++++---------------- src/PIL/ExifTags.py | 8 ++++++ 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/docs/reference/ExifTags.rst b/docs/reference/ExifTags.rst index ff5788524..d362334a5 100644 --- a/docs/reference/ExifTags.rst +++ b/docs/reference/ExifTags.rst @@ -4,8 +4,35 @@ :py:mod:`~PIL.ExifTags` Module ============================== -The :py:mod:`~PIL.ExifTags` module exposes two dictionaries which -provide constants and clear-text names for various well-known EXIF tags. +The :py:mod:`~PIL.ExifTags` module exposes several ``enum.IntEnum`` classes +which provide constants and clear-text names for various well-known EXIF tags. + +.. py:data:: Base + + >>> from PIL.ExifTags import Base + >>> Base.ImageDescription.value + 270 + >>> Base(270).name + 'ImageDescription' + +.. py:data:: GPS + + >>> from PIL.ExifTags import GPS + >>> GPS.GPSDestLatitude.value + 20 + >>> GPS(20).name + 'GPSDestLatitude' + +.. py:data:: Interop + + >>> from PIL.ExifTags import Interop + >>> Interop.RelatedImageFileFormat.value + 4096 + >>> Interop(4096).name + 'RelatedImageFileFormat' + + +Two of these values are also exposed as dictionaries. .. py:data:: TAGS :type: dict @@ -26,22 +53,3 @@ provide constants and clear-text names for various well-known EXIF tags. >>> from PIL.ExifTags import GPSTAGS >>> GPSTAGS[20] 'GPSDestLatitude' - - -These values are also exposed as ``enum.IntEnum`` classes. - -.. py:data:: Base - - >>> from PIL.ExifTags import Base - >>> Base.ImageDescription.value - 270 - >>> Base(270).name - 'ImageDescription' - -.. py:data:: GPS - - >>> from PIL.ExifTags import GPS - >>> GPS.GPSDestLatitude.value - 20 - >>> GPS(20).name - 'GPSDestLatitude' diff --git a/src/PIL/ExifTags.py b/src/PIL/ExifTags.py index f3a73bf1a..c00730ba9 100644 --- a/src/PIL/ExifTags.py +++ b/src/PIL/ExifTags.py @@ -338,3 +338,11 @@ class GPS(IntEnum): """Maps EXIF GPS tags to tag names.""" GPSTAGS = {i.value: i.name for i in GPS} + + +class Interop(IntEnum): + InteropIndex = 1 + InteropVersion = 2 + RelatedImageFileFormat = 4096 + RelatedImageWidth = 4097 + RleatedImageHeight = 4098 From ebde03eae829ca8396dbb446157daf5a5b04e67c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 8 Nov 2022 13:10:08 +1100 Subject: [PATCH 142/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index fc8d8362a..574fbdbd3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.4.0 (unreleased) ------------------ +- CVE-2007-4559 patch when building on Windows #6704 + [TrellixVulnTeam, nulano, radarhere] + - Fix compiler warning: accessing 64 bytes in a region of size 48 #6714 [wiredfool] From 73bec9622413cf52b71f23723aad6ce8b01c4445 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 8 Nov 2022 21:50:06 +1100 Subject: [PATCH 143/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 574fbdbd3..34c00c3d4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.4.0 (unreleased) ------------------ +- Added Interop to ExifTags #6724 + [radarhere] + - CVE-2007-4559 patch when building on Windows #6704 [TrellixVulnTeam, nulano, radarhere] From 62fd8336b93e97c15ed1e9afee31ccc1d9b15362 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 9 Nov 2022 18:42:46 +1100 Subject: [PATCH 144/239] Update to Python 3.11 in GitHub Actions --- .github/workflows/lint.yml | 4 ++-- .github/workflows/test-windows.yml | 2 +- .github/workflows/test.yml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 6195f973b..8a14dad92 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -5,7 +5,7 @@ on: [push, pull_request, workflow_dispatch] permissions: contents: read -concurrency: +concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true @@ -30,7 +30,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: "3.10" + python-version: "3.11" cache: pip cache-dependency-path: "setup.py" diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 6b7f62c23..5cabb6622 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -226,7 +226,7 @@ jobs: path: dist\*.whl - name: Upload fribidi.dll - if: "github.event_name != 'pull_request' && matrix.python-version == 3.10" + if: "github.event_name != 'pull_request' && matrix.python-version == 3.11" uses: actions/upload-artifact@v3 with: name: fribidi diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 645384c02..831e33c13 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,7 +5,7 @@ on: [push, pull_request, workflow_dispatch] permissions: contents: read -concurrency: +concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true @@ -96,7 +96,7 @@ jobs: path: Tests/errors - name: Docs - if: startsWith(matrix.os, 'ubuntu') && matrix.python-version == 3.10 + if: startsWith(matrix.os, 'ubuntu') && matrix.python-version == 3.11 run: | make doccheck From b0ab324f829f8016f89141b332998ba67e3867c5 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Wed, 9 Nov 2022 20:03:16 +1100 Subject: [PATCH 145/239] Use the latest Python version Co-authored-by: Hugo van Kemenade --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 8a14dad92..49611e287 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -30,7 +30,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: "3.11" + python-version: "3.x" cache: pip cache-dependency-path: "setup.py" From 1c032ff5db5854895314045ee25950a06bea2ae8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 10 Nov 2022 10:37:22 +1100 Subject: [PATCH 146/239] Revert "Install NumPy with OpenBLAS" This reverts commit c82483e35a37919df9700485aa752e8c5a38f28c. --- .github/workflows/macos-install.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/macos-install.sh b/.github/workflows/macos-install.sh index 65f2b81d5..dfd7d0553 100755 --- a/.github/workflows/macos-install.sh +++ b/.github/workflows/macos-install.sh @@ -2,7 +2,7 @@ set -e -brew install libtiff libjpeg openjpeg libimagequant webp little-cms2 freetype openblas libraqm +brew install libtiff libjpeg openjpeg libimagequant webp little-cms2 freetype libraqm PYTHONOPTIMIZE=0 python3 -m pip install cffi python3 -m pip install coverage @@ -13,7 +13,6 @@ python3 -m pip install -U pytest-cov python3 -m pip install -U pytest-timeout python3 -m pip install pyroma -echo -e "[openblas]\nlibraries = openblas\nlibrary_dirs = /usr/local/opt/openblas/lib" >> ~/.numpy-site.cfg python3 -m pip install numpy # extra test images From 99a11297b108c1427cef683d9dcd196f32794043 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 12 Nov 2022 16:08:42 +1100 Subject: [PATCH 147/239] Updated macOS tested Pillow versions [ci skip] --- docs/installation.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index c65095640..f4e959fbe 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -480,11 +480,13 @@ These platforms have been reported to work at the versions mentioned. | Operating system | | Tested Python | | Latest tested | | Tested | | | | versions | | Pillow version | | processors | +==================================+===========================+==================+==============+ -| macOS 12 Big Sur | 3.7, 3.8, 3.9, 3.10 | 9.2.0 |arm | +| macOS 13 Ventura | 3.7, 3.8, 3.9, 3.10, 3.11 | 9.3.0 |arm | ++----------------------------------+---------------------------+------------------+--------------+ +| macOS 12 Big Sur | 3.7, 3.8, 3.9, 3.10, 3.11 | 9.3.0 |arm | +----------------------------------+---------------------------+------------------+--------------+ | macOS 11 Big Sur | 3.7, 3.8, 3.9, 3.10 | 8.4.0 |arm | | +---------------------------+------------------+--------------+ -| | 3.7, 3.8, 3.9, 3.10 | 9.2.0 |x86-64 | +| | 3.7, 3.8, 3.9, 3.10, 3.11 | 9.3.0 |x86-64 | | +---------------------------+------------------+ | | | 3.6 | 8.4.0 | | +----------------------------------+---------------------------+------------------+--------------+ From 9fbfd3f00efbb6719bb35043315e457614617072 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 4 Nov 2022 21:32:40 +1100 Subject: [PATCH 148/239] Added oss-fuzz badge --- docs/index.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/index.rst b/docs/index.rst index 45af4c571..1efbe74c4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -61,6 +61,10 @@ Pillow for enterprise is available via the Tidelift Subscription. `Learn more Date: Sun, 13 Nov 2022 08:00:20 +1100 Subject: [PATCH 149/239] Added MP Format Version when saving --- Tests/test_file_mpo.py | 1 + src/PIL/MpoImagePlugin.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_mpo.py b/Tests/test_file_mpo.py index d94bdaa96..dba1ec1b1 100644 --- a/Tests/test_file_mpo.py +++ b/Tests/test_file_mpo.py @@ -268,6 +268,7 @@ def test_save_all(): im_reloaded = roundtrip(im, save_all=True, append_images=[im2]) assert_image_equal(im, im_reloaded) + assert im_reloaded.mpinfo[45056] == b"0100" im_reloaded.seek(1) assert_image_similar(im2, im_reloaded, 1) diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py index 5bfd8efc1..92d288f2f 100644 --- a/src/PIL/MpoImagePlugin.py +++ b/src/PIL/MpoImagePlugin.py @@ -51,7 +51,7 @@ def _save_all(im, fp, filename): if not offsets: # APP2 marker im.encoderinfo["extra"] = ( - b"\xFF\xE2" + struct.pack(">H", 6 + 70) + b"MPF\0" + b" " * 70 + b"\xFF\xE2" + struct.pack(">H", 6 + 82) + b"MPF\0" + b" " * 82 ) JpegImagePlugin._save(im_frame, fp, filename) offsets.append(fp.tell()) @@ -60,6 +60,7 @@ def _save_all(im, fp, filename): offsets.append(fp.tell() - offsets[-1]) ifd = TiffImagePlugin.ImageFileDirectory_v2() + ifd[0xB000] = b"0100" ifd[0xB001] = len(offsets) mpentries = b"" From 20f17cc6a79881ba441adfb734e8dbe6901a9749 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Sat, 12 Nov 2022 17:14:37 -0600 Subject: [PATCH 150/239] remove unused ImagingAccess->line() method defs --- src/libImaging/Access.c | 62 +++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 40 deletions(-) diff --git a/src/libImaging/Access.c b/src/libImaging/Access.c index 514fb2929..83860c38a 100644 --- a/src/libImaging/Access.c +++ b/src/libImaging/Access.c @@ -43,23 +43,6 @@ add_item(const char *mode) { return &access_table[i]; } -/* fetch pointer to pixel line */ - -static void * -line_8(Imaging im, int x, int y) { - return &im->image8[y][x]; -} - -static void * -line_16(Imaging im, int x, int y) { - return &im->image8[y][x + x]; -} - -static void * -line_32(Imaging im, int x, int y) { - return &im->image32[y][x]; -} - /* fetch individual pixel */ static void @@ -187,36 +170,35 @@ put_pixel_32(Imaging im, int x, int y, const void *color) { void ImagingAccessInit() { -#define ADD(mode_, line_, get_pixel_, put_pixel_) \ +#define ADD(mode_, get_pixel_, put_pixel_) \ { \ ImagingAccess access = add_item(mode_); \ - access->line = line_; \ access->get_pixel = get_pixel_; \ access->put_pixel = put_pixel_; \ } /* populate access table */ - ADD("1", line_8, get_pixel_8, put_pixel_8); - ADD("L", line_8, get_pixel_8, put_pixel_8); - ADD("LA", line_32, get_pixel, put_pixel); - ADD("La", line_32, get_pixel, put_pixel); - ADD("I", line_32, get_pixel_32, put_pixel_32); - ADD("I;16", line_16, get_pixel_16L, put_pixel_16L); - ADD("I;16L", line_16, get_pixel_16L, put_pixel_16L); - ADD("I;16B", line_16, get_pixel_16B, put_pixel_16B); - ADD("I;32L", line_32, get_pixel_32L, put_pixel_32L); - ADD("I;32B", line_32, get_pixel_32B, put_pixel_32B); - ADD("F", line_32, get_pixel_32, put_pixel_32); - ADD("P", line_8, get_pixel_8, put_pixel_8); - ADD("PA", line_32, get_pixel, put_pixel); - ADD("RGB", line_32, get_pixel_32, put_pixel_32); - ADD("RGBA", line_32, get_pixel_32, put_pixel_32); - ADD("RGBa", line_32, get_pixel_32, put_pixel_32); - ADD("RGBX", line_32, get_pixel_32, put_pixel_32); - ADD("CMYK", line_32, get_pixel_32, put_pixel_32); - ADD("YCbCr", line_32, get_pixel_32, put_pixel_32); - ADD("LAB", line_32, get_pixel_32, put_pixel_32); - ADD("HSV", line_32, get_pixel_32, put_pixel_32); + ADD("1", get_pixel_8, put_pixel_8); + ADD("L", get_pixel_8, put_pixel_8); + ADD("LA", get_pixel, put_pixel); + ADD("La", get_pixel, put_pixel); + ADD("I", get_pixel_32, put_pixel_32); + ADD("I;16", get_pixel_16L, put_pixel_16L); + ADD("I;16L", get_pixel_16L, put_pixel_16L); + ADD("I;16B", get_pixel_16B, put_pixel_16B); + ADD("I;32L", get_pixel_32L, put_pixel_32L); + ADD("I;32B", get_pixel_32B, put_pixel_32B); + ADD("F", get_pixel_32, put_pixel_32); + ADD("P", get_pixel_8, put_pixel_8); + ADD("PA", get_pixel, put_pixel); + ADD("RGB", get_pixel_32, put_pixel_32); + ADD("RGBA", get_pixel_32, put_pixel_32); + ADD("RGBa", get_pixel_32, put_pixel_32); + ADD("RGBX", get_pixel_32, put_pixel_32); + ADD("CMYK", get_pixel_32, put_pixel_32); + ADD("YCbCr", get_pixel_32, put_pixel_32); + ADD("LAB", get_pixel_32, put_pixel_32); + ADD("HSV", get_pixel_32, put_pixel_32); } ImagingAccess From 16994ccc9b40f97113cb1c8f56abe9a37a2744fa Mon Sep 17 00:00:00 2001 From: Yay295 Date: Sat, 12 Nov 2022 17:15:50 -0600 Subject: [PATCH 151/239] remove unused ImagingAccess->line() method def --- src/libImaging/Imaging.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libImaging/Imaging.h b/src/libImaging/Imaging.h index b65f8eadd..d9ded1852 100644 --- a/src/libImaging/Imaging.h +++ b/src/libImaging/Imaging.h @@ -124,7 +124,6 @@ struct ImagingMemoryInstance { struct ImagingAccessInstance { const char *mode; - void *(*line)(Imaging im, int x, int y); void (*get_pixel)(Imaging im, int x, int y, void *pixel); void (*put_pixel)(Imaging im, int x, int y, const void *pixel); }; From 55abf18f1020b456cd782ebfe94b5847148559f6 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Sat, 12 Nov 2022 17:16:50 -0600 Subject: [PATCH 152/239] remove comment about Access.c line methods --- src/PIL/PyAccess.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/PIL/PyAccess.py b/src/PIL/PyAccess.py index 9a2ec48fc..039f5ceea 100644 --- a/src/PIL/PyAccess.py +++ b/src/PIL/PyAccess.py @@ -13,8 +13,7 @@ # Notes: # -# * Implements the pixel access object following Access. -# * Does not implement the line functions, as they don't appear to be used +# * Implements the pixel access object following Access.c # * Taking only the tuple form, which is used from python. # * Fill.c uses the integer form, but it's still going to use the old # Access.c implementation. From 21f202a22a712e0392c4ee148946967bd05eb936 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 14 Nov 2022 06:06:08 +1100 Subject: [PATCH 153/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 34c00c3d4..bf9a236cc 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.4.0 (unreleased) ------------------ +- Added MP Format Version when saving MPO #6735 + [radarhere] + - Added Interop to ExifTags #6724 [radarhere] From 84458c3988ad22d82afaa33a4d66080a2a089908 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 14 Nov 2022 08:18:31 +1100 Subject: [PATCH 154/239] Updated xz to 5.2.8 --- winbuild/build_prepare.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 1fcec66b3..10e2000ae 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -152,9 +152,9 @@ deps = { "libs": [r"*.lib"], }, "xz": { - "url": SF_PROJECTS + "/lzmautils/files/xz-5.2.7.tar.gz/download", - "filename": "xz-5.2.7.tar.gz", - "dir": "xz-5.2.7", + "url": SF_PROJECTS + "/lzmautils/files/xz-5.2.8.tar.gz/download", + "filename": "xz-5.2.8.tar.gz", + "dir": "xz-5.2.8", "license": "COPYING", "patch": { r"src\liblzma\api\lzma.h": { From 8a3ba659450f2b46da7c0e4df92fa10dfc0a21d4 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Mon, 14 Nov 2022 10:57:30 -0500 Subject: [PATCH 155/239] Remove Tidelift alignment action and badge Not sure if we still care about this? cf. #5762 #5763 --- .github/workflows/tidelift.yml | 36 ---------------------------------- 1 file changed, 36 deletions(-) delete mode 100644 .github/workflows/tidelift.yml diff --git a/.github/workflows/tidelift.yml b/.github/workflows/tidelift.yml deleted file mode 100644 index 69f9e5476..000000000 --- a/.github/workflows/tidelift.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Tidelift Align - -on: - schedule: - - cron: "30 2 * * *" # daily at 02:30 UTC - push: - paths: - - "Pipfile*" - - ".github/workflows/tidelift.yml" - pull_request: - paths: - - "Pipfile*" - - ".github/workflows/tidelift.yml" - workflow_dispatch: - -permissions: - contents: read - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - build: - if: github.repository_owner == 'python-pillow' - name: Run Tidelift to ensure approved open source packages are in use - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Scan - uses: tidelift/alignment-action@main - env: - TIDELIFT_API_KEY: ${{ secrets.TIDELIFT_API_KEY }} - TIDELIFT_ORGANIZATION: team/aclark4life - TIDELIFT_PROJECT: pillow From 70cc8a57415f6a9744cf4d4f0407a4415319dda2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 15 Nov 2022 09:06:41 +1100 Subject: [PATCH 156/239] Fixed writing int as BYTE tag --- Tests/test_file_tiff_metadata.py | 16 ++++++++++++++++ src/PIL/TiffImagePlugin.py | 2 ++ 2 files changed, 18 insertions(+) diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index d38c1c523..b90dde3d9 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -201,6 +201,22 @@ def test_writing_bytes_to_ascii(tmp_path): assert reloaded.tag_v2[271] == "test" +def test_writing_int_to_bytes(tmp_path): + im = hopper() + info = TiffImagePlugin.ImageFileDirectory_v2() + + tag = TiffTags.TAGS_V2[700] + assert tag.type == TiffTags.BYTE + + info[700] = 1 + + out = str(tmp_path / "temp.tiff") + im.save(out, tiffinfo=info) + + with Image.open(out) as reloaded: + assert reloaded.tag_v2[700] == b"\x01" + + def test_undefined_zero(tmp_path): # Check that the tag has not been changed since this test was created tag = TiffTags.TAGS_V2[45059] diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 1dfd5275f..ab9ac5ea2 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -719,6 +719,8 @@ class ImageFileDirectory_v2(MutableMapping): @_register_writer(1) # Basic type, except for the legacy API. def write_byte(self, data): + if isinstance(data, int): + data = bytes((data,)) return data @_register_loader(2, 1) From ddc215ce3c6772a9b3aa6995f38c97ea922ecf48 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 15 Nov 2022 09:51:20 +1100 Subject: [PATCH 157/239] Revert "Added Tidelift Align badge to docs" This reverts commit 06ab0324a3bb66965c7c1505dbdd0aa640ba308b. --- docs/index.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 1efbe74c4..5bcd5afa5 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -57,10 +57,6 @@ Pillow for enterprise is available via the Tidelift Subscription. `Learn more Date: Tue, 15 Nov 2022 11:35:14 +1100 Subject: [PATCH 158/239] Revert "Add tidelift alignment badge" This reverts commit c8822a6cac65bbe0a7d831ef2b8431435f1feb1d. --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 7a81e0c40..8ee68f9b8 100644 --- a/README.md +++ b/README.md @@ -54,9 +54,6 @@ As of 2019, Pillow development is Code coverage - Tidelift Align Fuzzing Status From d4c7bd7e19e926d5d47c70d04eaba86cf20a67a4 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 16 Nov 2022 09:04:02 +1100 Subject: [PATCH 159/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index bf9a236cc..87ff33f1f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.4.0 (unreleased) ------------------ +- Fixed writing int as BYTE tag #6740 + [radarhere] + - Added MP Format Version when saving MPO #6735 [radarhere] From 70c8e342a514815ae698a3fb22e2ab52f3c09d7f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 16 Nov 2022 09:15:56 +1100 Subject: [PATCH 160/239] Added "start" argument to docstring --- src/PIL/ImageFont.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index c8de65be2..3b1a2a23a 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -649,6 +649,11 @@ class FreeTypeFont: .. versionadded:: 8.0.0 + :param start: Tuple of horizontal and vertical offset, as text may render + differently when starting at fractional coordinates. + + .. versionadded:: 9.4.0 + :return: An internal PIL storage memory instance as defined by the :py:mod:`PIL.Image.core` interface module. """ @@ -743,6 +748,11 @@ class FreeTypeFont: .. versionadded:: 8.0.0 + :param start: Tuple of horizontal and vertical offset, as text may render + differently when starting at fractional coordinates. + + .. versionadded:: 9.4.0 + :return: A tuple of an internal PIL storage memory instance as defined by the :py:mod:`PIL.Image.core` interface module, and the text offset, the gap between the starting coordinate and the first marking From 62db04478733e9baddd41d365f96f9a8dbeb388d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 16 Nov 2022 09:27:33 +1100 Subject: [PATCH 161/239] Added release notes --- docs/releasenotes/9.4.0.rst | 54 +++++++++++++++++++++++++++++++++++++ docs/releasenotes/index.rst | 1 + 2 files changed, 55 insertions(+) create mode 100644 docs/releasenotes/9.4.0.rst diff --git a/docs/releasenotes/9.4.0.rst b/docs/releasenotes/9.4.0.rst new file mode 100644 index 000000000..46c7e2f22 --- /dev/null +++ b/docs/releasenotes/9.4.0.rst @@ -0,0 +1,54 @@ +9.4.0 +----- + +Backwards Incompatible Changes +============================== + +TODO +^^^^ + +TODO + +Deprecations +============ + +TODO +^^^^ + +TODO + +API Changes +=========== + +TODO +^^^^ + +TODO + +API Additions +============= + +Added start position for getmask and getmask2 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Text may render differently when starting at fractional coordinates, so +:py:meth:`.FreeTypeFont.getmask` and :py:meth:`.FreeTypeFont.getmask2` now +support a ``start`` argument. This tuple of horizontal and vertical offset +will be used internally by :py:meth:`.ImageDraw.text` to more accurately place +text at the ``xy`` coordinates. + +Security +======== + +TODO +^^^^ + +TODO + +Other Changes +============= + +TODO +^^^^ + +TODO diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index 8c436be3b..a2b588696 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -14,6 +14,7 @@ expected to be backported to earlier versions. .. toctree:: :maxdepth: 2 + 9.4.0 9.3.0 9.2.0 9.1.1 From cb40f46ec13f2413163baa00e6e44958dd3f67f6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 16 Nov 2022 14:58:21 +1100 Subject: [PATCH 162/239] Added Fedora 37 --- .github/workflows/test-docker.yml | 1 + docs/installation.rst | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.github/workflows/test-docker.yml b/.github/workflows/test-docker.yml index 1e36b3382..7331cf8ee 100644 --- a/.github/workflows/test-docker.yml +++ b/.github/workflows/test-docker.yml @@ -31,6 +31,7 @@ jobs: debian-10-buster-x86, debian-11-bullseye-x86, fedora-36-amd64, + fedora-37-amd64, gentoo, ubuntu-18.04-bionic-amd64, ubuntu-20.04-focal-amd64, diff --git a/docs/installation.rst b/docs/installation.rst index f4e959fbe..cf6b9ca8f 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -442,6 +442,8 @@ These platforms are built and tested for every change. +----------------------------------+----------------------------+---------------------+ | Fedora 36 | 3.10 | x86-64 | +----------------------------------+----------------------------+---------------------+ +| Fedora 37 | 3.11 | x86-64 | ++----------------------------------+----------------------------+---------------------+ | Gentoo | 3.9 | x86-64 | +----------------------------------+----------------------------+---------------------+ | macOS 11 Big Sur | 3.7, 3.8, 3.9, 3.10, 3.11, | x86-64 | From df8e87291254dc22415a0c6e371950e79311dbf2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 17 Nov 2022 08:26:47 +1100 Subject: [PATCH 163/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 87ff33f1f..cd1b07be4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.4.0 (unreleased) ------------------ +- Use fractional coordinates when drawing text #6722 + [radarhere] + - Fixed writing int as BYTE tag #6740 [radarhere] From 1f6df76c42dab44e00b128b40fd6657185925aca Mon Sep 17 00:00:00 2001 From: Alireza Shafaei Date: Thu, 17 Nov 2022 13:58:07 -0800 Subject: [PATCH 164/239] updated webp with exact parameter. --- Tests/test_file_webp_alpha.py | 34 ++++++++++++++++++++++++++++ docs/handbook/image-file-formats.rst | 4 ++++ src/PIL/WebPImagePlugin.py | 2 ++ src/_webp.c | 5 +++- 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_webp_alpha.py b/Tests/test_file_webp_alpha.py index dc82fb742..07df7a068 100644 --- a/Tests/test_file_webp_alpha.py +++ b/Tests/test_file_webp_alpha.py @@ -96,6 +96,40 @@ def test_write_rgba(tmp_path): else: assert_image_similar(image, pil_image, 1.0) +def test_write_rgba_keep_transparent(tmp_path): + """ + Can we write a RGBA mode file to WebP while preserving + the transparent RGB without error. + Does it have the bits we expect? + """ + + temp_output_file = str(tmp_path / "temp.webp") + + input_image = hopper("RGB") + # make a copy of the image + output_image = input_image.copy() + # make a single channel image with the same size as input_image + new_alpha = Image.new("L", input_image.size, 255) + # make the left half transparent + new_alpha.paste((0,), (0, 0, new_alpha.size[0]//2, new_alpha.size[1])) + # putalpha on output_image + output_image.putalpha(new_alpha) + + # now save with transparent area preserved. + output_image.save(temp_output_file, "WEBP", exact=True, lossless=True) + # even though it is lossless, if we don't put exact=True, the transparent + # area will be filled with black (or something more conducive to compression) + + with Image.open(temp_output_file) as image: + image.load() + + assert image.mode == "RGBA" + assert image.format == "WEBP" + image.load() + image = image.convert("RGB") + assert_image_similar(image, input_image, 1.0) + + def test_write_unsupported_mode_PA(tmp_path): """ diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 1e79db68b..ffc949148 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -1124,6 +1124,10 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: **method** Quality/speed trade-off (0=fast, 6=slower-better). Defaults to 4. +**exact** + If true, preserve the transparent RGB values. Otherwise, discard + invisible RGB values for better compression. Defaults to false. + **icc_profile** The ICC Profile to include in the saved file. Only supported if the system WebP library was built with webpmux support. diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index 5eaeb10cc..c88f730a2 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -318,6 +318,7 @@ def _save(im, fp, filename): exif = exif[6:] xmp = im.encoderinfo.get("xmp", "") method = im.encoderinfo.get("method", 4) + exact = im.encoderinfo.get("exact", False) if im.mode not in _VALID_WEBP_LEGACY_MODES: alpha = ( @@ -336,6 +337,7 @@ def _save(im, fp, filename): im.mode, icc_profile, method, + 1 if exact else 0, exif, xmp, ) diff --git a/src/_webp.c b/src/_webp.c index fd99116cb..ec9425d36 100644 --- a/src/_webp.c +++ b/src/_webp.c @@ -576,6 +576,7 @@ WebPEncode_wrapper(PyObject *self, PyObject *args) { int lossless; float quality_factor; int method; + int exact; uint8_t *rgb; uint8_t *icc_bytes; uint8_t *exif_bytes; @@ -597,7 +598,7 @@ WebPEncode_wrapper(PyObject *self, PyObject *args) { if (!PyArg_ParseTuple( args, - "y#iiifss#is#s#", + "y#iiifss#iis#s#", (char **)&rgb, &size, &width, @@ -608,6 +609,7 @@ WebPEncode_wrapper(PyObject *self, PyObject *args) { &icc_bytes, &icc_size, &method, + &exact, &exif_bytes, &exif_size, &xmp_bytes, @@ -633,6 +635,7 @@ WebPEncode_wrapper(PyObject *self, PyObject *args) { config.lossless = lossless; config.quality = quality_factor; config.method = method; + config.exact = exact; // Validate the config if (!WebPValidateConfig(&config)) { From 770560d8e4972d69193a816713be2bda5ff3ed94 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 17 Nov 2022 22:06:14 +0000 Subject: [PATCH 165/239] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- Tests/test_file_webp_alpha.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_webp_alpha.py b/Tests/test_file_webp_alpha.py index 07df7a068..5a57d591a 100644 --- a/Tests/test_file_webp_alpha.py +++ b/Tests/test_file_webp_alpha.py @@ -96,6 +96,7 @@ def test_write_rgba(tmp_path): else: assert_image_similar(image, pil_image, 1.0) + def test_write_rgba_keep_transparent(tmp_path): """ Can we write a RGBA mode file to WebP while preserving @@ -111,7 +112,7 @@ def test_write_rgba_keep_transparent(tmp_path): # make a single channel image with the same size as input_image new_alpha = Image.new("L", input_image.size, 255) # make the left half transparent - new_alpha.paste((0,), (0, 0, new_alpha.size[0]//2, new_alpha.size[1])) + new_alpha.paste((0,), (0, 0, new_alpha.size[0] // 2, new_alpha.size[1])) # putalpha on output_image output_image.putalpha(new_alpha) @@ -130,7 +131,6 @@ def test_write_rgba_keep_transparent(tmp_path): assert_image_similar(image, input_image, 1.0) - def test_write_unsupported_mode_PA(tmp_path): """ Saving a palette-based file with transparency to WebP format From 3587f27780a5be7d02d0c781b39e13919c88d8d6 Mon Sep 17 00:00:00 2001 From: Alireza Shafaei Date: Fri, 18 Nov 2022 10:15:24 -0800 Subject: [PATCH 166/239] Added version check for WebP --- src/_webp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/_webp.c b/src/_webp.c index ec9425d36..9231150aa 100644 --- a/src/_webp.c +++ b/src/_webp.c @@ -635,7 +635,10 @@ WebPEncode_wrapper(PyObject *self, PyObject *args) { config.lossless = lossless; config.quality = quality_factor; config.method = method; +#if WEBP_ENCODER_ABI_VERSION >= 0x0209 + // the exact flag is only available in libwebp 0.5.0 and later config.exact = exact; +#endif // Validate the config if (!WebPValidateConfig(&config)) { From fdf074b050f272e60e37bbd7f6ff52f3fc95a299 Mon Sep 17 00:00:00 2001 From: Alireza Shafaei Date: Fri, 18 Nov 2022 10:22:33 -0800 Subject: [PATCH 167/239] added a note to the docs for webp --- docs/handbook/image-file-formats.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index ffc949148..9c2319b44 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -1127,6 +1127,7 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: **exact** If true, preserve the transparent RGB values. Otherwise, discard invisible RGB values for better compression. Defaults to false. + Requires LibWebP 0.5.0 or later. **icc_profile** The ICC Profile to include in the saved file. Only supported if From 96a4d98abc265dabc1442a3e7b0cfdd5043f90b5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 19 Nov 2022 17:07:43 +1100 Subject: [PATCH 168/239] Simplified code --- src/PIL/WebPImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index c88f730a2..e3c19db3d 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -318,7 +318,7 @@ def _save(im, fp, filename): exif = exif[6:] xmp = im.encoderinfo.get("xmp", "") method = im.encoderinfo.get("method", 4) - exact = im.encoderinfo.get("exact", False) + exact = 1 if im.encoderinfo.get("exact") else 0 if im.mode not in _VALID_WEBP_LEGACY_MODES: alpha = ( @@ -337,7 +337,7 @@ def _save(im, fp, filename): im.mode, icc_profile, method, - 1 if exact else 0, + exact, exif, xmp, ) From 7e5e843d5cd9f4a17361853138816773da28d8c9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 19 Nov 2022 17:12:51 +1100 Subject: [PATCH 169/239] Note that the fill behaviour only affects libwebp >= 0.5 --- Tests/test_file_webp_alpha.py | 45 ++++++++++++++++------------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/Tests/test_file_webp_alpha.py b/Tests/test_file_webp_alpha.py index 5a57d591a..df6cffb17 100644 --- a/Tests/test_file_webp_alpha.py +++ b/Tests/test_file_webp_alpha.py @@ -97,38 +97,33 @@ def test_write_rgba(tmp_path): assert_image_similar(image, pil_image, 1.0) -def test_write_rgba_keep_transparent(tmp_path): +def test_keep_rgb_values_when_transparent(tmp_path): """ - Can we write a RGBA mode file to WebP while preserving - the transparent RGB without error. - Does it have the bits we expect? + Saving transparent pixels should retain their original RGB values + when using the "exact" parameter. """ - temp_output_file = str(tmp_path / "temp.webp") + image = hopper("RGB") - input_image = hopper("RGB") - # make a copy of the image - output_image = input_image.copy() - # make a single channel image with the same size as input_image - new_alpha = Image.new("L", input_image.size, 255) - # make the left half transparent - new_alpha.paste((0,), (0, 0, new_alpha.size[0] // 2, new_alpha.size[1])) - # putalpha on output_image - output_image.putalpha(new_alpha) + # create a copy of the image + # with the left half transparent + half_transparent_image = image.copy() + new_alpha = Image.new("L", (128, 128), 255) + new_alpha.paste(0, (0, 0, 64, 128)) + half_transparent_image.putalpha(new_alpha) - # now save with transparent area preserved. - output_image.save(temp_output_file, "WEBP", exact=True, lossless=True) - # even though it is lossless, if we don't put exact=True, the transparent - # area will be filled with black (or something more conducive to compression) + # save with transparent area preserved + temp_file = str(tmp_path / "temp.webp") + half_transparent_image.save(temp_file, exact=True, lossless=True) - with Image.open(temp_output_file) as image: - image.load() + with Image.open(temp_file) as reloaded: + assert reloaded.mode == "RGBA" + assert reloaded.format == "WEBP" - assert image.mode == "RGBA" - assert image.format == "WEBP" - image.load() - image = image.convert("RGB") - assert_image_similar(image, input_image, 1.0) + # even though it is lossless, if we don't use exact=True + # in libwebp >= 0.5, the transparent area will be filled with black + # (or something more conducive to compression) + assert_image_similar(reloaded.convert("RGB"), image, 1) def test_write_unsupported_mode_PA(tmp_path): From 3c7aa133eb62d75c0f96360ba54c7c4ed19d5c6f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 19 Nov 2022 17:18:27 +1100 Subject: [PATCH 170/239] Assert that image is equal --- Tests/test_file_webp_alpha.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_file_webp_alpha.py b/Tests/test_file_webp_alpha.py index df6cffb17..5970fd2a3 100644 --- a/Tests/test_file_webp_alpha.py +++ b/Tests/test_file_webp_alpha.py @@ -123,7 +123,7 @@ def test_keep_rgb_values_when_transparent(tmp_path): # even though it is lossless, if we don't use exact=True # in libwebp >= 0.5, the transparent area will be filled with black # (or something more conducive to compression) - assert_image_similar(reloaded.convert("RGB"), image, 1) + assert_image_equal(reloaded.convert("RGB"), image) def test_write_unsupported_mode_PA(tmp_path): From 690446050a1963599dc926b06e0360aea0da3f67 Mon Sep 17 00:00:00 2001 From: Alireza Shafaei Date: Fri, 18 Nov 2022 23:26:08 -0800 Subject: [PATCH 171/239] minor fix in the comments --- src/_webp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_webp.c b/src/_webp.c index 9231150aa..c2532a496 100644 --- a/src/_webp.c +++ b/src/_webp.c @@ -636,7 +636,7 @@ WebPEncode_wrapper(PyObject *self, PyObject *args) { config.quality = quality_factor; config.method = method; #if WEBP_ENCODER_ABI_VERSION >= 0x0209 - // the exact flag is only available in libwebp 0.5.0 and later + // the "exact" flag is only available in libwebp 0.5.0 and later config.exact = exact; #endif From d6f10d4876e4f3e1126d3a1d6eac1f75b9d675c2 Mon Sep 17 00:00:00 2001 From: Alireza Shafaei Date: Fri, 18 Nov 2022 23:51:06 -0800 Subject: [PATCH 172/239] doc update for libwebp --- docs/handbook/image-file-formats.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 9c2319b44..ac39625a2 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -1127,7 +1127,7 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: **exact** If true, preserve the transparent RGB values. Otherwise, discard invisible RGB values for better compression. Defaults to false. - Requires LibWebP 0.5.0 or later. + Requires libwebp 0.5.0 or later. **icc_profile** The ICC Profile to include in the saved file. Only supported if From 55a75b9a696e968ab56a19a207959211cd33adf9 Mon Sep 17 00:00:00 2001 From: Alireza Shafaei Date: Sat, 19 Nov 2022 23:14:59 -0800 Subject: [PATCH 173/239] added RN for the new exact option. --- docs/releasenotes/9.4.0.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/releasenotes/9.4.0.rst b/docs/releasenotes/9.4.0.rst index 46c7e2f22..3a9c3977f 100644 --- a/docs/releasenotes/9.4.0.rst +++ b/docs/releasenotes/9.4.0.rst @@ -37,6 +37,13 @@ support a ``start`` argument. This tuple of horizontal and vertical offset will be used internally by :py:meth:`.ImageDraw.text` to more accurately place text at the ``xy`` coordinates. +Added the ``exact`` encoding option for WebP +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``exact`` encoding option for WebP is now supported. The WebP encoder +removes the hidden RGB values for better compression by default. By setting +this option to ``True``, the encoder will keep the hidden RGB values. + Security ======== From 9c5b00ef7e03b921fd55cd6c432bac3a00562d46 Mon Sep 17 00:00:00 2001 From: Alireza Shafaei Date: Sat, 19 Nov 2022 23:19:08 -0800 Subject: [PATCH 174/239] RN trailing space fix --- docs/releasenotes/9.4.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/releasenotes/9.4.0.rst b/docs/releasenotes/9.4.0.rst index 3a9c3977f..ad79022fe 100644 --- a/docs/releasenotes/9.4.0.rst +++ b/docs/releasenotes/9.4.0.rst @@ -41,7 +41,7 @@ Added the ``exact`` encoding option for WebP ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The ``exact`` encoding option for WebP is now supported. The WebP encoder -removes the hidden RGB values for better compression by default. By setting +removes the hidden RGB values for better compression by default. By setting this option to ``True``, the encoder will keep the hidden RGB values. Security From 8f73a895ec29a8f824466812ef6dae9c90ad0397 Mon Sep 17 00:00:00 2001 From: Alireza Shafaei Date: Sun, 20 Nov 2022 16:00:24 -0800 Subject: [PATCH 175/239] Update docs/releasenotes/9.4.0.rst Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- docs/releasenotes/9.4.0.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/releasenotes/9.4.0.rst b/docs/releasenotes/9.4.0.rst index ad79022fe..0f47f5ad6 100644 --- a/docs/releasenotes/9.4.0.rst +++ b/docs/releasenotes/9.4.0.rst @@ -41,8 +41,9 @@ Added the ``exact`` encoding option for WebP ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The ``exact`` encoding option for WebP is now supported. The WebP encoder -removes the hidden RGB values for better compression by default. By setting -this option to ``True``, the encoder will keep the hidden RGB values. +removes the hidden RGB values for better compression by default in libwebp 0.5 +or later. By setting this option to ``True``, the encoder will keep the hidden +RGB values. Security ======== From be7d350e3f5425fe9eaa2bd3fe8e0751d48b1013 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 21 Nov 2022 11:56:30 +1100 Subject: [PATCH 176/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index cd1b07be4..461f34e54 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.4.0 (unreleased) ------------------ +- Added "exact" option when saving WebP #6747 + [ashafaei, radarhere] + - Use fractional coordinates when drawing text #6722 [radarhere] From 100ed363ce1407481331d864ae444b22391bb397 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 21 Nov 2022 15:42:44 +1100 Subject: [PATCH 177/239] Updated libpng to 1.6.39 --- winbuild/build_prepare.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 10e2000ae..e4bf275a1 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -228,9 +228,9 @@ deps = { # "bins": [r"libtiff\*.dll"], }, "libpng": { - "url": SF_PROJECTS + "/libpng/files/libpng16/1.6.38/lpng1638.zip/download", - "filename": "lpng1638.zip", - "dir": "lpng1638", + "url": SF_PROJECTS + "/libpng/files/libpng16/1.6.39/lpng1639.zip/download", + "filename": "lpng1639.zip", + "dir": "lpng1639", "license": "LICENSE", "build": [ # lint: do not inline From 2c513c6448d24e393de63be85ec30e980b99d8fa Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 25 Nov 2022 22:05:57 +1100 Subject: [PATCH 178/239] Use stdlib for setuptools on Cygwin --- .github/workflows/test-cygwin.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 5b9ab0eda..bbf0ee736 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -5,7 +5,7 @@ on: [push, pull_request, workflow_dispatch] permissions: contents: read -concurrency: +concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true @@ -76,7 +76,7 @@ jobs: - name: Build shell: bash.exe -eo pipefail -o igncr "{0}" run: | - .ci/build.sh + SETUPTOOLS_USE_DISTUTILS=stdlib .ci/build.sh - name: Test run: | From 851e7b03ec2a1ccbc98c2d4fcb765e932187c985 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 24 Nov 2022 22:57:10 +1100 Subject: [PATCH 179/239] Document how to install Pillow from a directory --- docs/installation.rst | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index cf6b9ca8f..c50a6cc3c 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -103,10 +103,6 @@ Pillow can be installed on FreeBSD via the official Ports or Packages systems: Building From Source -------------------- -Download and extract the `compressed archive from PyPI`_. - -.. _compressed archive from PyPI: https://pypi.org/project/Pillow/ - .. _external-libraries: External Libraries @@ -191,7 +187,8 @@ Many of Pillow's features require external libraries: * **libxcb** provides X11 screengrab support. -Once you have installed the prerequisites, run:: +Once you have installed the prerequisites, to install Pillow from the source +code on PyPI, run:: python3 -m pip install --upgrade pip python3 -m pip install --upgrade Pillow --no-binary :all: @@ -211,6 +208,16 @@ prerequisites, it may be necessary to manually clear the pip cache or build without cache using the ``--no-cache-dir`` option to force a build with newly installed external libraries. +If you would like to install from a local copy of the source code instead, you +can download and extract the `compressed archive from PyPI`_, or clone from +GitHub with ``git clone https://github.com/python-pillow/Pillow``. + +After navigating to the Pillow directory, run:: + + python3 -m pip install --upgrade pip + python3 -m pip install . + +.. _compressed archive from PyPI: https://pypi.org/project/Pillow/ Build Options ^^^^^^^^^^^^^ From 58cbcbf10826039376b563521b24b84e3fddbb8f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 25 Nov 2022 08:47:40 +1100 Subject: [PATCH 180/239] Added getxmp() to WebPImagePlugin --- Tests/test_file_webp_metadata.py | 21 +++++++++++++++++++++ src/PIL/WebPImagePlugin.py | 3 +++ 2 files changed, 24 insertions(+) diff --git a/Tests/test_file_webp_metadata.py b/Tests/test_file_webp_metadata.py index f77a245c0..4f513d82b 100644 --- a/Tests/test_file_webp_metadata.py +++ b/Tests/test_file_webp_metadata.py @@ -11,6 +11,11 @@ pytestmark = [ skip_unless_feature("webp_mux"), ] +try: + from defusedxml import ElementTree +except ImportError: + ElementTree = None + def test_read_exif_metadata(): @@ -110,6 +115,22 @@ def test_read_no_exif(): assert not webp_image._getexif() +def test_getxmp(): + with Image.open("Tests/images/flower.webp") as im: + assert "xmp" not in im.info + assert im.getxmp() == {} + + with Image.open("Tests/images/flower2.webp") as im: + if ElementTree is None: + with pytest.warns(UserWarning): + assert im.getxmp() == {} + else: + assert ( + im.getxmp()["xmpmeta"]["xmptk"] + == "Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27 " + ) + + @skip_unless_feature("webp_anim") def test_write_animated_metadata(tmp_path): iccp_data = b"" diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index e3c19db3d..e9a7aac77 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -98,6 +98,9 @@ class WebPImageFile(ImageFile.ImageFile): return None return self.getexif()._get_merged_dict() + def getxmp(self): + return self._getxmp(self.info["xmp"]) if "xmp" in self.info else {} + def seek(self, frame): if not self._seek_check(frame): return From 710927a311c5699b69d78bcd13ea6ddabc0b0563 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 26 Nov 2022 19:08:49 +1100 Subject: [PATCH 181/239] Added docstring --- src/PIL/WebPImagePlugin.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index e9a7aac77..81ed550d9 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -99,6 +99,12 @@ class WebPImageFile(ImageFile.ImageFile): return self.getexif()._get_merged_dict() def getxmp(self): + """ + Returns a dictionary containing the XMP tags. + Requires defusedxml to be installed. + + :returns: XMP tags in a dictionary. + """ return self._getxmp(self.info["xmp"]) if "xmp" in self.info else {} def seek(self, frame): From 3f9410334cd9efe4d9ebca5eb59d42b7c1c8778c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 26 Nov 2022 19:11:51 +1100 Subject: [PATCH 182/239] Added getxmp() to release notes --- docs/releasenotes/9.4.0.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/releasenotes/9.4.0.rst b/docs/releasenotes/9.4.0.rst index 0f47f5ad6..f2b50fa5b 100644 --- a/docs/releasenotes/9.4.0.rst +++ b/docs/releasenotes/9.4.0.rst @@ -45,6 +45,12 @@ removes the hidden RGB values for better compression by default in libwebp 0.5 or later. By setting this option to ``True``, the encoder will keep the hidden RGB values. +getxmp() +^^^^^^^^ + +`XMP data `_ can now be +decoded for WEBP images through ``getxmp()``. + Security ======== From 72372ad23f612a320c470c442afb4adab39d988b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 26 Nov 2022 20:42:04 +1100 Subject: [PATCH 183/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 461f34e54..7fac5201c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.4.0 (unreleased) ------------------ +- Added getxmp() to WebPImagePlugin #6758 + [radarhere] + - Added "exact" option when saving WebP #6747 [ashafaei, radarhere] From 24a5405a9f7ea22f28f9c98b3e407292ea5ee1d3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 28 Nov 2022 08:39:56 +1100 Subject: [PATCH 184/239] Added IFD enum --- docs/reference/ExifTags.rst | 7 ++++++ src/PIL/ExifTags.py | 7 ++++++ src/PIL/Image.py | 45 ++++++++++++++++++++++++------------- src/PIL/MpoImagePlugin.py | 11 +++++++-- 4 files changed, 52 insertions(+), 18 deletions(-) diff --git a/docs/reference/ExifTags.rst b/docs/reference/ExifTags.rst index d362334a5..650bb4f95 100644 --- a/docs/reference/ExifTags.rst +++ b/docs/reference/ExifTags.rst @@ -31,6 +31,13 @@ which provide constants and clear-text names for various well-known EXIF tags. >>> Interop(4096).name 'RelatedImageFileFormat' +.. py:data:: IFD + + >>> from PIL.ExifTags import IFD + >>> IFD.Exif.value + 34665 + >>> IFD(34665).name + 'Exif' Two of these values are also exposed as dictionaries. diff --git a/src/PIL/ExifTags.py b/src/PIL/ExifTags.py index c00730ba9..97a21335f 100644 --- a/src/PIL/ExifTags.py +++ b/src/PIL/ExifTags.py @@ -346,3 +346,10 @@ class Interop(IntEnum): RelatedImageFileFormat = 4096 RelatedImageWidth = 4097 RleatedImageHeight = 4098 + + +class IFD(IntEnum): + Exif = 34665 + GPSInfo = 34853 + Makernote = 37500 + Interop = 40965 diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 7faf0c248..3fcc86931 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -47,7 +47,14 @@ except ImportError: # VERSION was removed in Pillow 6.0.0. # PILLOW_VERSION was removed in Pillow 9.0.0. # Use __version__ instead. -from . import ImageMode, TiffTags, UnidentifiedImageError, __version__, _plugins +from . import ( + ExifTags, + ImageMode, + TiffTags, + UnidentifiedImageError, + __version__, + _plugins, +) from ._binary import i32le, o32be, o32le from ._deprecate import deprecate from ._util import DeferredError, is_path @@ -3598,14 +3605,16 @@ class Exif(MutableMapping): merged_dict = dict(self) # get EXIF extension - if 0x8769 in self: - ifd = self._get_ifd_dict(self[0x8769]) + if ExifTags.IFD.Exif in self: + ifd = self._get_ifd_dict(self[ExifTags.IFD.Exif]) if ifd: merged_dict.update(ifd) # GPS - if 0x8825 in self: - merged_dict[0x8825] = self._get_ifd_dict(self[0x8825]) + if ExifTags.IFD.GPSInfo in self: + merged_dict[ExifTags.IFD.GPSInfo] = self._get_ifd_dict( + self[ExifTags.IFD.GPSInfo] + ) return merged_dict @@ -3615,30 +3624,34 @@ class Exif(MutableMapping): head = self._get_head() ifd = TiffImagePlugin.ImageFileDirectory_v2(ifh=head) for tag, value in self.items(): - if tag in [0x8769, 0x8225, 0x8825] and not isinstance(value, dict): + if tag in [ + ExifTags.IFD.Exif, + 0x8225, + ExifTags.IFD.GPSInfo, + ] and not isinstance(value, dict): value = self.get_ifd(tag) if ( - tag == 0x8769 - and 0xA005 in value - and not isinstance(value[0xA005], dict) + tag == ExifTags.IFD.Exif + and ExifTags.IFD.Interop in value + and not isinstance(value[ExifTags.IFD.Interop], dict) ): value = value.copy() - value[0xA005] = self.get_ifd(0xA005) + value[ExifTags.IFD.Interop] = self.get_ifd(ExifTags.IFD.Interop) ifd[tag] = value return b"Exif\x00\x00" + head + ifd.tobytes(offset) def get_ifd(self, tag): if tag not in self._ifds: - if tag in [0x8769, 0x8825]: + if tag in [ExifTags.IFD.Exif, ExifTags.IFD.GPSInfo]: # exif, gpsinfo if tag in self: self._ifds[tag] = self._get_ifd_dict(self[tag]) - elif tag in [0xA005, 0x927C]: + elif tag in [ExifTags.IFD.Interop, ExifTags.IFD.Makernote]: # interop, makernote - if 0x8769 not in self._ifds: - self.get_ifd(0x8769) - tag_data = self._ifds[0x8769][tag] - if tag == 0x927C: + if ExifTags.IFD.Exif not in self._ifds: + self.get_ifd(ExifTags.IFD.Exif) + tag_data = self._ifds[ExifTags.IFD.Exif][tag] + if tag == ExifTags.IFD.Makernote: # makernote from .TiffImagePlugin import ImageFileDirectory_v2 diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py index 92d288f2f..3ae4d4abf 100644 --- a/src/PIL/MpoImagePlugin.py +++ b/src/PIL/MpoImagePlugin.py @@ -22,7 +22,14 @@ import itertools import os import struct -from . import Image, ImageFile, ImageSequence, JpegImagePlugin, TiffImagePlugin +from . import ( + ExifTags, + Image, + ImageFile, + ImageSequence, + JpegImagePlugin, + TiffImagePlugin, +) from ._binary import i16be as i16 from ._binary import o32le @@ -137,7 +144,7 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile): mptype = self.mpinfo[0xB002][frame]["Attribute"]["MPType"] if mptype.startswith("Large Thumbnail"): - exif = self.getexif().get_ifd(0x8769) + exif = self.getexif().get_ifd(ExifTags.IFD.Exif) if 40962 in exif and 40963 in exif: self._size = (exif[40962], exif[40963]) elif "exif" in self.info: From a0326245a288801b7ea4753eef32d94398c5b9af Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 9 Nov 2022 21:19:13 +1100 Subject: [PATCH 185/239] Removed typo --- src/PIL/Image.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 3fcc86931..d07fc716c 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -3626,7 +3626,6 @@ class Exif(MutableMapping): for tag, value in self.items(): if tag in [ ExifTags.IFD.Exif, - 0x8225, ExifTags.IFD.GPSInfo, ] and not isinstance(value, dict): value = self.get_ifd(tag) From 50cdf39f505158e37af2cbe39458f3ab27e7377e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 28 Nov 2022 09:18:47 +1100 Subject: [PATCH 186/239] List dependency instructions first --- docs/installation.rst | 186 ++++++++++++++++++++---------------------- 1 file changed, 87 insertions(+), 99 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index c50a6cc3c..af1d3399c 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -187,85 +187,8 @@ Many of Pillow's features require external libraries: * **libxcb** provides X11 screengrab support. -Once you have installed the prerequisites, to install Pillow from the source -code on PyPI, run:: - - python3 -m pip install --upgrade pip - python3 -m pip install --upgrade Pillow --no-binary :all: - -If the prerequisites are installed in the standard library locations -for your machine (e.g. :file:`/usr` or :file:`/usr/local`), no -additional configuration should be required. If they are installed in -a non-standard location, you may need to configure setuptools to use -those locations by editing :file:`setup.py` or -:file:`setup.cfg`, or by adding environment variables on the command -line:: - - CFLAGS="-I/usr/pkg/include" python3 -m pip install --upgrade Pillow --no-binary :all: - -If Pillow has been previously built without the required -prerequisites, it may be necessary to manually clear the pip cache or -build without cache using the ``--no-cache-dir`` option to force a -build with newly installed external libraries. - -If you would like to install from a local copy of the source code instead, you -can download and extract the `compressed archive from PyPI`_, or clone from -GitHub with ``git clone https://github.com/python-pillow/Pillow``. - -After navigating to the Pillow directory, run:: - - python3 -m pip install --upgrade pip - python3 -m pip install . - -.. _compressed archive from PyPI: https://pypi.org/project/Pillow/ - -Build Options -^^^^^^^^^^^^^ - -* Environment variable: ``MAX_CONCURRENCY=n``. Pillow can use - multiprocessing to build the extension. Setting ``MAX_CONCURRENCY`` - sets the number of CPUs to use, or can disable parallel building by - using a setting of 1. By default, it uses 4 CPUs, or if 4 are not - available, as many as are present. - -* Build flags: ``--disable-zlib``, ``--disable-jpeg``, - ``--disable-tiff``, ``--disable-freetype``, ``--disable-lcms``, - ``--disable-webp``, ``--disable-webpmux``, ``--disable-jpeg2000``, - ``--disable-imagequant``, ``--disable-xcb``. - Disable building the corresponding feature even if the development - libraries are present on the building machine. - -* Build flags: ``--enable-zlib``, ``--enable-jpeg``, - ``--enable-tiff``, ``--enable-freetype``, ``--enable-lcms``, - ``--enable-webp``, ``--enable-webpmux``, ``--enable-jpeg2000``, - ``--enable-imagequant``, ``--enable-xcb``. - Require that the corresponding feature is built. The build will raise - an exception if the libraries are not found. Webpmux (WebP metadata) - relies on WebP support. Tcl and Tk also must be used together. - -* Build flags: ``--vendor-raqm --vendor-fribidi`` - These flags are used to compile a modified version of libraqm and - a shim that dynamically loads libfribidi at runtime. These are - used to compile the standard Pillow wheels. Compiling libraqm requires - a C99-compliant compiler. - -* Build flag: ``--disable-platform-guessing``. Skips all of the - platform dependent guessing of include and library directories for - automated build systems that configure the proper paths in the - environment variables (e.g. Buildroot). - -* Build flag: ``--debug``. Adds a debugging flag to the include and - library search process to dump all paths searched for and found to - stdout. - - -Sample usage:: - - python3 -m pip install --upgrade Pillow --global-option="build_ext" --global-option="--enable-[feature]" - - Building on macOS -^^^^^^^^^^^^^^^^^ +""""""""""""""""" The Xcode command line tools are required to compile portions of Pillow. The tools are installed by running ``xcode-select --install`` @@ -285,25 +208,19 @@ To install libraqm on macOS use Homebrew to install its dependencies:: Then see ``depends/install_raqm_cmake.sh`` to install libraqm. -Now install Pillow with:: - - python3 -m pip install --upgrade pip - python3 -m pip install --upgrade Pillow --no-binary :all: - -or from within the uncompressed source directory:: - - python3 -m pip install . - Building on Windows -^^^^^^^^^^^^^^^^^^^ +""""""""""""""""""" We recommend you use prebuilt wheels from PyPI. If you wish to compile Pillow manually, you can use the build scripts in the ``winbuild`` directory used for CI testing and development. These scripts require Visual Studio 2017 or newer and NASM. +The scripts also install Pillow from the local copy of the source code, so the +`Installing`_ instructions will not be necessary afterwards. + Building on Windows using MSYS2/MinGW -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +""""""""""""""""""""""""""""""""""""" To build Pillow using MSYS2, make sure you run the **MSYS2 MinGW 32-bit** or **MSYS2 MinGW 64-bit** console, *not* **MSYS2** directly. @@ -332,14 +249,8 @@ Prerequisites are installed on **MSYS2 MinGW 64-bit** with:: mingw-w64-x86_64-libimagequant \ mingw-w64-x86_64-libraqm -Now install Pillow with:: - - python3 -m pip install --upgrade pip - python3 -m pip install --upgrade Pillow --no-binary :all: - - Building on FreeBSD -^^^^^^^^^^^^^^^^^^^ +""""""""""""""""""" .. Note:: Only FreeBSD 10 and 11 tested @@ -353,9 +264,8 @@ Prerequisites are installed on **FreeBSD 10 or 11** with:: Then see ``depends/install_raqm_cmake.sh`` to install libraqm. - Building on Linux -^^^^^^^^^^^^^^^^^ +""""""""""""""""" If you didn't build Python from source, make sure you have Python's development libraries installed. @@ -403,7 +313,7 @@ See also the ``Dockerfile``\s in the Test Infrastructure repo install process for other tested distros. Building on Android -^^^^^^^^^^^^^^^^^^^ +""""""""""""""""""" Basic Android support has been added for compilation within the Termux environment. The dependencies can be installed by:: @@ -413,6 +323,84 @@ environment. The dependencies can be installed by:: This has been tested within the Termux app on ChromeOS, on x86. +Installing +^^^^^^^^^^ + +Once you have installed the prerequisites, to install Pillow from the source +code on PyPI, run:: + + python3 -m pip install --upgrade pip + python3 -m pip install --upgrade Pillow --no-binary :all: + +If the prerequisites are installed in the standard library locations +for your machine (e.g. :file:`/usr` or :file:`/usr/local`), no +additional configuration should be required. If they are installed in +a non-standard location, you may need to configure setuptools to use +those locations by editing :file:`setup.py` or +:file:`setup.cfg`, or by adding environment variables on the command +line:: + + CFLAGS="-I/usr/pkg/include" python3 -m pip install --upgrade Pillow --no-binary :all: + +If Pillow has been previously built without the required +prerequisites, it may be necessary to manually clear the pip cache or +build without cache using the ``--no-cache-dir`` option to force a +build with newly installed external libraries. + +If you would like to install from a local copy of the source code instead, you +can download and extract the `compressed archive from PyPI`_, or clone from +GitHub with ``git clone https://github.com/python-pillow/Pillow``. + +After navigating to the Pillow directory, run:: + + python3 -m pip install --upgrade pip + python3 -m pip install . + +.. _compressed archive from PyPI: https://pypi.org/project/Pillow/ + +Build Options +""""""""""""" + +* Environment variable: ``MAX_CONCURRENCY=n``. Pillow can use + multiprocessing to build the extension. Setting ``MAX_CONCURRENCY`` + sets the number of CPUs to use, or can disable parallel building by + using a setting of 1. By default, it uses 4 CPUs, or if 4 are not + available, as many as are present. + +* Build flags: ``--disable-zlib``, ``--disable-jpeg``, + ``--disable-tiff``, ``--disable-freetype``, ``--disable-lcms``, + ``--disable-webp``, ``--disable-webpmux``, ``--disable-jpeg2000``, + ``--disable-imagequant``, ``--disable-xcb``. + Disable building the corresponding feature even if the development + libraries are present on the building machine. + +* Build flags: ``--enable-zlib``, ``--enable-jpeg``, + ``--enable-tiff``, ``--enable-freetype``, ``--enable-lcms``, + ``--enable-webp``, ``--enable-webpmux``, ``--enable-jpeg2000``, + ``--enable-imagequant``, ``--enable-xcb``. + Require that the corresponding feature is built. The build will raise + an exception if the libraries are not found. Webpmux (WebP metadata) + relies on WebP support. Tcl and Tk also must be used together. + +* Build flags: ``--vendor-raqm --vendor-fribidi`` + These flags are used to compile a modified version of libraqm and + a shim that dynamically loads libfribidi at runtime. These are + used to compile the standard Pillow wheels. Compiling libraqm requires + a C99-compliant compiler. + +* Build flag: ``--disable-platform-guessing``. Skips all of the + platform dependent guessing of include and library directories for + automated build systems that configure the proper paths in the + environment variables (e.g. Buildroot). + +* Build flag: ``--debug``. Adds a debugging flag to the include and + library search process to dump all paths searched for and found to + stdout. + + +Sample usage:: + + python3 -m pip install --upgrade Pillow --global-option="build_ext" --global-option="--enable-[feature]" Platform Support ---------------- From 556b672eb2f982a90e271a580d2a1f00b78ca131 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Sun, 27 Nov 2022 17:48:12 -0600 Subject: [PATCH 187/239] Fix webp dealloc method definitions --- src/_webp.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/_webp.c b/src/_webp.c index c2532a496..493e0709c 100644 --- a/src/_webp.c +++ b/src/_webp.c @@ -178,12 +178,11 @@ _anim_encoder_new(PyObject *self, PyObject *args) { return NULL; } -PyObject * +void _anim_encoder_dealloc(PyObject *self) { WebPAnimEncoderObject *encp = (WebPAnimEncoderObject *)self; WebPPictureFree(&(encp->frame)); WebPAnimEncoderDelete(encp->enc); - Py_RETURN_NONE; } PyObject * @@ -400,12 +399,11 @@ _anim_decoder_new(PyObject *self, PyObject *args) { return NULL; } -PyObject * +void _anim_decoder_dealloc(PyObject *self) { WebPAnimDecoderObject *decp = (WebPAnimDecoderObject *)self; WebPDataClear(&(decp->data)); WebPAnimDecoderDelete(decp->dec); - Py_RETURN_NONE; } PyObject * From 91fe817911cd8f4a4fa30632aeff621e495cfa7f Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Mon, 28 Nov 2022 18:03:08 +1100 Subject: [PATCH 188/239] Updated instructions to download source code Co-authored-by: Hugo van Kemenade --- docs/installation.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index af1d3399c..6d67a2536 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -348,15 +348,15 @@ build without cache using the ``--no-cache-dir`` option to force a build with newly installed external libraries. If you would like to install from a local copy of the source code instead, you -can download and extract the `compressed archive from PyPI`_, or clone from -GitHub with ``git clone https://github.com/python-pillow/Pillow``. +can clone from GitHub with ``git clone https://github.com/python-pillow/Pillow`` +or download and extract the `compressed archive from PyPI`_. After navigating to the Pillow directory, run:: python3 -m pip install --upgrade pip python3 -m pip install . -.. _compressed archive from PyPI: https://pypi.org/project/Pillow/ +.. _compressed archive from PyPI: https://pypi.org/project/Pillow/#files Build Options """"""""""""" From e3a46fcfd0111d7f080da0efe5846430771afeeb Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 28 Nov 2022 11:08:39 +0200 Subject: [PATCH 189/239] Use sphinx-inline-tabs to organise installation per OS --- .editorconfig | 4 + docs/Makefile | 2 +- docs/conf.py | 7 +- docs/installation.rst | 293 +++++++++++++++++++++--------------------- setup.cfg | 1 + 5 files changed, 157 insertions(+), 150 deletions(-) diff --git a/.editorconfig b/.editorconfig index d74549fe2..7f5eab056 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,6 +13,10 @@ indent_style = space trim_trailing_whitespace = true +[*.rst] +# Three-space indentation +indent_size = 3 + [*.yml] # Two-space indentation indent_size = 2 diff --git a/docs/Makefile b/docs/Makefile index 458299aac..0a663ce2b 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -43,7 +43,7 @@ clean: -rm -rf $(BUILDDIR)/* install-sphinx: - $(PYTHON) -m pip install --quiet sphinx sphinx-copybutton sphinx-issues sphinx-removed-in sphinxext-opengraph furo olefile + $(PYTHON) -m pip install --quiet furo olefile sphinx sphinx-copybutton sphinx-inline-tabs sphinx-issues sphinx-removed-in sphinxext-opengraph html: $(MAKE) install-sphinx diff --git a/docs/conf.py b/docs/conf.py index bc67d9368..04823e2d7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -27,12 +27,13 @@ needs_sphinx = "2.4" # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - "sphinx_copybutton", - "sphinx_issues", - "sphinx_removed_in", "sphinx.ext.autodoc", "sphinx.ext.intersphinx", "sphinx.ext.viewcode", + "sphinx_copybutton", + "sphinx_inline_tabs", + "sphinx_issues", + "sphinx_removed_in", "sphinxext.opengraph", ] diff --git a/docs/installation.rst b/docs/installation.rst index 6d67a2536..3c86f09cc 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -23,6 +23,11 @@ Pillow supports these Python versions. :file: older-versions.csv :header-rows: 1 +.. _Windows Installation: +.. _macOS Installation: +.. _Linux Installation: +.. _FreeBSD Installation: + Basic Installation ------------------ @@ -38,67 +43,69 @@ Install Pillow with :command:`pip`:: python3 -m pip install --upgrade Pillow -Windows Installation -^^^^^^^^^^^^^^^^^^^^ +.. tab:: Windows -We provide Pillow binaries for Windows compiled for the matrix of -supported Pythons in both 32 and 64-bit versions in the wheel format. -These binaries include support for all optional libraries except -libimagequant and libxcb. Raqm support requires -FriBiDi to be installed separately:: + We provide Pillow binaries for Windows compiled for the matrix of + supported Pythons in both 32 and 64-bit versions in the wheel format. + These binaries include support for all optional libraries except + libimagequant and libxcb. Raqm support requires + FriBiDi to be installed separately:: - python3 -m pip install --upgrade pip - python3 -m pip install --upgrade Pillow + python3 -m pip install --upgrade pip + python3 -m pip install --upgrade Pillow -To install Pillow in MSYS2, see `Building on Windows using MSYS2/MinGW`_. + To install Pillow in MSYS2, see `Building on Windows using MSYS2/MinGW`_. + +.. tab:: macOS + + We provide binaries for macOS for each of the supported Python + versions in the wheel format. These include support for all optional + libraries except libimagequant. Raqm support requires + FriBiDi to be installed separately:: + + python3 -m pip install --upgrade pip + python3 -m pip install --upgrade Pillow + +.. tab:: Linux + + We provide binaries for Linux for each of the supported Python + versions in the manylinux wheel format. These include support for all + optional libraries except libimagequant. Raqm support requires + FriBiDi to be installed separately:: + + python3 -m pip install --upgrade pip + python3 -m pip install --upgrade Pillow + + Most major Linux distributions, including Fedora, Ubuntu and ArchLinux + also include Pillow in packages that previously contained PIL e.g. + ``python-imaging``. Debian splits it into two packages, ``python3-pil`` + and ``python3-pil.imagetk``. + +.. tab:: FreeBSD + + Pillow can be installed on FreeBSD via the official Ports or Packages systems: + + **Ports**:: + + cd /usr/ports/graphics/py-pillow && make install clean + + **Packages**:: + + pkg install py38-pillow + + .. note:: + + The `Pillow FreeBSD port + `_ and packages + are tested by the ports team with all supported FreeBSD versions. -macOS Installation -^^^^^^^^^^^^^^^^^^ - -We provide binaries for macOS for each of the supported Python -versions in the wheel format. These include support for all optional -libraries except libimagequant. Raqm support requires -FriBiDi to be installed separately:: - - python3 -m pip install --upgrade pip - python3 -m pip install --upgrade Pillow - -Linux Installation -^^^^^^^^^^^^^^^^^^ - -We provide binaries for Linux for each of the supported Python -versions in the manylinux wheel format. These include support for all -optional libraries except libimagequant. Raqm support requires -FriBiDi to be installed separately:: - - python3 -m pip install --upgrade pip - python3 -m pip install --upgrade Pillow - -Most major Linux distributions, including Fedora, Ubuntu and ArchLinux -also include Pillow in packages that previously contained PIL e.g. -``python-imaging``. Debian splits it into two packages, ``python3-pil`` -and ``python3-pil.imagetk``. - -FreeBSD Installation -^^^^^^^^^^^^^^^^^^^^ - -Pillow can be installed on FreeBSD via the official Ports or Packages systems: - -**Ports**:: - - cd /usr/ports/graphics/py-pillow && make install clean - -**Packages**:: - - pkg install py38-pillow - -.. note:: - - The `Pillow FreeBSD port - `_ and packages - are tested by the ports team with all supported FreeBSD versions. - +.. _Building on macOS: +.. _Building on Windows: +.. _Building on Windows using MSYS2/MinGW: +.. _Building on FreeBSD: +.. _Building on Linux: +.. _Building on Android: Building From Source -------------------- @@ -187,141 +194,135 @@ Many of Pillow's features require external libraries: * **libxcb** provides X11 screengrab support. -Building on macOS -""""""""""""""""" +.. tab:: macOS -The Xcode command line tools are required to compile portions of -Pillow. The tools are installed by running ``xcode-select --install`` -from the command line. The command line tools are required even if you -have the full Xcode package installed. It may be necessary to run -``sudo xcodebuild -license`` to accept the license prior to using the -tools. + The Xcode command line tools are required to compile portions of + Pillow. The tools are installed by running ``xcode-select --install`` + from the command line. The command line tools are required even if you + have the full Xcode package installed. It may be necessary to run + ``sudo xcodebuild -license`` to accept the license prior to using the + tools. -The easiest way to install external libraries is via `Homebrew -`_. After you install Homebrew, run:: + The easiest way to install external libraries is via `Homebrew + `_. After you install Homebrew, run:: - brew install libjpeg libtiff little-cms2 openjpeg webp + brew install libjpeg libtiff little-cms2 openjpeg webp -To install libraqm on macOS use Homebrew to install its dependencies:: + To install libraqm on macOS use Homebrew to install its dependencies:: - brew install freetype harfbuzz fribidi + brew install freetype harfbuzz fribidi -Then see ``depends/install_raqm_cmake.sh`` to install libraqm. + Then see ``depends/install_raqm_cmake.sh`` to install libraqm. -Building on Windows -""""""""""""""""""" +.. tab:: Windows -We recommend you use prebuilt wheels from PyPI. -If you wish to compile Pillow manually, you can use the build scripts -in the ``winbuild`` directory used for CI testing and development. -These scripts require Visual Studio 2017 or newer and NASM. + We recommend you use prebuilt wheels from PyPI. + If you wish to compile Pillow manually, you can use the build scripts + in the ``winbuild`` directory used for CI testing and development. + These scripts require Visual Studio 2017 or newer and NASM. -The scripts also install Pillow from the local copy of the source code, so the -`Installing`_ instructions will not be necessary afterwards. + The scripts also install Pillow from the local copy of the source code, so the + `Installing`_ instructions will not be necessary afterwards. -Building on Windows using MSYS2/MinGW -""""""""""""""""""""""""""""""""""""" +.. tab:: Windows using MSYS2/MinGW -To build Pillow using MSYS2, make sure you run the **MSYS2 MinGW 32-bit** or -**MSYS2 MinGW 64-bit** console, *not* **MSYS2** directly. + To build Pillow using MSYS2, make sure you run the **MSYS2 MinGW 32-bit** or + **MSYS2 MinGW 64-bit** console, *not* **MSYS2** directly. -The following instructions target the 64-bit build, for 32-bit -replace all occurrences of ``mingw-w64-x86_64-`` with ``mingw-w64-i686-``. + The following instructions target the 64-bit build, for 32-bit + replace all occurrences of ``mingw-w64-x86_64-`` with ``mingw-w64-i686-``. -Make sure you have Python and GCC installed:: + Make sure you have Python and GCC installed:: - pacman -S \ - mingw-w64-x86_64-gcc \ - mingw-w64-x86_64-python3 \ - mingw-w64-x86_64-python3-pip \ - mingw-w64-x86_64-python3-setuptools + pacman -S \ + mingw-w64-x86_64-gcc \ + mingw-w64-x86_64-python3 \ + mingw-w64-x86_64-python3-pip \ + mingw-w64-x86_64-python3-setuptools -Prerequisites are installed on **MSYS2 MinGW 64-bit** with:: + Prerequisites are installed on **MSYS2 MinGW 64-bit** with:: - pacman -S \ - mingw-w64-x86_64-libjpeg-turbo \ - mingw-w64-x86_64-zlib \ - mingw-w64-x86_64-libtiff \ - mingw-w64-x86_64-freetype \ - mingw-w64-x86_64-lcms2 \ - mingw-w64-x86_64-libwebp \ - mingw-w64-x86_64-openjpeg2 \ - mingw-w64-x86_64-libimagequant \ - mingw-w64-x86_64-libraqm + pacman -S \ + mingw-w64-x86_64-libjpeg-turbo \ + mingw-w64-x86_64-zlib \ + mingw-w64-x86_64-libtiff \ + mingw-w64-x86_64-freetype \ + mingw-w64-x86_64-lcms2 \ + mingw-w64-x86_64-libwebp \ + mingw-w64-x86_64-openjpeg2 \ + mingw-w64-x86_64-libimagequant \ + mingw-w64-x86_64-libraqm -Building on FreeBSD -""""""""""""""""""" +.. tab:: FreeBSD -.. Note:: Only FreeBSD 10 and 11 tested + .. Note:: Only FreeBSD 10 and 11 tested -Make sure you have Python's development libraries installed:: + Make sure you have Python's development libraries installed:: - sudo pkg install python3 + sudo pkg install python3 -Prerequisites are installed on **FreeBSD 10 or 11** with:: + Prerequisites are installed on **FreeBSD 10 or 11** with:: - sudo pkg install jpeg-turbo tiff webp lcms2 freetype2 openjpeg harfbuzz fribidi libxcb + sudo pkg install jpeg-turbo tiff webp lcms2 freetype2 openjpeg harfbuzz fribidi libxcb -Then see ``depends/install_raqm_cmake.sh`` to install libraqm. + Then see ``depends/install_raqm_cmake.sh`` to install libraqm. -Building on Linux -""""""""""""""""" +.. tab:: Linux -If you didn't build Python from source, make sure you have Python's -development libraries installed. + If you didn't build Python from source, make sure you have Python's + development libraries installed. -In Debian or Ubuntu:: + In Debian or Ubuntu:: - sudo apt-get install python3-dev python3-setuptools + sudo apt-get install python3-dev python3-setuptools -In Fedora, the command is:: + In Fedora, the command is:: - sudo dnf install python3-devel redhat-rpm-config + sudo dnf install python3-devel redhat-rpm-config -In Alpine, the command is:: + In Alpine, the command is:: - sudo apk add python3-dev py3-setuptools + sudo apk add python3-dev py3-setuptools -.. Note:: ``redhat-rpm-config`` is required on Fedora 23, but not earlier versions. + .. Note:: ``redhat-rpm-config`` is required on Fedora 23, but not earlier versions. -Prerequisites for **Ubuntu 16.04 LTS - 22.04 LTS** are installed with:: + Prerequisites for **Ubuntu 16.04 LTS - 22.04 LTS** are installed with:: - sudo apt-get install libtiff5-dev libjpeg8-dev libopenjp2-7-dev zlib1g-dev \ - libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python3-tk \ - libharfbuzz-dev libfribidi-dev libxcb1-dev + sudo apt-get install libtiff5-dev libjpeg8-dev libopenjp2-7-dev zlib1g-dev \ + libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python3-tk \ + libharfbuzz-dev libfribidi-dev libxcb1-dev -To install libraqm, ``sudo apt-get install meson`` and then see -``depends/install_raqm.sh``. + To install libraqm, ``sudo apt-get install meson`` and then see + ``depends/install_raqm.sh``. -Prerequisites are installed on recent **Red Hat**, **CentOS** or **Fedora** with:: + Prerequisites are installed on recent **Red Hat**, **CentOS** or **Fedora** with:: - sudo dnf install libtiff-devel libjpeg-devel openjpeg2-devel zlib-devel \ - freetype-devel lcms2-devel libwebp-devel tcl-devel tk-devel \ - harfbuzz-devel fribidi-devel libraqm-devel libimagequant-devel libxcb-devel + sudo dnf install libtiff-devel libjpeg-devel openjpeg2-devel zlib-devel \ + freetype-devel lcms2-devel libwebp-devel tcl-devel tk-devel \ + harfbuzz-devel fribidi-devel libraqm-devel libimagequant-devel libxcb-devel -Note that the package manager may be yum or DNF, depending on the -exact distribution. + Note that the package manager may be yum or DNF, depending on the + exact distribution. -Prerequisites are installed for **Alpine** with:: + Prerequisites are installed for **Alpine** with:: - sudo apk add tiff-dev jpeg-dev openjpeg-dev zlib-dev freetype-dev lcms2-dev \ - libwebp-dev tcl-dev tk-dev harfbuzz-dev fribidi-dev libimagequant-dev \ - libxcb-dev libpng-dev + sudo apk add tiff-dev jpeg-dev openjpeg-dev zlib-dev freetype-dev lcms2-dev \ + libwebp-dev tcl-dev tk-dev harfbuzz-dev fribidi-dev libimagequant-dev \ + libxcb-dev libpng-dev -See also the ``Dockerfile``\s in the Test Infrastructure repo -(https://github.com/python-pillow/docker-images) for a known working -install process for other tested distros. + See also the ``Dockerfile``\s in the Test Infrastructure repo + (https://github.com/python-pillow/docker-images) for a known working + install process for other tested distros. -Building on Android -""""""""""""""""""" +.. tab:: Android -Basic Android support has been added for compilation within the Termux -environment. The dependencies can be installed by:: + Basic Android support has been added for compilation within the Termux + environment. The dependencies can be installed by:: - pkg install -y python ndk-sysroot clang make \ - libjpeg-turbo + pkg install -y python ndk-sysroot clang make \ + libjpeg-turbo -This has been tested within the Termux app on ChromeOS, on x86. + This has been tested within the Termux app on ChromeOS, on x86. Installing ^^^^^^^^^^ diff --git a/setup.cfg b/setup.cfg index 44feb25ff..b562e2934 100644 --- a/setup.cfg +++ b/setup.cfg @@ -46,6 +46,7 @@ docs = olefile sphinx>=2.4 sphinx-copybutton + sphinx-inline-tabs sphinx-issues>=3.0.1 sphinx-removed-in sphinxext-opengraph From e6e5a0018e27779827678552ea2b17a7c1034e7e Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 28 Nov 2022 12:34:43 +0200 Subject: [PATCH 190/239] Add missing 'make help' for serve and livehtml --- docs/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/Makefile b/docs/Makefile index 0a663ce2b..a65e2d3f5 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -20,6 +20,8 @@ I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" + @echo " serve to start a local server for viewing docs" + @echo " livehtml to start a local server for viewing docs and auto-reload on change" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @@ -38,6 +40,8 @@ help: @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " serve to start a local server for viewing docs" + @echo " livehtml to start a local server for viewing docs and auto-reload on change" clean: -rm -rf $(BUILDDIR)/* From d12c119ec41e62642157a2add640fc0211d46066 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 28 Nov 2022 12:36:04 +0200 Subject: [PATCH 191/239] Inline PHONY targets to help avoid omissions (texinfo, info, livehtml, serve were missing) --- docs/Makefile | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/docs/Makefile b/docs/Makefile index a65e2d3f5..d5242f935 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -15,8 +15,7 @@ ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext - +.PHONY: help help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @@ -40,45 +39,50 @@ help: @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" - @echo " serve to start a local server for viewing docs" - @echo " livehtml to start a local server for viewing docs and auto-reload on change" +.PHONY: clean clean: -rm -rf $(BUILDDIR)/* install-sphinx: $(PYTHON) -m pip install --quiet furo olefile sphinx sphinx-copybutton sphinx-inline-tabs sphinx-issues sphinx-removed-in sphinxext-opengraph +.PHONY: html html: $(MAKE) install-sphinx $(SPHINXBUILD) -b html -W --keep-going $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." +.PHONY: dirhtml dirhtml: $(MAKE) install-sphinx $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." +.PHONY: singlehtml singlehtml: $(MAKE) install-sphinx $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." +.PHONY: pickle pickle: $(MAKE) install-sphinx $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." +.PHONY: json json: $(MAKE) install-sphinx $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." +.PHONY: htmlhelp htmlhelp: $(MAKE) install-sphinx $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @@ -86,6 +90,7 @@ htmlhelp: @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." +.PHONY: qthelp qthelp: $(MAKE) install-sphinx $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @@ -96,6 +101,7 @@ qthelp: @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PillowPILfork.qhc" +.PHONY: devhelp devhelp: $(MAKE) install-sphinx $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @@ -106,12 +112,14 @@ devhelp: @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PillowPILfork" @echo "# devhelp" +.PHONY: epub epub: $(MAKE) install-sphinx $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." +.PHONY: latex latex: $(MAKE) install-sphinx $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @@ -120,6 +128,7 @@ latex: @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." +.PHONY: latexpdf latexpdf: $(MAKE) install-sphinx $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @@ -127,18 +136,21 @@ latexpdf: $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." +.PHONY: text text: $(MAKE) install-sphinx $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." +.PHONY: man man: $(MAKE) install-sphinx $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." +.PHONY: texinfo texinfo: $(MAKE) install-sphinx $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @@ -147,6 +159,7 @@ texinfo: @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." +.PHONY: info info: $(MAKE) install-sphinx $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @@ -154,18 +167,21 @@ info: make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." +.PHONY: gettext gettext: $(MAKE) install-sphinx $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." +.PHONY: changes changes: $(MAKE) install-sphinx $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." +.PHONY: linkcheck linkcheck: $(MAKE) install-sphinx $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck -j auto @@ -173,14 +189,17 @@ linkcheck: @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." +.PHONY: doctest doctest: $(MAKE) install-sphinx $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." +.PHONY: livehtml livehtml: html livereload $(BUILDDIR)/html -p 33233 +.PHONY: serve serve: cd $(BUILDDIR)/html; $(PYTHON) -m http.server From 5e42b1779e29ecbd34adcdd911b44cbe87ad439b Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 28 Nov 2022 12:50:55 +0200 Subject: [PATCH 192/239] Reorder tabs: big three OS first --- docs/installation.rst | 146 +++++++++++++++++++++--------------------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 3c86f09cc..00924eab9 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -23,9 +23,9 @@ Pillow supports these Python versions. :file: older-versions.csv :header-rows: 1 -.. _Windows Installation: -.. _macOS Installation: .. _Linux Installation: +.. _macOS Installation: +.. _Windows Installation: .. _FreeBSD Installation: Basic Installation @@ -43,29 +43,6 @@ Install Pillow with :command:`pip`:: python3 -m pip install --upgrade Pillow -.. tab:: Windows - - We provide Pillow binaries for Windows compiled for the matrix of - supported Pythons in both 32 and 64-bit versions in the wheel format. - These binaries include support for all optional libraries except - libimagequant and libxcb. Raqm support requires - FriBiDi to be installed separately:: - - python3 -m pip install --upgrade pip - python3 -m pip install --upgrade Pillow - - To install Pillow in MSYS2, see `Building on Windows using MSYS2/MinGW`_. - -.. tab:: macOS - - We provide binaries for macOS for each of the supported Python - versions in the wheel format. These include support for all optional - libraries except libimagequant. Raqm support requires - FriBiDi to be installed separately:: - - python3 -m pip install --upgrade pip - python3 -m pip install --upgrade Pillow - .. tab:: Linux We provide binaries for Linux for each of the supported Python @@ -81,6 +58,29 @@ Install Pillow with :command:`pip`:: ``python-imaging``. Debian splits it into two packages, ``python3-pil`` and ``python3-pil.imagetk``. +.. tab:: macOS + + We provide binaries for macOS for each of the supported Python + versions in the wheel format. These include support for all optional + libraries except libimagequant. Raqm support requires + FriBiDi to be installed separately:: + + python3 -m pip install --upgrade pip + python3 -m pip install --upgrade Pillow + +.. tab:: Windows + + We provide Pillow binaries for Windows compiled for the matrix of + supported Pythons in both 32 and 64-bit versions in the wheel format. + These binaries include support for all optional libraries except + libimagequant and libxcb. Raqm support requires + FriBiDi to be installed separately:: + + python3 -m pip install --upgrade pip + python3 -m pip install --upgrade Pillow + + To install Pillow in MSYS2, see `Building on Windows using MSYS2/MinGW`_. + .. tab:: FreeBSD Pillow can be installed on FreeBSD via the official Ports or Packages systems: @@ -100,11 +100,11 @@ Install Pillow with :command:`pip`:: are tested by the ports team with all supported FreeBSD versions. +.. _Building on Linux: .. _Building on macOS: .. _Building on Windows: .. _Building on Windows using MSYS2/MinGW: .. _Building on FreeBSD: -.. _Building on Linux: .. _Building on Android: Building From Source @@ -194,6 +194,53 @@ Many of Pillow's features require external libraries: * **libxcb** provides X11 screengrab support. +.. tab:: Linux + + If you didn't build Python from source, make sure you have Python's + development libraries installed. + + In Debian or Ubuntu:: + + sudo apt-get install python3-dev python3-setuptools + + In Fedora, the command is:: + + sudo dnf install python3-devel redhat-rpm-config + + In Alpine, the command is:: + + sudo apk add python3-dev py3-setuptools + + .. Note:: ``redhat-rpm-config`` is required on Fedora 23, but not earlier versions. + + Prerequisites for **Ubuntu 16.04 LTS - 22.04 LTS** are installed with:: + + sudo apt-get install libtiff5-dev libjpeg8-dev libopenjp2-7-dev zlib1g-dev \ + libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python3-tk \ + libharfbuzz-dev libfribidi-dev libxcb1-dev + + To install libraqm, ``sudo apt-get install meson`` and then see + ``depends/install_raqm.sh``. + + Prerequisites are installed on recent **Red Hat**, **CentOS** or **Fedora** with:: + + sudo dnf install libtiff-devel libjpeg-devel openjpeg2-devel zlib-devel \ + freetype-devel lcms2-devel libwebp-devel tcl-devel tk-devel \ + harfbuzz-devel fribidi-devel libraqm-devel libimagequant-devel libxcb-devel + + Note that the package manager may be yum or DNF, depending on the + exact distribution. + + Prerequisites are installed for **Alpine** with:: + + sudo apk add tiff-dev jpeg-dev openjpeg-dev zlib-dev freetype-dev lcms2-dev \ + libwebp-dev tcl-dev tk-dev harfbuzz-dev fribidi-dev libimagequant-dev \ + libxcb-dev libpng-dev + + See also the ``Dockerfile``\s in the Test Infrastructure repo + (https://github.com/python-pillow/docker-images) for a known working + install process for other tested distros. + .. tab:: macOS The Xcode command line tools are required to compile portions of @@ -267,53 +314,6 @@ Many of Pillow's features require external libraries: Then see ``depends/install_raqm_cmake.sh`` to install libraqm. -.. tab:: Linux - - If you didn't build Python from source, make sure you have Python's - development libraries installed. - - In Debian or Ubuntu:: - - sudo apt-get install python3-dev python3-setuptools - - In Fedora, the command is:: - - sudo dnf install python3-devel redhat-rpm-config - - In Alpine, the command is:: - - sudo apk add python3-dev py3-setuptools - - .. Note:: ``redhat-rpm-config`` is required on Fedora 23, but not earlier versions. - - Prerequisites for **Ubuntu 16.04 LTS - 22.04 LTS** are installed with:: - - sudo apt-get install libtiff5-dev libjpeg8-dev libopenjp2-7-dev zlib1g-dev \ - libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python3-tk \ - libharfbuzz-dev libfribidi-dev libxcb1-dev - - To install libraqm, ``sudo apt-get install meson`` and then see - ``depends/install_raqm.sh``. - - Prerequisites are installed on recent **Red Hat**, **CentOS** or **Fedora** with:: - - sudo dnf install libtiff-devel libjpeg-devel openjpeg2-devel zlib-devel \ - freetype-devel lcms2-devel libwebp-devel tcl-devel tk-devel \ - harfbuzz-devel fribidi-devel libraqm-devel libimagequant-devel libxcb-devel - - Note that the package manager may be yum or DNF, depending on the - exact distribution. - - Prerequisites are installed for **Alpine** with:: - - sudo apk add tiff-dev jpeg-dev openjpeg-dev zlib-dev freetype-dev lcms2-dev \ - libwebp-dev tcl-dev tk-dev harfbuzz-dev fribidi-dev libimagequant-dev \ - libxcb-dev libpng-dev - - See also the ``Dockerfile``\s in the Test Infrastructure repo - (https://github.com/python-pillow/docker-images) for a known working - install process for other tested distros. - .. tab:: Android Basic Android support has been added for compilation within the Termux From 50ccb27a4d57500b9b5e474e276df03158b6c870 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 28 Nov 2022 23:23:46 +0200 Subject: [PATCH 193/239] Remove extra space Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- docs/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Makefile b/docs/Makefile index d5242f935..d32d25a3c 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -90,7 +90,7 @@ htmlhelp: @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." -.PHONY: qthelp +.PHONY: qthelp qthelp: $(MAKE) install-sphinx $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp From 3ec8fa614705ae273426d60f994e3b01bb57a69a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 30 Nov 2022 13:49:07 +1100 Subject: [PATCH 194/239] Do not trust JPEG decoder to determine image is CMYK --- src/PIL/BlpImagePlugin.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index 533997737..45987ec03 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -373,6 +373,9 @@ class BLP1Decoder(_BLPBaseDecoder): data = BytesIO(data) image = JpegImageFile(data) Image._decompression_bomb_check(image.size) + if image.mode == "CMYK": + decoder_name, extents, offset, args = image.tile[0] + image.tile = [(decoder_name, extents, offset, (args[0], "CMYK"))] r, g, b = image.convert("RGB").split() image = Image.merge("RGB", (b, g, r)) self.set_as_raw(image.tobytes()) From aab7983146729c81a5105b6511858f41d76b53f7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 1 Dec 2022 07:57:26 +1100 Subject: [PATCH 195/239] Updated xz to 5.2.9 --- winbuild/build_prepare.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index e4bf275a1..66e352c73 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -152,9 +152,9 @@ deps = { "libs": [r"*.lib"], }, "xz": { - "url": SF_PROJECTS + "/lzmautils/files/xz-5.2.8.tar.gz/download", - "filename": "xz-5.2.8.tar.gz", - "dir": "xz-5.2.8", + "url": SF_PROJECTS + "/lzmautils/files/xz-5.2.9.tar.gz/download", + "filename": "xz-5.2.9.tar.gz", + "dir": "xz-5.2.9", "license": "COPYING", "patch": { r"src\liblzma\api\lzma.h": { From 96b316880e284f4221b2d8400ef1bec5c81bcb83 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 2 Dec 2022 11:40:06 +0200 Subject: [PATCH 196/239] Use 4-space indents for RST --- .editorconfig | 2 +- docs/installation.rst | 218 +++++++++++++++++++++--------------------- 2 files changed, 110 insertions(+), 110 deletions(-) diff --git a/.editorconfig b/.editorconfig index 7f5eab056..07f02c236 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,7 +15,7 @@ trim_trailing_whitespace = true [*.rst] # Three-space indentation -indent_size = 3 +indent_size = 4 [*.yml] # Two-space indentation diff --git a/docs/installation.rst b/docs/installation.rst index 00924eab9..89b2e558f 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -45,59 +45,59 @@ Install Pillow with :command:`pip`:: .. tab:: Linux - We provide binaries for Linux for each of the supported Python - versions in the manylinux wheel format. These include support for all - optional libraries except libimagequant. Raqm support requires - FriBiDi to be installed separately:: + We provide binaries for Linux for each of the supported Python + versions in the manylinux wheel format. These include support for all + optional libraries except libimagequant. Raqm support requires + FriBiDi to be installed separately:: - python3 -m pip install --upgrade pip - python3 -m pip install --upgrade Pillow + python3 -m pip install --upgrade pip + python3 -m pip install --upgrade Pillow - Most major Linux distributions, including Fedora, Ubuntu and ArchLinux - also include Pillow in packages that previously contained PIL e.g. - ``python-imaging``. Debian splits it into two packages, ``python3-pil`` - and ``python3-pil.imagetk``. + Most major Linux distributions, including Fedora, Ubuntu and ArchLinux + also include Pillow in packages that previously contained PIL e.g. + ``python-imaging``. Debian splits it into two packages, ``python3-pil`` + and ``python3-pil.imagetk``. .. tab:: macOS - We provide binaries for macOS for each of the supported Python - versions in the wheel format. These include support for all optional - libraries except libimagequant. Raqm support requires - FriBiDi to be installed separately:: + We provide binaries for macOS for each of the supported Python + versions in the wheel format. These include support for all optional + libraries except libimagequant. Raqm support requires + FriBiDi to be installed separately:: - python3 -m pip install --upgrade pip - python3 -m pip install --upgrade Pillow + python3 -m pip install --upgrade pip + python3 -m pip install --upgrade Pillow .. tab:: Windows - We provide Pillow binaries for Windows compiled for the matrix of - supported Pythons in both 32 and 64-bit versions in the wheel format. - These binaries include support for all optional libraries except - libimagequant and libxcb. Raqm support requires - FriBiDi to be installed separately:: + We provide Pillow binaries for Windows compiled for the matrix of + supported Pythons in both 32 and 64-bit versions in the wheel format. + These binaries include support for all optional libraries except + libimagequant and libxcb. Raqm support requires + FriBiDi to be installed separately:: - python3 -m pip install --upgrade pip - python3 -m pip install --upgrade Pillow + python3 -m pip install --upgrade pip + python3 -m pip install --upgrade Pillow - To install Pillow in MSYS2, see `Building on Windows using MSYS2/MinGW`_. + To install Pillow in MSYS2, see `Building on Windows using MSYS2/MinGW`_. .. tab:: FreeBSD - Pillow can be installed on FreeBSD via the official Ports or Packages systems: + Pillow can be installed on FreeBSD via the official Ports or Packages systems: - **Ports**:: + **Ports**:: - cd /usr/ports/graphics/py-pillow && make install clean + cd /usr/ports/graphics/py-pillow && make install clean - **Packages**:: + **Packages**:: - pkg install py38-pillow + pkg install py38-pillow - .. note:: + .. note:: - The `Pillow FreeBSD port - `_ and packages - are tested by the ports team with all supported FreeBSD versions. + The `Pillow FreeBSD port + `_ and packages + are tested by the ports team with all supported FreeBSD versions. .. _Building on Linux: @@ -196,133 +196,133 @@ Many of Pillow's features require external libraries: .. tab:: Linux - If you didn't build Python from source, make sure you have Python's - development libraries installed. + If you didn't build Python from source, make sure you have Python's + development libraries installed. - In Debian or Ubuntu:: + In Debian or Ubuntu:: - sudo apt-get install python3-dev python3-setuptools + sudo apt-get install python3-dev python3-setuptools - In Fedora, the command is:: + In Fedora, the command is:: sudo dnf install python3-devel redhat-rpm-config - In Alpine, the command is:: + In Alpine, the command is:: sudo apk add python3-dev py3-setuptools - .. Note:: ``redhat-rpm-config`` is required on Fedora 23, but not earlier versions. + .. Note:: ``redhat-rpm-config`` is required on Fedora 23, but not earlier versions. - Prerequisites for **Ubuntu 16.04 LTS - 22.04 LTS** are installed with:: + Prerequisites for **Ubuntu 16.04 LTS - 22.04 LTS** are installed with:: - sudo apt-get install libtiff5-dev libjpeg8-dev libopenjp2-7-dev zlib1g-dev \ - libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python3-tk \ - libharfbuzz-dev libfribidi-dev libxcb1-dev + sudo apt-get install libtiff5-dev libjpeg8-dev libopenjp2-7-dev zlib1g-dev \ + libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python3-tk \ + libharfbuzz-dev libfribidi-dev libxcb1-dev - To install libraqm, ``sudo apt-get install meson`` and then see - ``depends/install_raqm.sh``. + To install libraqm, ``sudo apt-get install meson`` and then see + ``depends/install_raqm.sh``. - Prerequisites are installed on recent **Red Hat**, **CentOS** or **Fedora** with:: + Prerequisites are installed on recent **Red Hat**, **CentOS** or **Fedora** with:: - sudo dnf install libtiff-devel libjpeg-devel openjpeg2-devel zlib-devel \ - freetype-devel lcms2-devel libwebp-devel tcl-devel tk-devel \ - harfbuzz-devel fribidi-devel libraqm-devel libimagequant-devel libxcb-devel + sudo dnf install libtiff-devel libjpeg-devel openjpeg2-devel zlib-devel \ + freetype-devel lcms2-devel libwebp-devel tcl-devel tk-devel \ + harfbuzz-devel fribidi-devel libraqm-devel libimagequant-devel libxcb-devel - Note that the package manager may be yum or DNF, depending on the - exact distribution. + Note that the package manager may be yum or DNF, depending on the + exact distribution. - Prerequisites are installed for **Alpine** with:: + Prerequisites are installed for **Alpine** with:: - sudo apk add tiff-dev jpeg-dev openjpeg-dev zlib-dev freetype-dev lcms2-dev \ - libwebp-dev tcl-dev tk-dev harfbuzz-dev fribidi-dev libimagequant-dev \ - libxcb-dev libpng-dev + sudo apk add tiff-dev jpeg-dev openjpeg-dev zlib-dev freetype-dev lcms2-dev \ + libwebp-dev tcl-dev tk-dev harfbuzz-dev fribidi-dev libimagequant-dev \ + libxcb-dev libpng-dev - See also the ``Dockerfile``\s in the Test Infrastructure repo - (https://github.com/python-pillow/docker-images) for a known working - install process for other tested distros. + See also the ``Dockerfile``\s in the Test Infrastructure repo + (https://github.com/python-pillow/docker-images) for a known working + install process for other tested distros. .. tab:: macOS - The Xcode command line tools are required to compile portions of - Pillow. The tools are installed by running ``xcode-select --install`` - from the command line. The command line tools are required even if you - have the full Xcode package installed. It may be necessary to run - ``sudo xcodebuild -license`` to accept the license prior to using the - tools. + The Xcode command line tools are required to compile portions of + Pillow. The tools are installed by running ``xcode-select --install`` + from the command line. The command line tools are required even if you + have the full Xcode package installed. It may be necessary to run + ``sudo xcodebuild -license`` to accept the license prior to using the + tools. - The easiest way to install external libraries is via `Homebrew - `_. After you install Homebrew, run:: + The easiest way to install external libraries is via `Homebrew + `_. After you install Homebrew, run:: - brew install libjpeg libtiff little-cms2 openjpeg webp + brew install libjpeg libtiff little-cms2 openjpeg webp - To install libraqm on macOS use Homebrew to install its dependencies:: + To install libraqm on macOS use Homebrew to install its dependencies:: - brew install freetype harfbuzz fribidi + brew install freetype harfbuzz fribidi - Then see ``depends/install_raqm_cmake.sh`` to install libraqm. + Then see ``depends/install_raqm_cmake.sh`` to install libraqm. .. tab:: Windows - We recommend you use prebuilt wheels from PyPI. - If you wish to compile Pillow manually, you can use the build scripts - in the ``winbuild`` directory used for CI testing and development. - These scripts require Visual Studio 2017 or newer and NASM. + We recommend you use prebuilt wheels from PyPI. + If you wish to compile Pillow manually, you can use the build scripts + in the ``winbuild`` directory used for CI testing and development. + These scripts require Visual Studio 2017 or newer and NASM. - The scripts also install Pillow from the local copy of the source code, so the - `Installing`_ instructions will not be necessary afterwards. + The scripts also install Pillow from the local copy of the source code, so the + `Installing`_ instructions will not be necessary afterwards. .. tab:: Windows using MSYS2/MinGW - To build Pillow using MSYS2, make sure you run the **MSYS2 MinGW 32-bit** or - **MSYS2 MinGW 64-bit** console, *not* **MSYS2** directly. + To build Pillow using MSYS2, make sure you run the **MSYS2 MinGW 32-bit** or + **MSYS2 MinGW 64-bit** console, *not* **MSYS2** directly. - The following instructions target the 64-bit build, for 32-bit - replace all occurrences of ``mingw-w64-x86_64-`` with ``mingw-w64-i686-``. + The following instructions target the 64-bit build, for 32-bit + replace all occurrences of ``mingw-w64-x86_64-`` with ``mingw-w64-i686-``. - Make sure you have Python and GCC installed:: + Make sure you have Python and GCC installed:: - pacman -S \ - mingw-w64-x86_64-gcc \ - mingw-w64-x86_64-python3 \ - mingw-w64-x86_64-python3-pip \ - mingw-w64-x86_64-python3-setuptools + pacman -S \ + mingw-w64-x86_64-gcc \ + mingw-w64-x86_64-python3 \ + mingw-w64-x86_64-python3-pip \ + mingw-w64-x86_64-python3-setuptools - Prerequisites are installed on **MSYS2 MinGW 64-bit** with:: + Prerequisites are installed on **MSYS2 MinGW 64-bit** with:: - pacman -S \ - mingw-w64-x86_64-libjpeg-turbo \ - mingw-w64-x86_64-zlib \ - mingw-w64-x86_64-libtiff \ - mingw-w64-x86_64-freetype \ - mingw-w64-x86_64-lcms2 \ - mingw-w64-x86_64-libwebp \ - mingw-w64-x86_64-openjpeg2 \ - mingw-w64-x86_64-libimagequant \ - mingw-w64-x86_64-libraqm + pacman -S \ + mingw-w64-x86_64-libjpeg-turbo \ + mingw-w64-x86_64-zlib \ + mingw-w64-x86_64-libtiff \ + mingw-w64-x86_64-freetype \ + mingw-w64-x86_64-lcms2 \ + mingw-w64-x86_64-libwebp \ + mingw-w64-x86_64-openjpeg2 \ + mingw-w64-x86_64-libimagequant \ + mingw-w64-x86_64-libraqm .. tab:: FreeBSD - .. Note:: Only FreeBSD 10 and 11 tested + .. Note:: Only FreeBSD 10 and 11 tested - Make sure you have Python's development libraries installed:: + Make sure you have Python's development libraries installed:: - sudo pkg install python3 + sudo pkg install python3 - Prerequisites are installed on **FreeBSD 10 or 11** with:: + Prerequisites are installed on **FreeBSD 10 or 11** with:: - sudo pkg install jpeg-turbo tiff webp lcms2 freetype2 openjpeg harfbuzz fribidi libxcb + sudo pkg install jpeg-turbo tiff webp lcms2 freetype2 openjpeg harfbuzz fribidi libxcb - Then see ``depends/install_raqm_cmake.sh`` to install libraqm. + Then see ``depends/install_raqm_cmake.sh`` to install libraqm. .. tab:: Android - Basic Android support has been added for compilation within the Termux - environment. The dependencies can be installed by:: + Basic Android support has been added for compilation within the Termux + environment. The dependencies can be installed by:: - pkg install -y python ndk-sysroot clang make \ - libjpeg-turbo + pkg install -y python ndk-sysroot clang make \ + libjpeg-turbo - This has been tested within the Termux app on ChromeOS, on x86. + This has been tested within the Termux app on ChromeOS, on x86. Installing ^^^^^^^^^^ From c120649632391c1f287cae6b7ff7ce7c28ddb20b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 2 Dec 2022 21:29:27 +1100 Subject: [PATCH 197/239] Remove specific number of jobs from comment --- .github/workflows/test-windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 5cabb6622..e2a9de65c 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -141,7 +141,7 @@ jobs: if: steps.build-cache.outputs.cache-hit != 'true' run: "& winbuild\\build\\build_dep_fribidi.cmd" - # trim ~150MB x 9 + # trim ~150MB for each job - name: Optimize build cache if: steps.build-cache.outputs.cache-hit != 'true' run: rmdir /S /Q winbuild\build\src From d822d85af6d9b1c108650832d56b75cd86b2f5a9 Mon Sep 17 00:00:00 2001 From: Sam Mason Date: Fri, 2 Dec 2022 17:57:19 +0000 Subject: [PATCH 198/239] support round-tripping JPEG comments --- Tests/test_file_jpeg.py | 12 ++++++++++++ src/PIL/JpegImagePlugin.py | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index fa96e425b..94ef59565 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -87,6 +87,18 @@ class TestFileJpeg: assert im.info["comment"] == b"File written by Adobe Photoshop\xa8 4.0\x00" + def test_com_write(self): + dummy_text = "this is a test comment" + with Image.open(TEST_FILE) as im: + with BytesIO() as buf: + im.save(buf, format="JPEG") + with Image.open(buf) as im2: + assert im.app['COM'] == im2.app['COM'] + with BytesIO() as buf: + im.save(buf, format="JPEG", comment=dummy_text) + with Image.open(buf) as im2: + assert im2.app['COM'].decode() == dummy_text + def test_cmyk(self): # Test CMYK handling. Thanks to Tim and Charlie for test data, # Michael for getting me to look one more time. diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index a6ed223bc..a6abe8b9f 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -44,6 +44,7 @@ import warnings from . import Image, ImageFile, TiffImagePlugin from ._binary import i16be as i16 from ._binary import i32be as i32 +from ._binary import o16be as o16 from ._binary import o8 from ._deprecate import deprecate from .JpegPresets import presets @@ -713,6 +714,15 @@ def _save(im, fp, filename): extra = info.get("extra", b"") + comment = info.get("comment") + if comment is None and isinstance(im, JpegImageFile): + comment = im.app.get('COM') + if comment: + if isinstance(comment, str): + comment = comment.encode() + size = o16(2 + len(comment)) + extra += b'\xFF\xFE%s%s' % (size, comment) + icc_profile = info.get("icc_profile") if icc_profile: ICC_OVERHEAD_LEN = 14 From e9f485849157100ddf75f289db6fcb509927706c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 2 Dec 2022 18:07:07 +0000 Subject: [PATCH 199/239] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- Tests/test_file_jpeg.py | 4 ++-- src/PIL/JpegImagePlugin.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 94ef59565..ffaf2caba 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -93,11 +93,11 @@ class TestFileJpeg: with BytesIO() as buf: im.save(buf, format="JPEG") with Image.open(buf) as im2: - assert im.app['COM'] == im2.app['COM'] + assert im.app["COM"] == im2.app["COM"] with BytesIO() as buf: im.save(buf, format="JPEG", comment=dummy_text) with Image.open(buf) as im2: - assert im2.app['COM'].decode() == dummy_text + assert im2.app["COM"].decode() == dummy_text def test_cmyk(self): # Test CMYK handling. Thanks to Tim and Charlie for test data, diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index a6abe8b9f..cb8a4e57f 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -44,8 +44,8 @@ import warnings from . import Image, ImageFile, TiffImagePlugin from ._binary import i16be as i16 from ._binary import i32be as i32 -from ._binary import o16be as o16 from ._binary import o8 +from ._binary import o16be as o16 from ._deprecate import deprecate from .JpegPresets import presets @@ -716,12 +716,12 @@ def _save(im, fp, filename): comment = info.get("comment") if comment is None and isinstance(im, JpegImageFile): - comment = im.app.get('COM') + comment = im.app.get("COM") if comment: if isinstance(comment, str): comment = comment.encode() size = o16(2 + len(comment)) - extra += b'\xFF\xFE%s%s' % (size, comment) + extra += b"\xFF\xFE%s%s" % (size, comment) icc_profile = info.get("icc_profile") if icc_profile: From 976ad5746a0155135337efa75648c550705215c0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 3 Dec 2022 09:29:02 +1100 Subject: [PATCH 200/239] Save comments from any image format by default --- src/PIL/JpegImagePlugin.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index cb8a4e57f..c9de714d8 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -714,9 +714,7 @@ def _save(im, fp, filename): extra = info.get("extra", b"") - comment = info.get("comment") - if comment is None and isinstance(im, JpegImageFile): - comment = im.app.get("COM") + comment = info.get("comment", im.info.get("comment")) if comment: if isinstance(comment, str): comment = comment.encode() From c1d0a00943ee6fcc993f47047b798e2b6f9bac6f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 3 Dec 2022 09:31:05 +1100 Subject: [PATCH 201/239] Use _binary instead of struct --- src/PIL/JpegImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index c9de714d8..92dbb3193 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -732,7 +732,7 @@ def _save(im, fp, filename): icc_profile = icc_profile[MAX_DATA_BYTES_IN_MARKER:] i = 1 for marker in markers: - size = struct.pack(">H", 2 + ICC_OVERHEAD_LEN + len(marker)) + size = o16(2 + ICC_OVERHEAD_LEN + len(marker)) extra += ( b"\xFF\xE2" + size From 525c01143a8a4e0133908826577ccb54ed829a1b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 3 Dec 2022 09:59:22 +1100 Subject: [PATCH 202/239] Test that comment is reread --- Tests/test_file_jpeg.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index ffaf2caba..bb4ebb686 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -86,18 +86,26 @@ class TestFileJpeg: assert len(im.applist) == 2 assert im.info["comment"] == b"File written by Adobe Photoshop\xa8 4.0\x00" + assert im.app["COM"] == im.info["comment"] - def test_com_write(self): - dummy_text = "this is a test comment" + def test_comment_write(self): with Image.open(TEST_FILE) as im: - with BytesIO() as buf: - im.save(buf, format="JPEG") - with Image.open(buf) as im2: - assert im.app["COM"] == im2.app["COM"] - with BytesIO() as buf: - im.save(buf, format="JPEG", comment=dummy_text) - with Image.open(buf) as im2: - assert im2.app["COM"].decode() == dummy_text + assert im.info["comment"] == b"File written by Adobe Photoshop\xa8 4.0\x00" + + # Test that existing comment is saved by default + out = BytesIO() + im.save(out, format="JPEG") + with Image.open(out) as reloaded: + assert im.info["comment"] == reloaded.info["comment"] + + # Test that a comment argument overrides the default comment + for comment in ("Test comment text", b"Text comment text"): + out = BytesIO() + im.save(out, format="JPEG", comment=comment) + with Image.open(out) as reloaded: + if not isinstance(comment, bytes): + comment = comment.encode() + assert reloaded.info["comment"] == comment def test_cmyk(self): # Test CMYK handling. Thanks to Tim and Charlie for test data, From 61cbcaee64a852bc9902d60ab0732f676d6d0e72 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 3 Dec 2022 10:35:01 +1100 Subject: [PATCH 203/239] Changed indentation to be consistent --- docs/installation.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 89b2e558f..b559c824d 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -205,11 +205,11 @@ Many of Pillow's features require external libraries: In Fedora, the command is:: - sudo dnf install python3-devel redhat-rpm-config + sudo dnf install python3-devel redhat-rpm-config In Alpine, the command is:: - sudo apk add python3-dev py3-setuptools + sudo apk add python3-dev py3-setuptools .. Note:: ``redhat-rpm-config`` is required on Fedora 23, but not earlier versions. From eafff0e1396a1b55522c561b7e355c4e7ebaa5b1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 3 Dec 2022 10:54:04 +1100 Subject: [PATCH 204/239] Use compile_python_fuzzer --- Tests/oss-fuzz/build.sh | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/Tests/oss-fuzz/build.sh b/Tests/oss-fuzz/build.sh index 09cc7bc16..b459ee47a 100755 --- a/Tests/oss-fuzz/build.sh +++ b/Tests/oss-fuzz/build.sh @@ -19,9 +19,7 @@ python3 setup.py build --build-base=/tmp/build install # Build fuzzers in $OUT. for fuzzer in $(find $SRC -name 'fuzz_*.py'); do - fuzzer_basename=$(basename -s .py $fuzzer) - fuzzer_package=${fuzzer_basename}.pkg - pyinstaller \ + compile_python_fuzzer $fuzzer \ --add-binary /usr/local/lib/libjpeg.so.62.3.0:. \ --add-binary /usr/local/lib/libfreetype.so.6:. \ --add-binary /usr/local/lib/liblcms2.so.2:. \ @@ -31,17 +29,7 @@ for fuzzer in $(find $SRC -name 'fuzz_*.py'); do --add-binary /usr/local/lib/libwebp.so.7:. \ --add-binary /usr/local/lib/libwebpdemux.so.2:. \ --add-binary /usr/local/lib/libwebpmux.so.3:. \ - --add-binary /usr/local/lib/libxcb.so.1:. \ - --distpath $OUT --onefile --name $fuzzer_package $fuzzer - - # Create execution wrapper. - echo "#!/bin/sh -# LLVMFuzzerTestOneInput for fuzzer detection. -this_dir=\$(dirname \"\$0\") -LD_PRELOAD=\$this_dir/sanitizer_with_fuzzer.so \ -ASAN_OPTIONS=\$ASAN_OPTIONS:symbolize=1:external_symbolizer_path=\$this_dir/llvm-symbolizer:detect_leaks=0 \ -\$this_dir/$fuzzer_package \$@" > $OUT/$fuzzer_basename - chmod u+x $OUT/$fuzzer_basename + --add-binary /usr/local/lib/libxcb.so.1:. done find Tests/images Tests/icc -print | zip -q $OUT/fuzz_pillow_seed_corpus.zip -@ From 8e70787cf20399f2a88976ad8ad4d3983f81c741 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 3 Dec 2022 01:44:21 +0000 Subject: [PATCH 205/239] Update cygwin/cygwin-install-action action to v3 --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index bbf0ee736..37dc694c6 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -30,7 +30,7 @@ jobs: uses: actions/checkout@v3 - name: Install Cygwin - uses: cygwin/cygwin-install-action@v2 + uses: cygwin/cygwin-install-action@v3 with: platform: x86_64 packages: > From 61f27211c2d50a20ac544ff8ab16c58439c4cbe8 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 3 Dec 2022 07:43:58 +0200 Subject: [PATCH 206/239] Fix comment Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 07f02c236..449530717 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,7 +14,7 @@ indent_style = space trim_trailing_whitespace = true [*.rst] -# Three-space indentation +# Four-space indentation indent_size = 4 [*.yml] From 1ed1a3a971e127b55353bde39cb7ea6bedb45c04 Mon Sep 17 00:00:00 2001 From: Sam Mason Date: Sat, 3 Dec 2022 15:07:37 +0000 Subject: [PATCH 207/239] make sure passing a blank comment removes existing comment --- Tests/test_file_jpeg.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index bb4ebb686..7a958c7da 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -98,6 +98,13 @@ class TestFileJpeg: with Image.open(out) as reloaded: assert im.info["comment"] == reloaded.info["comment"] + # Ensure that a blank comment causes any existing comment to be removed + for comment in ("", b"", None): + out = BytesIO() + im.save(out, format="JPEG", comment=comment) + with Image.open(out) as reloaded: + assert "comment" not in reloaded.info + # Test that a comment argument overrides the default comment for comment in ("Test comment text", b"Text comment text"): out = BytesIO() From 8ada23ed04ee18730d44d14dd82b0aabc12a0917 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 5 Dec 2022 09:09:00 +1100 Subject: [PATCH 208/239] Added IFD1 reading --- Tests/test_image.py | 21 ++++++++++++++++++++- src/PIL/ExifTags.py | 1 + src/PIL/Image.py | 10 +++++----- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/Tests/test_image.py b/Tests/test_image.py index e57903490..b4e81e466 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -7,7 +7,14 @@ import warnings import pytest -from PIL import Image, ImageDraw, ImagePalette, UnidentifiedImageError, features +from PIL import ( + ExifTags, + Image, + ImageDraw, + ImagePalette, + UnidentifiedImageError, + features, +) from .helper import ( assert_image_equal, @@ -808,6 +815,18 @@ class TestImage: reloaded_exif.load(exif.tobytes()) assert reloaded_exif.get_ifd(0xA005) == exif.get_ifd(0xA005) + def test_exif_ifd1(self): + with Image.open("Tests/images/flower.jpg") as im: + exif = im.getexif() + assert exif.get_ifd(ExifTags.IFD.IFD1) == { + 513: 2036, + 514: 5448, + 259: 6, + 296: 2, + 282: 180.0, + 283: 180.0, + } + def test_exif_ifd(self): with Image.open("Tests/images/flower.jpg") as im: exif = im.getexif() diff --git a/src/PIL/ExifTags.py b/src/PIL/ExifTags.py index 97a21335f..ffab7e554 100644 --- a/src/PIL/ExifTags.py +++ b/src/PIL/ExifTags.py @@ -353,3 +353,4 @@ class IFD(IntEnum): GPSInfo = 34853 Makernote = 37500 Interop = 40965 + IFD1 = -1 diff --git a/src/PIL/Image.py b/src/PIL/Image.py index d07fc716c..1f3d4b74f 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -3641,17 +3641,17 @@ class Exif(MutableMapping): def get_ifd(self, tag): if tag not in self._ifds: - if tag in [ExifTags.IFD.Exif, ExifTags.IFD.GPSInfo]: - # exif, gpsinfo + if tag == ExifTags.IFD.IFD1: + if self._info is not None: + self._ifds[tag] = self._get_ifd_dict(self._info.next) + elif tag in [ExifTags.IFD.Exif, ExifTags.IFD.GPSInfo]: if tag in self: self._ifds[tag] = self._get_ifd_dict(self[tag]) elif tag in [ExifTags.IFD.Interop, ExifTags.IFD.Makernote]: - # interop, makernote if ExifTags.IFD.Exif not in self._ifds: self.get_ifd(ExifTags.IFD.Exif) tag_data = self._ifds[ExifTags.IFD.Exif][tag] if tag == ExifTags.IFD.Makernote: - # makernote from .TiffImagePlugin import ImageFileDirectory_v2 if tag_data[:8] == b"FUJIFILM": @@ -3727,7 +3727,7 @@ class Exif(MutableMapping): makernote = {0x1101: dict(self._fixup_dict(camerainfo))} self._ifds[tag] = makernote else: - # interop + # Interop self._ifds[tag] = self._get_ifd_dict(tag_data) return self._ifds.get(tag, {}) From e50ae85ea406d86073ca88ffdec469e1e18d7527 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 5 Dec 2022 13:57:26 +1100 Subject: [PATCH 209/239] Use jpeg_write_marker to write comment --- src/PIL/JpegImagePlugin.py | 12 +++++------- src/encode.c | 26 ++++++++++++++++++++++---- src/libImaging/Jpeg.h | 4 ++++ src/libImaging/JpegEncode.c | 13 ++++++++++++- 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 92dbb3193..7b5b32be0 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -714,13 +714,6 @@ def _save(im, fp, filename): extra = info.get("extra", b"") - comment = info.get("comment", im.info.get("comment")) - if comment: - if isinstance(comment, str): - comment = comment.encode() - size = o16(2 + len(comment)) - extra += b"\xFF\xFE%s%s" % (size, comment) - icc_profile = info.get("icc_profile") if icc_profile: ICC_OVERHEAD_LEN = 14 @@ -743,6 +736,10 @@ def _save(im, fp, filename): ) i += 1 + comment = info.get("comment", im.info.get("comment")) or b"" + if isinstance(comment, str): + comment = comment.encode() + # "progressive" is the official name, but older documentation # says "progression" # FIXME: issue a warning if the wrong form is used (post-1.1.7) @@ -765,6 +762,7 @@ def _save(im, fp, filename): dpi[1], subsampling, qtables, + comment, extra, exif, ) diff --git a/src/encode.c b/src/encode.c index 72c7f64d0..a2eae81fd 100644 --- a/src/encode.c +++ b/src/encode.c @@ -1048,6 +1048,8 @@ PyImaging_JpegEncoderNew(PyObject *self, PyObject *args) { PyObject *qtables = NULL; unsigned int *qarrays = NULL; int qtablesLen = 0; + char *comment = NULL; + Py_ssize_t comment_size; char *extra = NULL; Py_ssize_t extra_size; char *rawExif = NULL; @@ -1055,7 +1057,7 @@ PyImaging_JpegEncoderNew(PyObject *self, PyObject *args) { if (!PyArg_ParseTuple( args, - "ss|nnnnnnnnOy#y#", + "ss|nnnnnnnnOy#y#y#", &mode, &rawmode, &quality, @@ -1067,6 +1069,8 @@ PyImaging_JpegEncoderNew(PyObject *self, PyObject *args) { &ydpi, &subsampling, &qtables, + &comment, + &comment_size, &extra, &extra_size, &rawExif, @@ -1090,12 +1094,24 @@ PyImaging_JpegEncoderNew(PyObject *self, PyObject *args) { return NULL; } - // Freed in JpegEncode, Case 5 + // Freed in JpegEncode, Case 6 qarrays = get_qtables_arrays(qtables, &qtablesLen); + if (comment && comment_size > 0) { + /* malloc check ok, length is from python parsearg */ + char *p = malloc(comment_size); // Freed in JpegEncode, Case 6 + if (!p) { + return ImagingError_MemoryError(); + } + memcpy(p, comment, comment_size); + comment = p; + } else { + comment = NULL; + } + if (extra && extra_size > 0) { /* malloc check ok, length is from python parsearg */ - char *p = malloc(extra_size); // Freed in JpegEncode, Case 5 + char *p = malloc(extra_size); // Freed in JpegEncode, Case 6 if (!p) { return ImagingError_MemoryError(); } @@ -1107,7 +1123,7 @@ PyImaging_JpegEncoderNew(PyObject *self, PyObject *args) { if (rawExif && rawExifLen > 0) { /* malloc check ok, length is from python parsearg */ - char *pp = malloc(rawExifLen); // Freed in JpegEncode, Case 5 + char *pp = malloc(rawExifLen); // Freed in JpegEncode, Case 6 if (!pp) { if (extra) { free(extra); @@ -1134,6 +1150,8 @@ PyImaging_JpegEncoderNew(PyObject *self, PyObject *args) { ((JPEGENCODERSTATE *)encoder->state.context)->streamtype = streamtype; ((JPEGENCODERSTATE *)encoder->state.context)->xdpi = xdpi; ((JPEGENCODERSTATE *)encoder->state.context)->ydpi = ydpi; + ((JPEGENCODERSTATE *)encoder->state.context)->comment = comment; + ((JPEGENCODERSTATE *)encoder->state.context)->comment_size = comment_size; ((JPEGENCODERSTATE *)encoder->state.context)->extra = extra; ((JPEGENCODERSTATE *)encoder->state.context)->extra_size = extra_size; ((JPEGENCODERSTATE *)encoder->state.context)->rawExif = rawExif; diff --git a/src/libImaging/Jpeg.h b/src/libImaging/Jpeg.h index a876d3bb6..1d7550818 100644 --- a/src/libImaging/Jpeg.h +++ b/src/libImaging/Jpeg.h @@ -92,6 +92,10 @@ typedef struct { /* in factors of DCTSIZE2 */ int qtablesLen; + /* Comment */ + char *comment; + size_t comment_size; + /* Extra data (to be injected after header) */ char *extra; int extra_size; diff --git a/src/libImaging/JpegEncode.c b/src/libImaging/JpegEncode.c index a44debcaf..b6e3acc95 100644 --- a/src/libImaging/JpegEncode.c +++ b/src/libImaging/JpegEncode.c @@ -277,6 +277,13 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) { } case 4: + + if (context->comment_size > 0) { + jpeg_write_marker(&context->cinfo, JPEG_COM, (unsigned char *)context->comment, context->comment_size); + } + state->state++; + + case 5: if (1024 > context->destination.pub.free_in_buffer) { break; } @@ -301,7 +308,7 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) { state->state++; /* fall through */ - case 5: + case 6: /* Finish compression */ if (context->destination.pub.free_in_buffer < 100) { @@ -310,6 +317,10 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) { jpeg_finish_compress(&context->cinfo); /* Clean up */ + if (context->comment) { + free(context->comment); + context->comment = NULL; + } if (context->extra) { free(context->extra); context->extra = NULL; From 72ac7d1ce9e15803e4adb759dbd318b75d652724 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 5 Dec 2022 18:53:28 +1100 Subject: [PATCH 210/239] Corrected default combined frame duration --- Tests/images/duplicate_frame.gif | Bin 0 -> 138 bytes Tests/test_file_gif.py | 16 ++++++++++++++++ src/PIL/GifImagePlugin.py | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 Tests/images/duplicate_frame.gif diff --git a/Tests/images/duplicate_frame.gif b/Tests/images/duplicate_frame.gif new file mode 100644 index 0000000000000000000000000000000000000000..ef0c894a540b0ca3074938666fd22a7d93d1fd0d GIT binary patch literal 138 zcmZ?wbhEHb Date: Mon, 5 Dec 2022 17:46:54 +0000 Subject: [PATCH 211/239] switch to #z for comment parameter * means `comment=None` can be passed directly * no need to conditionally run `str.encode()` * clean up checking of whether a comment is passed --- src/PIL/JpegImagePlugin.py | 4 +--- src/encode.c | 2 +- src/libImaging/JpegEncode.c | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 7b5b32be0..ef0be6699 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -736,9 +736,7 @@ def _save(im, fp, filename): ) i += 1 - comment = info.get("comment", im.info.get("comment")) or b"" - if isinstance(comment, str): - comment = comment.encode() + comment = info.get("comment", im.info.get("comment")) # "progressive" is the official name, but older documentation # says "progression" diff --git a/src/encode.c b/src/encode.c index a2eae81fd..d37cbfbcf 100644 --- a/src/encode.c +++ b/src/encode.c @@ -1057,7 +1057,7 @@ PyImaging_JpegEncoderNew(PyObject *self, PyObject *args) { if (!PyArg_ParseTuple( args, - "ss|nnnnnnnnOy#y#y#", + "ss|nnnnnnnnOz#y#y#", &mode, &rawmode, &quality, diff --git a/src/libImaging/JpegEncode.c b/src/libImaging/JpegEncode.c index b6e3acc95..2a24eff39 100644 --- a/src/libImaging/JpegEncode.c +++ b/src/libImaging/JpegEncode.c @@ -278,7 +278,7 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) { case 4: - if (context->comment_size > 0) { + if (context->comment) { jpeg_write_marker(&context->cinfo, JPEG_COM, (unsigned char *)context->comment, context->comment_size); } state->state++; From b786ff819a9799974e55db3d639b0de0b49d9b89 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 Dec 2022 18:25:01 +0000 Subject: [PATCH 212/239] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/PyCQA/flake8: 5.0.4 → 6.0.0](https://github.com/PyCQA/flake8/compare/5.0.4...6.0.0) - [github.com/pre-commit/pre-commit-hooks: v4.3.0 → v4.4.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.3.0...v4.4.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2c13fb3b1..d44874bf7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,7 +25,7 @@ repos: exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.opt$) - repo: https://github.com/PyCQA/flake8 - rev: 5.0.4 + rev: 6.0.0 hooks: - id: flake8 additional_dependencies: [flake8-2020, flake8-implicit-str-concat] @@ -37,7 +37,7 @@ repos: - id: rst-backticks - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v4.4.0 hooks: - id: check-merge-conflict - id: check-json From c2a42655e10c7b3888f3c50b49717886296e1720 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 6 Dec 2022 19:30:53 +1100 Subject: [PATCH 213/239] Allow get_child_images to access JPEG thumbnails --- Tests/images/flower_thumbnail.png | Bin 0 -> 35617 bytes Tests/test_file_jpeg.py | 7 +++++ src/PIL/Image.py | 43 ++++++++++++++++++++++++++++++ src/PIL/JpegImagePlugin.py | 1 + src/PIL/TiffImagePlugin.py | 33 ----------------------- 5 files changed, 51 insertions(+), 33 deletions(-) create mode 100644 Tests/images/flower_thumbnail.png diff --git a/Tests/images/flower_thumbnail.png b/Tests/images/flower_thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..4a362535f25fcdf10c49c368f4252f48f8d45bd0 GIT binary patch literal 35617 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 RR_6c!002ovPDHLkV1fuV02lxO literal 0 HcmV?d00001 diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index fa96e425b..987187556 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -415,6 +415,13 @@ class TestFileJpeg: info = im._getexif() assert info[305] == "Adobe Photoshop CS Macintosh" + def test_get_child_images(self): + with Image.open("Tests/images/flower.jpg") as im: + ims = im.get_child_images() + + assert len(ims) == 1 + assert_image_equal_tofile(ims[0], "Tests/images/flower_thumbnail.png") + def test_mp(self): with Image.open("Tests/images/pil_sample_rgb.jpg") as im: assert im._getmp() is None diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 1f3d4b74f..e568e6afa 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1454,6 +1454,49 @@ class Image: self._exif._loaded = False self.getexif() + def get_child_images(self): + child_images = [] + exif = self.getexif() + ifds = [] + if ExifTags.Base.SubIFDs in exif: + subifd_offsets = exif[ExifTags.Base.SubIFDs] + if subifd_offsets: + if not isinstance(subifd_offsets, tuple): + subifd_offsets = (subifd_offsets,) + for subifd_offset in subifd_offsets: + ifds.append((exif._get_ifd_dict(subifd_offset), subifd_offset)) + ifd1 = exif.get_ifd(ExifTags.IFD.IFD1) + if ifd1 and ifd1.get(513): + ifds.append((ifd1, exif._info.next)) + + offset = None + for ifd, ifd_offset in ifds: + current_offset = self.fp.tell() + if offset is None: + offset = current_offset + + fp = self.fp + thumbnailOffset = ifd.get(513) + if thumbnailOffset is not None: + try: + thumbnailOffset += self._exif_offset + except AttributeError: + pass + self.fp.seek(thumbnailOffset) + data = self.fp.read(ifd.get(514)) + fp = io.BytesIO(data) + + with open(fp) as im: + if thumbnailOffset is None: + im._frame_pos = [ifd_offset] + im._seek(0) + im.load() + child_images.append(im) + + if offset is not None: + self.fp.seek(offset) + return child_images + def getim(self): """ Returns a capsule that points to the internal image memory. diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index a6ed223bc..f2d8c4846 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -89,6 +89,7 @@ def APP(self, marker): if "exif" not in self.info: # extract EXIF information (incomplete) self.info["exif"] = s # FIXME: value will change + self._exif_offset = self.fp.tell() - n + 6 elif marker == 0xFFE2 and s[:5] == b"FPXR\0": # extract FlashPix information (incomplete) self.info["flashpix"] = s # FIXME: value will change diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index ab9ac5ea2..aa2a782c2 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1153,39 +1153,6 @@ class TiffImageFile(ImageFile.ImageFile): """Return the current frame number""" return self.__frame - def get_child_images(self): - if SUBIFD not in self.tag_v2: - return [] - child_images = [] - exif = self.getexif() - offset = None - for im_offset in self.tag_v2[SUBIFD]: - # reset buffered io handle in case fp - # was passed to libtiff, invalidating the buffer - current_offset = self._fp.tell() - if offset is None: - offset = current_offset - - fp = self._fp - ifd = exif._get_ifd_dict(im_offset) - jpegInterchangeFormat = ifd.get(513) - if jpegInterchangeFormat is not None: - fp.seek(jpegInterchangeFormat) - jpeg_data = fp.read(ifd.get(514)) - - fp = io.BytesIO(jpeg_data) - - with Image.open(fp) as im: - if jpegInterchangeFormat is None: - im._frame_pos = [im_offset] - im._seek(0) - im.load() - child_images.append(im) - - if offset is not None: - self._fp.seek(offset) - return child_images - def getxmp(self): """ Returns a dictionary containing the XMP tags. From 1d780081a620b00007a8fe93db469e5759c86868 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 6 Dec 2022 20:22:12 +1100 Subject: [PATCH 214/239] Free comment when returning early --- src/encode.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/encode.c b/src/encode.c index d37cbfbcf..e6352cbfe 100644 --- a/src/encode.c +++ b/src/encode.c @@ -1113,6 +1113,9 @@ PyImaging_JpegEncoderNew(PyObject *self, PyObject *args) { /* malloc check ok, length is from python parsearg */ char *p = malloc(extra_size); // Freed in JpegEncode, Case 6 if (!p) { + if (comment) { + free(comment); + } return ImagingError_MemoryError(); } memcpy(p, extra, extra_size); @@ -1125,6 +1128,9 @@ PyImaging_JpegEncoderNew(PyObject *self, PyObject *args) { /* malloc check ok, length is from python parsearg */ char *pp = malloc(rawExifLen); // Freed in JpegEncode, Case 6 if (!pp) { + if (comment) { + free(comment); + } if (extra) { free(extra); } From 674ec6ec4dd1083b4666e283459beeba0e422fb4 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 6 Dec 2022 20:55:34 +0200 Subject: [PATCH 215/239] Add support for PyPy3.9, drop PyPy3.7 --- .github/workflows/test-windows.yml | 4 ++-- .github/workflows/test.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index e2a9de65c..487c3586f 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -19,9 +19,9 @@ jobs: architecture: ["x86", "x64"] include: # PyPy 7.3.4+ only ships 64-bit binaries for Windows - - python-version: "pypy-3.7" + - python-version: "pypy3.8" architecture: "x64" - - python-version: "pypy-3.8" + - python-version: "pypy3.9" architecture: "x64" timeout-minutes: 30 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 831e33c13..11c7b77be 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,8 +20,8 @@ jobs: "ubuntu-latest", ] python-version: [ - "pypy-3.8", - "pypy-3.7", + "pypy3.9", + "pypy3.8", "3.11", "3.10", "3.9", From 4704cab1a1b4dfc34b0bc0c06bdcdc56b365b69f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 7 Dec 2022 22:16:14 +1100 Subject: [PATCH 216/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 7fac5201c..f3ad8c797 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.4.0 (unreleased) ------------------ +- Support saving JPEG comments #6774 + [smason, radarhere] + - Added getxmp() to WebPImagePlugin #6758 [radarhere] From bef128b04bcc220aa6b57afa58b796f7b289ddf7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 7 Dec 2022 22:30:15 +1100 Subject: [PATCH 217/239] Added support for saving JPEG comments --- docs/handbook/image-file-formats.rst | 5 +++++ docs/releasenotes/9.4.0.rst | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index ac39625a2..c9e32835a 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -474,6 +474,11 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: .. versionadded:: 2.5.0 +**comment** + A comment about the image. + + .. versionadded:: 9.4.0 + .. note:: diff --git a/docs/releasenotes/9.4.0.rst b/docs/releasenotes/9.4.0.rst index f2b50fa5b..ccbe62a6b 100644 --- a/docs/releasenotes/9.4.0.rst +++ b/docs/releasenotes/9.4.0.rst @@ -51,6 +51,14 @@ getxmp() `XMP data `_ can now be decoded for WEBP images through ``getxmp()``. +Writing JPEG comments +^^^^^^^^^^^^^^^^^^^^^ + +When saving a JPEG image, a comment can now be written from +:py:attr:`~PIL.Image.Image.info`, or by using an argument when saving:: + + im.save(out, comment="Test comment") + Security ======== From 7436ae0933ea4897111d2aebfb16b59a5c960a35 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 8 Dec 2022 16:58:04 +0200 Subject: [PATCH 218/239] Remove unnecessary Pipfile --- MANIFEST.in | 2 - Pipfile | 22 ---- Pipfile.lock | 324 --------------------------------------------------- 3 files changed, 348 deletions(-) delete mode 100644 Pipfile delete mode 100644 Pipfile.lock diff --git a/MANIFEST.in b/MANIFEST.in index 08f6dfc08..f51551303 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,7 +1,6 @@ include *.c include *.h include *.in -include *.lock include *.md include *.py include *.rst @@ -10,7 +9,6 @@ include *.txt include *.yaml include LICENSE include Makefile -include Pipfile include tox.ini graft Tests graft src diff --git a/Pipfile b/Pipfile deleted file mode 100644 index 1e611a63c..000000000 --- a/Pipfile +++ /dev/null @@ -1,22 +0,0 @@ -[[source]] -url = "https://pypi.org/simple" -verify_ssl = true -name = "pypi" - -[packages] -black = "*" -check-manifest = "*" -coverage = "*" -defusedxml = "*" -packaging = "*" -markdown2 = "*" -olefile = "*" -pyroma = "*" -pytest = "*" -pytest-cov = "*" -pytest-timeout = "*" - -[dev-packages] - -[requires] -python_version = "3.9" diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index 600b19050..000000000 --- a/Pipfile.lock +++ /dev/null @@ -1,324 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "e5cad23bf4187647d53b613a64dc4792b7064bf86b08dfb5737580e32943f54d" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.9" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "attrs": { - "hashes": [ - "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1", - "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==21.2.0" - }, - "black": { - "hashes": [ - "sha256:77b80f693a569e2e527958459634f18df9b0ba2625ba4e0c2d5da5be42e6f2b3", - "sha256:a615e69ae185e08fdd73e4715e260e2479c861b5740057fde6e8b4e3b7dd589f" - ], - "index": "pypi", - "version": "==21.12b0" - }, - "build": { - "hashes": [ - "sha256:1aaadcd69338252ade4f7ec1265e1a19184bf916d84c9b7df095f423948cb89f", - "sha256:21b7ebbd1b22499c4dac536abc7606696ea4d909fd755e00f09f3c0f2c05e3c8" - ], - "markers": "python_version >= '3.6'", - "version": "==0.7.0" - }, - "certifi": { - "hashes": [ - "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872", - "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569" - ], - "version": "==2021.10.8" - }, - "charset-normalizer": { - "hashes": [ - "sha256:1eecaa09422db5be9e29d7fc65664e6c33bd06f9ced7838578ba40d58bdf3721", - "sha256:b0b883e8e874edfdece9c28f314e3dd5badf067342e42fb162203335ae61aa2c" - ], - "markers": "python_version >= '3'", - "version": "==2.0.9" - }, - "check-manifest": { - "hashes": [ - "sha256:365c94d65de4c927d9d8b505371d08ee19f9f369c86b9ac3db97c2754c827c95", - "sha256:56dadd260a9c7d550b159796d2894b6d0bcc176a94cbc426d9bb93e5e48d12ce" - ], - "index": "pypi", - "version": "==0.47" - }, - "click": { - "hashes": [ - "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3", - "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b" - ], - "markers": "python_version >= '3.6'", - "version": "==8.0.3" - }, - "coverage": { - "hashes": [ - "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0", - "sha256:03b20e52b7d31be571c9c06b74746746d4eb82fc260e594dc662ed48145e9efd", - "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884", - "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48", - "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76", - "sha256:174cf9b4bef0db2e8244f82059a5a72bd47e1d40e71c68ab055425172b16b7d0", - "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64", - "sha256:215f8afcc02a24c2d9a10d3790b21054b58d71f4b3c6f055d4bb1b15cecce685", - "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47", - "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d", - "sha256:276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840", - "sha256:3f7c17209eef285c86f819ff04a6d4cbee9b33ef05cbcaae4c0b4e8e06b3ec8f", - "sha256:3feac4084291642165c3a0d9eaebedf19ffa505016c4d3db15bfe235718d4971", - "sha256:49dbff64961bc9bdd2289a2bda6a3a5a331964ba5497f694e2cbd540d656dc1c", - "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a", - "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de", - "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17", - "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4", - "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521", - "sha256:63c424e6f5b4ab1cf1e23a43b12f542b0ec2e54f99ec9f11b75382152981df57", - "sha256:6dbc1536e105adda7a6312c778f15aaabe583b0e9a0b0a324990334fd458c94b", - "sha256:6e1394d24d5938e561fbeaa0cd3d356207579c28bd1792f25a068743f2d5b282", - "sha256:86f2e78b1eff847609b1ca8050c9e1fa3bd44ce755b2ec30e70f2d3ba3844644", - "sha256:8bdfe9ff3a4ea37d17f172ac0dff1e1c383aec17a636b9b35906babc9f0f5475", - "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d", - "sha256:92b8c845527eae547a2a6617d336adc56394050c3ed8a6918683646328fbb6da", - "sha256:9365ed5cce5d0cf2c10afc6add145c5037d3148585b8ae0e77cc1efdd6aa2953", - "sha256:9a29311bd6429be317c1f3fe4bc06c4c5ee45e2fa61b2a19d4d1d6111cb94af2", - "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e", - "sha256:a4bdeb0a52d1d04123b41d90a4390b096f3ef38eee35e11f0b22c2d031222c6c", - "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc", - "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64", - "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74", - "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617", - "sha256:c332d8f8d448ded473b97fefe4a0983265af21917d8b0cdcb8bb06b2afe632c3", - "sha256:c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d", - "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa", - "sha256:d5d2033d5db1d58ae2d62f095e1aefb6988af65b4b12cb8987af409587cc0739", - "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8", - "sha256:e2cad8093172b7d1595b4ad66f24270808658e11acf43a8f95b41276162eb5b8", - "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781", - "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58", - "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9", - "sha256:f506af4f27def639ba45789fa6fde45f9a217da0be05f8910458e4557eed020c", - "sha256:f614fc9956d76d8a88a88bb41ddc12709caa755666f580af3a688899721efecd", - "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e", - "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49" - ], - "index": "pypi", - "version": "==6.2" - }, - "defusedxml": { - "hashes": [ - "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", - "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61" - ], - "index": "pypi", - "version": "==0.7.1" - }, - "docutils": { - "hashes": [ - "sha256:23010f129180089fbcd3bc08cfefccb3b890b0050e1ca00c867036e9d161b98c", - "sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==0.18.1" - }, - "idna": { - "hashes": [ - "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff", - "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d" - ], - "markers": "python_version >= '3'", - "version": "==3.3" - }, - "iniconfig": { - "hashes": [ - "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3", - "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32" - ], - "version": "==1.1.1" - }, - "markdown2": { - "hashes": [ - "sha256:8f4ac8d9a124ab408c67361090ed512deda746c04362c36c2ec16190c720c2b0", - "sha256:91113caf23aa662570fe21984f08fe74f814695c0a0ea8e863a8b4c4f63f9f6e" - ], - "index": "pypi", - "version": "==2.4.2" - }, - "mypy-extensions": { - "hashes": [ - "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", - "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" - ], - "version": "==0.4.3" - }, - "olefile": { - "hashes": [ - "sha256:133b031eaf8fd2c9399b78b8bc5b8fcbe4c31e85295749bb17a87cba8f3c3964" - ], - "index": "pypi", - "version": "==0.46" - }, - "packaging": { - "hashes": [ - "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", - "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" - ], - "index": "pypi", - "version": "==21.3" - }, - "pathspec": { - "hashes": [ - "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a", - "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1" - ], - "version": "==0.9.0" - }, - "pep517": { - "hashes": [ - "sha256:931378d93d11b298cf511dd634cf5ea4cb249a28ef84160b3247ee9afb4e8ab0", - "sha256:dd884c326898e2c6e11f9e0b64940606a93eb10ea022a2e067959f3a110cf161" - ], - "version": "==0.12.0" - }, - "platformdirs": { - "hashes": [ - "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2", - "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d" - ], - "markers": "python_version >= '3.6'", - "version": "==2.4.0" - }, - "pluggy": { - "hashes": [ - "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", - "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3" - ], - "markers": "python_version >= '3.6'", - "version": "==1.0.0" - }, - "py": { - "hashes": [ - "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719", - "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==1.11.0" - }, - "pygments": { - "hashes": [ - "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380", - "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6" - ], - "markers": "python_version >= '3.5'", - "version": "==2.10.0" - }, - "pyparsing": { - "hashes": [ - "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4", - "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81" - ], - "markers": "python_version >= '3.6'", - "version": "==3.0.6" - }, - "pyroma": { - "hashes": [ - "sha256:0fba67322913026091590e68e0d9e0d4fbd6420fcf34d315b2ad6985ab104d65", - "sha256:f8c181e0d5d292f11791afc18f7d0218a83c85cf64d6f8fb1571ce9d29a24e4a" - ], - "index": "pypi", - "version": "==3.2" - }, - "pytest": { - "hashes": [ - "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89", - "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134" - ], - "index": "pypi", - "version": "==6.2.5" - }, - "pytest-cov": { - "hashes": [ - "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6", - "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470" - ], - "index": "pypi", - "version": "==3.0.0" - }, - "pytest-timeout": { - "hashes": [ - "sha256:e6f98b54dafde8d70e4088467ff621260b641eb64895c4195b6e5c8f45638112", - "sha256:fe9c3d5006c053bb9e062d60f641e6a76d6707aedb645350af9593e376fcc717" - ], - "index": "pypi", - "version": "==2.0.2" - }, - "requests": { - "hashes": [ - "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24", - "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==2.26.0" - }, - "setuptools": { - "hashes": [ - "sha256:5ec2bbb534ed160b261acbbdd1b463eb3cf52a8d223d96a8ab9981f63798e85c", - "sha256:75fd345a47ce3d79595b27bf57e6f49c2ca7904f3c7ce75f8a87012046c86b0b" - ], - "markers": "python_version >= '3.7'", - "version": "==60.0.0" - }, - "toml": { - "hashes": [ - "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", - "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" - ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'", - "version": "==0.10.2" - }, - "tomli": { - "hashes": [ - "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f", - "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c" - ], - "markers": "python_version >= '3.6'", - "version": "==1.2.3" - }, - "typing-extensions": { - "hashes": [ - "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e", - "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b" - ], - "markers": "python_version >= '3.6'", - "version": "==4.0.1" - }, - "urllib3": { - "hashes": [ - "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece", - "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", - "version": "==1.26.7" - } - }, - "develop": {} -} From 66f5ad0eae90b6f4b07df1a3154f996c6fe00069 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 9 Dec 2022 10:45:09 +1100 Subject: [PATCH 219/239] Ignore non-opaque WebP background when saving as GIF --- Tests/test_file_gif.py | 13 +++++++++++-- src/PIL/GifImagePlugin.py | 15 +++++++++------ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 926f5c1ee..a196c1612 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -859,14 +859,23 @@ def test_background(tmp_path): im.info["background"] = 1 im.save(out) with Image.open(out) as reread: - assert reread.info["background"] == im.info["background"] + +def test_webp_background(tmp_path): + out = str(tmp_path / "temp.gif") + + # Test opaque WebP background if features.check("webp") and features.check("webp_anim"): with Image.open("Tests/images/hopper.webp") as im: - assert isinstance(im.info["background"], tuple) + assert im.info["background"] == (255, 255, 255, 255) im.save(out) + # Test non-opaque WebP background + im = Image.new("L", (100, 100), "#000") + im.info["background"] = (0, 0, 0, 0) + im.save(out) + def test_comment(tmp_path): with Image.open(TEST_GIF) as im: diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index dd1b21f2e..01518b378 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -886,20 +886,23 @@ def _get_palette_bytes(im): def _get_background(im, info_background): background = 0 if info_background: - background = info_background - if isinstance(background, tuple): + if isinstance(info_background, tuple): # WebPImagePlugin stores an RGBA value in info["background"] # So it must be converted to the same format as GifImagePlugin's # info["background"] - a global color table index try: - background = im.palette.getcolor(background, im) + background = im.palette.getcolor(info_background, im) except ValueError as e: - if str(e) == "cannot allocate more than 256 colors": + if str(e) not in ( # If all 256 colors are in use, # then there is no need for the background color - return 0 - else: + "cannot allocate more than 256 colors", + # Ignore non-opaque WebP background + "cannot add non-opaque RGBA color to RGB palette", + ): raise + else: + background = info_background return background From 4f0b83cc54230728bbd3593a3116cac046c5ee4d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 9 Dec 2022 12:29:27 +1100 Subject: [PATCH 220/239] Only set tile in ImageFile __setstate__ --- src/PIL/Image.py | 1 - src/PIL/ImageFile.py | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 7faf0c248..bf93917ed 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -704,7 +704,6 @@ class Image: def __setstate__(self, state): Image.__init__(self) - self.tile = [] info, mode, size, palette, data = state self.info = info self.mode = mode diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index f281b9e14..dbdc0cb38 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -137,6 +137,10 @@ class ImageFile(Image.Image): if self.format is not None: return Image.MIME.get(self.format.upper()) + def __setstate__(self, state): + self.tile = [] + super().__setstate__(state) + def verify(self): """Check file integrity""" From ae3f43de64afbd59fdc424f37f18964dd25765e1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 10 Dec 2022 19:48:07 +1100 Subject: [PATCH 221/239] Document Hue range --- docs/handbook/concepts.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/handbook/concepts.rst b/docs/handbook/concepts.rst index a9b33e437..083351eec 100644 --- a/docs/handbook/concepts.rst +++ b/docs/handbook/concepts.rst @@ -41,6 +41,9 @@ supports the following standard modes: * ``LAB`` (3x8-bit pixels, the L*a*b color space) * ``HSV`` (3x8-bit pixels, Hue, Saturation, Value color space) + + * Hue's range of 0-255 is a scaled version of 0 degrees <= Hue < 360 degrees + * ``I`` (32-bit signed integer pixels) * ``F`` (32-bit floating point pixels) From 164311a7568c7fed3c7a1dd60570cc182d3d5a0c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 12 Dec 2022 06:55:10 +1100 Subject: [PATCH 222/239] Specify "I" and "F" ranges --- docs/handbook/concepts.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/handbook/concepts.rst b/docs/handbook/concepts.rst index 083351eec..f3fa1f2b1 100644 --- a/docs/handbook/concepts.rst +++ b/docs/handbook/concepts.rst @@ -24,9 +24,10 @@ To get the number and names of bands in an image, use the Modes ----- -The ``mode`` of an image is a string which defines the type and depth of a pixel in the image. -Each pixel uses the full range of the bit depth. So a 1-bit pixel has a range -of 0-1, an 8-bit pixel has a range of 0-255 and so on. The current release +The ``mode`` of an image is a string which defines the type and depth of a pixel in the +image. Each pixel uses the full range of the bit depth. So a 1-bit pixel has a range of +0-1, an 8-bit pixel has a range of 0-255, a 32-signed integer pixel has the range of +INT32 and a 32-bit floating point pixel has the range of FLOAT32. The current release supports the following standard modes: * ``1`` (1-bit pixels, black and white, stored with one pixel per byte) From 1f9754cdc0c03405bbe1e3aa73b5dbb6750aa608 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 13 Dec 2022 18:04:30 +0200 Subject: [PATCH 223/239] Format tox.ini with tox-ini-fmt --- .pre-commit-config.yaml | 9 +++++++-- tox.ini | 24 ++++++++++-------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d44874bf7..8d133b18d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/psf/black - rev: 22.10.0 + rev: 22.12.0 hooks: - id: black args: ["--target-version", "py37"] @@ -9,7 +9,7 @@ repos: types: [] - repo: https://github.com/PyCQA/isort - rev: 5.10.1 + rev: 5.11.1 hooks: - id: isort @@ -48,5 +48,10 @@ repos: hooks: - id: sphinx-lint + - repo: https://github.com/tox-dev/tox-ini-fmt + rev: 0.5.2 + hooks: + - id: tox-ini-fmt + ci: autoupdate_schedule: monthly diff --git a/tox.ini b/tox.ini index 21b5d4b50..195522ffa 100644 --- a/tox.ini +++ b/tox.ini @@ -1,15 +1,13 @@ -# Tox (https://tox.readthedocs.io/en/latest/) is a tool for running tests -# in multiple virtualenvs. This configuration file will run the -# test suite on all supported python versions. To use it, -# "python3 -m pip install tox" and then run "tox" from this directory. - [tox] envlist = lint - py{37,38,39,310,311,py3} + py{py3, 311, 310, 39, 38, 37} minversion = 1.9 [testenv] +deps = + cffi + numpy extras = tests commands = @@ -17,16 +15,14 @@ commands = {envpython} -m pip install --global-option="build_ext" --global-option="--inplace" . {envpython} selftest.py {envpython} -m pytest -W always {posargs} -deps = - cffi - numpy [testenv:lint] +passenv = + PRE_COMMIT_COLOR +skip_install = true +deps = + check-manifest + pre-commit commands = pre-commit run --all-files --show-diff-on-failure check-manifest -deps = - pre-commit - check-manifest -skip_install = true -passenv = PRE_COMMIT_COLOR From bfa1f3290c8ae830e0240dbfad2626fa6b49bb1b Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 13 Dec 2022 18:06:58 +0200 Subject: [PATCH 224/239] Add allowlist_externals=make to fix tox 4 --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index 195522ffa..9a41ca96b 100644 --- a/tox.ini +++ b/tox.ini @@ -15,6 +15,7 @@ commands = {envpython} -m pip install --global-option="build_ext" --global-option="--inplace" . {envpython} selftest.py {envpython} -m pytest -W always {posargs} +allowlist_externals = make [testenv:lint] passenv = From 56964da7487c7fff897cd3b41f11d62922f84046 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 14 Dec 2022 06:45:57 +1100 Subject: [PATCH 225/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index f3ad8c797..1bcb9d2e9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.4.0 (unreleased) ------------------ +- Fixed bug combining GIF frame durations #6779 + [radarhere] + - Support saving JPEG comments #6774 [smason, radarhere] From 5301b86f1cd255fc55a464b38af176f37f91c396 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 14 Dec 2022 06:48:36 +1100 Subject: [PATCH 226/239] Use snake case --- src/PIL/Image.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index e568e6afa..c2216e27a 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1476,18 +1476,18 @@ class Image: offset = current_offset fp = self.fp - thumbnailOffset = ifd.get(513) - if thumbnailOffset is not None: + thumbnail_offset = ifd.get(513) + if thumbnail_offset is not None: try: - thumbnailOffset += self._exif_offset + thumbnail_offset += self._exif_offset except AttributeError: pass - self.fp.seek(thumbnailOffset) + self.fp.seek(thumbnail_offset) data = self.fp.read(ifd.get(514)) fp = io.BytesIO(data) with open(fp) as im: - if thumbnailOffset is None: + if thumbnail_offset is None: im._frame_pos = [ifd_offset] im._seek(0) im.load() From b564f3e6bf82bb705ae410b44422aefbc56198e2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 14 Dec 2022 07:41:39 +1100 Subject: [PATCH 227/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 1bcb9d2e9..0372b5b37 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.4.0 (unreleased) ------------------ +- Added IFD enum to ExifTags #6748 + [radarhere] + - Fixed bug combining GIF frame durations #6779 [radarhere] From e25d6031891cd53917379bb489d7b22614fe06fb Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 14 Dec 2022 09:48:46 +1100 Subject: [PATCH 228/239] Updated xz to 5.4.0 --- winbuild/build_prepare.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 66e352c73..0c3152b06 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -152,9 +152,9 @@ deps = { "libs": [r"*.lib"], }, "xz": { - "url": SF_PROJECTS + "/lzmautils/files/xz-5.2.9.tar.gz/download", - "filename": "xz-5.2.9.tar.gz", - "dir": "xz-5.2.9", + "url": SF_PROJECTS + "/lzmautils/files/xz-5.4.0.tar.gz/download", + "filename": "xz-5.4.0.tar.gz", + "dir": "xz-5.4.0", "license": "COPYING", "patch": { r"src\liblzma\api\lzma.h": { From d1cb81976cba7fbd3b13525a26b163cc42f029a7 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 13 Dec 2022 18:32:55 +0200 Subject: [PATCH 229/239] Run Bandit on CI via pre-commit --- .pre-commit-config.yaml | 9 ++++++++- src/PIL/ImageShow.py | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8d133b18d..609352f22 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ repos: rev: 22.12.0 hooks: - id: black - args: ["--target-version", "py37"] + args: [--target-version=py37] # Only .py files, until https://github.com/psf/black/issues/402 resolved files: \.py$ types: [] @@ -13,6 +13,13 @@ repos: hooks: - id: isort + - repo: https://github.com/PyCQA/bandit + rev: 1.7.4 + hooks: + - id: bandit + args: [--severity-level=high] + files: ^src/ + - repo: https://github.com/asottile/yesqa rev: v1.4.0 hooks: diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index 76f42a307..9d5224588 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -125,7 +125,7 @@ class Viewer: path = options.pop("file") else: raise TypeError("Missing required argument: 'path'") - os.system(self.get_command(path, **options)) + os.system(self.get_command(path, **options)) # nosec return 1 From 1a051f2e079253c74918ac89cbe899f4c6136bc3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 16 Dec 2022 07:50:40 +0000 Subject: [PATCH 230/239] Update egor-tensin/cleanup-path action to v3 --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 37dc694c6..f297eb1b5 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -48,7 +48,7 @@ jobs: qt5-devel-tools subversion xorg-server-extra zlib-devel - name: Add Lapack to PATH - uses: egor-tensin/cleanup-path@v2 + uses: egor-tensin/cleanup-path@v3 with: dirs: 'C:\cygwin\bin;C:\cygwin\lib\lapack' From 7f6fe3c28728f0e68dba58b6ea9843de0b00ca3b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 17 Dec 2022 08:15:32 +1100 Subject: [PATCH 231/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 0372b5b37..1e5f71b86 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.4.0 (unreleased) ------------------ +- When reading BLP, do not trust JPEG decoder to determine image is CMYK #6767 + [radarhere] + - Added IFD enum to ExifTags #6748 [radarhere] From 5eaca52efd86e41dc068802fd2683d433a45003e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 19 Dec 2022 07:04:05 +1100 Subject: [PATCH 232/239] Updated harfbuzz to 6.0.0 --- winbuild/build_prepare.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 0c3152b06..a1908e35e 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -355,9 +355,9 @@ deps = { "libs": [r"imagequant.lib"], }, "harfbuzz": { - "url": "https://github.com/harfbuzz/harfbuzz/archive/5.3.1.zip", - "filename": "harfbuzz-5.3.1.zip", - "dir": "harfbuzz-5.3.1", + "url": "https://github.com/harfbuzz/harfbuzz/archive/6.0.0.zip", + "filename": "harfbuzz-6.0.0.zip", + "dir": "harfbuzz-6.0.0", "license": "COPYING", "build": [ cmd_set("CXXFLAGS", "-d2FH4-"), From 88e127d1b27f6dfbc6ddfe4ffba13748e89f4d11 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 20 Dec 2022 22:16:07 +0000 Subject: [PATCH 233/239] Update actions/stale action to v7 --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index ffac91cec..8c210bc90 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -20,7 +20,7 @@ jobs: steps: - name: "Check issues" - uses: actions/stale@v6 + uses: actions/stale@v7 with: repo-token: ${{ secrets.GITHUB_TOKEN }} only-labels: "Awaiting OP Action" From a065e0252b563b4d7a490ddd1a1eb7c8089662c8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 21 Dec 2022 09:29:25 +1100 Subject: [PATCH 234/239] Updated deprecated NumPy alias --- Tests/test_numpy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py index 185e477ec..3de7ec30f 100644 --- a/Tests/test_numpy.py +++ b/Tests/test_numpy.py @@ -34,7 +34,7 @@ def test_numpy_to_image(): # Check supported 1-bit integer formats assert_image(to_image(bool, 1, 1), "1", TEST_IMAGE_SIZE) - assert_image(to_image(numpy.bool8, 1, 1), "1", TEST_IMAGE_SIZE) + assert_image(to_image(numpy.bool_, 1, 1), "1", TEST_IMAGE_SIZE) # Check supported 8-bit integer formats assert_image(to_image(numpy.uint8), "L", TEST_IMAGE_SIZE) @@ -193,7 +193,7 @@ def test_putdata(): "dtype", ( bool, - numpy.bool8, + numpy.bool_, numpy.int8, numpy.int16, numpy.int32, From d6e79045280be42cf2273716b18d82661cf7f779 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 21 Dec 2022 12:47:46 +1100 Subject: [PATCH 235/239] Removed Python 3.7 on Cygwin --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index f297eb1b5..7b8070d34 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -15,7 +15,7 @@ jobs: strategy: fail-fast: false matrix: - python-minor-version: [7, 8, 9] + python-minor-version: [8, 9] timeout-minutes: 40 From 967034356a72d02e4cddad5ac4b6c75299d08394 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 21 Dec 2022 14:20:47 +1100 Subject: [PATCH 236/239] Fixed BytesWarning --- src/PIL/PpmImagePlugin.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index 392771d3e..1670d9d64 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -208,7 +208,9 @@ class PpmPlainDecoder(ImageFile.PyDecoder): tokens = b"".join(block.split()) for token in tokens: if token not in (48, 49): - raise ValueError(f"Invalid token for this mode: {bytes([token])}") + raise ValueError( + b"Invalid token for this mode: %s" % bytes([token]) + ) data = (data + tokens)[:total_bytes] invert = bytes.maketrans(b"01", b"\xFF\x00") return data.translate(invert) @@ -242,13 +244,13 @@ class PpmPlainDecoder(ImageFile.PyDecoder): half_token = tokens.pop() # save half token for later if len(half_token) > max_len: # prevent buildup of half_token raise ValueError( - f"Token too long found in data: {half_token[:max_len + 1]}" + b"Token too long found in data: %s" % half_token[: max_len + 1] ) for token in tokens: if len(token) > max_len: raise ValueError( - f"Token too long found in data: {token[:max_len + 1]}" + b"Token too long found in data: %s" % token[: max_len + 1] ) value = int(token) if value > maxval: From 1df7e75205247ae3ef021a623659d532bd5a4f15 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 22 Dec 2022 06:52:06 +1100 Subject: [PATCH 237/239] Python 3.7 on Cygwin is no longer part of CI --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index b559c824d..b188020b9 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -460,7 +460,7 @@ These platforms are built and tested for every change. | +----------------------------+---------------------+ | | 3.9 (MinGW) | x86, x86-64 | | +----------------------------+---------------------+ -| | 3.7, 3.8, 3.9 (Cygwin) | x86-64 | +| | 3.8, 3.9 (Cygwin) | x86-64 | +----------------------------------+----------------------------+---------------------+ From a4ac40354916401063028fc9af402e830eaf8606 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 22 Dec 2022 07:14:02 +1100 Subject: [PATCH 238/239] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 1e5f71b86..04b3fc4c6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog (Pillow) 9.4.0 (unreleased) ------------------ +- Ignore non-opaque WebP background when saving as GIF #6792 + [radarhere] + +- Only set tile in ImageFile __setstate__ #6793 + [radarhere] + - When reading BLP, do not trust JPEG decoder to determine image is CMYK #6767 [radarhere] From 318b658c9cf5fb004f27de8090ffd30ff3e025ff Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 22 Dec 2022 17:16:52 +1100 Subject: [PATCH 239/239] Initialize unsigned char variables --- src/libImaging/Quant.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libImaging/Quant.c b/src/libImaging/Quant.c index dfa6d842d..783852c24 100644 --- a/src/libImaging/Quant.c +++ b/src/libImaging/Quant.c @@ -1717,7 +1717,7 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) { withAlpha = !strcmp(im->mode, "RGBA"); int transparency = 0; - unsigned char r, g, b; + unsigned char r = 0, g = 0, b = 0; for (i = y = 0; y < im->ysize; y++) { for (x = 0; x < im->xsize; x++, i++) { p[i].v = im->image32[y][x];