From 1c7f68c1c17102ac8a39eec324bd32daef96a6f6 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 18 Mar 2014 20:59:42 -0700 Subject: [PATCH 0001/1037] depends --inprogress see #553 --- winbuild/build_dep.cmd | 165 ++++++++++++++++++++++++++++++++++++++++ winbuild/build_dep.cmd~ | 165 ++++++++++++++++++++++++++++++++++++++++ winbuild/fetch.py | 7 ++ winbuild/fixproj.py | 7 ++ winbuild/untar.py | 4 + winbuild/unzip.py | 3 + 6 files changed, 351 insertions(+) create mode 100644 winbuild/build_dep.cmd create mode 100644 winbuild/build_dep.cmd~ create mode 100644 winbuild/fetch.py create mode 100644 winbuild/fixproj.py create mode 100644 winbuild/untar.py create mode 100644 winbuild/unzip.py diff --git a/winbuild/build_dep.cmd b/winbuild/build_dep.cmd new file mode 100644 index 000000000..840931327 --- /dev/null +++ b/winbuild/build_dep.cmd @@ -0,0 +1,165 @@ +@echo off +rem Build Matplotlib Dependencies + +setlocal +set MSBUILD=C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe +set CMAKE="C:\Program Files (x86)\CMake 2.8\bin\cmake.exe" +set INCLIB=%~dp0\depends +set BUILD=%~dp0\build + +rd /S /Q %INCLIB% +rd /S /Q %BUILD% + +mkdir %INCLIB% +mkdir %BUILD% + +rem Get freetype +py -3 fetch.py http://download.savannah.gnu.org/releases/freetype/ft253.zip +py -3 unzip.py ft253.zip %BUILD% +set FREETYPE=%BUILD%\freetype-2.5.3 +copy /Y /B ft253.zip %INCLIB% + +rem Get zlib +py -3 fetch.py http://zlib.net/zlib128.zip +py -3 unzip.py zlib128.zip %BUILD% +set ZLIB=%BUILD%\zlib-1.2.8 +copy /Y /B zlib128.zip %INCLIB% + +rem Get tcl/tk +py -3 fetch.py http://hivelocity.dl.sourceforge.net/project/tcl/Tcl/8.5.13/tcl8513-src.zip +py -3 unzip.py tcl8513-src.zip %BUILD% +copy /Y /B tcl8513-src.zip %INCLIB% +py -3 fetch.py http://hivelocity.dl.sourceforge.net/project/tcl/Tcl/8.5.13/tk8513-src.zip +py -3 unzip.py tk8513-src.zip %BUILD% +copy /Y /B tk8513-src.zip %INCLIB% + +mkdir %INCLIB%\tcl85\include\X11 +copy /Y /B %BUILD%\tcl8.5.13\generic\*.h %INCLIB%\tcl85\include\ +copy /Y /B %BUILD%\tk8.5.13\generic\*.h %INCLIB%\tcl85\include\ +copy /Y /B %BUILD%\tk8.5.13\xlib\X11\* %INCLIB%\tcl85\include\X11\ + +rem Build for VC 2008 64 bit +setlocal EnableDelayedExpansion +call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x64 /vista +set INCLIB=%INCLIB%\msvcr90-x64 +mkdir %INCLIB% + +rem Build zlib +setlocal +cd /D %ZLIB% +nmake -f win32\Makefile.msc clean +nmake -f win32\Makefile.msc +copy /Y /B *.dll %INCLIB% +copy /Y /B *.lib %INCLIB% +copy /Y /B zlib.lib %INCLIB%\z.lib +copy /Y /B zlib.h %INCLIB% +copy /Y /B zconf.h %INCLIB% +endlocal + +rem Build freetype +setlocal +py -3 %~dp0\fixproj.py %FREETYPE%\builds\win32\vc2008\freetype.sln x64 +py -3 %~dp0\fixproj.py %FREETYPE%\builds\win32\vc2008\freetype.vcproj x64 +rd /S /Q %FREETYPE%\objs +%MSBUILD% %FREETYPE%\builds\win32\vc2008\freetype.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=x64 +xcopy /E /Q %FREETYPE%\include %INCLIB% +xcopy /E /Q %FREETYPE%\objs\win32\vc2008 %INCLIB% +copy /Y /B %FREETYPE%\objs\win32\vc2008\*.lib %INCLIB%\freetype.lib +endlocal + +endlocal + +rem Build for VC 2008 32 bit +setlocal EnableDelayedExpansion +call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x86 /xp +set INCLIB=%INCLIB%\msvcr90-x32 +mkdir %INCLIB% + +rem Build zlib +setlocal +cd /D %ZLIB% +nmake -f win32\Makefile.msc clean +nmake -f win32\Makefile.msc +copy /Y /B *.dll %INCLIB% +copy /Y /B *.lib %INCLIB% +copy /Y /B zlib.lib %INCLIB%\z.lib +copy /Y /B zlib.h %INCLIB% +copy /Y /B zconf.h %INCLIB% +endlocal + +rem Build freetype +setlocal +%~dp0\fixproj.py %FREETYPE%\builds\win32\vc2008\freetype.sln Win32 +%~dp0\fixproj.py %FREETYPE%\builds\win32\vc2008\freetype.vcproj Win32 +rd /S /Q %FREETYPE%\objs +%MSBUILD% %FREETYPE%\builds\win32\vc2008\freetype.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=Win32 +xcopy /E /Q %FREETYPE%\include %INCLIB% +xcopy /E /Q %FREETYPE%\objs\win32\vc2008 %INCLIB% +copy /Y /B %FREETYPE%\objs\win32\vc2008\*.lib %INCLIB%\freetype.lib +endlocal + +endlocal + +rem Build for VC 2010 64 bit +setlocal EnableDelayedExpansion +call "%ProgramFiles%\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" /Release /x64 /vista +set INCLIB=%INCLIB%\msvcr100-x64 +mkdir %INCLIB% + +rem Build zlib +setlocal +cd /D %ZLIB% +nmake -f win32\Makefile.msc clean +nmake -f win32\Makefile.msc +copy /Y /B *.dll %INCLIB% +copy /Y /B *.lib %INCLIB% +copy /Y /B zlib.lib %INCLIB%\z.lib +copy /Y /B zlib.h %INCLIB% +copy /Y /B zconf.h %INCLIB% +endlocal + +rem Build freetype +setlocal +py -3 %~dp0\fixproj.py %FREETYPE%\builds\win32\vc2010\freetype.sln x64 +py -3 %~dp0\fixproj.py %FREETYPE%\builds\win32\vc2010\freetype.vcxproj x64 +rd /S /Q %FREETYPE%\objs +%MSBUILD% %FREETYPE%\builds\win32\vc2010\freetype.sln /t:Clean;Build /p:Configuration="Release";Platform=x64 +xcopy /E /Q %FREETYPE%\include %INCLIB% +xcopy /E /Q %FREETYPE%\objs\win32\vc2010 %INCLIB% +copy /Y /B %FREETYPE%\objs\win32\vc2010\*.lib %INCLIB%\freetype.lib +endlocal + +endlocal + +rem Build for VC 2010 32 bit +setlocal EnableDelayedExpansion +call "%ProgramFiles%\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" /Release /x86 /xp +set INCLIB=%INCLIB%\msvcr100-x32 +mkdir %INCLIB% + +rem Build zlib +setlocal +cd /D %ZLIB% +nmake -f win32\Makefile.msc clean +nmake -f win32\Makefile.msc +copy /Y /B *.dll %INCLIB% +copy /Y /B *.lib %INCLIB% +copy /Y /B zlib.lib %INCLIB%\z.lib +copy /Y /B zlib.h %INCLIB% +copy /Y /B zconf.h %INCLIB% +endlocal + +rem Build freetype +setlocal +%~dp0\fixproj.py %FREETYPE%\builds\win32\vc2010\freetype.sln Win32 +%~dp0\fixproj.py %FREETYPE%\builds\win32\vc2010\freetype.vcxproj Win32 +rd /S /Q %FREETYPE%\objs +%MSBUILD% %FREETYPE%\builds\win32\vc2010\freetype.sln /t:Clean;Build /p:Configuration="Release";Platform=Win32 +xcopy /E /Q %FREETYPE%\include %INCLIB% +xcopy /E /Q %FREETYPE%\objs\win32\vc2010 %INCLIB% +copy /Y /B %FREETYPE%\objs\win32\vc2010\*.lib %INCLIB%\freetype.lib +endlocal + +endlocal + +endlocal \ No newline at end of file diff --git a/winbuild/build_dep.cmd~ b/winbuild/build_dep.cmd~ new file mode 100644 index 000000000..0bb983972 --- /dev/null +++ b/winbuild/build_dep.cmd~ @@ -0,0 +1,165 @@ +@echo off +rem Build Matplotlib Dependencies + +setlocal +set MSBUILD=C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe +set CMAKE="C:\Program Files (x86)\CMake 2.8\bin\cmake.exe" +set INCLIB=%~dp0\pillow-build-dependencies +set BUILD=%~dp0\build + +rd /S /Q %INCLIB% +rd /S /Q %BUILD% + +mkdir %INCLIB% +mkdir %BUILD% + +rem Get freetype +py -3 fetch.py http://download.savannah.gnu.org/releases/freetype/ft253.zip +py -3 unzip.py ft253.zip %BUILD% +set FREETYPE=%BUILD%\freetype-2.5.3 +copy /Y /B ft253.zip %INCLIB% + +rem Get zlib +py -3 fetch.py http://zlib.net/zlib128.zip +py -3 unzip.py zlib128.zip %BUILD% +set ZLIB=%BUILD%\zlib-1.2.8 +copy /Y /B zlib128.zip %INCLIB% + +rem Get tcl/tk +py -3 fetch.py http://hivelocity.dl.sourceforge.net/project/tcl/Tcl/8.5.13/tcl8513-src.zip +py -3 unzip.py tcl8513-src.zip %BUILD% +copy /Y /B tcl8513-src.zip %INCLIB% +py -3 fetch.py http://hivelocity.dl.sourceforge.net/project/tcl/Tcl/8.5.13/tk8513-src.zip +py -3 unzip.py tk8513-src.zip %BUILD% +copy /Y /B tk8513-src.zip %INCLIB% + +mkdir %INCLIB%\tcl85\include\X11 +copy /Y /B %BUILD%\tcl8.5.13\generic\*.h %INCLIB%\tcl85\include\ +copy /Y /B %BUILD%\tk8.5.13\generic\*.h %INCLIB%\tcl85\include\ +copy /Y /B %BUILD%\tk8.5.13\xlib\X11\* %INCLIB%\tcl85\include\X11\ + +rem Build for VC 2008 64 bit +setlocal EnableDelayedExpansion +call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x64 /vista +set INCLIB=%INCLIB%\msvcr90-x64 +mkdir %INCLIB% + +rem Build zlib +setlocal +cd /D %ZLIB% +nmake -f win32\Makefile.msc clean +nmake -f win32\Makefile.msc +copy /Y /B *.dll %INCLIB% +copy /Y /B *.lib %INCLIB% +copy /Y /B zlib.lib %INCLIB%\z.lib +copy /Y /B zlib.h %INCLIB% +copy /Y /B zconf.h %INCLIB% +endlocal + +rem Build freetype +setlocal +py -3 %~dp0\fixproj.py %FREETYPE%\builds\win32\vc2008\freetype.sln x64 +py -3 %~dp0\fixproj.py %FREETYPE%\builds\win32\vc2008\freetype.vcproj x64 +rd /S /Q %FREETYPE%\objs +%MSBUILD% %FREETYPE%\builds\win32\vc2008\freetype.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=x64 +xcopy /E /Q %FREETYPE%\include %INCLIB% +xcopy /E /Q %FREETYPE%\objs\win32\vc2008 %INCLIB% +copy /Y /B %FREETYPE%\objs\win32\vc2008\*.lib %INCLIB%\freetype.lib +endlocal + +endlocal + +rem Build for VC 2008 32 bit +setlocal EnableDelayedExpansion +call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x86 /xp +set INCLIB=%INCLIB%\msvcr90-x32 +mkdir %INCLIB% + +rem Build zlib +setlocal +cd /D %ZLIB% +nmake -f win32\Makefile.msc clean +nmake -f win32\Makefile.msc +copy /Y /B *.dll %INCLIB% +copy /Y /B *.lib %INCLIB% +copy /Y /B zlib.lib %INCLIB%\z.lib +copy /Y /B zlib.h %INCLIB% +copy /Y /B zconf.h %INCLIB% +endlocal + +rem Build freetype +setlocal +%~dp0\fixproj.py %FREETYPE%\builds\win32\vc2008\freetype.sln Win32 +%~dp0\fixproj.py %FREETYPE%\builds\win32\vc2008\freetype.vcproj Win32 +rd /S /Q %FREETYPE%\objs +%MSBUILD% %FREETYPE%\builds\win32\vc2008\freetype.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=Win32 +xcopy /E /Q %FREETYPE%\include %INCLIB% +xcopy /E /Q %FREETYPE%\objs\win32\vc2008 %INCLIB% +copy /Y /B %FREETYPE%\objs\win32\vc2008\*.lib %INCLIB%\freetype.lib +endlocal + +endlocal + +rem Build for VC 2010 64 bit +setlocal EnableDelayedExpansion +call "%ProgramFiles%\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" /Release /x64 /vista +set INCLIB=%INCLIB%\msvcr100-x64 +mkdir %INCLIB% + +rem Build zlib +setlocal +cd /D %ZLIB% +nmake -f win32\Makefile.msc clean +nmake -f win32\Makefile.msc +copy /Y /B *.dll %INCLIB% +copy /Y /B *.lib %INCLIB% +copy /Y /B zlib.lib %INCLIB%\z.lib +copy /Y /B zlib.h %INCLIB% +copy /Y /B zconf.h %INCLIB% +endlocal + +rem Build freetype +setlocal +py -3 %~dp0\fixproj.py %FREETYPE%\builds\win32\vc2010\freetype.sln x64 +py -3 %~dp0\fixproj.py %FREETYPE%\builds\win32\vc2010\freetype.vcxproj x64 +rd /S /Q %FREETYPE%\objs +%MSBUILD% %FREETYPE%\builds\win32\vc2010\freetype.sln /t:Clean;Build /p:Configuration="Release";Platform=x64 +xcopy /E /Q %FREETYPE%\include %INCLIB% +xcopy /E /Q %FREETYPE%\objs\win32\vc2010 %INCLIB% +copy /Y /B %FREETYPE%\objs\win32\vc2010\*.lib %INCLIB%\freetype.lib +endlocal + +endlocal + +rem Build for VC 2010 32 bit +setlocal EnableDelayedExpansion +call "%ProgramFiles%\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" /Release /x86 /xp +set INCLIB=%INCLIB%\msvcr100-x32 +mkdir %INCLIB% + +rem Build zlib +setlocal +cd /D %ZLIB% +nmake -f win32\Makefile.msc clean +nmake -f win32\Makefile.msc +copy /Y /B *.dll %INCLIB% +copy /Y /B *.lib %INCLIB% +copy /Y /B zlib.lib %INCLIB%\z.lib +copy /Y /B zlib.h %INCLIB% +copy /Y /B zconf.h %INCLIB% +endlocal + +rem Build freetype +setlocal +%~dp0\fixproj.py %FREETYPE%\builds\win32\vc2010\freetype.sln Win32 +%~dp0\fixproj.py %FREETYPE%\builds\win32\vc2010\freetype.vcxproj Win32 +rd /S /Q %FREETYPE%\objs +%MSBUILD% %FREETYPE%\builds\win32\vc2010\freetype.sln /t:Clean;Build /p:Configuration="Release";Platform=Win32 +xcopy /E /Q %FREETYPE%\include %INCLIB% +xcopy /E /Q %FREETYPE%\objs\win32\vc2010 %INCLIB% +copy /Y /B %FREETYPE%\objs\win32\vc2010\*.lib %INCLIB%\freetype.lib +endlocal + +endlocal + +endlocal \ No newline at end of file diff --git a/winbuild/fetch.py b/winbuild/fetch.py new file mode 100644 index 000000000..00845678e --- /dev/null +++ b/winbuild/fetch.py @@ -0,0 +1,7 @@ +import sys, os, urllib.parse, urllib.request +name = urllib.parse.urlsplit(sys.argv[1])[2].split('/')[-1] +if not os.path.exists(name): + print("Fetching", sys.argv[1]) + content = urllib.request.urlopen(sys.argv[1]).read() + with open(name, 'wb') as fd: + fd.write(content) diff --git a/winbuild/fixproj.py b/winbuild/fixproj.py new file mode 100644 index 000000000..e52de707f --- /dev/null +++ b/winbuild/fixproj.py @@ -0,0 +1,7 @@ +import sys, os +with open(sys.argv[1], 'r') as fd: + content = '\n'.join(line.strip() for line in fd if line.strip()) +if len(sys.argv) == 3: + content = content.replace('Win32', sys.argv[2]).replace('x64', sys.argv[2]) +with open(sys.argv[1], 'w') as fd: + fd.write(content) diff --git a/winbuild/untar.py b/winbuild/untar.py new file mode 100644 index 000000000..26a47678c --- /dev/null +++ b/winbuild/untar.py @@ -0,0 +1,4 @@ +import sys, tarfile +with tarfile.open(sys.argv[1], 'r:gz') as tgz: + tgz.extractall(sys.argv[2]) + diff --git a/winbuild/unzip.py b/winbuild/unzip.py new file mode 100644 index 000000000..9793906cb --- /dev/null +++ b/winbuild/unzip.py @@ -0,0 +1,3 @@ +import sys, zipfile +with zipfile.ZipFile(sys.argv[1]) as zf: + zf.extractall(sys.argv[2]) From 21130a10a5be0156baf78ab956e47e25ad2302bb Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 19 Mar 2014 04:50:34 +0000 Subject: [PATCH 0002/1037] apparently working freetype build --- winbuild/build_dep.cmd | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/winbuild/build_dep.cmd b/winbuild/build_dep.cmd index 840931327..059e677ef 100644 --- a/winbuild/build_dep.cmd +++ b/winbuild/build_dep.cmd @@ -7,6 +7,7 @@ set CMAKE="C:\Program Files (x86)\CMake 2.8\bin\cmake.exe" set INCLIB=%~dp0\depends set BUILD=%~dp0\build +echo "Removing Directories" rd /S /Q %INCLIB% rd /S /Q %BUILD% @@ -58,10 +59,10 @@ endlocal rem Build freetype setlocal -py -3 %~dp0\fixproj.py %FREETYPE%\builds\win32\vc2008\freetype.sln x64 -py -3 %~dp0\fixproj.py %FREETYPE%\builds\win32\vc2008\freetype.vcproj x64 +py -3 %~dp0\fixproj.py %FREETYPE%\builds\windows\vc2008\freetype.sln x64 +py -3 %~dp0\fixproj.py %FREETYPE%\builds\windows\vc2008\freetype.vcproj x64 rd /S /Q %FREETYPE%\objs -%MSBUILD% %FREETYPE%\builds\win32\vc2008\freetype.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=x64 +%MSBUILD% %FREETYPE%\builds\windows\vc2008\freetype.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=x64 xcopy /E /Q %FREETYPE%\include %INCLIB% xcopy /E /Q %FREETYPE%\objs\win32\vc2008 %INCLIB% copy /Y /B %FREETYPE%\objs\win32\vc2008\*.lib %INCLIB%\freetype.lib @@ -89,10 +90,10 @@ endlocal rem Build freetype setlocal -%~dp0\fixproj.py %FREETYPE%\builds\win32\vc2008\freetype.sln Win32 -%~dp0\fixproj.py %FREETYPE%\builds\win32\vc2008\freetype.vcproj Win32 +%~dp0\fixproj.py %FREETYPE%\builds\windows\vc2008\freetype.sln Win32 +%~dp0\fixproj.py %FREETYPE%\builds\windows\vc2008\freetype.vcproj Win32 rd /S /Q %FREETYPE%\objs -%MSBUILD% %FREETYPE%\builds\win32\vc2008\freetype.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=Win32 +%MSBUILD% %FREETYPE%\builds\windows\vc2008\freetype.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=Win32 xcopy /E /Q %FREETYPE%\include %INCLIB% xcopy /E /Q %FREETYPE%\objs\win32\vc2008 %INCLIB% copy /Y /B %FREETYPE%\objs\win32\vc2008\*.lib %INCLIB%\freetype.lib @@ -120,10 +121,10 @@ endlocal rem Build freetype setlocal -py -3 %~dp0\fixproj.py %FREETYPE%\builds\win32\vc2010\freetype.sln x64 -py -3 %~dp0\fixproj.py %FREETYPE%\builds\win32\vc2010\freetype.vcxproj x64 +py -3 %~dp0\fixproj.py %FREETYPE%\builds\windows\vc2010\freetype.sln x64 +py -3 %~dp0\fixproj.py %FREETYPE%\builds\windows\vc2010\freetype.vcxproj x64 rd /S /Q %FREETYPE%\objs -%MSBUILD% %FREETYPE%\builds\win32\vc2010\freetype.sln /t:Clean;Build /p:Configuration="Release";Platform=x64 +%MSBUILD% %FREETYPE%\builds\windows\vc2010\freetype.sln /t:Clean;Build /p:Configuration="Release";Platform=x64 xcopy /E /Q %FREETYPE%\include %INCLIB% xcopy /E /Q %FREETYPE%\objs\win32\vc2010 %INCLIB% copy /Y /B %FREETYPE%\objs\win32\vc2010\*.lib %INCLIB%\freetype.lib @@ -151,10 +152,10 @@ endlocal rem Build freetype setlocal -%~dp0\fixproj.py %FREETYPE%\builds\win32\vc2010\freetype.sln Win32 -%~dp0\fixproj.py %FREETYPE%\builds\win32\vc2010\freetype.vcxproj Win32 +%~dp0\fixproj.py %FREETYPE%\builds\windows\vc2010\freetype.sln Win32 +%~dp0\fixproj.py %FREETYPE%\builds\windows\vc2010\freetype.vcxproj Win32 rd /S /Q %FREETYPE%\objs -%MSBUILD% %FREETYPE%\builds\win32\vc2010\freetype.sln /t:Clean;Build /p:Configuration="Release";Platform=Win32 +%MSBUILD% %FREETYPE%\builds\windows\vc2010\freetype.sln /t:Clean;Build /p:Configuration="Release";Platform=Win32 xcopy /E /Q %FREETYPE%\include %INCLIB% xcopy /E /Q %FREETYPE%\objs\win32\vc2010 %INCLIB% copy /Y /B %FREETYPE%\objs\win32\vc2010\*.lib %INCLIB%\freetype.lib From b2a52caad90b18731b491f14ef3ec4693ef1fded Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 18 Mar 2014 22:07:52 -0700 Subject: [PATCH 0003/1037] take 1 at libjpeg --- winbuild/build_dep.cmd | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/winbuild/build_dep.cmd b/winbuild/build_dep.cmd index 840931327..6aa108b24 100644 --- a/winbuild/build_dep.cmd +++ b/winbuild/build_dep.cmd @@ -25,6 +25,12 @@ py -3 unzip.py zlib128.zip %BUILD% set ZLIB=%BUILD%\zlib-1.2.8 copy /Y /B zlib128.zip %INCLIB% +rem Get libjpeg +py -3 fetch.py http://www.ijg.org/files/jpegsr9a.zip +py -3 unzip.py jpegsr9a.zip %BUILD% +set LIBJPEG=%BUILD%\jpeg-9a +copy /Y /B jpegsr9a.zip %INCLIB% + rem Get tcl/tk py -3 fetch.py http://hivelocity.dl.sourceforge.net/project/tcl/Tcl/8.5.13/tcl8513-src.zip py -3 unzip.py tcl8513-src.zip %BUILD% @@ -44,6 +50,17 @@ call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x64 / set INCLIB=%INCLIB%\msvcr90-x64 mkdir %INCLIB% +rem Build libjpeg +setlocal +cd /D %LIBJPEG% +nmake -f makefile.vc setup-vc6 +nmake -f makefile.vc clean +nmake -f makefile.vc all +copy /Y /B *.dll %INCLIB% +copy /Y /B *.lib %INCLIB% +copy /Y /B libjpeg.h %INCLIB% +endlocal + rem Build zlib setlocal cd /D %ZLIB% From 74794d473fd3a9ec07cd224c765044099da5c048 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 18 Mar 2014 22:32:13 -0700 Subject: [PATCH 0004/1037] take 1 at lcms2 --- winbuild/build_dep.cmd | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/winbuild/build_dep.cmd b/winbuild/build_dep.cmd index 6aa108b24..d26ff5227 100644 --- a/winbuild/build_dep.cmd +++ b/winbuild/build_dep.cmd @@ -31,7 +31,14 @@ py -3 unzip.py jpegsr9a.zip %BUILD% set LIBJPEG=%BUILD%\jpeg-9a copy /Y /B jpegsr9a.zip %INCLIB% +rem Get lcms2 +py -3 fetch.py http://hivelocity.dl.sourceforge.net/project/lcms/lcms/2.6/lcms2-2.6.zip +py -3 unzip.py lcms2-2.6.zip %BUILD% +set LCMS=%BUILD%\lcms2-2.6 +copy /Y /B lcms2-2.6.zip %INCLIB% + rem Get tcl/tk + py -3 fetch.py http://hivelocity.dl.sourceforge.net/project/tcl/Tcl/8.5.13/tcl8513-src.zip py -3 unzip.py tcl8513-src.zip %BUILD% copy /Y /B tcl8513-src.zip %INCLIB% @@ -84,6 +91,17 @@ xcopy /E /Q %FREETYPE%\objs\win32\vc2008 %INCLIB% copy /Y /B %FREETYPE%\objs\win32\vc2008\*.lib %INCLIB%\freetype.lib endlocal +rem Build lcms2 +setlocal +py -3 %~dp0\fixproj.py %LCMS%\Projects\vc2008\lcms2.sln x64 +py -3 %~dp0\fixproj.py %LCMS%\Projects\vc2008\lcms2.vcproj x64 +rd /S /Q %LCMS%\objs +%MSBUILD% %LCMS%\Projects\vc2008\lcms2.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=x64 +xcopy /E /Q %LCMS%\include %INCLIB% +xcopy /E /Q %LCMS%\objs\win32\vc2008 %INCLIB% +copy /Y /B %LCMS%\objs\win32\vc2008\*.lib %INCLIB%\lcms2.lib +endlocal + endlocal rem Build for VC 2008 32 bit From 684c922cc692963e61829036248605a9d4e52b7b Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 19 Mar 2014 05:34:53 +0000 Subject: [PATCH 0005/1037] working tweaks --- winbuild/build_dep.cmd | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/winbuild/build_dep.cmd b/winbuild/build_dep.cmd index 4c04497af..8eb88c368 100644 --- a/winbuild/build_dep.cmd +++ b/winbuild/build_dep.cmd @@ -1,5 +1,5 @@ @echo off -rem Build Matplotlib Dependencies +rem Build Pillow Dependencies setlocal set MSBUILD=C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe @@ -59,7 +59,7 @@ nmake -f makefile.vc clean nmake -f makefile.vc all copy /Y /B *.dll %INCLIB% copy /Y /B *.lib %INCLIB% -copy /Y /B libjpeg.h %INCLIB% +copy /Y /B jpeglib.h %INCLIB% endlocal rem Build zlib @@ -86,6 +86,8 @@ copy /Y /B %FREETYPE%\objs\win32\vc2008\*.lib %INCLIB%\freetype.lib endlocal endlocal +rem UNDONE --removeme! +exit rem Build for VC 2008 32 bit setlocal EnableDelayedExpansion From 2405a8e1be717feb5d950bc19886a90b52c227d8 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 19 Mar 2014 05:48:56 +0000 Subject: [PATCH 0006/1037] lcms is failing with an incorrect version of the solution file --- winbuild/lcms.cmd | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 winbuild/lcms.cmd diff --git a/winbuild/lcms.cmd b/winbuild/lcms.cmd new file mode 100644 index 000000000..dd6852c18 --- /dev/null +++ b/winbuild/lcms.cmd @@ -0,0 +1,41 @@ +setlocal +set MSBUILD=C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe +set CMAKE="C:\Program Files (x86)\CMake 2.8\bin\cmake.exe" +set INCLIB=%~dp0\depends +set BUILD=%~dp0\build + +echo "Removing Directories" +rd /S /Q %INCLIB% +rd /S /Q %BUILD% + +mkdir %INCLIB% +mkdir %BUILD% + +rem Get lcms2 +py -3 fetch.py http://hivelocity.dl.sourceforge.net/project/lcms/lcms/2.6/lcms2-2.6.zip +py -3 unzip.py lcms2-2.6.zip %BUILD% +set LCMS=%BUILD%\lcms2-2.6 +copy /Y /B lcms2-2.6.zip %INCLIB% + +rem Build for VC 2008 64 bit +setlocal EnableDelayedExpansion +call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x64 /vista +set INCLIB=%INCLIB%\msvcr90-x64 +mkdir %INCLIB% + +rem Build lcms2 +setlocal +py -3 %~dp0\fixproj.py %LCMS%\Projects\VC2008\lcms2.sln x64 +py -3 %~dp0\fixproj.py %LCMS%\Projects\VC2008\lcms2.vcproj x64 +rd /S /Q %LCMS%\objs +%MSBUILD% %LCMS%\Projects\VC2008\lcms2.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=x64 +xcopy /E /Q %LCMS%\include %INCLIB% +xcopy /E /Q %LCMS%\objs\win32\VC2008 %INCLIB% +copy /Y /B %LCMS%\objs\win32\VC2008\*.lib %INCLIB%\lcms2.lib +endlocal + +endlocal +rem UNDONE --removeme! +exit + +endlocal \ No newline at end of file From b5cffa7d472cedd10b4d10d2a553c204f6fbb77a Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 19 Mar 2014 05:52:42 +0000 Subject: [PATCH 0007/1037] case --- winbuild/build_dep.cmd | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/winbuild/build_dep.cmd b/winbuild/build_dep.cmd index a0183147a..b746cb1cc 100644 --- a/winbuild/build_dep.cmd +++ b/winbuild/build_dep.cmd @@ -94,13 +94,13 @@ endlocal rem Build lcms2 setlocal -py -3 %~dp0\fixproj.py %LCMS%\Projects\vc2008\lcms2.sln x64 -py -3 %~dp0\fixproj.py %LCMS%\Projects\vc2008\lcms2.vcproj x64 +py -3 %~dp0\fixproj.py %LCMS%\Projects\VC2008\lcms2.sln x64 +py -3 %~dp0\fixproj.py %LCMS%\Projects\VC2008\lcms2.vcproj x64 rd /S /Q %LCMS%\objs -%MSBUILD% %LCMS%\Projects\vc2008\lcms2.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=x64 +%MSBUILD% %LCMS%\Projects\VC2008\lcms2.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=x64 xcopy /E /Q %LCMS%\include %INCLIB% -xcopy /E /Q %LCMS%\objs\win32\vc2008 %INCLIB% -copy /Y /B %LCMS%\objs\win32\vc2008\*.lib %INCLIB%\lcms2.lib +xcopy /E /Q %LCMS%\objs\win32\VC2008 %INCLIB% +copy /Y /B %LCMS%\objs\win32\VC2008\*.lib %INCLIB%\lcms2.lib endlocal endlocal From c9fdded9634dbc7a5d565ea27bb4e6c3f92dcee8 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 18 Mar 2014 23:00:18 -0700 Subject: [PATCH 0008/1037] take 1 on libtiff --- winbuild/build_dep.cmd | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/winbuild/build_dep.cmd b/winbuild/build_dep.cmd index b746cb1cc..06dd826cb 100644 --- a/winbuild/build_dep.cmd +++ b/winbuild/build_dep.cmd @@ -32,6 +32,12 @@ py -3 unzip.py jpegsr9a.zip %BUILD% set LIBJPEG=%BUILD%\jpeg-9a copy /Y /B jpegsr9a.zip %INCLIB% +rem get libtiff +py -3 fetch.py ftp://ftp.remotesensing.org/pub/libtiff/tiff-4.0.3.zip +py -3 unzip.py tiff-4.0.3.zip %BUILD% +set TIFF=%BUILD%\tiff-4.0.3 +copy /Y /B tiff-4.0.3.zip %INCLIB% + rem Get lcms2 py -3 fetch.py http://hivelocity.dl.sourceforge.net/project/lcms/lcms/2.6/lcms2-2.6.zip py -3 unzip.py lcms2-2.6.zip %BUILD% @@ -81,6 +87,16 @@ copy /Y /B zlib.h %INCLIB% copy /Y /B zconf.h %INCLIB% endlocal +rem Build libtiff +setlocal +cd /D %TIFF% +nmake -f makefile.vc clean +nmake -f makefile.vc +copy /Y /B *.dll %INCLIB% +copy /Y /B *.lib %INCLIB% +copy /Y /B Tiff.h %INCLIB% +endlocal + rem Build freetype setlocal py -3 %~dp0\fixproj.py %FREETYPE%\builds\windows\vc2008\freetype.sln x64 From 4b6c8132906149ebd855a272adaf358a853d0706 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 19 Mar 2014 06:26:39 +0000 Subject: [PATCH 0009/1037] slash direction --- winbuild/build_dep.cmd | 7 ++++--- winbuild/tiff.cmd | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 winbuild/tiff.cmd diff --git a/winbuild/build_dep.cmd b/winbuild/build_dep.cmd index 06dd826cb..a48a1dab0 100644 --- a/winbuild/build_dep.cmd +++ b/winbuild/build_dep.cmd @@ -92,9 +92,10 @@ setlocal cd /D %TIFF% nmake -f makefile.vc clean nmake -f makefile.vc -copy /Y /B *.dll %INCLIB% -copy /Y /B *.lib %INCLIB% -copy /Y /B Tiff.h %INCLIB% +copy /Y /B libtiff\*.dll %INCLIB% +copy /Y /B libtiff\*.lib %INCLIB% +copy /Y /B libtiff\tiff.h %INCLIB% +copy /Y /B libtiff\tiffio.h %INCLIB% endlocal rem Build freetype diff --git a/winbuild/tiff.cmd b/winbuild/tiff.cmd new file mode 100644 index 000000000..393379284 --- /dev/null +++ b/winbuild/tiff.cmd @@ -0,0 +1,40 @@ +setlocal +set MSBUILD=C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe +set CMAKE="C:\Program Files (x86)\CMake 2.8\bin\cmake.exe" +set INCLIB=%~dp0\depends +set BUILD=%~dp0\build + +echo "Removing Directories" +rd /S /Q %INCLIB% +rd /S /Q %BUILD% + +mkdir %INCLIB% +mkdir %BUILD% + +rem get libtiff +py -3 fetch.py ftp://ftp.remotesensing.org/pub/libtiff/tiff-4.0.3.zip +py -3 unzip.py tiff-4.0.3.zip %BUILD% +set TIFF=%BUILD%\tiff-4.0.3 +copy /Y /B tiff-4.0.3.zip %INCLIB% + +rem Build for VC 2008 64 bit +setlocal EnableDelayedExpansion +call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x64 /vista +set INCLIB=%INCLIB%\msvcr90-x64 +mkdir %INCLIB% + + +rem Build libtiff +setlocal +cd /D %TIFF% +nmake -f makefile.vc clean +nmake -f makefile.vc +copy /Y /B libtiff\*.dll %INCLIB% +copy /Y /B libtiff\*.lib %INCLIB% +copy /Y /B libtiff\tiff.h %INCLIB% +copy /Y /B libtiff\tiffio.h %INCLIB% +endlocal + +endlocal +rem UNDONE --removeme! +exit From e4d9c315b9c0d7aec9d9ae819f00685a1866741b Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 19 Mar 2014 17:05:00 +0000 Subject: [PATCH 0010/1037] winbuild/build_dep.cmd~ --- winbuild/build_dep.cmd~ | 165 ---------------------------------------- 1 file changed, 165 deletions(-) delete mode 100644 winbuild/build_dep.cmd~ diff --git a/winbuild/build_dep.cmd~ b/winbuild/build_dep.cmd~ deleted file mode 100644 index 0bb983972..000000000 --- a/winbuild/build_dep.cmd~ +++ /dev/null @@ -1,165 +0,0 @@ -@echo off -rem Build Matplotlib Dependencies - -setlocal -set MSBUILD=C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe -set CMAKE="C:\Program Files (x86)\CMake 2.8\bin\cmake.exe" -set INCLIB=%~dp0\pillow-build-dependencies -set BUILD=%~dp0\build - -rd /S /Q %INCLIB% -rd /S /Q %BUILD% - -mkdir %INCLIB% -mkdir %BUILD% - -rem Get freetype -py -3 fetch.py http://download.savannah.gnu.org/releases/freetype/ft253.zip -py -3 unzip.py ft253.zip %BUILD% -set FREETYPE=%BUILD%\freetype-2.5.3 -copy /Y /B ft253.zip %INCLIB% - -rem Get zlib -py -3 fetch.py http://zlib.net/zlib128.zip -py -3 unzip.py zlib128.zip %BUILD% -set ZLIB=%BUILD%\zlib-1.2.8 -copy /Y /B zlib128.zip %INCLIB% - -rem Get tcl/tk -py -3 fetch.py http://hivelocity.dl.sourceforge.net/project/tcl/Tcl/8.5.13/tcl8513-src.zip -py -3 unzip.py tcl8513-src.zip %BUILD% -copy /Y /B tcl8513-src.zip %INCLIB% -py -3 fetch.py http://hivelocity.dl.sourceforge.net/project/tcl/Tcl/8.5.13/tk8513-src.zip -py -3 unzip.py tk8513-src.zip %BUILD% -copy /Y /B tk8513-src.zip %INCLIB% - -mkdir %INCLIB%\tcl85\include\X11 -copy /Y /B %BUILD%\tcl8.5.13\generic\*.h %INCLIB%\tcl85\include\ -copy /Y /B %BUILD%\tk8.5.13\generic\*.h %INCLIB%\tcl85\include\ -copy /Y /B %BUILD%\tk8.5.13\xlib\X11\* %INCLIB%\tcl85\include\X11\ - -rem Build for VC 2008 64 bit -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x64 /vista -set INCLIB=%INCLIB%\msvcr90-x64 -mkdir %INCLIB% - -rem Build zlib -setlocal -cd /D %ZLIB% -nmake -f win32\Makefile.msc clean -nmake -f win32\Makefile.msc -copy /Y /B *.dll %INCLIB% -copy /Y /B *.lib %INCLIB% -copy /Y /B zlib.lib %INCLIB%\z.lib -copy /Y /B zlib.h %INCLIB% -copy /Y /B zconf.h %INCLIB% -endlocal - -rem Build freetype -setlocal -py -3 %~dp0\fixproj.py %FREETYPE%\builds\win32\vc2008\freetype.sln x64 -py -3 %~dp0\fixproj.py %FREETYPE%\builds\win32\vc2008\freetype.vcproj x64 -rd /S /Q %FREETYPE%\objs -%MSBUILD% %FREETYPE%\builds\win32\vc2008\freetype.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=x64 -xcopy /E /Q %FREETYPE%\include %INCLIB% -xcopy /E /Q %FREETYPE%\objs\win32\vc2008 %INCLIB% -copy /Y /B %FREETYPE%\objs\win32\vc2008\*.lib %INCLIB%\freetype.lib -endlocal - -endlocal - -rem Build for VC 2008 32 bit -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x86 /xp -set INCLIB=%INCLIB%\msvcr90-x32 -mkdir %INCLIB% - -rem Build zlib -setlocal -cd /D %ZLIB% -nmake -f win32\Makefile.msc clean -nmake -f win32\Makefile.msc -copy /Y /B *.dll %INCLIB% -copy /Y /B *.lib %INCLIB% -copy /Y /B zlib.lib %INCLIB%\z.lib -copy /Y /B zlib.h %INCLIB% -copy /Y /B zconf.h %INCLIB% -endlocal - -rem Build freetype -setlocal -%~dp0\fixproj.py %FREETYPE%\builds\win32\vc2008\freetype.sln Win32 -%~dp0\fixproj.py %FREETYPE%\builds\win32\vc2008\freetype.vcproj Win32 -rd /S /Q %FREETYPE%\objs -%MSBUILD% %FREETYPE%\builds\win32\vc2008\freetype.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=Win32 -xcopy /E /Q %FREETYPE%\include %INCLIB% -xcopy /E /Q %FREETYPE%\objs\win32\vc2008 %INCLIB% -copy /Y /B %FREETYPE%\objs\win32\vc2008\*.lib %INCLIB%\freetype.lib -endlocal - -endlocal - -rem Build for VC 2010 64 bit -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" /Release /x64 /vista -set INCLIB=%INCLIB%\msvcr100-x64 -mkdir %INCLIB% - -rem Build zlib -setlocal -cd /D %ZLIB% -nmake -f win32\Makefile.msc clean -nmake -f win32\Makefile.msc -copy /Y /B *.dll %INCLIB% -copy /Y /B *.lib %INCLIB% -copy /Y /B zlib.lib %INCLIB%\z.lib -copy /Y /B zlib.h %INCLIB% -copy /Y /B zconf.h %INCLIB% -endlocal - -rem Build freetype -setlocal -py -3 %~dp0\fixproj.py %FREETYPE%\builds\win32\vc2010\freetype.sln x64 -py -3 %~dp0\fixproj.py %FREETYPE%\builds\win32\vc2010\freetype.vcxproj x64 -rd /S /Q %FREETYPE%\objs -%MSBUILD% %FREETYPE%\builds\win32\vc2010\freetype.sln /t:Clean;Build /p:Configuration="Release";Platform=x64 -xcopy /E /Q %FREETYPE%\include %INCLIB% -xcopy /E /Q %FREETYPE%\objs\win32\vc2010 %INCLIB% -copy /Y /B %FREETYPE%\objs\win32\vc2010\*.lib %INCLIB%\freetype.lib -endlocal - -endlocal - -rem Build for VC 2010 32 bit -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" /Release /x86 /xp -set INCLIB=%INCLIB%\msvcr100-x32 -mkdir %INCLIB% - -rem Build zlib -setlocal -cd /D %ZLIB% -nmake -f win32\Makefile.msc clean -nmake -f win32\Makefile.msc -copy /Y /B *.dll %INCLIB% -copy /Y /B *.lib %INCLIB% -copy /Y /B zlib.lib %INCLIB%\z.lib -copy /Y /B zlib.h %INCLIB% -copy /Y /B zconf.h %INCLIB% -endlocal - -rem Build freetype -setlocal -%~dp0\fixproj.py %FREETYPE%\builds\win32\vc2010\freetype.sln Win32 -%~dp0\fixproj.py %FREETYPE%\builds\win32\vc2010\freetype.vcxproj Win32 -rd /S /Q %FREETYPE%\objs -%MSBUILD% %FREETYPE%\builds\win32\vc2010\freetype.sln /t:Clean;Build /p:Configuration="Release";Platform=Win32 -xcopy /E /Q %FREETYPE%\include %INCLIB% -xcopy /E /Q %FREETYPE%\objs\win32\vc2010 %INCLIB% -copy /Y /B %FREETYPE%\objs\win32\vc2010\*.lib %INCLIB%\freetype.lib -endlocal - -endlocal - -endlocal \ No newline at end of file From 6611301fe7332ec86472ca353153310b01528d8b Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 19 Mar 2014 17:05:25 +0000 Subject: [PATCH 0011/1037] got all the headers --- winbuild/tiff.cmd | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/winbuild/tiff.cmd b/winbuild/tiff.cmd index 393379284..c38d7aec1 100644 --- a/winbuild/tiff.cmd +++ b/winbuild/tiff.cmd @@ -31,8 +31,7 @@ nmake -f makefile.vc clean nmake -f makefile.vc copy /Y /B libtiff\*.dll %INCLIB% copy /Y /B libtiff\*.lib %INCLIB% -copy /Y /B libtiff\tiff.h %INCLIB% -copy /Y /B libtiff\tiffio.h %INCLIB% +copy /Y /B libtiff\tiff*.h %INCLIB% endlocal endlocal From 8ed7c9ad3f04047e59afe2342859a373b0d40eac Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 19 Mar 2014 17:06:33 +0000 Subject: [PATCH 0012/1037] UNDONE -- are we using unistd.h? --- libImaging/TiffDecode.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libImaging/TiffDecode.h b/libImaging/TiffDecode.h index 90fe3c9d4..2dd557d41 100644 --- a/libImaging/TiffDecode.h +++ b/libImaging/TiffDecode.h @@ -13,10 +13,11 @@ #include #endif -#ifndef _UNISTD_H -#include -#endif - +/* UNDONE -- what are we using from this? */ +/*#ifndef _UNISTD_H + # include + # endif +*/ #ifndef min #define min(x,y) (( x > y ) ? y : x ) From fb5d473683ccfffee9188054ce8c8a756117d24f Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 19 Mar 2014 17:07:44 +0000 Subject: [PATCH 0013/1037] emacs save files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a2a3dc417..498cbdd14 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ dist .tox *.so docs/_build +*~ From 7ec906a1312e1cc600683460f6ca4ac3ae8a8e6d Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 19 Mar 2014 17:18:52 +0000 Subject: [PATCH 0014/1037] incremental --- winbuild/tiff.cmd | 4 ---- 1 file changed, 4 deletions(-) diff --git a/winbuild/tiff.cmd b/winbuild/tiff.cmd index c38d7aec1..48be1cb1b 100644 --- a/winbuild/tiff.cmd +++ b/winbuild/tiff.cmd @@ -4,10 +4,6 @@ set CMAKE="C:\Program Files (x86)\CMake 2.8\bin\cmake.exe" set INCLIB=%~dp0\depends set BUILD=%~dp0\build -echo "Removing Directories" -rd /S /Q %INCLIB% -rd /S /Q %BUILD% - mkdir %INCLIB% mkdir %BUILD% From 6fbe8d5406b2b24e16c5a27732b88a16a705377b Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 19 Mar 2014 17:19:33 +0000 Subject: [PATCH 0015/1037] individual/incremental build --- winbuild/jpeg.cmd | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 winbuild/jpeg.cmd diff --git a/winbuild/jpeg.cmd b/winbuild/jpeg.cmd new file mode 100644 index 000000000..4f16f31c7 --- /dev/null +++ b/winbuild/jpeg.cmd @@ -0,0 +1,37 @@ +setlocal +set MSBUILD=C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe +set CMAKE="C:\Program Files (x86)\CMake 2.8\bin\cmake.exe" +set INCLIB=%~dp0\depends +set BUILD=%~dp0\build + +mkdir %INCLIB% +mkdir %BUILD% + +rem Get libjpeg +py -3 fetch.py http://www.ijg.org/files/jpegsr9a.zip +py -3 unzip.py jpegsr9a.zip %BUILD% +set LIBJPEG=%BUILD%\jpeg-9a +copy /Y /B jpegsr9a.zip %INCLIB% + +rem Build for VC 2008 64 bit +setlocal EnableDelayedExpansion +call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x64 /vista +set INCLIB=%INCLIB%\msvcr90-x64 +mkdir %INCLIB% + + +rem Build libjpeg +setlocal +cd /D %LIBJPEG% +nmake -f makefile.vc setup-vc6 +nmake -f makefile.vc clean +nmake -f makefile.vc all +copy /Y /B *.dll %INCLIB% +copy /Y /B *.lib %INCLIB% +copy /Y /B j*.h %INCLIB% + +endlocal + +endlocal +rem UNDONE --removeme! +exit From 00ce7ed29231ba7e4250a42acbacf1b9b564e857 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 19 Mar 2014 17:20:22 +0000 Subject: [PATCH 0016/1037] reported successful build using tkinter, jpeg, zlib, libtiff, and freetype --- winbuild/build_dep.cmd | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/winbuild/build_dep.cmd b/winbuild/build_dep.cmd index a48a1dab0..c9af41730 100644 --- a/winbuild/build_dep.cmd +++ b/winbuild/build_dep.cmd @@ -72,7 +72,7 @@ nmake -f makefile.vc clean nmake -f makefile.vc all copy /Y /B *.dll %INCLIB% copy /Y /B *.lib %INCLIB% -copy /Y /B jpeglib.h %INCLIB% +copy /Y /B j*.h %INCLIB% endlocal rem Build zlib @@ -94,8 +94,7 @@ nmake -f makefile.vc clean nmake -f makefile.vc copy /Y /B libtiff\*.dll %INCLIB% copy /Y /B libtiff\*.lib %INCLIB% -copy /Y /B libtiff\tiff.h %INCLIB% -copy /Y /B libtiff\tiffio.h %INCLIB% +copy /Y /B libtiff\tiff*.h %INCLIB% endlocal rem Build freetype From a2c2a83162363c1c98141646c4e62ebde3a35b07 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 19 Mar 2014 17:20:35 +0000 Subject: [PATCH 0017/1037] reported successful build using tkinter, jpeg, zlib, libtiff, and freetype --- winbuild/build.cmd | 71 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 winbuild/build.cmd diff --git a/winbuild/build.cmd b/winbuild/build.cmd new file mode 100644 index 000000000..36532dd59 --- /dev/null +++ b/winbuild/build.cmd @@ -0,0 +1,71 @@ +rem @echo off +rem Build Pillow +rem for Python 2.6, 2.7, 3.2, and 3.3, 32 and 64 bit +rem using Windows SDK 7.0 and 7.1 + +setlocal +rem Adjust the following if necessary +set MPLSRC=%~dp0\.. +set INCLIB=%~dp0\depends +rem set BLDOPT=bdist_wininst --user-access-control=auto +set BLDOPT=build_ext +cd /D %MPLSRC% + +rem Set TkAgg as default backend +echo [rc_options] > setup.cfg +echo backend = TkAgg >> setup.cfg +echo [gui_support] >> setup.cfg +echo tkagg = True >> setup.cfg + +rem Using Windows SDK 7.0 +rem "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Setup\WindowsSdkVer.exe" -q -version:v7.0 + +rem Build for 64 bit Python 2.6, 2.7, 3.2 +setlocal EnableDelayedExpansion +call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x64 /vista +set DISTUTILS_USE_SDK=1 +set LIB=%LIB%;%INCLIB%\msvcr90-x64 +set INCLUDE=%INCLUDE%;%INCLIB%\msvcr90-x64;%INCLIB%\tcl85\include + +setlocal +set LIB=%LIB%;C:\Python27x64\tcl +rd /q /s build +call C:\Python27x64\python.exe setup.py %BLDOPT% +endlocal + +endlocal + +exit + +rem Using Windows SDK 7.1 +rem "%ProgramFiles%\Microsoft SDKs\Windows\v7.1\Setup\WindowsSdkVer.exe" -q -version:v7.1 + +rem Build for 32 bit Python 3.3 +setlocal EnableDelayedExpansion +call "%ProgramFiles%\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" /Release /x86 /xp +set DISTUTILS_USE_SDK=1 +set LIB=%LIB%;%INCLIB%\msvcr100-x32 +set INCLUDE=%INCLUDE%;%INCLIB%\msvcr100-x32;%INCLIB%\tcl85\include +setlocal +set LIB=%LIB%;C:\Python33\tcl +rd /q /s build +call C:\Python33\python.exe setup.py %BLDOPT% +endlocal +endlocal + +rem Build for 64 bit Python 3.3 +setlocal EnableDelayedExpansion +call "%ProgramFiles%\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" /Release /x64 /vista +set DISTUTILS_USE_SDK=1 +set LIB=%LIB%;%INCLIB%\msvcr100-x64 +set INCLUDE=%INCLUDE%;%INCLIB%\msvcr100-x64;%INCLIB%\tcl85\include +setlocal +set LIB=%LIB%;C:\Python33x64\tcl +rd /q /s build +call C:\Python33x64\python.exe setup.py %BLDOPT% +endlocal +endlocal + +rd /q /s build +rem copy /Y /B dist\*.* %~dp0 +endlocal \ No newline at end of file From 30c06dee8dbc2e328d3bc109325785d77588eef6 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 20 Mar 2014 05:35:07 +0000 Subject: [PATCH 0018/1037] changes required for libtiff to build with jpeg/zlib --- winbuild/nmake.opt | 218 +++++++++++++++++++++++++++++++++++++++++++++ winbuild/tiff.cmd | 4 + 2 files changed, 222 insertions(+) create mode 100644 winbuild/nmake.opt diff --git a/winbuild/nmake.opt b/winbuild/nmake.opt new file mode 100644 index 000000000..2d7797ab6 --- /dev/null +++ b/winbuild/nmake.opt @@ -0,0 +1,218 @@ +# $Id: nmake.opt,v 1.18 2006/06/07 16:33:45 dron Exp $ +# +# Copyright (C) 2004, Andrey Kiselev +# +# Permission to use, copy, modify, distribute, and sell this software and +# its documentation for any purpose is hereby granted without fee, provided +# that (i) the above copyright notices and this permission notice appear in +# all copies of the software and related documentation, and (ii) the names of +# Sam Leffler and Silicon Graphics may not be used in any advertising or +# publicity relating to the software without the specific, prior written +# permission of Sam Leffler and Silicon Graphics. +# +# THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, +# EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY +# WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +# +# IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR +# ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, +# OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF +# LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. + +# Compile time parameters for MS Visual C++ compiler. +# You may edit this file to specify building options. + +# +###### Edit the following lines to choose a feature set you need. ####### +# + +# +# Select WINMODE_CONSOLE to build a library which reports errors to stderr, or +# WINMODE_WINDOWED to build such that errors are reported via MessageBox(). +# +WINMODE_CONSOLE = 1 +#WINMODE_WINDOWED = 1 + +# +# Comment out the following lines to disable internal codecs. +# +# Support for CCITT Group 3 & 4 algorithms +CCITT_SUPPORT = 1 +# Support for Macintosh PackBits algorithm +PACKBITS_SUPPORT = 1 +# Support for LZW algorithm +LZW_SUPPORT = 1 +# Support for ThunderScan 4-bit RLE algorithm +THUNDER_SUPPORT = 1 +# Support for NeXT 2-bit RLE algorithm +NEXT_SUPPORT = 1 +# Support for LogLuv high dynamic range encoding +LOGLUV_SUPPORT = 1 + +# +# Uncomment and edit following lines to enable JPEG support. +# +JPEG_SUPPORT = 1 +JPEGDIR = $(BUILD)\jpeg-9a +JPEG_INCLUDE = -I$(JPEGDIR) +JPEG_LIB = $(JPEGDIR)/libjpeg.lib + +# +# Uncomment and edit following lines to enable ZIP support +# (required for Deflate compression and Pixar log-format) +# +ZIP_SUPPORT = 1 +ZLIBDIR = $(BUILD)\zlib-1.2.8 +ZLIB_INCLUDE = -I$(ZLIBDIR) +ZLIB_LIB = $(ZLIBDIR)/zlib.lib + +# +# Uncomment and edit following lines to enable ISO JBIG support +# +#JBIG_SUPPORT = 1 +#JBIGDIR = d:/projects/jbigkit +#JBIG_INCLUDE = -I$(JBIGDIR)/libjbig +#JBIG_LIB = $(JBIGDIR)/libjbig/jbig.lib + +# +# Uncomment following line to enable Pixar log-format algorithm +# (Zlib required). +# +#PIXARLOG_SUPPORT = 1 + +# +# Comment out the following lines to disable strip chopping +# (whether or not to convert single-strip uncompressed images to mutiple +# strips of specified size to reduce memory usage). Default strip size +# is 8192 bytes, it can be configured via the STRIP_SIZE_DEFAULT parameter +# +STRIPCHOP_SUPPORT = 1 +STRIP_SIZE_DEFAULT = 8192 + +# +# Comment out the following lines to disable treating the fourth sample with +# no EXTRASAMPLE_ value as being ASSOCALPHA. Many packages produce RGBA +# files but don't mark the alpha properly. +# +EXTRASAMPLE_AS_ALPHA_SUPPORT = 1 + +# +# Comment out the following lines to disable picking up YCbCr subsampling +# info from the JPEG data stream to support files lacking the tag. +# See Bug 168 in Bugzilla, and JPEGFixupTestSubsampling() for details. +# +CHECK_JPEG_YCBCR_SUBSAMPLING = 1 + +# +####################### Compiler related options. ####################### +# + +# +# Pick debug or optimized build flags. We default to an optimized build +# with no debugging information. +# NOTE: /EHsc option required if you want to build the C++ stream API +# +OPTFLAGS = /Ox /MD /EHsc /W3 /D_CRT_SECURE_NO_DEPRECATE +#OPTFLAGS = /Zi + +# +# Uncomment following line to enable using Windows Common RunTime Library +# instead of Windows specific system calls. See notes on top of tif_unix.c +# module for details. +# +USE_WIN_CRT_LIB = 1 + +# Compiler specific options. You may probably want to adjust compilation +# parameters in CFLAGS variable. Refer to your compiler documentation +# for the option reference. +# +MAKE = nmake /nologo +CC = cl /nologo +CXX = cl /nologo +AR = lib /nologo +LD = link /nologo + +CFLAGS = $(OPTFLAGS) $(INCL) $(EXTRAFLAGS) +CXXFLAGS = $(OPTFLAGS) $(INCL) $(EXTRAFLAGS) +EXTRAFLAGS = +LIBS = + +# Name of the output shared library +DLLNAME = libtiff.dll + +# +########### There is nothing to edit below this line normally. ########### +# + +# Set the native cpu bit order +EXTRAFLAGS = -DFILLODER_LSB2MSB $(EXTRAFLAGS) + +!IFDEF WINMODE_WINDOWED +EXTRAFLAGS = -DTIF_PLATFORM_WINDOWED $(EXTRAFLAGS) +LIBS = user32.lib $(LIBS) +!ELSE +EXTRAFLAGS = -DTIF_PLATFORM_CONSOLE $(EXTRAFLAGS) +!ENDIF + +# Codec stuff +!IFDEF CCITT_SUPPORT +EXTRAFLAGS = -DCCITT_SUPPORT $(EXTRAFLAGS) +!ENDIF + +!IFDEF PACKBITS_SUPPORT +EXTRAFLAGS = -DPACKBITS_SUPPORT $(EXTRAFLAGS) +!ENDIF + +!IFDEF LZW_SUPPORT +EXTRAFLAGS = -DLZW_SUPPORT $(EXTRAFLAGS) +!ENDIF + +!IFDEF THUNDER_SUPPORT +EXTRAFLAGS = -DTHUNDER_SUPPORT $(EXTRAFLAGS) +!ENDIF + +!IFDEF NEXT_SUPPORT +EXTRAFLAGS = -DNEXT_SUPPORT $(EXTRAFLAGS) +!ENDIF + +!IFDEF LOGLUV_SUPPORT +EXTRAFLAGS = -DLOGLUV_SUPPORT $(EXTRAFLAGS) +!ENDIF + +!IFDEF JPEG_SUPPORT +LIBS = $(LIBS) $(JPEG_LIB) +EXTRAFLAGS = -DJPEG_SUPPORT -DOJPEG_SUPPORT $(EXTRAFLAGS) +!ENDIF + +!IFDEF ZIP_SUPPORT +LIBS = $(LIBS) $(ZLIB_LIB) +EXTRAFLAGS = -DZIP_SUPPORT $(EXTRAFLAGS) +!IFDEF PIXARLOG_SUPPORT +EXTRAFLAGS = -DPIXARLOG_SUPPORT $(EXTRAFLAGS) +!ENDIF +!ENDIF + +!IFDEF JBIG_SUPPORT +LIBS = $(LIBS) $(JBIG_LIB) +EXTRAFLAGS = -DJBIG_SUPPORT $(EXTRAFLAGS) +!ENDIF + +!IFDEF STRIPCHOP_SUPPORT +EXTRAFLAGS = -DSTRIPCHOP_DEFAULT=TIFF_STRIPCHOP -DSTRIP_SIZE_DEFAULT=$(STRIP_SIZE_DEFAULT) $(EXTRAFLAGS) +!ENDIF + +!IFDEF EXTRASAMPLE_AS_ALPHA_SUPPORT +EXTRAFLAGS = -DDEFAULT_EXTRASAMPLE_AS_ALPHA $(EXTRAFLAGS) +!ENDIF + +!IFDEF CHECK_JPEG_YCBCR_SUBSAMPLING +EXTRAFLAGS = -DCHECK_JPEG_YCBCR_SUBSAMPLING $(EXTRAFLAGS) +!ENDIF + +!IFDEF USE_WIN_CRT_LIB +EXTRAFLAGS = -DAVOID_WIN32_FILEIO $(EXTRAFLAGS) +!ELSE +EXTRAFLAGS = -DUSE_WIN32_FILEIO $(EXTRAFLAGS) +!ENDIF diff --git a/winbuild/tiff.cmd b/winbuild/tiff.cmd index 48be1cb1b..1f2777528 100644 --- a/winbuild/tiff.cmd +++ b/winbuild/tiff.cmd @@ -22,6 +22,10 @@ mkdir %INCLIB% rem Build libtiff setlocal +@echo on +rem do after building jpeg and zlib +copy %~dp0\nmake.opt %TIFF% + cd /D %TIFF% nmake -f makefile.vc clean nmake -f makefile.vc From 953dfb0943ffdabf5875ecaaa6ddce604dec1048 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 20 Mar 2014 05:35:44 +0000 Subject: [PATCH 0019/1037] virtualenv --- winbuild/build.cmd | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/winbuild/build.cmd b/winbuild/build.cmd index 36532dd59..cd54fb2b4 100644 --- a/winbuild/build.cmd +++ b/winbuild/build.cmd @@ -8,7 +8,7 @@ rem Adjust the following if necessary set MPLSRC=%~dp0\.. set INCLIB=%~dp0\depends rem set BLDOPT=bdist_wininst --user-access-control=auto -set BLDOPT=build_ext +set BLDOPT=install cd /D %MPLSRC% rem Set TkAgg as default backend @@ -30,7 +30,8 @@ set INCLUDE=%INCLUDE%;%INCLIB%\msvcr90-x64;%INCLIB%\tcl85\include setlocal set LIB=%LIB%;C:\Python27x64\tcl rd /q /s build -call C:\Python27x64\python.exe setup.py %BLDOPT% +call z:\vpy27x64\Scripts\activate.bat +call python.exe setup.py %BLDOPT% endlocal endlocal From 354c6333f34404ffc2009c8598a6d5cfb0a64c7d Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 20 Mar 2014 05:36:08 +0000 Subject: [PATCH 0020/1037] tiff merge --- winbuild/build_dep.cmd | 3 +++ 1 file changed, 3 insertions(+) diff --git a/winbuild/build_dep.cmd b/winbuild/build_dep.cmd index c9af41730..f3f256427 100644 --- a/winbuild/build_dep.cmd +++ b/winbuild/build_dep.cmd @@ -89,6 +89,9 @@ endlocal rem Build libtiff setlocal +rem do after building jpeg and zlib +copy %~dp0\nmake.opt %TIFF% + cd /D %TIFF% nmake -f makefile.vc clean nmake -f makefile.vc From ebca9d64e9fbac5c66c9d358f94ddf2609d377e1 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 21 Mar 2014 16:52:05 -0700 Subject: [PATCH 0021/1037] build instructions -- inprogress --- docs/build.rst | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 docs/build.rst diff --git a/docs/build.rst b/docs/build.rst new file mode 100644 index 000000000..5d30ff39e --- /dev/null +++ b/docs/build.rst @@ -0,0 +1,83 @@ +Building Pillow on Windows +========================== + +.. note:: For most people, the :doc:`installation instructions + ` should be sufficient + +This page will describe a build setup to build Pillow against the +supported python versions in 32 and 64 bit modes, using freely +availble Microsoft compilers. This has been developed and tested +against a bare Windows Server 2012 64bit RTM version on Amazon EC2. + +Prerequsites +------------ + +Extra Build Helpers +^^^^^^^^^^^^^^^^^^^ + +* Google Chrome (optional - for sanity) +* GPG (for checking signatures) (UNDONE -- python signature checking) +* Powershell (available by default on Windows Server) +* Github client (provides git+bash shell) + +Pythons +^^^^^^^ + +Download and install Python 2.6, 2.7, 3.2, 3.3, and 3.4, both 32 and +64 bit versions. There is a python script that will download the +installers in `winbuild/get_pythons.py`. It requires python to run, so +download and install one of them first. + +:: + + for version in ['2.6.5', '2.7.6', '3.2.5', '3.3.5', '3.4.0']: + http://legacy.python.org/ftp/python/%s/python-%s['','.amd64'].msi['','.asc'] + + +UNDONE -- gpg verify the signatures + +We also need virtualenv and setuptools in at least one of the pythons +to build testing versions. + +Compilers +^^^^^^^^^ + +Download and install: + +* `Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 + SP1 `_ + +* `Microsoft Windows SDK for Windows 7 and .NET Framework + 4 `_ + +* `CMake-2.8.10.2-win32-x86.exe `_ + +The samples and the .NET SDK portions aren't required, just the +compilers and other tools. UNDONE -- check exact wording. + +Dependencies +------------ + +Run `winbuild/build_dep.cmd` in a command window. There are times when +the output clears the terminal, so it's best to run it in the +Powershell IDE, which has a more powerful terminal than the standard +Powershell window. + +This will download libjpeg, libtiff, libz, and freetype. It will then +compile 32 and 64 bit versions of the libraries, with both versions of +the compilers. + +UNDONE -- lcms fails. +UNDONE -- webp not included yet. + +Testing Builds +-------------- + +Use the script UNDONE to build, install, selftest, and test Pillow in +virtualenvs for each Python that is installed. + +Installer Builds +---------------- + +Run `winbuild/build.cmd` in a powershell terminal to build Pillow +installers against each of the Pythons. From 590691d23d96fa15a41d94e4f446afae3de358d2 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 24 Mar 2014 22:09:53 -0700 Subject: [PATCH 0022/1037] scripted python fetching, checksumming, build script writing --- winbuild/build_dep.py | 224 ++++++++++++++++++++++++++++++++++++++++ winbuild/fetch.py | 21 ++-- winbuild/get_pythons.py | 11 ++ winbuild/unzip.py | 9 +- 4 files changed, 257 insertions(+), 8 deletions(-) create mode 100644 winbuild/build_dep.py create mode 100644 winbuild/get_pythons.py diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py new file mode 100644 index 000000000..b01dcefe3 --- /dev/null +++ b/winbuild/build_dep.py @@ -0,0 +1,224 @@ +from fetch import fetch +from unzip import unzip +import os, hashlib + + +libs = { 'zlib':{ + 'url':'http://zlib.net/zlib128.zip', + 'hash': 'md5:126f8676442ffbd97884eb4d6f32afb4', + 'dir': 'zlib-1.2.8', + }, + 'jpeg':{ + 'url':'http://www.ijg.org/files/jpegsr9a.zip', + 'hash': 'md5:a34f3c82760270ee1e1885b15b90a72e', # not found - generated by wiredfool + 'dir': 'jpeg-9a', + }, + 'tiff':{ + 'url':'ftp://ftp.remotesensing.org/pub/libtiff/tiff-4.0.3.zip', + 'hash': 'md5:dd70349cedb3981371686e1c9b89a7f9', # not found - generated by wiredfool + 'dir': 'tiff-4.0.3', + }, + 'freetype':{ + 'url':'http://download.savannah.gnu.org/releases/freetype/ft253.zip', + 'hash': 'md5:b3858f7e69740ac04ef53366aeb172bc', # not found - generated by wiredfool + 'dir': 'freetype-2.5.3', + }, + 'lcms':{ + 'url':'http://hivelocity.dl.sourceforge.net/project/lcms/lcms/2.6/lcms2-2.6.zip', + 'hash': 'sha1:eea25f001246fa2e6b242ac456cecff7483cf061', + 'dir': 'lcms2-2.6', + }, + 'tcl':{ + 'url':'http://hivelocity.dl.sourceforge.net/project/tcl/Tcl/8.5.13/tcl8513-src.zip', + 'hash': 'sha1:3e01585c91293c532a3cd594ec59deca92153a5e', + 'dir': '', + }, + 'tk':{ + 'url':'http://hivelocity.dl.sourceforge.net/project/tcl/Tcl/8.5.13/tk8513-src.zip', + 'hash': 'sha1:23a1d7ddd416e11e06dfdb9f86111d4bab9420b4', + 'dir': '', + } + } + +compilers = [ { + 'env_version':'v7.0', + 'vc_version':'2008', + 'env_flags': '/x64 /vista', + 'inc_dir': 'msvcr90-x64', + 'platform': 'x64' + }, + { + 'env_version':'v7.0', + 'vc_version':'2008', + 'env_flags': '/x86 /xp', + 'inc_dir': 'msvcr90-x32', + 'platform': 'Win32' + }, + + { + 'env_version':'v7.1', + 'vc_version':'2010', + 'env_flags': '/x64 /vista', + 'inc_dir': 'msvcr10-x64', + 'platform': 'x64' + }, + { + 'env_version':'v7.1', + 'vc_version':'2010', + 'env_flags': '/x86 /xp', + 'inc_dir': 'msvcr10-x32', + 'platform': 'Win32' + }, + + ] + + +def _relpath(*args): + return os.path.join(os.getcwd(),*args) + +def _relbuild(*args): + return _relpath('build', *args) + +build_dir = _relpath('build') +inc_dir = _relpath('depends') + +def check_hash(filename, checksum): + if not checksum: return filename + + (algo, value) = checksum.split(':') + h = hashlib.new(algo) + with open(filename, 'rb') as f: + h.update(f.read()) + if not(h.hexdigest().lower() == value): + raise ValueError('Checksum Mismatch for %s' %filename) + return filename + +def check_sig(filename, signame): + #UNDONE -- need gpg + return filename + +def mkdirs(): + try: + os.mkdir(build_dir) + except: + pass + try: + os.mkdir(inc_dir) + except: + pass + +def fetch_libs(): + for lib in libs.values(): + unzip(check_hash(fetch(lib['url']),lib['hash']),build_dir) + +def cp_tk(): + return r""" +mkdir %INCLIB%\tcl85\include\X11 +copy /Y /B %BUILD%\tcl8.5.13\generic\*.h %INCLIB%\tcl85\include\ +copy /Y /B %BUILD%\tk8.5.13\generic\*.h %INCLIB%\tcl85\include\ +copy /Y /B %BUILD%\tk8.5.13\xlib\X11\* %INCLIB%\tcl85\include\X11\ +""" + +def header(): + return r"""setlocal +set MSBUILD=C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe +set CMAKE="C:\Program Files (x86)\CMake 2.8\bin\cmake.exe" +set INCLIB=%~dp0\depends +set BUILD=%~dp0\build +""" + "\n".join('set %s=%%BUILD%%\%s' %(k.upper(), v['dir']) + for (k,v) in libs.items() if v['dir']) + +def setup_compiler(compiler): + return r"""setlocal EnableDelayedExpansion +call "%%ProgramFiles%%\Microsoft SDKs\Windows\%(env_version)s\Bin\SetEnv.Cmd" /Release %(env_flags)s +set INCLIB=%%INCLIB%%\%(inc_dir)s +mkdir %%INCLIB%% +""" % compiler + +def end_compiler(): + return """ +endlocal +""" + +def nmake_libs(compiler): + # undone -- pre, makes, headers, libs + return r""" +rem Build libjpeg +setlocal +cd /D %s +nmake -f makefile.vc setup-vc6 +nmake -f makefile.vc clean +nmake -f makefile.vc all +copy /Y /B *.dll %%INCLIB%% +copy /Y /B *.lib %%INCLIB%% +copy /Y /B j*.h %%INCLIB%% +endlocal + +rem Build zlib +setlocal +cd /D %s +nmake -f win32\Makefile.msc clean +nmake -f win32\Makefile.msc +copy /Y /B *.dll %%INCLIB%% +copy /Y /B *.lib %%INCLIB%% +copy /Y /B zlib.lib %%INCLIB%%\z.lib +copy /Y /B zlib.h %%INCLIB%% +copy /Y /B zconf.h %%INCLIB%% +endlocal + +rem Build libtiff +setlocal +rem do after building jpeg and zlib +copy %%~dp0\nmake.opt %s + +cd /D %s +nmake -f makefile.vc clean +nmake -f makefile.vc +copy /Y /B libtiff\*.dll %%INCLIB%% +copy /Y /B libtiff\*.lib %%INCLIB%% +copy /Y /B libtiff\tiff*.h %%INCLIB%% +endlocal +""" %(_relbuild(libs['jpeg']['dir']), + _relbuild(libs['zlib']['dir']), + _relbuild(libs['tiff']['dir']), + _relbuild(libs['tiff']['dir']), + ) + + +def msbuild_libs(compiler): + return r""" +rem Build freetype +setlocal +py -3 %%~dp0\fixproj.py %%FREETYPE%%\builds\windows\vc%(vc_version)s\freetype.sln %(platform)s +py -3 %%~dp0\fixproj.py %%FREETYPE%%\builds\windows\vc%(vc_version)s\freetype.vcproj %(platform)s +rd /S /Q %%FREETYPE%%\objs +%%MSBUILD%% %%FREETYPE%%\builds\windows\vc%(vc_version)s\freetype.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=%(platform)s +xcopy /E /Q %%FREETYPE%%\include %%INCLIB%% +xcopy /E /Q %%FREETYPE%%\objs\win32\vc%(vc_version)s %%INCLIB%% +copy /Y /B %%FREETYPE%%\objs\win32\vc%(vc_version)s\*.lib %%INCLIB%%\freetype.lib +endlocal + +rem Build lcms2 +setlocal +py -3 %%~dp0\fixproj.py %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln %(platform)s +py -3 %%~dp0\fixproj.py %%LCMS%%\Projects\VC%(vc_version)s\lcms2.vcproj %(platform)s +rd /S /Q %%LCMS%%\objs +%%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=%(platform)s +xcopy /E /Q %%LCMS%%\include %%INCLIB%% +xcopy /E /Q %%LCMS%%\objs\win32\VC%(vc_version)s %%INCLIB%% +copy /Y /B %%LCMS%%\objs\win32\VC%(vc_version)s\*.lib %%INCLIB%%\lcms2.lib +endlocal +""" % compiler + +mkdirs() +fetch_libs() + +script = [header(), cp_tk()] + +for compiler in compilers: + script.append(setup_compiler(compiler)) + script.append(nmake_libs(compiler)) + script.append(msbuild_libs(compiler)) + script.append(end_compiler()) + +print ("\n".join(script)) diff --git a/winbuild/fetch.py b/winbuild/fetch.py index 00845678e..dfedb9835 100644 --- a/winbuild/fetch.py +++ b/winbuild/fetch.py @@ -1,7 +1,16 @@ import sys, os, urllib.parse, urllib.request -name = urllib.parse.urlsplit(sys.argv[1])[2].split('/')[-1] -if not os.path.exists(name): - print("Fetching", sys.argv[1]) - content = urllib.request.urlopen(sys.argv[1]).read() - with open(name, 'wb') as fd: - fd.write(content) + +def fetch(url): + name = urllib.parse.urlsplit(url)[2].split('/')[-1] + + if not os.path.exists(name): + print("Fetching", url) + content = urllib.request.urlopen(url).read() + with open(name, 'wb') as fd: + fd.write(content) + return name + +if __name__=='__main__': + fetch(sys.argv[1]) + + diff --git a/winbuild/get_pythons.py b/winbuild/get_pythons.py new file mode 100644 index 000000000..fcce5fe3e --- /dev/null +++ b/winbuild/get_pythons.py @@ -0,0 +1,11 @@ +from fetch import fetch + +if __name__=='__main__': + for version in ['2.6.5', '2.7.6', '3.2.5', '3.3.5', '3.4.0']: + for platform in ['', '.amd64']: + for extension in ['','.asc']: + fetch('https://www.python.org/ftp/python/%s/python-%s%s.msi%s' %( + version, version, platform, extension)) + + # find pip, if it's not in the path! + os.system('pip install virtualenv') diff --git a/winbuild/unzip.py b/winbuild/unzip.py index 9793906cb..18588433c 100644 --- a/winbuild/unzip.py +++ b/winbuild/unzip.py @@ -1,3 +1,8 @@ import sys, zipfile -with zipfile.ZipFile(sys.argv[1]) as zf: - zf.extractall(sys.argv[2]) + +def unzip(src, dest): + with zipfile.ZipFile(src) as zf: + zf.extractall(dest) + +if __name__=='__main__': + unzip(sys.argv[1], sys.argv[2]) From d0c2de89ab0c3e3b28f09fd5c044aa59182b3173 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 24 Mar 2014 22:38:45 -0700 Subject: [PATCH 0023/1037] inprogress -- webp --- winbuild/build_dep.py | 45 ++++++++++++++++++++++++++++++------------- winbuild/untar.py | 8 ++++++-- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index b01dcefe3..565802604 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -1,5 +1,6 @@ from fetch import fetch from unzip import unzip +from untar import untar import os, hashlib @@ -37,7 +38,12 @@ libs = { 'zlib':{ 'url':'http://hivelocity.dl.sourceforge.net/project/tcl/Tcl/8.5.13/tk8513-src.zip', 'hash': 'sha1:23a1d7ddd416e11e06dfdb9f86111d4bab9420b4', 'dir': '', - } + }, + 'webp':{ + 'url':'https://webp.googlecode.com/files/libwebp-0.4.0.tar.gz', + 'hash':'sha1:326c4b6787a01e5e32a9b30bae76442d18d2d1b6', + 'dir':'libwebp-0.4.0', + }, } compilers = [ { @@ -60,14 +66,14 @@ compilers = [ { 'vc_version':'2010', 'env_flags': '/x64 /vista', 'inc_dir': 'msvcr10-x64', - 'platform': 'x64' + 'platform': 'x64', }, { 'env_version':'v7.1', 'vc_version':'2010', 'env_flags': '/x86 /xp', 'inc_dir': 'msvcr10-x32', - 'platform': 'Win32' + 'platform': 'Win32', }, ] @@ -107,9 +113,15 @@ def mkdirs(): except: pass +def extract(src, dest): + if '.zip' in src: + return unzip(src, dest) + if '.tar.gz' in src or '.tgz' in src: + return untar(src, dest) + def fetch_libs(): for lib in libs.values(): - unzip(check_hash(fetch(lib['url']),lib['hash']),build_dir) + extract(check_hash(fetch(lib['url']),lib['hash']),build_dir) def cp_tk(): return r""" @@ -145,7 +157,7 @@ def nmake_libs(compiler): return r""" rem Build libjpeg setlocal -cd /D %s +cd /D %%JPEG%% nmake -f makefile.vc setup-vc6 nmake -f makefile.vc clean nmake -f makefile.vc all @@ -156,7 +168,7 @@ endlocal rem Build zlib setlocal -cd /D %s +cd /D %%ZLIB%% nmake -f win32\Makefile.msc clean nmake -f win32\Makefile.msc copy /Y /B *.dll %%INCLIB%% @@ -166,23 +178,30 @@ copy /Y /B zlib.h %%INCLIB%% copy /Y /B zconf.h %%INCLIB%% endlocal +rem Build webp +setlocal +cd /D %%WEBP%% +nmake -f Makefile.vc clean +nmake -f Makefile.vc CFG=release-static RTLIBCFG=static OBJDIR=output +copy /Y /B release-static\output\%(platform)s\* %%INCLIB%% +copy /Y /B src\webp\*.h %%INCLIB%% +endlocal + rem Build libtiff setlocal rem do after building jpeg and zlib -copy %%~dp0\nmake.opt %s +copy %%~dp0\nmake.opt %%TIFF%% -cd /D %s +cd /D %%TIFF%% nmake -f makefile.vc clean nmake -f makefile.vc copy /Y /B libtiff\*.dll %%INCLIB%% copy /Y /B libtiff\*.lib %%INCLIB%% copy /Y /B libtiff\tiff*.h %%INCLIB%% endlocal -""" %(_relbuild(libs['jpeg']['dir']), - _relbuild(libs['zlib']['dir']), - _relbuild(libs['tiff']['dir']), - _relbuild(libs['tiff']['dir']), - ) + + +""" % compiler def msbuild_libs(compiler): diff --git a/winbuild/untar.py b/winbuild/untar.py index 26a47678c..412ba4bf6 100644 --- a/winbuild/untar.py +++ b/winbuild/untar.py @@ -1,4 +1,8 @@ import sys, tarfile -with tarfile.open(sys.argv[1], 'r:gz') as tgz: - tgz.extractall(sys.argv[2]) +def untar(src, dest): + with tarfile.open(src, 'r:gz') as tgz: + tgz.extractall(dest) + +if __name__=='__main__': + untar(sys.argv[1],sys.argv[2]) From be8595853d32823c4abdc205c3132e1a92b7ae14 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 25 Mar 2014 09:00:41 -0700 Subject: [PATCH 0024/1037] tweaks --- docs/build.rst | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/build.rst b/docs/build.rst index 5d30ff39e..dbcb7f4ff 100644 --- a/docs/build.rst +++ b/docs/build.rst @@ -31,14 +31,19 @@ download and install one of them first. :: for version in ['2.6.5', '2.7.6', '3.2.5', '3.3.5', '3.4.0']: - http://legacy.python.org/ftp/python/%s/python-%s['','.amd64'].msi['','.asc'] + for platform in ['', '.amd64']: + for extension in ['','.asc']: + fetch('https://www.python.org/ftp/python/%s/python-%s%s.msi%s' %( + version, version, platform, extension)) - -UNDONE -- gpg verify the signatures +UNDONE -- gpg verify the signatures (note that we can download from https) We also need virtualenv and setuptools in at least one of the pythons to build testing versions. +Python 3.4 comes with pip. That makes it an ideal python to install +first. + Compilers ^^^^^^^^^ From a5377fd4b0687931ef8b50de6d7b995d290b4831 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 31 Mar 2014 19:50:52 -0700 Subject: [PATCH 0025/1037] apparently successful builds for everything ex. lcms2 --- winbuild/.gitignore | 6 ++++++ winbuild/build_dep.py | 31 +++++++++++++++++++------------ 2 files changed, 25 insertions(+), 12 deletions(-) create mode 100644 winbuild/.gitignore diff --git a/winbuild/.gitignore b/winbuild/.gitignore new file mode 100644 index 000000000..adc679fa8 --- /dev/null +++ b/winbuild/.gitignore @@ -0,0 +1,6 @@ +*.zip +*.tar.gz +*.msi +*.asc +__pycache__ +depends/ \ No newline at end of file diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index 565802604..d8697deff 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -3,6 +3,8 @@ from unzip import unzip from untar import untar import os, hashlib +SF_MIRROR = 'http://hivelocity.dl.sourceforge.net' +SF_MIRROR = 'http://iweb.dl.sourceforge.net' libs = { 'zlib':{ 'url':'http://zlib.net/zlib128.zip', @@ -25,17 +27,17 @@ libs = { 'zlib':{ 'dir': 'freetype-2.5.3', }, 'lcms':{ - 'url':'http://hivelocity.dl.sourceforge.net/project/lcms/lcms/2.6/lcms2-2.6.zip', + 'url':SF_MIRROR+'/project/lcms/lcms/2.6/lcms2-2.6.zip', 'hash': 'sha1:eea25f001246fa2e6b242ac456cecff7483cf061', 'dir': 'lcms2-2.6', }, 'tcl':{ - 'url':'http://hivelocity.dl.sourceforge.net/project/tcl/Tcl/8.5.13/tcl8513-src.zip', + 'url':SF_MIRROR+'/project/tcl/Tcl/8.5.13/tcl8513-src.zip', 'hash': 'sha1:3e01585c91293c532a3cd594ec59deca92153a5e', 'dir': '', }, 'tk':{ - 'url':'http://hivelocity.dl.sourceforge.net/project/tcl/Tcl/8.5.13/tk8513-src.zip', + 'url':SF_MIRROR+'/project/tcl/Tcl/8.5.13/tk8513-src.zip', 'hash': 'sha1:23a1d7ddd416e11e06dfdb9f86111d4bab9420b4', 'dir': '', }, @@ -211,9 +213,9 @@ setlocal py -3 %%~dp0\fixproj.py %%FREETYPE%%\builds\windows\vc%(vc_version)s\freetype.sln %(platform)s py -3 %%~dp0\fixproj.py %%FREETYPE%%\builds\windows\vc%(vc_version)s\freetype.vcproj %(platform)s rd /S /Q %%FREETYPE%%\objs -%%MSBUILD%% %%FREETYPE%%\builds\windows\vc%(vc_version)s\freetype.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=%(platform)s -xcopy /E /Q %%FREETYPE%%\include %%INCLIB%% -xcopy /E /Q %%FREETYPE%%\objs\win32\vc%(vc_version)s %%INCLIB%% +%%MSBUILD%% %%FREETYPE%%\builds\windows\vc%(vc_version)s\freetype.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=%(platform)s /m +xcopy /Y /E /Q %%FREETYPE%%\include %%INCLIB%% +xcopy /Y /E /Q %%FREETYPE%%\objs\win32\vc%(vc_version)s %%INCLIB%% copy /Y /B %%FREETYPE%%\objs\win32\vc%(vc_version)s\*.lib %%INCLIB%%\freetype.lib endlocal @@ -222,9 +224,9 @@ setlocal py -3 %%~dp0\fixproj.py %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln %(platform)s py -3 %%~dp0\fixproj.py %%LCMS%%\Projects\VC%(vc_version)s\lcms2.vcproj %(platform)s rd /S /Q %%LCMS%%\objs -%%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=%(platform)s -xcopy /E /Q %%LCMS%%\include %%INCLIB%% -xcopy /E /Q %%LCMS%%\objs\win32\VC%(vc_version)s %%INCLIB%% +%%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=%(platform)s /m +xcopy /Y /E /Q %%LCMS%%\include %%INCLIB%% +xcopy /Y /E /Q %%LCMS%%\objs\win32\VC%(vc_version)s %%INCLIB%% copy /Y /B %%LCMS%%\objs\win32\VC%(vc_version)s\*.lib %%INCLIB%%\lcms2.lib endlocal """ % compiler @@ -235,9 +237,14 @@ fetch_libs() script = [header(), cp_tk()] for compiler in compilers: +#if True: + # compiler = compilers[0] script.append(setup_compiler(compiler)) - script.append(nmake_libs(compiler)) + #script.append(nmake_libs(compiler)) script.append(msbuild_libs(compiler)) script.append(end_compiler()) - -print ("\n".join(script)) + +with open('build_deps.cmd', 'w') as f: + f.write("\n".join(script)) + + From 95b79926dedd22334452d0a0a2d9e102e70e005e Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 4 Apr 2014 13:00:52 -0700 Subject: [PATCH 0026/1037] factored out config --- winbuild/build_dep.py | 95 +++++++------------------------------------ winbuild/config.py | 89 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 80 deletions(-) create mode 100644 winbuild/config.py diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index d8697deff..b137b3224 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -3,83 +3,7 @@ from unzip import unzip from untar import untar import os, hashlib -SF_MIRROR = 'http://hivelocity.dl.sourceforge.net' -SF_MIRROR = 'http://iweb.dl.sourceforge.net' - -libs = { 'zlib':{ - 'url':'http://zlib.net/zlib128.zip', - 'hash': 'md5:126f8676442ffbd97884eb4d6f32afb4', - 'dir': 'zlib-1.2.8', - }, - 'jpeg':{ - 'url':'http://www.ijg.org/files/jpegsr9a.zip', - 'hash': 'md5:a34f3c82760270ee1e1885b15b90a72e', # not found - generated by wiredfool - 'dir': 'jpeg-9a', - }, - 'tiff':{ - 'url':'ftp://ftp.remotesensing.org/pub/libtiff/tiff-4.0.3.zip', - 'hash': 'md5:dd70349cedb3981371686e1c9b89a7f9', # not found - generated by wiredfool - 'dir': 'tiff-4.0.3', - }, - 'freetype':{ - 'url':'http://download.savannah.gnu.org/releases/freetype/ft253.zip', - 'hash': 'md5:b3858f7e69740ac04ef53366aeb172bc', # not found - generated by wiredfool - 'dir': 'freetype-2.5.3', - }, - 'lcms':{ - 'url':SF_MIRROR+'/project/lcms/lcms/2.6/lcms2-2.6.zip', - 'hash': 'sha1:eea25f001246fa2e6b242ac456cecff7483cf061', - 'dir': 'lcms2-2.6', - }, - 'tcl':{ - 'url':SF_MIRROR+'/project/tcl/Tcl/8.5.13/tcl8513-src.zip', - 'hash': 'sha1:3e01585c91293c532a3cd594ec59deca92153a5e', - 'dir': '', - }, - 'tk':{ - 'url':SF_MIRROR+'/project/tcl/Tcl/8.5.13/tk8513-src.zip', - 'hash': 'sha1:23a1d7ddd416e11e06dfdb9f86111d4bab9420b4', - 'dir': '', - }, - 'webp':{ - 'url':'https://webp.googlecode.com/files/libwebp-0.4.0.tar.gz', - 'hash':'sha1:326c4b6787a01e5e32a9b30bae76442d18d2d1b6', - 'dir':'libwebp-0.4.0', - }, - } - -compilers = [ { - 'env_version':'v7.0', - 'vc_version':'2008', - 'env_flags': '/x64 /vista', - 'inc_dir': 'msvcr90-x64', - 'platform': 'x64' - }, - { - 'env_version':'v7.0', - 'vc_version':'2008', - 'env_flags': '/x86 /xp', - 'inc_dir': 'msvcr90-x32', - 'platform': 'Win32' - }, - - { - 'env_version':'v7.1', - 'vc_version':'2010', - 'env_flags': '/x64 /vista', - 'inc_dir': 'msvcr10-x64', - 'platform': 'x64', - }, - { - 'env_version':'v7.1', - 'vc_version':'2010', - 'env_flags': '/x86 /xp', - 'inc_dir': 'msvcr10-x32', - 'platform': 'Win32', - }, - - ] - +from config import * def _relpath(*args): return os.path.join(os.getcwd(),*args) @@ -189,6 +113,17 @@ copy /Y /B release-static\output\%(platform)s\* %%INCLIB%% copy /Y /B src\webp\*.h %%INCLIB%% endlocal +rem build openjpeg +setlocal +cd /D %%OPENJPEG&& +%%CMAKE%% -DBUILD_THIRDPARTY:BOOL=ON . +nmake -f Makefile.vc clean +nmake -f Makefile.vc +copy /Y /B bin/* %%INCLIB%% +copy /Y /B src/lib/openjp2/openjpeg.h %%INCLIB%% +copy /Y /B src/lib/openjp2/opj_stdint.h %%INCLIB%% +endlocal + rem Build libtiff setlocal rem do after building jpeg and zlib @@ -236,11 +171,11 @@ fetch_libs() script = [header(), cp_tk()] -for compiler in compilers: +for compiler in compilers.values(): #if True: - # compiler = compilers[0] +# compiler = compilers[(7,64)] script.append(setup_compiler(compiler)) - #script.append(nmake_libs(compiler)) + script.append(nmake_libs(compiler)) script.append(msbuild_libs(compiler)) script.append(end_compiler()) diff --git a/winbuild/config.py b/winbuild/config.py new file mode 100644 index 000000000..75b2bc635 --- /dev/null +++ b/winbuild/config.py @@ -0,0 +1,89 @@ +SF_MIRROR = 'http://hivelocity.dl.sourceforge.net' +SF_MIRROR = 'http://iweb.dl.sourceforge.net' + +pythons = {'26':7, + '27':7, + '32':7, + '33':7.1, + '34':7.1} + +VIRT_BASE = "c:/vp/" + +libs = { 'zlib':{ + 'url':'http://zlib.net/zlib128.zip', + 'hash': 'md5:126f8676442ffbd97884eb4d6f32afb4', + 'dir': 'zlib-1.2.8', + }, + 'jpeg':{ + 'url':'http://www.ijg.org/files/jpegsr9a.zip', + 'hash': 'md5:a34f3c82760270ee1e1885b15b90a72e', # not found - generated by wiredfool + 'dir': 'jpeg-9a', + }, + 'tiff':{ + 'url':'ftp://ftp.remotesensing.org/pub/libtiff/tiff-4.0.3.zip', + 'hash': 'md5:dd70349cedb3981371686e1c9b89a7f9', # not found - generated by wiredfool + 'dir': 'tiff-4.0.3', + }, + 'freetype':{ + 'url':'http://download.savannah.gnu.org/releases/freetype/ft253.zip', + 'hash': 'md5:b3858f7e69740ac04ef53366aeb172bc', # not found - generated by wiredfool + 'dir': 'freetype-2.5.3', + }, + 'lcms':{ + 'url':SF_MIRROR+'/project/lcms/lcms/2.6/lcms2-2.6.zip', + 'hash': 'sha1:eea25f001246fa2e6b242ac456cecff7483cf061', + 'dir': 'lcms2-2.6', + }, + 'tcl':{ + 'url':SF_MIRROR+'/project/tcl/Tcl/8.5.13/tcl8513-src.zip', + 'hash': 'sha1:3e01585c91293c532a3cd594ec59deca92153a5e', + 'dir': '', + }, + 'tk':{ + 'url':SF_MIRROR+'/project/tcl/Tcl/8.5.13/tk8513-src.zip', + 'hash': 'sha1:23a1d7ddd416e11e06dfdb9f86111d4bab9420b4', + 'dir': '', + }, + 'webp':{ + 'url':'https://webp.googlecode.com/files/libwebp-0.4.0.tar.gz', + 'hash':'sha1:326c4b6787a01e5e32a9b30bae76442d18d2d1b6', + 'dir':'libwebp-0.4.0', + }, + 'openjpeg':{ + 'url':'https://openjpeg.googlecode.com/files/openjpeg-2.0.0.tar.gz', + 'hash':'sha1:0af78ab2283b43421458f80373422d8029a9f7a7', + 'dir':'openjpeg-2.0.0', + }, + } + +compilers = { (7,64): { + 'env_version':'v7.0', + 'vc_version':'2008', + 'env_flags': '/x64 /vista', + 'inc_dir': 'msvcr90-x64', + 'platform': 'x64' + }, + (7,32): { + 'env_version':'v7.0', + 'vc_version':'2008', + 'env_flags': '/x86 /xp', + 'inc_dir': 'msvcr90-x32', + 'platform': 'Win32' + }, + + (7.1,64): { + 'env_version':'v7.1', + 'vc_version':'2010', + 'env_flags': '/x64 /vista', + 'inc_dir': 'msvcr10-x64', + 'platform': 'x64', + }, + (7.1,32): { + 'env_version':'v7.1', + 'vc_version':'2010', + 'env_flags': '/x86 /xp', + 'inc_dir': 'msvcr10-x32', + 'platform': 'Win32', + }, + + } From 1ad678a5eab80240cfb2851ba073154b6192cef0 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 4 Apr 2014 13:01:07 -0700 Subject: [PATCH 0027/1037] build pillow for all the pythons --- winbuild/build.py | 55 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 winbuild/build.py diff --git a/winbuild/build.py b/winbuild/build.py new file mode 100644 index 000000000..5e04166e5 --- /dev/null +++ b/winbuild/build.py @@ -0,0 +1,55 @@ +#/usr/bin/env python34 + +from config import * + +def setup_vms(): + ret = [] + for py in pythons.keys(): + for arch in ('', 'x64'): + ret.append("virtualenv -p c:/Python%s%s/python.exe --clear %s%s%s" % + (py, arch, VIRT_BASE, py, arch)) + return "\n".join(ret) + +def header(op): + return r""" +setlocal +set MPLSRC=%%~dp0\.. +set INCLIB=%%~dp0\depends +set BLDOPT=%s +cd /D %%MPLSRC%% +""" % (op) + +def footer(): + return """endlocal +exit +""" + +def build_one(py_ver, compiler): + args = {} + args.update(compiler) + args['py_ver'] = py_ver + return r""" +setlocal EnableDelayedExpansion +call "%%ProgramFiles%%\Microsoft SDKs\Windows\%(env_version)s\Bin\SetEnv.Cmd" /Release %(env_flags)s +set DISTUTILS_USE_SDK=1 +set LIB=%%LIB%%;%%INCLIB%%\%(inc_dir)s +set INCLUDE=%%INCLUDE%%;%%INCLIB%%\%(inc_dir)s;%%INCLIB%%\tcl85\include + +setlocal +set LIB=%%LIB%%;C:\Python%(py_ver)s\tcl +rd /q /s build +call c:\vp\%(py_ver)s\Scripts\python.exe setup.py %%BLDOPT%% +endlocal + +endlocal +""" % args + +script = [setup_vms(), header('install')] +for py_version, compiler_version in pythons.items(): + script.append(build_one(py_version, compilers[(compiler_version, 32)])) + script.append(build_one("%sx64" %py_version, compilers[(compiler_version, 64)])) +script.append(footer()) + +with open('build_pillows.cmd', 'w') as f: + f.write("\n".join(script)) + From 4e6ea0fc42365e3a34c623165cff6dd08bbc9395 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 4 Apr 2014 14:57:18 -0700 Subject: [PATCH 0028/1037] test runner --- winbuild/test.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 winbuild/test.py diff --git a/winbuild/test.py b/winbuild/test.py new file mode 100644 index 000000000..43e394d66 --- /dev/null +++ b/winbuild/test.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 + +import subprocess, os, multiprocessing + +from config import * + +def test_one(params): + python, architecture = params + try: + print ("Running: %s, %s" %params) + command = [r'%s\%s%s\Scripts\python.exe' % (VIRT_BASE, python, architecture), + 'Tests/run.py', + '--installed'] + proc = subprocess.Popen(command, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + proc.stdin.close() + status = proc.wait() + print ("Waiting on read: %s, %s" % params) + trace = proc.stdout.read() + proc.stdout.close() + print ("Done with %s, %s -- %s" % (python, architecture, status )) + return (python, architecture, status, trace) + except Exception as msg: + print ("Error with %s, %s: %s" % (python, architecture, msg)) + return (python, architecture, -1, str(msg)) + + +if __name__=='__main__': + + os.chdir('..') + #pool = multiprocessing.Pool() + matrix = [(python, architecture) for python in pythons + for architecture in ('', 'x64')] + results = map(test_one, matrix) + + for (python, architecture, status, trace) in results: + print ("%s%s: %s" % (python, architecture, status)) + + + + + + + + From 2f7bbffb5b2cfbd3f6c18484632bf7ecfe3a5148 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 4 Apr 2014 15:18:14 -0700 Subject: [PATCH 0029/1037] waiting to read stdout was blocking when the buffer filled --- winbuild/test.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/winbuild/test.py b/winbuild/test.py index 43e394d66..22f80eefe 100644 --- a/winbuild/test.py +++ b/winbuild/test.py @@ -14,11 +14,8 @@ def test_one(params): proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE) - proc.stdin.close() - status = proc.wait() - print ("Waiting on read: %s, %s" % params) - trace = proc.stdout.read() - proc.stdout.close() + (trace, stderr) = proc.communicate() + status = proc.returncode print ("Done with %s, %s -- %s" % (python, architecture, status )) return (python, architecture, status, trace) except Exception as msg: @@ -29,16 +26,15 @@ def test_one(params): if __name__=='__main__': os.chdir('..') - #pool = multiprocessing.Pool() - matrix = [(python, architecture) for python in pythons + pool = multiprocessing.Pool() + matrix = [(python, architecture) for python in pythons for architecture in ('', 'x64')] - results = map(test_one, matrix) + + results = pool.map(test_one, matrix) for (python, architecture, status, trace) in results: print ("%s%s: %s" % (python, architecture, status)) - - From 251749b110472ebd179ee5b2adc125dbb1ce838a Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 4 Apr 2014 16:29:58 -0700 Subject: [PATCH 0030/1037] python/multiprocessing build process + clean/dist options --- winbuild/build.py | 79 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 70 insertions(+), 9 deletions(-) diff --git a/winbuild/build.py b/winbuild/build.py index 5e04166e5..277d19f1e 100644 --- a/winbuild/build.py +++ b/winbuild/build.py @@ -1,4 +1,8 @@ -#/usr/bin/env python34 +#/usr/bin/env python3 + +import subprocess, multiprocessing +import shutil +import sys, getopt from config import * @@ -10,6 +14,29 @@ def setup_vms(): (py, arch, VIRT_BASE, py, arch)) return "\n".join(ret) +def run_script(params): + (version, script) = params + try: + print ("Running %s" %version) + filename = 'build_pillow_%s.cmd' % version + with open(filename, 'w') as f: + f.write(script) + + command = ['powershell', "./%s" %filename] + proc = subprocess.Popen(command, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + (trace, stderr) = proc.communicate() + status = proc.returncode + print ("Done with %s: %s" % (version, status)) + return (version, status, trace, stderr) + except Exception as msg: + print ("Error with %s: %s" % (version, str(msg))) + return (version, -1, "", str(msg)) + + def header(op): return r""" setlocal @@ -37,19 +64,53 @@ set INCLUDE=%%INCLUDE%%;%%INCLIB%%\%(inc_dir)s;%%INCLIB%%\tcl85\include setlocal set LIB=%%LIB%%;C:\Python%(py_ver)s\tcl -rd /q /s build call c:\vp\%(py_ver)s\Scripts\python.exe setup.py %%BLDOPT%% endlocal endlocal """ % args -script = [setup_vms(), header('install')] -for py_version, compiler_version in pythons.items(): - script.append(build_one(py_version, compilers[(compiler_version, 32)])) - script.append(build_one("%sx64" %py_version, compilers[(compiler_version, 64)])) -script.append(footer()) +def clean(): + try: + shutil.rmtree('../build') + except: + # could already be removed + pass + run_script(('virtualenvs', setup_vms())) -with open('build_pillows.cmd', 'w') as f: - f.write("\n".join(script)) +def main(op): + scripts = [] + for py_version, compiler_version in pythons.items(): + scripts.append((py_version, + "\n".join([header(op), + build_one(py_version, + compilers[(compiler_version, 32)]), + footer()]))) + + scripts.append(("%sx64" % py_version, + "\n".join([header(op), + build_one("%sx64" %py_version, + compilers[(compiler_version, 64)]), + footer()]))) + + pool = multiprocessing.Pool() + results = pool.map(run_script, scripts) + + for (version, status, trace, err) in results: + print ("Compiled %s: %s" % (version, status)) + + + +if __name__=='__main__': + opts, args = getopt.getopt(sys.argv[1:], '', ['clean', 'dist']) + opts = dict(opts) + + if '--clean' in opts: + clean() + + op = 'install' + if '--dist' in opts: + op = "bdist_wininst --user-access-control=auto" + + main(op) From 3a484bab09fa6299f491cbf5f77ecdf2ea3c777c Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 4 Apr 2014 16:44:56 -0700 Subject: [PATCH 0031/1037] basic readme + pointer --- winbuild/README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 winbuild/README.md diff --git a/winbuild/README.md b/winbuild/README.md new file mode 100644 index 000000000..dd70e5b0c --- /dev/null +++ b/winbuild/README.md @@ -0,0 +1,13 @@ +Quick README +------------ + +For more extensive info, see the windows build instructions `docs/build.rst`. + +* See https://github.com/python-imaging/Pillow/issues/553#issuecomment-37877416 and https://github.com/matplotlib/matplotlib/issues/1717#issuecomment-13343859 + +* Works best with Python 3.4, due to virtualenv and pip batteries included. +* Check config.py for virtual env paths. +* `python get_pythons.py` downloads all the python releases, and their signatures. Install in `c:\PythonXX[x64]\`. +* `python build_dep.py` downloads and builds all the dependencies, in 32 and 64 bit versions, and with both compiler versions. +* `python build.py --clean` makes pillow for the matrix of pythons. +* `python test.py` runs the tests on pillow in all the virtual envs. \ No newline at end of file From 71ebbe492d0cbdc28366d7ea5622a09809aa0adb Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 4 Apr 2014 17:05:42 -0700 Subject: [PATCH 0032/1037] another pass on the docs --- docs/build.rst | 68 +++++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/docs/build.rst b/docs/build.rst index dbcb7f4ff..577c218d9 100644 --- a/docs/build.rst +++ b/docs/build.rst @@ -7,7 +7,8 @@ Building Pillow on Windows This page will describe a build setup to build Pillow against the supported python versions in 32 and 64 bit modes, using freely availble Microsoft compilers. This has been developed and tested -against a bare Windows Server 2012 64bit RTM version on Amazon EC2. +against 64bit Windows 7 Professional and a bare Windows Server 2012 +64bit RTM version on Amazon EC2. Prerequsites ------------ @@ -15,34 +16,35 @@ Prerequsites Extra Build Helpers ^^^^^^^^^^^^^^^^^^^ -* Google Chrome (optional - for sanity) -* GPG (for checking signatures) (UNDONE -- python signature checking) * Powershell (available by default on Windows Server) * Github client (provides git+bash shell) +Optional: +* GPG (for checking signatures) (UNDONE -- python signature checking) + + Pythons ^^^^^^^ -Download and install Python 2.6, 2.7, 3.2, 3.3, and 3.4, both 32 and -64 bit versions. There is a python script that will download the -installers in `winbuild/get_pythons.py`. It requires python to run, so -download and install one of them first. +The build routines expect Python to be installed at C:\PythonXX for 32 +bit versions or C:\PythonXXx64 for the 64 bit versions. -:: +Download Python 3.4.0, install it, and add it to the path. This is the +Python that we will use to bootstrap the build process. (The download +routines are using 3.2+ features, and installing 3.4 gives us pip and +virtualenv as well, reducing the number of packages that we need to +install.) - for version in ['2.6.5', '2.7.6', '3.2.5', '3.3.5', '3.4.0']: - for platform in ['', '.amd64']: - for extension in ['','.asc']: - fetch('https://www.python.org/ftp/python/%s/python-%s%s.msi%s' %( - version, version, platform, extension)) +Download the rest of the pythons by opening a command window, changing +to the `winbuild` directory, and running `python +get_pythons.py`. -UNDONE -- gpg verify the signatures (note that we can download from https) +UNDONE -- gpg verify the signatures (note that we can download from +https) -We also need virtualenv and setuptools in at least one of the pythons -to build testing versions. +Run each installer and set the proper path to the installation. Don't +set any of them as the default python, or add them to the path. -Python 3.4 comes with pip. That makes it an ideal python to install -first. Compilers ^^^^^^^^^ @@ -63,26 +65,30 @@ compilers and other tools. UNDONE -- check exact wording. Dependencies ------------ -Run `winbuild/build_dep.cmd` in a command window. There are times when -the output clears the terminal, so it's best to run it in the -Powershell IDE, which has a more powerful terminal than the standard -Powershell window. +The script 'build_dep.py' downloads and builds the dependencies. Open +a command window, change directory into `winbuild` and run `python +build_dep.py`. This will download libjpeg, libtiff, libz, and freetype. It will then compile 32 and 64 bit versions of the libraries, with both versions of the compilers. UNDONE -- lcms fails. -UNDONE -- webp not included yet. +UNDONE -- webp, jpeg2k not recognized -Testing Builds +Building Pillow +--------------- + +Once the dependencies are built, run `python build.py --clean` to +build and install Pillow in virtualenvs for each python +build. `build.py --dist` will build windows installers instead of +installing into virtualenvs. + +UNDONE -- suppressed output, what about failures. + +Testing Pillow -------------- -Use the script UNDONE to build, install, selftest, and test Pillow in -virtualenvs for each Python that is installed. +Build and install Pillow, then run `python test.py` from the +`winbuild` directory. -Installer Builds ----------------- - -Run `winbuild/build.cmd` in a powershell terminal to build Pillow -installers against each of the Pythons. From 91d3e0cc7fe4071cd2e1f2dad81540fae9e78c5c Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 6 May 2014 10:21:37 -0700 Subject: [PATCH 0033/1037] typo, s/&&/%% --- winbuild/build_dep.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index b137b3224..33443b4f8 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -115,7 +115,7 @@ endlocal rem build openjpeg setlocal -cd /D %%OPENJPEG&& +cd /D %%OPENJPEG%% %%CMAKE%% -DBUILD_THIRDPARTY:BOOL=ON . nmake -f Makefile.vc clean nmake -f Makefile.vc From 991aeed975f4f009f3af2cb7d6243e58d50b41d7 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 5 Jun 2014 00:09:50 +0000 Subject: [PATCH 0034/1037] debugging updates --- winbuild/build.py | 1 + winbuild/build_dep.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/winbuild/build.py b/winbuild/build.py index 277d19f1e..ad43f0b13 100644 --- a/winbuild/build.py +++ b/winbuild/build.py @@ -30,6 +30,7 @@ def run_script(params): ) (trace, stderr) = proc.communicate() status = proc.returncode + print (stderr) print ("Done with %s: %s" % (version, status)) return (version, status, trace, stderr) except Exception as msg: diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index b137b3224..33443b4f8 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -115,7 +115,7 @@ endlocal rem build openjpeg setlocal -cd /D %%OPENJPEG&& +cd /D %%OPENJPEG%% %%CMAKE%% -DBUILD_THIRDPARTY:BOOL=ON . nmake -f Makefile.vc clean nmake -f Makefile.vc From 81af4bf9d7908beb0b3567a91e8482bc2792c21b Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 5 Jun 2014 22:17:47 +0000 Subject: [PATCH 0035/1037] successful build of openjpeg -- unsuccessful linking --- winbuild/build_dep.py | 44 +++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index 33443b4f8..ff4546fe1 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -46,8 +46,16 @@ def extract(src, dest): return untar(src, dest) def fetch_libs(): - for lib in libs.values(): - extract(check_hash(fetch(lib['url']),lib['hash']),build_dir) + for name,lib in libs.items(): + if name == 'openjpeg': + filename = check_hash(fetch(lib['url']),lib['hash']) + for compiler in compilers.values(): + if not os.path.exists(os.path.join(build_dir,lib['dir']+compiler['inc_dir'])): + extract(filename, build_dir) + os.rename(os.path.join(build_dir,lib['dir']), + os.path.join(build_dir,lib['dir']+compiler['inc_dir'])) + else: + extract(check_hash(fetch(lib['url']),lib['hash']),build_dir) def cp_tk(): return r""" @@ -78,6 +86,21 @@ def end_compiler(): endlocal """ +def nmake_openjpeg(compiler): + return r""" +rem build openjpeg +setlocal +@echo on +cd /D %%OPENJPEG%%%(inc_dir)s +%%CMAKE%% -DBUILD_THIRDPARTY:BOOL=ON -G "NMake Makefiles" . +nmake -f Makefile clean +nmake -f Makefile +copy /Y /B bin\* %%INCLIB%% +mkdir %%INCLIB%%\openjpeg-2.0 +copy /Y /B src\lib\openjp2\*.h %%INCLIB%%\openjpeg-2.0 +endlocal +""" % compiler + def nmake_libs(compiler): # undone -- pre, makes, headers, libs return r""" @@ -115,13 +138,13 @@ endlocal rem build openjpeg setlocal -cd /D %%OPENJPEG%% -%%CMAKE%% -DBUILD_THIRDPARTY:BOOL=ON . -nmake -f Makefile.vc clean -nmake -f Makefile.vc -copy /Y /B bin/* %%INCLIB%% -copy /Y /B src/lib/openjp2/openjpeg.h %%INCLIB%% -copy /Y /B src/lib/openjp2/opj_stdint.h %%INCLIB%% +cd /D %%OPENJPEG%%%(inc_dir)s +%%CMAKE%% -DBUILD_THIRDPARTY:BOOL=ON -G "NMake Makefiles" . +nmake -f Makefile clean +nmake -f Makefile +copy /Y /B bin\* %%INCLIB%% +mkdir %%INCLIB%%\openjpeg-2.0 +copy /Y /B src\lib\openjp2\*.h %%INCLIB%%\openjpeg-2.0 endlocal rem Build libtiff @@ -169,13 +192,14 @@ endlocal mkdirs() fetch_libs() -script = [header(), cp_tk()] +script = [header()] #, cp_tk()] for compiler in compilers.values(): #if True: # compiler = compilers[(7,64)] script.append(setup_compiler(compiler)) script.append(nmake_libs(compiler)) +# script.append(nmake_openjpeg(compiler)) script.append(msbuild_libs(compiler)) script.append(end_compiler()) From 7a1563576ed8c7ee2be0a3a1259a25961909e1ae Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 11 Jun 2014 05:22:28 +0000 Subject: [PATCH 0036/1037] working on openjpeg --- winbuild/build_dep.py | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index 33443b4f8..0085d625b 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -78,6 +78,22 @@ def end_compiler(): endlocal """ +def nmake_openjpeg(compiler): + return r""" +rem build openjpeg +setlocal +@echo on +cd /D %%OPENJPEG%% +nmake -f Makefile clean +%%CMAKE%% -DBUILD_THIRDPARTY:BOOL=ON -G "NMake Makefiles" . +nmake -f Makefile +copy /Y /B bin/* %%INCLIB%% +mkdir %%INCLIB%%/openjpeg-2.0 +copy /Y /B src/lib/openjp2/openjpeg.h %%INCLIB%%/openjpeg-2.0 +copy /Y /B src/lib/openjp2/opj_stdint.h %%INCLIB%%/openjpeg-2.0 +endlocal +""" % compiler + def nmake_libs(compiler): # undone -- pre, makes, headers, libs return r""" @@ -116,12 +132,13 @@ endlocal rem build openjpeg setlocal cd /D %%OPENJPEG%% -%%CMAKE%% -DBUILD_THIRDPARTY:BOOL=ON . -nmake -f Makefile.vc clean -nmake -f Makefile.vc +%%CMAKE%% -DBUILD_THIRDPARTY:BOOL=ON -G "NMake Makefiles" . +nmake -f Makefile clean +nmake -f Makefile copy /Y /B bin/* %%INCLIB%% -copy /Y /B src/lib/openjp2/openjpeg.h %%INCLIB%% -copy /Y /B src/lib/openjp2/opj_stdint.h %%INCLIB%% +mkdir %%INCLIB%%/openjpeg-2.0 +copy /Y /B src/lib/openjp2/openjpeg.h %%INCLIB%%/openjpeg-2.0 +copy /Y /B src/lib/openjp2/opj_stdint.h %%INCLIB%%/openjpeg-2.0 endlocal rem Build libtiff @@ -169,14 +186,15 @@ endlocal mkdirs() fetch_libs() -script = [header(), cp_tk()] +script = [header()] #, cp_tk()] for compiler in compilers.values(): #if True: # compiler = compilers[(7,64)] script.append(setup_compiler(compiler)) - script.append(nmake_libs(compiler)) - script.append(msbuild_libs(compiler)) +# script.append(nmake_libs(compiler)) + script.append(nmake_openjpeg(compiler)) +# script.append(msbuild_libs(compiler)) script.append(end_compiler()) with open('build_deps.cmd', 'w') as f: From 4f32ddeec467a8bb449d9b25eb319bdfbf2efe91 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 11 Jun 2014 06:36:03 +0000 Subject: [PATCH 0037/1037] non-workign attempt at trying the openjpeg binaries --- winbuild/build_dep.py | 51 ++++++++++++++++++++++++++++++------------- winbuild/config.py | 9 +++++--- 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index ff4546fe1..d90c68b1d 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -2,6 +2,7 @@ from fetch import fetch from unzip import unzip from untar import untar import os, hashlib +import shutil from config import * @@ -38,6 +39,11 @@ def mkdirs(): os.mkdir(inc_dir) except: pass + for compiler in compilers.values(): + try: + os.mkdir(os.path.join(inc_dir, compiler['inc_dir'])) + except: + pass def extract(src, dest): if '.zip' in src: @@ -57,6 +63,32 @@ def fetch_libs(): else: extract(check_hash(fetch(lib['url']),lib['hash']),build_dir) +def extract_binlib(): + lib = bin_libs['openjpeg'] + extract(lib['filename'], build_dir) + return + base = os.path.splitext(lib['filename'])[0] + for compiler in compilers.values(): + shutil.copy(os.path.join(inc_dir, base, 'include', 'openjpeg-%s' % lib['version']), + os.path.join(inc_dir, compiler['inc_dir'])) + shutil.copy(os.path.join(inc_dir, base, 'bin', 'openjp2.dll'), + os.path.join(inc_dir, compiler['inc_dir'])) + shutil.copy(os.path.join(inc_dir, base, 'lib', 'openjp2.lib'), + os.path.join(inc_dir, compiler['inc_dir'])) + +def extract_openjpeg(compiler): + return r""" +rem build openjpeg +setlocal +@echo on +cd %%BUILD%% +mkdir %%INCLIB%%\openjpeg-2.0 +copy /Y /B openjpeg-2.0.0-win32-x86\include\openjpeg-2.0 %%INCLIB%%\openjpeg-2.0 +copy /Y /B openjpeg-2.0.0-win32-x86\bin\ %%INCLIB%% +copy /Y /B openjpeg-2.0.0-win32-x86\lib\ %%INCLIB%% +endlocal +""" % compiler + def cp_tk(): return r""" mkdir %INCLIB%\tcl85\include\X11 @@ -78,7 +110,6 @@ def setup_compiler(compiler): return r"""setlocal EnableDelayedExpansion call "%%ProgramFiles%%\Microsoft SDKs\Windows\%(env_version)s\Bin\SetEnv.Cmd" /Release %(env_flags)s set INCLIB=%%INCLIB%%\%(inc_dir)s -mkdir %%INCLIB%% """ % compiler def end_compiler(): @@ -136,17 +167,6 @@ copy /Y /B release-static\output\%(platform)s\* %%INCLIB%% copy /Y /B src\webp\*.h %%INCLIB%% endlocal -rem build openjpeg -setlocal -cd /D %%OPENJPEG%%%(inc_dir)s -%%CMAKE%% -DBUILD_THIRDPARTY:BOOL=ON -G "NMake Makefiles" . -nmake -f Makefile clean -nmake -f Makefile -copy /Y /B bin\* %%INCLIB%% -mkdir %%INCLIB%%\openjpeg-2.0 -copy /Y /B src\lib\openjp2\*.h %%INCLIB%%\openjpeg-2.0 -endlocal - rem Build libtiff setlocal rem do after building jpeg and zlib @@ -191,6 +211,7 @@ endlocal mkdirs() fetch_libs() +extract_binlib() script = [header()] #, cp_tk()] @@ -198,9 +219,9 @@ for compiler in compilers.values(): #if True: # compiler = compilers[(7,64)] script.append(setup_compiler(compiler)) - script.append(nmake_libs(compiler)) -# script.append(nmake_openjpeg(compiler)) - script.append(msbuild_libs(compiler)) +# script.append(nmake_libs(compiler)) + script.append(extract_openjpeg(compiler)) + # script.append(msbuild_libs(compiler)) script.append(end_compiler()) with open('build_deps.cmd', 'w') as f: diff --git a/winbuild/config.py b/winbuild/config.py index 75b2bc635..1a0eff8c4 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -49,10 +49,13 @@ libs = { 'zlib':{ 'hash':'sha1:326c4b6787a01e5e32a9b30bae76442d18d2d1b6', 'dir':'libwebp-0.4.0', }, + } + +bin_libs = { 'openjpeg':{ - 'url':'https://openjpeg.googlecode.com/files/openjpeg-2.0.0.tar.gz', - 'hash':'sha1:0af78ab2283b43421458f80373422d8029a9f7a7', - 'dir':'openjpeg-2.0.0', + 'filename':'openjpeg-2.0.0-win32-x86.zip', + 'hash':'sha1:xxx', + 'version':'2.0' }, } From b2ea0941f7428d0684c447d89834a495459c5821 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 22 Aug 2014 16:14:19 -0700 Subject: [PATCH 0038/1037] working lcms2 build --- setup.py | 12 +++++---- winbuild/build_dep.py | 58 ++++++++++++++++++++++++++++++++----------- 2 files changed, 50 insertions(+), 20 deletions(-) diff --git a/setup.py b/setup.py index 5cf0e5e65..83dbb4421 100644 --- a/setup.py +++ b/setup.py @@ -465,7 +465,10 @@ class pil_build_ext(build_ext): if feature.want('lcms'): if _find_include_file(self, "lcms2.h"): if _find_library_file(self, "lcms2"): - feature.lcms = "lcms" + feature.lcms = "lcms2" + elif _find_library_file(self, "lcms2_static"): + #alternate windows name. + feature.lcms = "lcms2_static" if _tkinter and _find_include_file(self, "tk.h"): # the library names may vary somewhat (e.g. tcl84 or tcl8.4) @@ -556,7 +559,7 @@ class pil_build_ext(build_ext): exts.append(Extension( "PIL._imagingcms", ["_imagingcms.c"], - libraries=["lcms2"] + extra)) + libraries=[feature.lcms] + extra)) if os.path.isfile("_webp.c") and feature.webp: libs = ["webp"] @@ -749,6 +752,5 @@ setup( test_suite='PIL.tests', keywords=["Imaging", ], license='Standard PIL License', - zip_safe=True, -) -# End of file + zip_safe=False, + ) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index d90c68b1d..92e454631 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -118,19 +118,41 @@ endlocal """ def nmake_openjpeg(compiler): + atts = {'op_ver': '2.1'} + atts.update(compiler) return r""" rem build openjpeg setlocal @echo on cd /D %%OPENJPEG%%%(inc_dir)s -%%CMAKE%% -DBUILD_THIRDPARTY:BOOL=ON -G "NMake Makefiles" . +%%CMAKE%% -DBUILD_THIRDPARTY:BOOL=OFF -G "NMake Makefiles" . nmake -f Makefile clean nmake -f Makefile copy /Y /B bin\* %%INCLIB%% -mkdir %%INCLIB%%\openjpeg-2.0 -copy /Y /B src\lib\openjp2\*.h %%INCLIB%%\openjpeg-2.0 +mkdir %%INCLIB%%\openjpeg-%(op_ver)s +copy /Y /B src\lib\openjp2\*.h %%INCLIB%%\openjpeg-%(op_ver)s endlocal -""" % compiler +""" % atts + +def msbuild_openjpeg(compiler): + atts = {'op_ver': '2.1'} + atts.update(compiler) + return r""" +rem build openjpeg +setlocal +@echo on +cd /D %%OPENJPEG%%%(inc_dir)s + +%%CMAKE%% -DBUILD_THIRDPARTY:BOOL=OFF -G "NMake Makefiles" . +nmake -f Makefile clean +nmake -f Makefile +copy /Y /B bin\* %%INCLIB%% +mkdir %%INCLIB%%\openjpeg-%(op_ver)s +copy /Y /B src\lib\openjp2\*.h %%INCLIB%%\openjpeg-%(op_ver)s +endlocal +""" % atts + + def nmake_libs(compiler): # undone -- pre, makes, headers, libs @@ -184,7 +206,7 @@ endlocal """ % compiler -def msbuild_libs(compiler): +def msbuild_freetype(compiler): return r""" rem Build freetype setlocal @@ -196,32 +218,38 @@ xcopy /Y /E /Q %%FREETYPE%%\include %%INCLIB%% xcopy /Y /E /Q %%FREETYPE%%\objs\win32\vc%(vc_version)s %%INCLIB%% copy /Y /B %%FREETYPE%%\objs\win32\vc%(vc_version)s\*.lib %%INCLIB%%\freetype.lib endlocal +""" %compiler +def build_lcms2(compiler): + return r""" rem Build lcms2 setlocal -py -3 %%~dp0\fixproj.py %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln %(platform)s -py -3 %%~dp0\fixproj.py %%LCMS%%\Projects\VC%(vc_version)s\lcms2.vcproj %(platform)s -rd /S /Q %%LCMS%%\objs -%%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=%(platform)s /m +rd /S /Q %%LCMS%%\Lib +%%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:Clean /p:Configuration="Release" /p:Platform=%(platform)s /m +%%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:lcms2_static /p:Configuration="Release" /p:Platform=%(platform)s /m xcopy /Y /E /Q %%LCMS%%\include %%INCLIB%% -xcopy /Y /E /Q %%LCMS%%\objs\win32\VC%(vc_version)s %%INCLIB%% -copy /Y /B %%LCMS%%\objs\win32\VC%(vc_version)s\*.lib %%INCLIB%%\lcms2.lib +copy /Y /B %%LCMS%%\Projects\VC%(vc_version)s\Release\*.lib %%INCLIB%% +copy /Y /B %%LCMS%%\Lib\MS\*.lib %%INCLIB%% endlocal """ % compiler mkdirs() fetch_libs() -extract_binlib() +#extract_binlib() script = [header()] #, cp_tk()] for compiler in compilers.values(): #if True: -# compiler = compilers[(7,64)] +# compiler = compilers[(7,32)] script.append(setup_compiler(compiler)) # script.append(nmake_libs(compiler)) - script.append(extract_openjpeg(compiler)) - # script.append(msbuild_libs(compiler)) + +# script.append(extract_openjpeg(compiler)) +# +# script.append(build_freetype(compiler)) + script.append(build_lcms2(compiler)) +# script.append(nmake_openjpeg(compiler)) script.append(end_compiler()) with open('build_deps.cmd', 'w') as f: From 97b5c72630c553722ed79bc5ac152633f95be28a Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 22 Aug 2014 16:14:48 -0700 Subject: [PATCH 0039/1037] backup implementation of Round for windows platforms --- libImaging/Convert.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libImaging/Convert.c b/libImaging/Convert.c index 46a6cfb72..3d9119c7f 100644 --- a/libImaging/Convert.c +++ b/libImaging/Convert.c @@ -49,6 +49,12 @@ #define L(rgb)\ ((INT32) (rgb)[0]*299 + (INT32) (rgb)[1]*587 + (INT32) (rgb)[2]*114) +#ifndef round +double round(double x) { + return floor(x+0.5); +} +#endif + /* ------------------- */ /* 1 (bit) conversions */ /* ------------------- */ From cd44e1427a6437aea03a72612bd728cf563e09aa Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 22 Aug 2014 16:15:22 -0700 Subject: [PATCH 0040/1037] mp is a little twitchy on windows --- mp_compile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mp_compile.py b/mp_compile.py index b7c3e7ce4..adbac7c19 100644 --- a/mp_compile.py +++ b/mp_compile.py @@ -3,7 +3,7 @@ from multiprocessing import Pool, cpu_count from distutils.ccompiler import CCompiler -import os +import os, sys try: MAX_PROCS = int(os.environ.get('MAX_CONCURRENCY', cpu_count())) @@ -50,7 +50,7 @@ def _mp_compile(self, sources, output_dir=None, macros=None, return objects # explicitly don't enable if environment says 1 processor -if MAX_PROCS != 1: +if MAX_PROCS != 1 and not sys.platform.startswith('win'): try: # bug, only enable if we can make a Pool. see issue #790 and # http://stackoverflow.com/questions/6033599/oserror-38-errno-38-with-multiprocessing From f6dd5d3dbb05c7ed113b635d3f76383b44f7e4e8 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 22 Aug 2014 16:15:37 -0700 Subject: [PATCH 0041/1037] new openjpeg2k --- winbuild/config.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/winbuild/config.py b/winbuild/config.py index 1a0eff8c4..5719e8779 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -49,6 +49,18 @@ libs = { 'zlib':{ 'hash':'sha1:326c4b6787a01e5e32a9b30bae76442d18d2d1b6', 'dir':'libwebp-0.4.0', }, +# 'openjpeg':{ +# 'url':'https://openjpeg.googlecode.com/files/openjpeg-2.0.0.tar.gz', +# 'hash':'sha1:0af78ab2283b43421458f80373422d8029a9f7a7', +# 'dir':'openjpeg-2.0.0', +# }, + + 'openjpeg':{ + 'url':SF_MIRROR+'/project/openjpeg/openjpeg/2.1.0/openjpeg-2.1.0.tar.gz', + 'hash':'md5:f6419fcc233df84f9a81eb36633c6db6', + 'dir':'openjpeg-2.1.0', + }, + } bin_libs = { From aa105bf89d1f83001c83896eb7193198a0a0f2f7 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 22 Aug 2014 17:04:26 -0700 Subject: [PATCH 0042/1037] allowing for libwebp* library names --- setup.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 83dbb4421..00715ee47 100644 --- a/setup.py +++ b/setup.py @@ -490,6 +490,8 @@ class pil_build_ext(build_ext): # In Google's precompiled zip it is call "libwebp": if _find_library_file(self, "webp"): feature.webp = "webp" + elif _find_library_file(self, "libwebp"): + feature.webp = "libwebp" if feature.want('webpmux'): if (_find_include_file(self, "webp/mux.h") and @@ -497,6 +499,9 @@ class pil_build_ext(build_ext): if (_find_library_file(self, "webpmux") and _find_library_file(self, "webpdemux")): feature.webpmux = "webpmux" + if (_find_library_file(self, "libwebpmux") and + _find_library_file(self, "libwebpdemux")): + feature.webpmux = "libwebpmux" for f in feature: if not getattr(feature, f) and feature.require(f): @@ -562,13 +567,13 @@ class pil_build_ext(build_ext): libraries=[feature.lcms] + extra)) if os.path.isfile("_webp.c") and feature.webp: - libs = ["webp"] + libs = [feature.webp] defs = [] if feature.webpmux: defs.append(("HAVE_WEBPMUX", None)) - libs.append("webpmux") - libs.append("webpdemux") + libs.append(feature.webpmux) + libs.append(feature.webpmux.replace('pmux','pdemux')) exts.append(Extension( "PIL._webp", ["_webp.c"], libraries=libs, define_macros=defs)) From e99631125df736b711d8b91f55845200a36f5210 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 22 Aug 2014 17:06:11 -0700 Subject: [PATCH 0043/1037] working webp build --- winbuild/build_dep.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index 92e454631..12c4cf2d6 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -183,10 +183,11 @@ endlocal rem Build webp setlocal cd /D %%WEBP%% -nmake -f Makefile.vc clean -nmake -f Makefile.vc CFG=release-static RTLIBCFG=static OBJDIR=output -copy /Y /B release-static\output\%(platform)s\* %%INCLIB%% -copy /Y /B src\webp\*.h %%INCLIB%% +rd /S /Q %%WEBP%%\output\release-static +nmake -f Makefile.vc CFG=release-static RTLIBCFG=static OBJDIR=output all +copy /Y /B output\release-static\%(platform)s\lib\* %%INCLIB%% +mkdir %%INCLIB%%\webp +copy /Y /B src\webp\*.h %%INCLIB%%\\webp endlocal rem Build libtiff From c95515ec528a44167903db14fbfeb3239c788136 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 22 Aug 2014 17:06:30 -0700 Subject: [PATCH 0044/1037] only build tiff libs --- winbuild/build_dep.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index 12c4cf2d6..db4762b25 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -197,7 +197,7 @@ copy %%~dp0\nmake.opt %%TIFF%% cd /D %%TIFF%% nmake -f makefile.vc clean -nmake -f makefile.vc +nmake -f makefile.vc lib copy /Y /B libtiff\*.dll %%INCLIB%% copy /Y /B libtiff\*.lib %%INCLIB%% copy /Y /B libtiff\tiff*.h %%INCLIB%% From 4e3bc5558dd51648df7e78051ffc2e2a8e6343c1 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 22 Aug 2014 17:07:08 -0700 Subject: [PATCH 0045/1037] clean all of lcms2 --- winbuild/build_dep.py | 1 + 1 file changed, 1 insertion(+) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index db4762b25..4500a473f 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -226,6 +226,7 @@ def build_lcms2(compiler): rem Build lcms2 setlocal rd /S /Q %%LCMS%%\Lib +rd /S /Q %%LCMS%%\Projects\VC%(vc_version)s\Release %%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:Clean /p:Configuration="Release" /p:Platform=%(platform)s /m %%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:lcms2_static /p:Configuration="Release" /p:Platform=%(platform)s /m xcopy /Y /E /Q %%LCMS%%\include %%INCLIB%% From b14e3daea159b6636013ec64edf1f92c0b8d6a13 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 22 Aug 2014 20:49:29 -0700 Subject: [PATCH 0046/1037] Tests need to pass without imagemagick around --- Tests/test_file_palm.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_palm.py b/Tests/test_file_palm.py index 388df0237..9598b8cbc 100644 --- a/Tests/test_file_palm.py +++ b/Tests/test_file_palm.py @@ -26,8 +26,11 @@ class TestFilePalm(PillowTestCase): outfile = self.tempfile("temp.palm") im.save(outfile) - converted = self.open_withImagemagick(outfile) - self.assert_image_equal(converted, im) + try: + converted = self.open_withImagemagick(outfile) + self.assert_image_equal(converted, im) + except IOError: + pass def test_monochrome(self): From 9c6a9172c0e38b753eda817b00590539cb2f7f9c Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 22 Aug 2014 21:35:01 -0700 Subject: [PATCH 0047/1037] works with nose/unittest --- winbuild/build.py | 5 +++++ winbuild/test.py | 9 ++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/winbuild/build.py b/winbuild/build.py index ad43f0b13..c25faaecf 100644 --- a/winbuild/build.py +++ b/winbuild/build.py @@ -12,6 +12,11 @@ def setup_vms(): for arch in ('', 'x64'): ret.append("virtualenv -p c:/Python%s%s/python.exe --clear %s%s%s" % (py, arch, VIRT_BASE, py, arch)) + ret.append("%s%s%s\Scripts\pip.exe install nose" % + (VIRT_BASE, py, arch)) + if py == '26': + ret.append("%s%s%s\Scripts\pip.exe install unittest2" % + (VIRT_BASE, py, arch)) return "\n".join(ret) def run_script(params): diff --git a/winbuild/test.py b/winbuild/test.py index 22f80eefe..229b724cb 100644 --- a/winbuild/test.py +++ b/winbuild/test.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -import subprocess, os, multiprocessing +import subprocess, os, multiprocessing, glob from config import * @@ -9,8 +9,11 @@ def test_one(params): try: print ("Running: %s, %s" %params) command = [r'%s\%s%s\Scripts\python.exe' % (VIRT_BASE, python, architecture), - 'Tests/run.py', - '--installed'] + 'test-installed.py', + '--processes=-0', + '--process-timeout=30', + ] + command.extend(glob.glob('Tests/test*.py')) proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE) From 3e219d818c2b59e44c342dc21331ead061ccb330 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 22 Aug 2014 21:35:56 -0700 Subject: [PATCH 0048/1037] just build the libs, not the progs --- winbuild/build_dep.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index 4500a473f..2e9118fd6 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -162,7 +162,7 @@ setlocal cd /D %%JPEG%% nmake -f makefile.vc setup-vc6 nmake -f makefile.vc clean -nmake -f makefile.vc all +nmake -f makefile.vc libjpeg.lib copy /Y /B *.dll %%INCLIB%% copy /Y /B *.lib %%INCLIB%% copy /Y /B j*.h %%INCLIB%% @@ -172,7 +172,7 @@ rem Build zlib setlocal cd /D %%ZLIB%% nmake -f win32\Makefile.msc clean -nmake -f win32\Makefile.msc +nmake -f win32\Makefile.msc zlib.lib copy /Y /B *.dll %%INCLIB%% copy /Y /B *.lib %%INCLIB%% copy /Y /B zlib.lib %%INCLIB%%\z.lib From 304e8add466417d23c696622b5b0f98a8bca70ca Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 22 Aug 2014 21:36:28 -0700 Subject: [PATCH 0049/1037] working build on all 4 compilers --- winbuild/config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/winbuild/config.py b/winbuild/config.py index 5719e8779..467167f1f 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -74,7 +74,7 @@ bin_libs = { compilers = { (7,64): { 'env_version':'v7.0', 'vc_version':'2008', - 'env_flags': '/x64 /vista', + 'env_flags': '/x64 /xp', 'inc_dir': 'msvcr90-x64', 'platform': 'x64' }, @@ -96,7 +96,7 @@ compilers = { (7,64): { (7.1,32): { 'env_version':'v7.1', 'vc_version':'2010', - 'env_flags': '/x86 /xp', + 'env_flags': '/x86 /vista', 'inc_dir': 'msvcr10-x32', 'platform': 'Win32', }, From fb788b85a9f09273314b4118272f130477d45393 Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 29 Oct 2014 21:28:29 +0200 Subject: [PATCH 0050/1037] Cache hopper to reduce FS reads and speed up tests --- Tests/helper.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Tests/helper.py b/Tests/helper.py index 9ee5a8259..c7d947414 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -185,10 +185,10 @@ def tostring(im, format, **options): def hopper(mode="RGB", cache={}): from PIL import Image im = None - # FIXME: Implement caching to reduce reading from disk but so an original - # copy is returned each time and the cached image isn't modified by tests + # Use caching to reduce reading from disk but so an original copy is + # returned each time and the cached image isn't modified by tests # (for fast, isolated, repeatable tests). - # im = cache.get(mode) + im = cache.get(mode) if im is None: if mode == "RGB": im = Image.open("Tests/images/hopper.ppm") @@ -198,8 +198,8 @@ def hopper(mode="RGB", cache={}): im = hopper("I").convert(mode) else: im = hopper("RGB").convert(mode) - # cache[mode] = im - return im + cache[mode] = im + return im.copy() def command_succeeds(cmd): From a86e032a1844c96c38d6c906d2726cef09373687 Mon Sep 17 00:00:00 2001 From: homm Date: Wed, 19 Nov 2014 03:41:44 +0300 Subject: [PATCH 0051/1037] return fresh image is no mode specified and cached version otherwise --- Tests/helper.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Tests/helper.py b/Tests/helper.py index c7d947414..c33660fe6 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -182,23 +182,25 @@ def tostring(im, format, **options): return out.getvalue() -def hopper(mode="RGB", cache={}): +def hopper(mode=None, cache={}): from PIL import Image - im = None - # Use caching to reduce reading from disk but so an original copy is + if mode is None: + # Always return fresh not-yet-loaded version of image. + # Operations on not-yet-loaded images is separate class of errors + # what we should catch. + return Image.open("Tests/images/hopper.ppm") + # Use caching to reduce reading from disk but so an original copy is # returned each time and the cached image isn't modified by tests # (for fast, isolated, repeatable tests). im = cache.get(mode) if im is None: - if mode == "RGB": - im = Image.open("Tests/images/hopper.ppm") - elif mode == "F": + if mode == "F": im = hopper("L").convert(mode) elif mode[:4] == "I;16": im = hopper("I").convert(mode) else: - im = hopper("RGB").convert(mode) - cache[mode] = im + im = hopper().convert(mode) + cache[mode] = im return im.copy() From 5ec6fcdeaaab22aac76d61e540c0be825c2d2ccd Mon Sep 17 00:00:00 2001 From: Chris Adams Date: Tue, 10 Feb 2015 13:15:47 -0500 Subject: [PATCH 0052/1037] Tidy docs for DecompressionBombWarning * Fix formatting to display simplefilter examples as code blocks rather than italics * Wrap text so it's not one incredibly long line --- docs/reference/Image.rst | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst index 2617bc2bf..974d84a6e 100644 --- a/docs/reference/Image.rst +++ b/docs/reference/Image.rst @@ -49,7 +49,14 @@ Functions .. autofunction:: open - .. warning:: To protect against potential DOS attacks caused by "`decompression bombs`_" (i.e. malicious files which decompress into a huge amount of data and are designed to crash or cause disruption by using up a lot of memory), Pillow will issue a `DecompressionBombWarning` if the image is over a certain limit. If desired, the warning can be turned into an error with `warnings.simplefilter('error', Image.DecompressionBombWarning)` or suppressed entirely with `warnings.simplefilter('ignore', Image.DecompressionBombWarning)`. See also `the logging documentation`_ to have warnings output to the logging facility instead of stderr. + .. warning:: + To protect against potential DOS attacks caused by "`decompression bombs`_" (i.e. malicious files + which decompress into a huge amount of data and are designed to crash or cause disruption by using up + a lot of memory), Pillow will issue a `DecompressionBombWarning` if the image is over a certain + limit. If desired, the warning can be turned into an error with + ``warnings.simplefilter('error', Image.DecompressionBombWarning)`` or suppressed entirely with + ``warnings.simplefilter('ignore', Image.DecompressionBombWarning)``. See also `the logging + documentation`_ to have warnings output to the logging facility instead of stderr. .. _decompression bombs: https://en.wikipedia.org/wiki/Zip_bomb .. _the logging documentation: https://docs.python.org/2/library/logging.html?highlight=logging#integration-with-the-warnings-module @@ -193,6 +200,6 @@ Instances of the :py:class:`Image` class have the following attributes: operation affects the dictionary. If you need the information later on, keep a reference to the info dictionary returned from the open method. - Unless noted elsewhere, this dictionary does not affect saving files. + Unless noted elsewhere, this dictionary does not affect saving files. :type: :py:class:`dict` From 61fb1c5bd4215a2cff2ec80578f901f71d073919 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Pedersen?= Date: Mon, 23 Feb 2015 09:46:35 +0100 Subject: [PATCH 0053/1037] Tiff: allow writing floating poitn tag values Use the inverse logic used for loading floating point tag values to also write them out again. --- PIL/TiffImagePlugin.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index a533c27ea..d7291e87c 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -517,6 +517,15 @@ class ImageFileDirectory(collections.MutableMapping): elif typ == 7: # untyped data data = value = b"".join(value) + elif typ in (11, 12): + # float value + tmap = {11: 'f', 12: 'd'} + if not isinstance(value, tuple): + value = (value,) + a = array.array(tmap[typ], value) + if self.prefix != native_prefix: + a.byteswap() + data = a.tostring() elif isStringType(value[0]): # string data if isinstance(value, tuple): From 31be9f12ad096e4655a4415c2cfd2511229cd15b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Pedersen?= Date: Mon, 23 Feb 2015 10:51:42 +0100 Subject: [PATCH 0054/1037] Add tests for tiff float tag values Add tests for writing of float/double values in tiff file tags. --- Tests/test_file_tiff_metadata.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 314590006..1cc5721e4 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -16,10 +16,18 @@ class TestFileTiffMetadata(PillowTestCase): img = hopper() textdata = "This is some arbitrary metadata for a text field" + floatdata = 12.345 + doubledata = 67.89 + info = TiffImagePlugin.ImageFileDirectory() info[tag_ids['ImageJMetaDataByteCounts']] = len(textdata) info[tag_ids['ImageJMetaData']] = textdata + info[tag_ids['RollAngle']] = floatdata + info.tagtype[tag_ids['RollAngle']] = 11 + + info[tag_ids['YawAngle'] = doubledata + info.tagtype[tag_ids['YawAngle']] = 12 f = self.tempfile("temp.tif") @@ -29,6 +37,8 @@ class TestFileTiffMetadata(PillowTestCase): self.assertEqual(loaded.tag[50838], (len(textdata),)) self.assertEqual(loaded.tag[50839], textdata) + self.assertEqual(loaded.tag[tag_ids['RollAngle']], floatdata) + self.assertEqual(loaded.tag[tag_ids['YawAngle']], doubledata) def test_read_metadata(self): img = Image.open('Tests/images/hopper_g4.tif') From 1f11fc7be5dce094275f0efc78cf7fe6f57fcff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Pedersen?= Date: Mon, 23 Feb 2015 11:09:01 +0100 Subject: [PATCH 0055/1037] Fix typo in test --- Tests/test_file_tiff_metadata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 1cc5721e4..eb596f658 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -26,7 +26,7 @@ class TestFileTiffMetadata(PillowTestCase): info[tag_ids['RollAngle']] = floatdata info.tagtype[tag_ids['RollAngle']] = 11 - info[tag_ids['YawAngle'] = doubledata + info[tag_ids['YawAngle']] = doubledata info.tagtype[tag_ids['YawAngle']] = 12 f = self.tempfile("temp.tif") From 0238a80e249ac0786f9933b78180311825daa973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Pedersen?= Date: Mon, 23 Feb 2015 11:19:32 +0100 Subject: [PATCH 0056/1037] Fix test failure on float due to rounding, the values are just 'almost' equal. --- Tests/test_file_tiff_metadata.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index eb596f658..12689ba1c 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -37,8 +37,8 @@ class TestFileTiffMetadata(PillowTestCase): self.assertEqual(loaded.tag[50838], (len(textdata),)) self.assertEqual(loaded.tag[50839], textdata) - self.assertEqual(loaded.tag[tag_ids['RollAngle']], floatdata) - self.assertEqual(loaded.tag[tag_ids['YawAngle']], doubledata) + self.assertAlmostEqual(loaded.tag[tag_ids['RollAngle']], floatdata) + self.assertAlmostEqual(loaded.tag[tag_ids['YawAngle']], doubledata) def test_read_metadata(self): img = Image.open('Tests/images/hopper_g4.tif') From e8553e1dae56a4e5acc19c9df348a55b5fdbe5c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Pedersen?= Date: Mon, 23 Feb 2015 12:06:29 +0100 Subject: [PATCH 0057/1037] Tiff float tags: Use first val in tuple --- Tests/test_file_tiff_metadata.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 12689ba1c..4642780c2 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -37,8 +37,8 @@ class TestFileTiffMetadata(PillowTestCase): self.assertEqual(loaded.tag[50838], (len(textdata),)) self.assertEqual(loaded.tag[50839], textdata) - self.assertAlmostEqual(loaded.tag[tag_ids['RollAngle']], floatdata) - self.assertAlmostEqual(loaded.tag[tag_ids['YawAngle']], doubledata) + self.assertAlmostEqual(loaded.tag[tag_ids['RollAngle']][0], floatdata) + self.assertAlmostEqual(loaded.tag[tag_ids['YawAngle']][0], doubledata) def test_read_metadata(self): img = Image.open('Tests/images/hopper_g4.tif') From 47f5c9e650bf73dcc5a096d8bc931e74bd0b8060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Pedersen?= Date: Mon, 23 Feb 2015 12:11:20 +0100 Subject: [PATCH 0058/1037] Fix float precission --- Tests/test_file_tiff_metadata.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 4642780c2..2954c0879 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -37,7 +37,8 @@ class TestFileTiffMetadata(PillowTestCase): self.assertEqual(loaded.tag[50838], (len(textdata),)) self.assertEqual(loaded.tag[50839], textdata) - self.assertAlmostEqual(loaded.tag[tag_ids['RollAngle']][0], floatdata) + self.assertAlmostEqual(loaded.tag[tag_ids['RollAngle']][0], floatdata, + places=5) self.assertAlmostEqual(loaded.tag[tag_ids['YawAngle']][0], doubledata) def test_read_metadata(self): From 456bd96565abd71c28df98fd593f8f253c389c70 Mon Sep 17 00:00:00 2001 From: artscoop Date: Wed, 4 Mar 2015 18:15:56 +0100 Subject: [PATCH 0059/1037] Fix 32-bit BMP loading (RGBA or RGBX) PIL choked on perfectly valid BMP files (32 bits with Alpha). It could not handle valid RGBA masks to determine the raw format. To clarify things, I: - Rewrote the `BmpImagePlugin.BmpImageFile` class to be far more readable - Made error messages more explicit (e.g. say that RLE bitmaps are unsupported) - Made a readable dict to contain BMP header information - Kept the existing security checks - Instead of reading palette info by chunks of 3/4 bytes, read the whole palette info at once and parse the data. - Now works with BMPv4/5 with Alpha (and can be exported to alpha PNG for example) - Tested load and save with RGB24, RGB8, RGB8L, RGB32 and RGBA32. - Tested with one bogus file. File not accepted, as expected. I wanted to test more BMP formats, but I could not find that many images. But for all the types I tested, it worked flawlessly. --- PIL/BmpImagePlugin.py | 238 +++++++++++++++++++++--------------------- 1 file changed, 119 insertions(+), 119 deletions(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index 917d43b9c..28551c041 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -16,6 +16,7 @@ # 2002-12-30 fl Fixed load of 1-bit palette images # 2003-04-21 fl Fixed load of 1-bit monochrome images # 2003-04-23 fl Added limited support for BI_BITFIELDS compression +# 2015-03-04 sk Added support for 32-bit images + alpha channel # # Copyright (c) 1997-2003 by Secret Labs AB # Copyright (c) 1995-2003 by Fredrik Lundh @@ -23,13 +24,12 @@ # See the README file for information on usage and redistribution. # - __version__ = "0.7" - from PIL import Image, ImageFile, ImagePalette, _binary import math + i8 = _binary.i8 i16 = _binary.i16le i32 = _binary.i32le @@ -56,131 +56,127 @@ def _accept(prefix): return prefix[:2] == b"BM" -## +#=============================================================================== # Image plugin for the Windows BMP format. - +#=============================================================================== class BmpImageFile(ImageFile.ImageFile): - - format = "BMP" + """ Image plugin for the Windows Bitmap format (BMP) """ + + #--------------------------------------------------------------- Description format_description = "Windows Bitmap" - - def _bitmap(self, header=0, offset=0): - if header: - self.fp.seek(header) - - read = self.fp.read - - # CORE/INFO - s = read(4) - s = s + ImageFile._safe_read(self.fp, i32(s)-4) - - if len(s) == 12: - - # OS/2 1.0 CORE - bits = i16(s[10:]) - self.size = i16(s[4:]), i16(s[6:]) - compression = 0 - lutsize = 3 - colors = 0 - direction = -1 - - elif len(s) in [40, 64, 108, 124]: - - # WIN 3.1 or OS/2 2.0 INFO - bits = i16(s[14:]) - self.size = i32(s[4:]), i32(s[8:]) - compression = i32(s[16:]) - pxperm = (i32(s[24:]), i32(s[28:])) # Pixels per meter - lutsize = 4 - colors = i32(s[32:]) - direction = -1 - if i8(s[11]) == 0xff: - # upside-down storage - self.size = self.size[0], 2**32 - self.size[1] - direction = 0 - - self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), - pxperm)) - - else: - raise IOError("Unsupported BMP header type (%d)" % len(s)) - - if (self.size[0]*self.size[1]) > 2**31: - # Prevent DOS for > 2gb images - raise IOError("Unsupported BMP Size: (%dx%d)" % self.size) - - if not colors: - colors = 1 << bits - - # MODE - try: - self.mode, rawmode = BIT2MODE[bits] - except KeyError: - raise IOError("Unsupported BMP pixel depth (%d)" % bits) - - if compression == 3: - # BI_BITFIELDS compression - mask = i32(read(4)), i32(read(4)), i32(read(4)) - if bits == 32 and mask == (0xff0000, 0x00ff00, 0x0000ff): - rawmode = "BGRX" - elif bits == 16 and mask == (0x00f800, 0x0007e0, 0x00001f): - rawmode = "BGR;16" - elif bits == 16 and mask == (0x007c00, 0x0003e0, 0x00001f): - rawmode = "BGR;15" - else: - # print bits, map(hex, mask) - raise IOError("Unsupported BMP bitfields layout") - elif compression != 0: - raise IOError("Unsupported BMP compression (%d)" % compression) - - # LUT - if self.mode == "P": - palette = [] - greyscale = 1 - if colors == 2: - indices = (0, 255) - elif colors > 2**16 or colors <= 0: # We're reading a i32. - raise IOError("Unsupported BMP Palette size (%d)" % colors) - else: - indices = list(range(colors)) - for i in indices: - rgb = read(lutsize)[:3] - if rgb != o8(i)*3: - greyscale = 0 - palette.append(rgb) - if greyscale: - if colors == 2: - self.mode = rawmode = "1" - else: - self.mode = rawmode = "L" - else: - self.mode = "P" - self.palette = ImagePalette.raw( - "BGR", b"".join(palette) - ) - - if not offset: - offset = self.fp.tell() - - self.tile = [("raw", - (0, 0) + self.size, - offset, - (rawmode, ((self.size[0]*bits+31) >> 3) & (~3), - direction))] - - self.info["compression"] = compression + format = "BMP" + #---------------------------------------------------- BMP Compression values + COMPRESSIONS = {'RAW': 0, 'RLE8': 1, 'RLE4': 2, 'BITFIELDS': 3, 'JPEG': 4, 'PNG': 5} + RAW, RLE8, RLE4, BITFIELDS, JPEG, PNG = 0, 1, 2, 3, 4, 5 def _open(self): - - # HEAD - s = self.fp.read(14) - if s[:2] != b"BM": - raise SyntaxError("Not a BMP file") - offset = i32(s[10:]) - + """ Open file, check magic number and read header """ + # read 14 bytes: magic number, filesize, reserved, header final offset + head_data = self.fp.read(14) + # choke if the file does not have the required magic bytes + if head_data[0:2] != b"BM": + raise SyntaxError("Expected a BMP file.") + # read the start position of the BMP image data (u32) + offset = i32(head_data[10:14]) + # load bitmap information (offset=raster info) self._bitmap(offset=offset) + def _bitmap(self, header=0, offset=0): + """ Read relevant info about the BMP """ + read, seek = self.fp.read, self.fp.seek + seek(2) + file_info = dict() + file_info['filesize'] = i32(read(12)[0:4]) # file size @offset 2 (offsets 4, 12 are reserved for OS/2 Icons) + file_info['header_size'] = i32(read(4)) # read bmp header size @offset 14 (this is part of the header size) + file_info['direction'] = -1 + header_data = ImageFile._safe_read(self.fp, file_info['header_size'] - 4) # read the rest of the bmp header, without its size + + #---------------------------------------------------- IBM OS/2 Bitmap v1 + #------- This format has different offsets because of width/height types + if file_info['header_size'] == 12: + file_info['width'] = i16(header_data[0:2]) + file_info['height'] = i16(header_data[2:4]) + file_info['planes'] = i16(header_data[4:6]) + file_info['bits'] = i16(header_data[6:8]) + file_info['compression'] = self.RAW + file_info['palette_padding'] = 3 + #----------------------------------------------- Windows Bitmap v2 to v5 + elif file_info['header_size'] in {40, 64, 108, 124}: # v3, OS/2 v2, v4, v5 + if file_info['header_size'] >= 64: + file_info['r_mask'] = i32(header_data[36:40]) + file_info['g_mask'] = i32(header_data[40:44]) + file_info['b_mask'] = i32(header_data[44:48]) + file_info['a_mask'] = i32(header_data[48:52]) + file_info['rgb_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask']) + file_info['rgba_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'], file_info['a_mask']) + if file_info['header_size'] >= 40: # v3 and OS/2 + file_info['y_flip'] = i8(header_data[7]) == 0xff + file_info['direction'] = 0 if file_info['y_flip'] else -1 + file_info['width'] = i32(header_data[0:4]) + file_info['height'] = i32(header_data[4:8]) if not file_info['y_flip'] else 2**32 - i32(header_data[4:8]) + file_info['planes'] = i16(header_data[8:10]) + file_info['bits'] = i16(header_data[10:12]) + file_info['compression'] = i32(header_data[12:16]) + file_info['data_size'] = i32(header_data[16:20]) # byte size of pixel data + file_info['pixels_per_meter'] = (i32(header_data[20:24]), i32(header_data[24:28])) + file_info['colors'] = i32(header_data[28:32]) + file_info['palette_padding'] = 4 + self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), file_info['pixels_per_meter'])) + else: + raise IOError("BMP images with a {0} byte header are not supported".format(file_info['header_size'])) + self.size = file_info['width'], file_info['height'] + #--------- If color count was not found in the header, compute from bits + file_info['colors'] = file_info['colors'] if file_info.get('colors', 0) else (1 << file_info['bits']) + #--------------------------------- Check abnormal values for DOS attacks + if file_info['width'] * file_info['height'] > 2**31: + raise IOError("BMP images with more than 2 billion pixels are not supported (here {0} pixels)".format(file_info['width'] * file_info['height'])) + + #------------------------ Check bit depth for unusual unsupported values + self.mode, raw_mode = BIT2MODE.get(file_info['bits'], (None, None)) + if self.mode is None: + raise IOError("BMP images with a {0}-bit pixel depth are not supported".format(file_info['bits'])) + + #------------------ Process BMP with Bitfields compression (not palette) + if file_info['compression'] == self.BITFIELDS: + SUPPORTED = {32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000)], 24: [(0xff0000, 0xff00, 0xff, 0x0)], 16: [(0xf800, 0x7e0, 0x1f, 0x0), (0x7c00, 0x3e0, 0x1f, 0x0)]} + MASK_MODES = {(32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX", (32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA", (24, (0xff0000, 0xff00, 0xff)): "BGR", (16, (0xf800, 0x7e0, 0x1f)): "BGR;16", (16, (0x7c00, 0x3e0, 0x1f)): "BGR;15"} + if file_info['bits'] in SUPPORTED and file_info['rgba_mask'] in SUPPORTED[file_info['bits']]: + raw_mode = MASK_MODES[(file_info['bits'], file_info['rgba_mask'])] + if raw_mode in {"BGRA"}: + self.mode = "RGBA" + else: + raise IOError("BMP images with the provided bitfield information are not supported") + elif file_info['compression'] != self.RAW: + raise IOError("BMP files with RLE (1/2), JPEG (4) and PNG (5) compression are not supported") + + #----------------- Once the header is processed, process the palette/LUT + if self.mode == "P": # Paletted for 1, 4 and 8 bit images + #------------------------------------------------------ 1-bit images + if not (0 < file_info['colors'] <= 65536): + raise IOError("BMP palette must have between 1 and 256 colors") + else: + padding = file_info['palette_padding'] + palette = read(padding * file_info['colors']) + #------------------- Check if greyscale and ignore palette if so + greyscale = all([palette[ind] == palette[ind+1] == palette[ind+2] for ind in range(len(palette), padding)]) + #--------- If all colors are grey, white or black, ditch palette + if greyscale: + self.mode = "1" if file_info['colors'] == 2 else "L" + raw_mode = self.mode + else: + self.mode = "P" + self.palette = ImagePalette.raw("BGRX", b"".join(palette)) + + #------------------------------ Finally set the tile data for the plugin + self.info['compression'] = file_info['compression'] + self.tile = [('raw', (0, 0, file_info['width'], file_info['height']), self.fp.tell(), + (raw_mode, ((file_info['width'] * file_info['bits'] + 31) >> 3) & (~3), file_info['direction']) + )] + +#=============================================================================== +# Image plugin for the DIB format (BMP alias) +#=============================================================================== class DibImageFile(BmpImageFile): format = "DIB" @@ -189,6 +185,8 @@ class DibImageFile(BmpImageFile): def _open(self): self._bitmap() + + # # -------------------------------------------------------------------- # Write BMP file @@ -198,11 +196,13 @@ SAVE = { "L": ("L", 8, 256), "P": ("P", 8, 256), "RGB": ("BGR", 24, 0), + "RGBA": ("BGRA", 32, 0), } def _save(im, fp, filename, check=0): try: + print im.mode rawmode, bits, colors = SAVE[im.mode] except KeyError: raise IOError("cannot write mode %s as BMP" % im.mode) From c0ce8d0ae814797d78e72ba475b368765be845bb Mon Sep 17 00:00:00 2001 From: artscoop Date: Wed, 4 Mar 2015 18:29:28 +0100 Subject: [PATCH 0060/1037] Removed debug print Debug print left in _save. Removed. --- PIL/BmpImagePlugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index 28551c041..c0e3e5afb 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -202,7 +202,6 @@ SAVE = { def _save(im, fp, filename, check=0): try: - print im.mode rawmode, bits, colors = SAVE[im.mode] except KeyError: raise IOError("cannot write mode %s as BMP" % im.mode) From 877c138e219257407c73e789eb1dfe2bcbf8add6 Mon Sep 17 00:00:00 2001 From: artscoop Date: Wed, 4 Mar 2015 19:26:15 +0100 Subject: [PATCH 0061/1037] 1bpp BMP fix It appears that {{{ The handling of 1bpp bitmaps is a little complicated. When reading 1bpp bitmaps, the palette is ignored. 1's are considered foreground, and they are considered black. 0's are considered background, and they are considered white. }}} so the raw mode has to be `1;I` --- PIL/BmpImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index c0e3e5afb..6f29fb767 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -162,7 +162,7 @@ class BmpImageFile(ImageFile.ImageFile): #--------- If all colors are grey, white or black, ditch palette if greyscale: self.mode = "1" if file_info['colors'] == 2 else "L" - raw_mode = self.mode + raw_mode = self.mode if self.mode != "1" else "1;I" # rule for 1bpp : 1=b, 0=w else: self.mode = "P" self.palette = ImagePalette.raw("BGRX", b"".join(palette)) From 497ddf9c68ac4794540dbe4bdf14ca53e7a38e21 Mon Sep 17 00:00:00 2001 From: artscoop Date: Wed, 4 Mar 2015 19:50:52 +0100 Subject: [PATCH 0062/1037] Remember correct image offset if bitfields+palette Use the provided image offset if there is palette data while the image is bitfielded. --- PIL/BmpImagePlugin.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index 6f29fb767..b2a601cf1 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -85,8 +85,10 @@ class BmpImageFile(ImageFile.ImageFile): """ Read relevant info about the BMP """ read, seek = self.fp.read, self.fp.seek seek(2) + start_data = read(12) file_info = dict() - file_info['filesize'] = i32(read(12)[0:4]) # file size @offset 2 (offsets 4, 12 are reserved for OS/2 Icons) + file_info['filesize'] = i32(start_data[0:4]) # file size @offset 2 (offsets 4, 12 are reserved for OS/2 Icons) + file_info['image_offset'] = i32(start_data[8:12]) # file size @offset 2 (offsets 4, 12 are reserved for OS/2 Icons) file_info['header_size'] = i32(read(4)) # read bmp header size @offset 14 (this is part of the header size) file_info['direction'] = -1 header_data = ImageFile._safe_read(self.fp, file_info['header_size'] - 4) # read the rest of the bmp header, without its size @@ -101,7 +103,7 @@ class BmpImageFile(ImageFile.ImageFile): file_info['compression'] = self.RAW file_info['palette_padding'] = 3 #----------------------------------------------- Windows Bitmap v2 to v5 - elif file_info['header_size'] in {40, 64, 108, 124}: # v3, OS/2 v2, v4, v5 + elif file_info['header_size'] in (40, 64, 108, 124): # v3, OS/2 v2, v4, v5 if file_info['header_size'] >= 64: file_info['r_mask'] = i32(header_data[36:40]) file_info['g_mask'] = i32(header_data[40:44]) From c8551770eb13bf3fcc551ece62c075e01d4445d9 Mon Sep 17 00:00:00 2001 From: artscoop Date: Wed, 4 Mar 2015 19:55:08 +0100 Subject: [PATCH 0063/1037] Quickfix number 4 Until tests pass. --- PIL/BmpImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index b2a601cf1..8c34fe8a9 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -171,7 +171,7 @@ class BmpImageFile(ImageFile.ImageFile): #------------------------------ Finally set the tile data for the plugin self.info['compression'] = file_info['compression'] - self.tile = [('raw', (0, 0, file_info['width'], file_info['height']), self.fp.tell(), + self.tile = [('raw', (0, 0, file_info['width'], file_info['height']), file_info['image_offset'], (raw_mode, ((file_info['width'] * file_info['bits'] + 31) >> 3) & (~3), file_info['direction']) )] From 7b657f688794aed84f4150f2389059d4507355cd Mon Sep 17 00:00:00 2001 From: artscoop Date: Wed, 4 Mar 2015 22:05:32 +0100 Subject: [PATCH 0064/1037] Fix for all good_tests Fixed loading of all types of provided images (+rgba). Added edge case where the header is reported as 40 bytes long with BITFIELDS (they start past the 40 bytes of the header). Loading fails for RLE, but IIRC, they're unsupported so it's normal. --- PIL/BmpImagePlugin.py | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index 8c34fe8a9..d6457f1e2 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -16,7 +16,6 @@ # 2002-12-30 fl Fixed load of 1-bit palette images # 2003-04-21 fl Fixed load of 1-bit monochrome images # 2003-04-23 fl Added limited support for BI_BITFIELDS compression -# 2015-03-04 sk Added support for 32-bit images + alpha channel # # Copyright (c) 1997-2003 by Secret Labs AB # Copyright (c) 1995-2003 by Fredrik Lundh @@ -92,7 +91,6 @@ class BmpImageFile(ImageFile.ImageFile): file_info['header_size'] = i32(read(4)) # read bmp header size @offset 14 (this is part of the header size) file_info['direction'] = -1 header_data = ImageFile._safe_read(self.fp, file_info['header_size'] - 4) # read the rest of the bmp header, without its size - #---------------------------------------------------- IBM OS/2 Bitmap v1 #------- This format has different offsets because of width/height types if file_info['header_size'] == 12: @@ -103,7 +101,7 @@ class BmpImageFile(ImageFile.ImageFile): file_info['compression'] = self.RAW file_info['palette_padding'] = 3 #----------------------------------------------- Windows Bitmap v2 to v5 - elif file_info['header_size'] in (40, 64, 108, 124): # v3, OS/2 v2, v4, v5 + elif file_info['header_size'] in {40, 64, 108, 124}: # v3, OS/2 v2, v4, v5 if file_info['header_size'] >= 64: file_info['r_mask'] = i32(header_data[36:40]) file_info['g_mask'] = i32(header_data[40:44]) @@ -113,7 +111,7 @@ class BmpImageFile(ImageFile.ImageFile): file_info['rgba_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'], file_info['a_mask']) if file_info['header_size'] >= 40: # v3 and OS/2 file_info['y_flip'] = i8(header_data[7]) == 0xff - file_info['direction'] = 0 if file_info['y_flip'] else -1 + file_info['direction'] = 1 if file_info['y_flip'] else -1 file_info['width'] = i32(header_data[0:4]) file_info['height'] = i32(header_data[4:8]) if not file_info['y_flip'] else 2**32 - i32(header_data[4:8]) file_info['planes'] = i16(header_data[8:10]) @@ -123,6 +121,16 @@ class BmpImageFile(ImageFile.ImageFile): file_info['pixels_per_meter'] = (i32(header_data[20:24]), i32(header_data[24:28])) file_info['colors'] = i32(header_data[28:32]) file_info['palette_padding'] = 4 + #------------------- Special case : header is reported 40, which + #----------------------- is shorter than real size for bpp >= 16 + if file_info['header_size'] < 64 and file_info['bits'] >= 16: + file_info['r_mask'] = i32(read(4)) + file_info['g_mask'] = i32(read(4)) + file_info['b_mask'] = i32(read(4)) + file_info['a_mask'] = i32(read(4)) + file_info['rgb_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask']) + file_info['rgba_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'], file_info['a_mask']) + self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), file_info['pixels_per_meter'])) else: raise IOError("BMP images with a {0} byte header are not supported".format(file_info['header_size'])) @@ -140,12 +148,16 @@ class BmpImageFile(ImageFile.ImageFile): #------------------ Process BMP with Bitfields compression (not palette) if file_info['compression'] == self.BITFIELDS: - SUPPORTED = {32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000)], 24: [(0xff0000, 0xff00, 0xff, 0x0)], 16: [(0xf800, 0x7e0, 0x1f, 0x0), (0x7c00, 0x3e0, 0x1f, 0x0)]} + SUPPORTED = {32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000)], 24: [(0xff0000, 0xff00, 0xff)], 16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]} MASK_MODES = {(32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX", (32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA", (24, (0xff0000, 0xff00, 0xff)): "BGR", (16, (0xf800, 0x7e0, 0x1f)): "BGR;16", (16, (0x7c00, 0x3e0, 0x1f)): "BGR;15"} - if file_info['bits'] in SUPPORTED and file_info['rgba_mask'] in SUPPORTED[file_info['bits']]: - raw_mode = MASK_MODES[(file_info['bits'], file_info['rgba_mask'])] - if raw_mode in {"BGRA"}: - self.mode = "RGBA" + if file_info['bits'] in SUPPORTED: + if file_info['bits'] == 32 and file_info['rgba_mask'] in SUPPORTED[file_info['bits']]: + raw_mode = MASK_MODES[(file_info['bits'], file_info['rgba_mask'])] + self.mode = "RGBA" if raw_mode in ("BGRA",) else self.mode + elif file_info['bits'] in (24, 16) and file_info['rgb_mask'] in SUPPORTED[file_info['bits']]: + raw_mode = MASK_MODES[(file_info['bits'], file_info['rgb_mask'])] + else: + raise IOError("BMP images with the provided bitfield mask are not supported") else: raise IOError("BMP images with the provided bitfield information are not supported") elif file_info['compression'] != self.RAW: @@ -160,14 +172,14 @@ class BmpImageFile(ImageFile.ImageFile): padding = file_info['palette_padding'] palette = read(padding * file_info['colors']) #------------------- Check if greyscale and ignore palette if so - greyscale = all([palette[ind] == palette[ind+1] == palette[ind+2] for ind in range(len(palette), padding)]) + greyscale = all([palette[ind] == palette[ind+1] == palette[ind+2] == ind / padding for ind in range(0, len(palette), padding)]) #--------- If all colors are grey, white or black, ditch palette if greyscale: self.mode = "1" if file_info['colors'] == 2 else "L" - raw_mode = self.mode if self.mode != "1" else "1;I" # rule for 1bpp : 1=b, 0=w + raw_mode = self.mode else: self.mode = "P" - self.palette = ImagePalette.raw("BGRX", b"".join(palette)) + self.palette = ImagePalette.raw("BGRX" if padding == 4 else "BGR", b"".join(palette)) #------------------------------ Finally set the tile data for the plugin self.info['compression'] = file_info['compression'] From f953b982859c43add1e035ab6346c59b2a43cc5b Mon Sep 17 00:00:00 2001 From: artscoop Date: Wed, 4 Mar 2015 22:40:04 +0100 Subject: [PATCH 0065/1037] Try to fix tests Choked on roundtrip, where a P;1 image was returned instead of a 1 image. --- PIL/BmpImagePlugin.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index d6457f1e2..755cfb0fc 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -171,8 +171,13 @@ class BmpImageFile(ImageFile.ImageFile): else: padding = file_info['palette_padding'] palette = read(padding * file_info['colors']) + greyscale = True + indices = (0, 255) if file_info['colors'] == 2 else list(range(file_info['colors'])) #------------------- Check if greyscale and ignore palette if so - greyscale = all([palette[ind] == palette[ind+1] == palette[ind+2] == ind / padding for ind in range(0, len(palette), padding)]) + for ind in indices: + rgb = palette[ind*padding:ind*padding + 3] + if rgb != o8(ind) * 3: + greyscale = False #--------- If all colors are grey, white or black, ditch palette if greyscale: self.mode = "1" if file_info['colors'] == 2 else "L" From 56439b728f891a7c63ab64ae95b18e673874880c Mon Sep 17 00:00:00 2001 From: artscoop Date: Wed, 4 Mar 2015 23:06:21 +0100 Subject: [PATCH 0066/1037] Test with original code ... --- PIL/BmpImagePlugin.py | 252 ++++++++++++++++++++---------------------- 1 file changed, 117 insertions(+), 135 deletions(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index 755cfb0fc..917d43b9c 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -23,12 +23,13 @@ # See the README file for information on usage and redistribution. # + __version__ = "0.7" + from PIL import Image, ImageFile, ImagePalette, _binary import math - i8 = _binary.i8 i16 = _binary.i16le i32 = _binary.i32le @@ -55,147 +56,131 @@ def _accept(prefix): return prefix[:2] == b"BM" -#=============================================================================== +## # Image plugin for the Windows BMP format. -#=============================================================================== -class BmpImageFile(ImageFile.ImageFile): - """ Image plugin for the Windows Bitmap format (BMP) """ - - #--------------------------------------------------------------- Description - format_description = "Windows Bitmap" - format = "BMP" - #---------------------------------------------------- BMP Compression values - COMPRESSIONS = {'RAW': 0, 'RLE8': 1, 'RLE4': 2, 'BITFIELDS': 3, 'JPEG': 4, 'PNG': 5} - RAW, RLE8, RLE4, BITFIELDS, JPEG, PNG = 0, 1, 2, 3, 4, 5 - def _open(self): - """ Open file, check magic number and read header """ - # read 14 bytes: magic number, filesize, reserved, header final offset - head_data = self.fp.read(14) - # choke if the file does not have the required magic bytes - if head_data[0:2] != b"BM": - raise SyntaxError("Expected a BMP file.") - # read the start position of the BMP image data (u32) - offset = i32(head_data[10:14]) - # load bitmap information (offset=raster info) - self._bitmap(offset=offset) +class BmpImageFile(ImageFile.ImageFile): + + format = "BMP" + format_description = "Windows Bitmap" def _bitmap(self, header=0, offset=0): - """ Read relevant info about the BMP """ - read, seek = self.fp.read, self.fp.seek - seek(2) - start_data = read(12) - file_info = dict() - file_info['filesize'] = i32(start_data[0:4]) # file size @offset 2 (offsets 4, 12 are reserved for OS/2 Icons) - file_info['image_offset'] = i32(start_data[8:12]) # file size @offset 2 (offsets 4, 12 are reserved for OS/2 Icons) - file_info['header_size'] = i32(read(4)) # read bmp header size @offset 14 (this is part of the header size) - file_info['direction'] = -1 - header_data = ImageFile._safe_read(self.fp, file_info['header_size'] - 4) # read the rest of the bmp header, without its size - #---------------------------------------------------- IBM OS/2 Bitmap v1 - #------- This format has different offsets because of width/height types - if file_info['header_size'] == 12: - file_info['width'] = i16(header_data[0:2]) - file_info['height'] = i16(header_data[2:4]) - file_info['planes'] = i16(header_data[4:6]) - file_info['bits'] = i16(header_data[6:8]) - file_info['compression'] = self.RAW - file_info['palette_padding'] = 3 - #----------------------------------------------- Windows Bitmap v2 to v5 - elif file_info['header_size'] in {40, 64, 108, 124}: # v3, OS/2 v2, v4, v5 - if file_info['header_size'] >= 64: - file_info['r_mask'] = i32(header_data[36:40]) - file_info['g_mask'] = i32(header_data[40:44]) - file_info['b_mask'] = i32(header_data[44:48]) - file_info['a_mask'] = i32(header_data[48:52]) - file_info['rgb_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask']) - file_info['rgba_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'], file_info['a_mask']) - if file_info['header_size'] >= 40: # v3 and OS/2 - file_info['y_flip'] = i8(header_data[7]) == 0xff - file_info['direction'] = 1 if file_info['y_flip'] else -1 - file_info['width'] = i32(header_data[0:4]) - file_info['height'] = i32(header_data[4:8]) if not file_info['y_flip'] else 2**32 - i32(header_data[4:8]) - file_info['planes'] = i16(header_data[8:10]) - file_info['bits'] = i16(header_data[10:12]) - file_info['compression'] = i32(header_data[12:16]) - file_info['data_size'] = i32(header_data[16:20]) # byte size of pixel data - file_info['pixels_per_meter'] = (i32(header_data[20:24]), i32(header_data[24:28])) - file_info['colors'] = i32(header_data[28:32]) - file_info['palette_padding'] = 4 - #------------------- Special case : header is reported 40, which - #----------------------- is shorter than real size for bpp >= 16 - if file_info['header_size'] < 64 and file_info['bits'] >= 16: - file_info['r_mask'] = i32(read(4)) - file_info['g_mask'] = i32(read(4)) - file_info['b_mask'] = i32(read(4)) - file_info['a_mask'] = i32(read(4)) - file_info['rgb_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask']) - file_info['rgba_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'], file_info['a_mask']) - - self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), file_info['pixels_per_meter'])) + if header: + self.fp.seek(header) + + read = self.fp.read + + # CORE/INFO + s = read(4) + s = s + ImageFile._safe_read(self.fp, i32(s)-4) + + if len(s) == 12: + + # OS/2 1.0 CORE + bits = i16(s[10:]) + self.size = i16(s[4:]), i16(s[6:]) + compression = 0 + lutsize = 3 + colors = 0 + direction = -1 + + elif len(s) in [40, 64, 108, 124]: + + # WIN 3.1 or OS/2 2.0 INFO + bits = i16(s[14:]) + self.size = i32(s[4:]), i32(s[8:]) + compression = i32(s[16:]) + pxperm = (i32(s[24:]), i32(s[28:])) # Pixels per meter + lutsize = 4 + colors = i32(s[32:]) + direction = -1 + if i8(s[11]) == 0xff: + # upside-down storage + self.size = self.size[0], 2**32 - self.size[1] + direction = 0 + + self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), + pxperm)) + else: - raise IOError("BMP images with a {0} byte header are not supported".format(file_info['header_size'])) - self.size = file_info['width'], file_info['height'] - #--------- If color count was not found in the header, compute from bits - file_info['colors'] = file_info['colors'] if file_info.get('colors', 0) else (1 << file_info['bits']) - #--------------------------------- Check abnormal values for DOS attacks - if file_info['width'] * file_info['height'] > 2**31: - raise IOError("BMP images with more than 2 billion pixels are not supported (here {0} pixels)".format(file_info['width'] * file_info['height'])) - - #------------------------ Check bit depth for unusual unsupported values - self.mode, raw_mode = BIT2MODE.get(file_info['bits'], (None, None)) - if self.mode is None: - raise IOError("BMP images with a {0}-bit pixel depth are not supported".format(file_info['bits'])) - - #------------------ Process BMP with Bitfields compression (not palette) - if file_info['compression'] == self.BITFIELDS: - SUPPORTED = {32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000)], 24: [(0xff0000, 0xff00, 0xff)], 16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]} - MASK_MODES = {(32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX", (32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA", (24, (0xff0000, 0xff00, 0xff)): "BGR", (16, (0xf800, 0x7e0, 0x1f)): "BGR;16", (16, (0x7c00, 0x3e0, 0x1f)): "BGR;15"} - if file_info['bits'] in SUPPORTED: - if file_info['bits'] == 32 and file_info['rgba_mask'] in SUPPORTED[file_info['bits']]: - raw_mode = MASK_MODES[(file_info['bits'], file_info['rgba_mask'])] - self.mode = "RGBA" if raw_mode in ("BGRA",) else self.mode - elif file_info['bits'] in (24, 16) and file_info['rgb_mask'] in SUPPORTED[file_info['bits']]: - raw_mode = MASK_MODES[(file_info['bits'], file_info['rgb_mask'])] - else: - raise IOError("BMP images with the provided bitfield mask are not supported") + raise IOError("Unsupported BMP header type (%d)" % len(s)) + + if (self.size[0]*self.size[1]) > 2**31: + # Prevent DOS for > 2gb images + raise IOError("Unsupported BMP Size: (%dx%d)" % self.size) + + if not colors: + colors = 1 << bits + + # MODE + try: + self.mode, rawmode = BIT2MODE[bits] + except KeyError: + raise IOError("Unsupported BMP pixel depth (%d)" % bits) + + if compression == 3: + # BI_BITFIELDS compression + mask = i32(read(4)), i32(read(4)), i32(read(4)) + if bits == 32 and mask == (0xff0000, 0x00ff00, 0x0000ff): + rawmode = "BGRX" + elif bits == 16 and mask == (0x00f800, 0x0007e0, 0x00001f): + rawmode = "BGR;16" + elif bits == 16 and mask == (0x007c00, 0x0003e0, 0x00001f): + rawmode = "BGR;15" else: - raise IOError("BMP images with the provided bitfield information are not supported") - elif file_info['compression'] != self.RAW: - raise IOError("BMP files with RLE (1/2), JPEG (4) and PNG (5) compression are not supported") - - #----------------- Once the header is processed, process the palette/LUT - if self.mode == "P": # Paletted for 1, 4 and 8 bit images - #------------------------------------------------------ 1-bit images - if not (0 < file_info['colors'] <= 65536): - raise IOError("BMP palette must have between 1 and 256 colors") + # print bits, map(hex, mask) + raise IOError("Unsupported BMP bitfields layout") + elif compression != 0: + raise IOError("Unsupported BMP compression (%d)" % compression) + + # LUT + if self.mode == "P": + palette = [] + greyscale = 1 + if colors == 2: + indices = (0, 255) + elif colors > 2**16 or colors <= 0: # We're reading a i32. + raise IOError("Unsupported BMP Palette size (%d)" % colors) else: - padding = file_info['palette_padding'] - palette = read(padding * file_info['colors']) - greyscale = True - indices = (0, 255) if file_info['colors'] == 2 else list(range(file_info['colors'])) - #------------------- Check if greyscale and ignore palette if so - for ind in indices: - rgb = palette[ind*padding:ind*padding + 3] - if rgb != o8(ind) * 3: - greyscale = False - #--------- If all colors are grey, white or black, ditch palette - if greyscale: - self.mode = "1" if file_info['colors'] == 2 else "L" - raw_mode = self.mode + indices = list(range(colors)) + for i in indices: + rgb = read(lutsize)[:3] + if rgb != o8(i)*3: + greyscale = 0 + palette.append(rgb) + if greyscale: + if colors == 2: + self.mode = rawmode = "1" else: - self.mode = "P" - self.palette = ImagePalette.raw("BGRX" if padding == 4 else "BGR", b"".join(palette)) - - #------------------------------ Finally set the tile data for the plugin - self.info['compression'] = file_info['compression'] - self.tile = [('raw', (0, 0, file_info['width'], file_info['height']), file_info['image_offset'], - (raw_mode, ((file_info['width'] * file_info['bits'] + 31) >> 3) & (~3), file_info['direction']) - )] + self.mode = rawmode = "L" + else: + self.mode = "P" + self.palette = ImagePalette.raw( + "BGR", b"".join(palette) + ) + + if not offset: + offset = self.fp.tell() + + self.tile = [("raw", + (0, 0) + self.size, + offset, + (rawmode, ((self.size[0]*bits+31) >> 3) & (~3), + direction))] + + self.info["compression"] = compression + + def _open(self): + + # HEAD + s = self.fp.read(14) + if s[:2] != b"BM": + raise SyntaxError("Not a BMP file") + offset = i32(s[10:]) + + self._bitmap(offset=offset) -#=============================================================================== -# Image plugin for the DIB format (BMP alias) -#=============================================================================== class DibImageFile(BmpImageFile): format = "DIB" @@ -204,8 +189,6 @@ class DibImageFile(BmpImageFile): def _open(self): self._bitmap() - - # # -------------------------------------------------------------------- # Write BMP file @@ -215,7 +198,6 @@ SAVE = { "L": ("L", 8, 256), "P": ("P", 8, 256), "RGB": ("BGR", 24, 0), - "RGBA": ("BGRA", 32, 0), } From 934651427d6c27da0998b9a4095d682bb5c2b419 Mon Sep 17 00:00:00 2001 From: artscoop Date: Thu, 5 Mar 2015 08:19:14 +0100 Subject: [PATCH 0067/1037] Use 1 and not P for basic 1bpp BMP Readapted some original code. --- PIL/BmpImagePlugin.py | 256 ++++++++++++++++++++++-------------------- 1 file changed, 137 insertions(+), 119 deletions(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index 917d43b9c..8fe537586 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -23,13 +23,12 @@ # See the README file for information on usage and redistribution. # - __version__ = "0.7" - from PIL import Image, ImageFile, ImagePalette, _binary import math + i8 = _binary.i8 i16 = _binary.i16le i32 = _binary.i32le @@ -56,131 +55,147 @@ def _accept(prefix): return prefix[:2] == b"BM" -## +#=============================================================================== # Image plugin for the Windows BMP format. - +#=============================================================================== class BmpImageFile(ImageFile.ImageFile): - - format = "BMP" + """ Image plugin for the Windows Bitmap format (BMP) """ + + #--------------------------------------------------------------- Description format_description = "Windows Bitmap" - - def _bitmap(self, header=0, offset=0): - if header: - self.fp.seek(header) - - read = self.fp.read - - # CORE/INFO - s = read(4) - s = s + ImageFile._safe_read(self.fp, i32(s)-4) - - if len(s) == 12: - - # OS/2 1.0 CORE - bits = i16(s[10:]) - self.size = i16(s[4:]), i16(s[6:]) - compression = 0 - lutsize = 3 - colors = 0 - direction = -1 - - elif len(s) in [40, 64, 108, 124]: - - # WIN 3.1 or OS/2 2.0 INFO - bits = i16(s[14:]) - self.size = i32(s[4:]), i32(s[8:]) - compression = i32(s[16:]) - pxperm = (i32(s[24:]), i32(s[28:])) # Pixels per meter - lutsize = 4 - colors = i32(s[32:]) - direction = -1 - if i8(s[11]) == 0xff: - # upside-down storage - self.size = self.size[0], 2**32 - self.size[1] - direction = 0 - - self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), - pxperm)) - - else: - raise IOError("Unsupported BMP header type (%d)" % len(s)) - - if (self.size[0]*self.size[1]) > 2**31: - # Prevent DOS for > 2gb images - raise IOError("Unsupported BMP Size: (%dx%d)" % self.size) - - if not colors: - colors = 1 << bits - - # MODE - try: - self.mode, rawmode = BIT2MODE[bits] - except KeyError: - raise IOError("Unsupported BMP pixel depth (%d)" % bits) - - if compression == 3: - # BI_BITFIELDS compression - mask = i32(read(4)), i32(read(4)), i32(read(4)) - if bits == 32 and mask == (0xff0000, 0x00ff00, 0x0000ff): - rawmode = "BGRX" - elif bits == 16 and mask == (0x00f800, 0x0007e0, 0x00001f): - rawmode = "BGR;16" - elif bits == 16 and mask == (0x007c00, 0x0003e0, 0x00001f): - rawmode = "BGR;15" - else: - # print bits, map(hex, mask) - raise IOError("Unsupported BMP bitfields layout") - elif compression != 0: - raise IOError("Unsupported BMP compression (%d)" % compression) - - # LUT - if self.mode == "P": - palette = [] - greyscale = 1 - if colors == 2: - indices = (0, 255) - elif colors > 2**16 or colors <= 0: # We're reading a i32. - raise IOError("Unsupported BMP Palette size (%d)" % colors) - else: - indices = list(range(colors)) - for i in indices: - rgb = read(lutsize)[:3] - if rgb != o8(i)*3: - greyscale = 0 - palette.append(rgb) - if greyscale: - if colors == 2: - self.mode = rawmode = "1" - else: - self.mode = rawmode = "L" - else: - self.mode = "P" - self.palette = ImagePalette.raw( - "BGR", b"".join(palette) - ) - - if not offset: - offset = self.fp.tell() - - self.tile = [("raw", - (0, 0) + self.size, - offset, - (rawmode, ((self.size[0]*bits+31) >> 3) & (~3), - direction))] - - self.info["compression"] = compression + format = "BMP" + #---------------------------------------------------- BMP Compression values + COMPRESSIONS = {'RAW': 0, 'RLE8': 1, 'RLE4': 2, 'BITFIELDS': 3, 'JPEG': 4, 'PNG': 5} + RAW, RLE8, RLE4, BITFIELDS, JPEG, PNG = 0, 1, 2, 3, 4, 5 def _open(self): - - # HEAD - s = self.fp.read(14) - if s[:2] != b"BM": - raise SyntaxError("Not a BMP file") - offset = i32(s[10:]) - + """ Open file, check magic number and read header """ + # read 14 bytes: magic number, filesize, reserved, header final offset + head_data = self.fp.read(14) + # choke if the file does not have the required magic bytes + if head_data[0:2] != b"BM": + raise SyntaxError("Expected a BMP file.") + # read the start position of the BMP image data (u32) + offset = i32(head_data[10:14]) + # load bitmap information (offset=raster info) self._bitmap(offset=offset) + def _bitmap(self, header=0, offset=0): + """ Read relevant info about the BMP """ + read, seek = self.fp.read, self.fp.seek + seek(2) + start_data = read(12) + file_info = dict() + file_info['filesize'] = i32(start_data[0:4]) # file size @offset 2 (offsets 4, 12 are reserved for OS/2 Icons) + file_info['image_offset'] = i32(start_data[8:12]) # file size @offset 2 (offsets 4, 12 are reserved for OS/2 Icons) + file_info['header_size'] = i32(read(4)) # read bmp header size @offset 14 (this is part of the header size) + file_info['direction'] = -1 + header_data = ImageFile._safe_read(self.fp, file_info['header_size'] - 4) # read the rest of the bmp header, without its size + #---------------------------------------------------- IBM OS/2 Bitmap v1 + #------- This format has different offsets because of width/height types + if file_info['header_size'] == 12: + file_info['width'] = i16(header_data[0:2]) + file_info['height'] = i16(header_data[2:4]) + file_info['planes'] = i16(header_data[4:6]) + file_info['bits'] = i16(header_data[6:8]) + file_info['compression'] = self.RAW + file_info['palette_padding'] = 3 + #----------------------------------------------- Windows Bitmap v2 to v5 + elif file_info['header_size'] in {40, 64, 108, 124}: # v3, OS/2 v2, v4, v5 + if file_info['header_size'] >= 64: + file_info['r_mask'] = i32(header_data[36:40]) + file_info['g_mask'] = i32(header_data[40:44]) + file_info['b_mask'] = i32(header_data[44:48]) + file_info['a_mask'] = i32(header_data[48:52]) + file_info['rgb_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask']) + file_info['rgba_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'], file_info['a_mask']) + if file_info['header_size'] >= 40: # v3 and OS/2 + file_info['y_flip'] = i8(header_data[7]) == 0xff + file_info['direction'] = 1 if file_info['y_flip'] else -1 + file_info['width'] = i32(header_data[0:4]) + file_info['height'] = i32(header_data[4:8]) if not file_info['y_flip'] else 2**32 - i32(header_data[4:8]) + file_info['planes'] = i16(header_data[8:10]) + file_info['bits'] = i16(header_data[10:12]) + file_info['compression'] = i32(header_data[12:16]) + file_info['data_size'] = i32(header_data[16:20]) # byte size of pixel data + file_info['pixels_per_meter'] = (i32(header_data[20:24]), i32(header_data[24:28])) + file_info['colors'] = i32(header_data[28:32]) + file_info['palette_padding'] = 4 + #------------------- Special case : header is reported 40, which + #----------------------- is shorter than real size for bpp >= 16 + if file_info['header_size'] < 64 and file_info['bits'] >= 16: + file_info['r_mask'] = i32(read(4)) + file_info['g_mask'] = i32(read(4)) + file_info['b_mask'] = i32(read(4)) + file_info['a_mask'] = i32(read(4)) + file_info['rgb_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask']) + file_info['rgba_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'], file_info['a_mask']) + + self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), file_info['pixels_per_meter'])) + else: + raise IOError("BMP images with a {0} byte header are not supported".format(file_info['header_size'])) + self.size = file_info['width'], file_info['height'] + #--------- If color count was not found in the header, compute from bits + file_info['colors'] = file_info['colors'] if file_info.get('colors', 0) else (1 << file_info['bits']) + #--------------------------------- Check abnormal values for DOS attacks + if file_info['width'] * file_info['height'] > 2**31: + raise IOError("BMP images with more than 2 billion pixels are not supported (here {0} pixels)".format(file_info['width'] * file_info['height'])) + + #------------------------ Check bit depth for unusual unsupported values + self.mode, raw_mode = BIT2MODE.get(file_info['bits'], (None, None)) + if self.mode is None: + raise IOError("BMP images with a {0}-bit pixel depth are not supported".format(file_info['bits'])) + + #------------------ Process BMP with Bitfields compression (not palette) + if file_info['compression'] == self.BITFIELDS: + SUPPORTED = {32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000)], 24: [(0xff0000, 0xff00, 0xff)], 16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]} + MASK_MODES = {(32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX", (32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA", (24, (0xff0000, 0xff00, 0xff)): "BGR", (16, (0xf800, 0x7e0, 0x1f)): "BGR;16", (16, (0x7c00, 0x3e0, 0x1f)): "BGR;15"} + if file_info['bits'] in SUPPORTED: + if file_info['bits'] == 32 and file_info['rgba_mask'] in SUPPORTED[file_info['bits']]: + raw_mode = MASK_MODES[(file_info['bits'], file_info['rgba_mask'])] + self.mode = "RGBA" if raw_mode in ("BGRA",) else self.mode + elif file_info['bits'] in (24, 16) and file_info['rgb_mask'] in SUPPORTED[file_info['bits']]: + raw_mode = MASK_MODES[(file_info['bits'], file_info['rgb_mask'])] + else: + raise IOError("BMP images with the provided bitfield mask are not supported") + else: + raise IOError("BMP images with the provided bitfield information are not supported") + elif file_info['compression'] != self.RAW: + raise IOError("BMP files with RLE (1/2), JPEG (4) and PNG (5) compression are not supported") + + #----------------- Once the header is processed, process the palette/LUT + if self.mode == "P": # Paletted for 1, 4 and 8 bit images + #------------------------------------------------------ 1-bit images + if not (0 < file_info['colors'] <= 65536): + raise IOError("BMP palette must have between 1 and 256 colors") + else: + padding = file_info['palette_padding'] + palette = read(padding * file_info['colors']) + greyscale = True + indices = (0, 255) if file_info['colors'] == 2 else list(range(file_info['colors'])) + #------------------- Check if greyscale and ignore palette if so + for ind, val in enumerate(indices): + rgb = palette[ind*padding:ind*padding + 3] + if rgb != o8(val) * 3: + greyscale = False + #--------- If all colors are grey, white or black, ditch palette + if greyscale: + self.mode = "1" if file_info['colors'] == 2 else "L" + raw_mode = self.mode + else: + self.mode = "P" + self.palette = ImagePalette.raw("BGRX" if padding == 4 else "BGR", b"".join(palette)) + + #------------------------------ Finally set the tile data for the plugin + self.info['compression'] = file_info['compression'] + self.tile = [('raw', (0, 0, file_info['width'], file_info['height']), file_info['image_offset'], + (raw_mode, ((file_info['width'] * file_info['bits'] + 31) >> 3) & (~3), file_info['direction']) + )] + +#=============================================================================== +# Image plugin for the DIB format (BMP alias) +#=============================================================================== class DibImageFile(BmpImageFile): format = "DIB" @@ -189,6 +204,8 @@ class DibImageFile(BmpImageFile): def _open(self): self._bitmap() + + # # -------------------------------------------------------------------- # Write BMP file @@ -198,6 +215,7 @@ SAVE = { "L": ("L", 8, 256), "P": ("P", 8, 256), "RGB": ("BGR", 24, 0), + "RGBA": ("BGRA", 32, 0), } From c8fddb19f531445e0409f5ea5cec62c62c7d0c4c Mon Sep 17 00:00:00 2001 From: artscoop Date: Thu, 5 Mar 2015 08:34:01 +0100 Subject: [PATCH 0068/1037] Update BmpImagePlugin.py Getting bonkers but I need to know --- PIL/BmpImagePlugin.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index 8fe537586..a57fb145b 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -91,6 +91,7 @@ class BmpImageFile(ImageFile.ImageFile): file_info['header_size'] = i32(read(4)) # read bmp header size @offset 14 (this is part of the header size) file_info['direction'] = -1 header_data = ImageFile._safe_read(self.fp, file_info['header_size'] - 4) # read the rest of the bmp header, without its size + file_info['header_size'] = len(header_data) + 4 #---------------------------------------------------- IBM OS/2 Bitmap v1 #------- This format has different offsets because of width/height types if file_info['header_size'] == 12: @@ -101,7 +102,7 @@ class BmpImageFile(ImageFile.ImageFile): file_info['compression'] = self.RAW file_info['palette_padding'] = 3 #----------------------------------------------- Windows Bitmap v2 to v5 - elif file_info['header_size'] in {40, 64, 108, 124}: # v3, OS/2 v2, v4, v5 + elif file_info['header_size'] in (40, 64, 108, 124): # v3, OS/2 v2, v4, v5 if file_info['header_size'] >= 64: file_info['r_mask'] = i32(header_data[36:40]) file_info['g_mask'] = i32(header_data[40:44]) From 82aa9011c9e31645b02faf9015ac0d105e30027f Mon Sep 17 00:00:00 2001 From: artscoop Date: Thu, 5 Mar 2015 08:47:34 +0100 Subject: [PATCH 0069/1037] Restored original error messages I suspect the tests to check against an exact string when expecting an error --- PIL/BmpImagePlugin.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index a57fb145b..7162bd3ee 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -91,7 +91,6 @@ class BmpImageFile(ImageFile.ImageFile): file_info['header_size'] = i32(read(4)) # read bmp header size @offset 14 (this is part of the header size) file_info['direction'] = -1 header_data = ImageFile._safe_read(self.fp, file_info['header_size'] - 4) # read the rest of the bmp header, without its size - file_info['header_size'] = len(header_data) + 4 #---------------------------------------------------- IBM OS/2 Bitmap v1 #------- This format has different offsets because of width/height types if file_info['header_size'] == 12: @@ -134,18 +133,18 @@ class BmpImageFile(ImageFile.ImageFile): self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), file_info['pixels_per_meter'])) else: - raise IOError("BMP images with a {0} byte header are not supported".format(file_info['header_size'])) + raise IOError("Unsupported BMP header type (%d)" % file_info['header_size']) self.size = file_info['width'], file_info['height'] #--------- If color count was not found in the header, compute from bits file_info['colors'] = file_info['colors'] if file_info.get('colors', 0) else (1 << file_info['bits']) #--------------------------------- Check abnormal values for DOS attacks if file_info['width'] * file_info['height'] > 2**31: - raise IOError("BMP images with more than 2 billion pixels are not supported (here {0} pixels)".format(file_info['width'] * file_info['height'])) + raise IOError("Unsupported BMP Size: (%dx%d)" % self.size) #------------------------ Check bit depth for unusual unsupported values self.mode, raw_mode = BIT2MODE.get(file_info['bits'], (None, None)) if self.mode is None: - raise IOError("BMP images with a {0}-bit pixel depth are not supported".format(file_info['bits'])) + raise IOError("Unsupported BMP pixel depth (%d)" % file_info['bits']) #------------------ Process BMP with Bitfields compression (not palette) if file_info['compression'] == self.BITFIELDS: @@ -158,17 +157,17 @@ class BmpImageFile(ImageFile.ImageFile): elif file_info['bits'] in (24, 16) and file_info['rgb_mask'] in SUPPORTED[file_info['bits']]: raw_mode = MASK_MODES[(file_info['bits'], file_info['rgb_mask'])] else: - raise IOError("BMP images with the provided bitfield mask are not supported") + raise IOError("Unsupported BMP bitfields layout") else: - raise IOError("BMP images with the provided bitfield information are not supported") + raise IOError("Unsupported BMP bitfields layout") elif file_info['compression'] != self.RAW: - raise IOError("BMP files with RLE (1/2), JPEG (4) and PNG (5) compression are not supported") + raise IOError("Unsupported BMP compression (%d)" % compression) #----------------- Once the header is processed, process the palette/LUT if self.mode == "P": # Paletted for 1, 4 and 8 bit images #------------------------------------------------------ 1-bit images if not (0 < file_info['colors'] <= 65536): - raise IOError("BMP palette must have between 1 and 256 colors") + raise IOError("Unsupported BMP Palette size (%d)" % file_info['colors']) else: padding = file_info['palette_padding'] palette = read(padding * file_info['colors']) From d7a78f38138d93757991d0a12f755415a983db92 Mon Sep 17 00:00:00 2001 From: artscoop Date: Thu, 5 Mar 2015 09:02:09 +0100 Subject: [PATCH 0070/1037] Fixing .cur special header offset use offset provided --- PIL/BmpImagePlugin.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index 7162bd3ee..d7ef866fc 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -90,6 +90,9 @@ class BmpImageFile(ImageFile.ImageFile): file_info['image_offset'] = i32(start_data[8:12]) # file size @offset 2 (offsets 4, 12 are reserved for OS/2 Icons) file_info['header_size'] = i32(read(4)) # read bmp header size @offset 14 (this is part of the header size) file_info['direction'] = -1 + #---------------------- If requested, read header at a specific position + if header: + seek(header) header_data = ImageFile._safe_read(self.fp, file_info['header_size'] - 4) # read the rest of the bmp header, without its size #---------------------------------------------------- IBM OS/2 Bitmap v1 #------- This format has different offsets because of width/height types From 9e3af5a161dff2d4db130834d7b1830b992cc341 Mon Sep 17 00:00:00 2001 From: artscoop Date: Thu, 5 Mar 2015 10:40:10 +0100 Subject: [PATCH 0071/1037] Fix .cur 32/rgba, offsets etc. Various fixes on code broken or not passing tests --- PIL/BmpImagePlugin.py | 52 ++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index d7ef866fc..06aa4e4a1 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -81,18 +81,15 @@ class BmpImageFile(ImageFile.ImageFile): self._bitmap(offset=offset) def _bitmap(self, header=0, offset=0): + print offset, header """ Read relevant info about the BMP """ read, seek = self.fp.read, self.fp.seek - seek(2) - start_data = read(12) + if header: + seek(header) file_info = dict() - file_info['filesize'] = i32(start_data[0:4]) # file size @offset 2 (offsets 4, 12 are reserved for OS/2 Icons) - file_info['image_offset'] = i32(start_data[8:12]) # file size @offset 2 (offsets 4, 12 are reserved for OS/2 Icons) file_info['header_size'] = i32(read(4)) # read bmp header size @offset 14 (this is part of the header size) file_info['direction'] = -1 #---------------------- If requested, read header at a specific position - if header: - seek(header) header_data = ImageFile._safe_read(self.fp, file_info['header_size'] - 4) # read the rest of the bmp header, without its size #---------------------------------------------------- IBM OS/2 Bitmap v1 #------- This format has different offsets because of width/height types @@ -105,13 +102,6 @@ class BmpImageFile(ImageFile.ImageFile): file_info['palette_padding'] = 3 #----------------------------------------------- Windows Bitmap v2 to v5 elif file_info['header_size'] in (40, 64, 108, 124): # v3, OS/2 v2, v4, v5 - if file_info['header_size'] >= 64: - file_info['r_mask'] = i32(header_data[36:40]) - file_info['g_mask'] = i32(header_data[40:44]) - file_info['b_mask'] = i32(header_data[44:48]) - file_info['a_mask'] = i32(header_data[48:52]) - file_info['rgb_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask']) - file_info['rgba_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'], file_info['a_mask']) if file_info['header_size'] >= 40: # v3 and OS/2 file_info['y_flip'] = i8(header_data[7]) == 0xff file_info['direction'] = 1 if file_info['y_flip'] else -1 @@ -124,26 +114,25 @@ class BmpImageFile(ImageFile.ImageFile): file_info['pixels_per_meter'] = (i32(header_data[20:24]), i32(header_data[24:28])) file_info['colors'] = i32(header_data[28:32]) file_info['palette_padding'] = 4 - #------------------- Special case : header is reported 40, which - #----------------------- is shorter than real size for bpp >= 16 - if file_info['header_size'] < 64 and file_info['bits'] >= 16: - file_info['r_mask'] = i32(read(4)) - file_info['g_mask'] = i32(read(4)) - file_info['b_mask'] = i32(read(4)) - file_info['a_mask'] = i32(read(4)) - file_info['rgb_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask']) - file_info['rgba_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'], file_info['a_mask']) - self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), file_info['pixels_per_meter'])) else: raise IOError("Unsupported BMP header type (%d)" % file_info['header_size']) + #------------------- Special case : header is reported 40, which + #----------------------- is shorter than real size for bpp >= 16 + if file_info['compression'] == self.BITFIELDS: + file_info['r_mask'] = i32(read(4)) + file_info['g_mask'] = i32(read(4)) + file_info['b_mask'] = i32(read(4)) + file_info['a_mask'] = i32(read(4)) + file_info['rgb_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask']) + file_info['rgba_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'], file_info['a_mask']) + self.size = file_info['width'], file_info['height'] #--------- If color count was not found in the header, compute from bits file_info['colors'] = file_info['colors'] if file_info.get('colors', 0) else (1 << file_info['bits']) #--------------------------------- Check abnormal values for DOS attacks if file_info['width'] * file_info['height'] > 2**31: raise IOError("Unsupported BMP Size: (%dx%d)" % self.size) - #------------------------ Check bit depth for unusual unsupported values self.mode, raw_mode = BIT2MODE.get(file_info['bits'], (None, None)) if self.mode is None: @@ -151,8 +140,8 @@ class BmpImageFile(ImageFile.ImageFile): #------------------ Process BMP with Bitfields compression (not palette) if file_info['compression'] == self.BITFIELDS: - SUPPORTED = {32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000)], 24: [(0xff0000, 0xff00, 0xff)], 16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]} - MASK_MODES = {(32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX", (32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA", (24, (0xff0000, 0xff00, 0xff)): "BGR", (16, (0xf800, 0x7e0, 0x1f)): "BGR;16", (16, (0x7c00, 0x3e0, 0x1f)): "BGR;15"} + SUPPORTED = {32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0)], 24: [(0xff0000, 0xff00, 0xff)], 16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]} + MASK_MODES = {(32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX", (32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA", (32, (0x0, 0x0, 0x0, 0x0)): "BGRA", (24, (0xff0000, 0xff00, 0xff)): "BGR", (16, (0xf800, 0x7e0, 0x1f)): "BGR;16", (16, (0x7c00, 0x3e0, 0x1f)): "BGR;15"} if file_info['bits'] in SUPPORTED: if file_info['bits'] == 32 and file_info['rgba_mask'] in SUPPORTED[file_info['bits']]: raw_mode = MASK_MODES[(file_info['bits'], file_info['rgba_mask'])] @@ -163,9 +152,11 @@ class BmpImageFile(ImageFile.ImageFile): raise IOError("Unsupported BMP bitfields layout") else: raise IOError("Unsupported BMP bitfields layout") - elif file_info['compression'] != self.RAW: - raise IOError("Unsupported BMP compression (%d)" % compression) - + elif file_info['compression'] == self.RAW: + if file_info['bits'] == 32 and header == 22: # 32-bit .cur offset + raw_mode, self.mode = "BGRA", "RGBA" + else: + raise IOError("Unsupported BMP compression (%d)" % file_info['compression']) #----------------- Once the header is processed, process the palette/LUT if self.mode == "P": # Paletted for 1, 4 and 8 bit images #------------------------------------------------------ 1-bit images @@ -189,9 +180,10 @@ class BmpImageFile(ImageFile.ImageFile): self.mode = "P" self.palette = ImagePalette.raw("BGRX" if padding == 4 else "BGR", b"".join(palette)) + print file_info, self.mode, raw_mode, self.fp.tell() #------------------------------ Finally set the tile data for the plugin self.info['compression'] = file_info['compression'] - self.tile = [('raw', (0, 0, file_info['width'], file_info['height']), file_info['image_offset'], + self.tile = [('raw', (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']) )] From 613d22fc75455383d43f97965dabd9b199b2b0f0 Mon Sep 17 00:00:00 2001 From: artscoop Date: Thu, 5 Mar 2015 10:44:54 +0100 Subject: [PATCH 0072/1037] Removed debug print Again. --- PIL/BmpImagePlugin.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index 06aa4e4a1..170d5ec98 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -81,7 +81,6 @@ class BmpImageFile(ImageFile.ImageFile): self._bitmap(offset=offset) def _bitmap(self, header=0, offset=0): - print offset, header """ Read relevant info about the BMP """ read, seek = self.fp.read, self.fp.seek if header: @@ -180,7 +179,6 @@ class BmpImageFile(ImageFile.ImageFile): self.mode = "P" self.palette = ImagePalette.raw("BGRX" if padding == 4 else "BGR", b"".join(palette)) - print file_info, self.mode, raw_mode, self.fp.tell() #------------------------------ Finally set the tile data for the plugin self.info['compression'] = file_info['compression'] self.tile = [('raw', (0, 0, file_info['width'], file_info['height']), offset or self.fp.tell(), From 514c55aa162e1258361401245a226a749856787c Mon Sep 17 00:00:00 2001 From: artscoop Date: Thu, 5 Mar 2015 10:55:32 +0100 Subject: [PATCH 0073/1037] Test fix for supported 32bit RGBA .cur file Some .cur file with alpha was loaded fully opaque with PIL. Fixed, and fixed the test to take that into account. --- Tests/test_file_cur.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/test_file_cur.py b/Tests/test_file_cur.py index 54bfe84fe..07bf3a750 100644 --- a/Tests/test_file_cur.py +++ b/Tests/test_file_cur.py @@ -16,9 +16,9 @@ class TestFileCur(PillowTestCase): self.assertEqual(im.size, (32, 32)) self.assertIsInstance(im, CurImagePlugin.CurImageFile) # Check some pixel colors to ensure image is loaded properly - self.assertEqual(im.getpixel((10, 1)), (0, 0, 0)) - self.assertEqual(im.getpixel((11, 1)), (253, 254, 254)) - self.assertEqual(im.getpixel((16, 16)), (84, 87, 86)) + self.assertEqual(im.getpixel((10, 1)), (0, 0, 0, 0)) + self.assertEqual(im.getpixel((11, 1)), (253, 254, 254, 1)) + self.assertEqual(im.getpixel((16, 16)), (84, 87, 86, 255)) if __name__ == '__main__': From 310684521ae0bc970022406a48a1c0afd0147b30 Mon Sep 17 00:00:00 2001 From: artscoop Date: Thu, 5 Mar 2015 11:32:03 +0100 Subject: [PATCH 0074/1037] Update BmpImagePlugin.py Fails on Python 3, tried some fixes before going the virtualenv3 route --- PIL/BmpImagePlugin.py | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index 170d5ec98..88f940708 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -23,8 +23,10 @@ # See the README file for information on usage and redistribution. # + __version__ = "0.7" + from PIL import Image, ImageFile, ImagePalette, _binary import math @@ -47,7 +49,7 @@ BIT2MODE = { 8: ("P", "P"), 16: ("RGB", "BGR;15"), 24: ("RGB", "BGR"), - 32: ("RGB", "BGRX") + 32: ("RGB", "BGRX"), } @@ -68,28 +70,16 @@ class BmpImageFile(ImageFile.ImageFile): COMPRESSIONS = {'RAW': 0, 'RLE8': 1, 'RLE4': 2, 'BITFIELDS': 3, 'JPEG': 4, 'PNG': 5} RAW, RLE8, RLE4, BITFIELDS, JPEG, PNG = 0, 1, 2, 3, 4, 5 - def _open(self): - """ Open file, check magic number and read header """ - # read 14 bytes: magic number, filesize, reserved, header final offset - head_data = self.fp.read(14) - # choke if the file does not have the required magic bytes - if head_data[0:2] != b"BM": - raise SyntaxError("Expected a BMP file.") - # read the start position of the BMP image data (u32) - offset = i32(head_data[10:14]) - # load bitmap information (offset=raster info) - self._bitmap(offset=offset) - def _bitmap(self, header=0, offset=0): """ Read relevant info about the BMP """ read, seek = self.fp.read, self.fp.seek if header: seek(header) file_info = dict() - file_info['header_size'] = i32(read(4)) # read bmp header size @offset 14 (this is part of the header size) + file_info['header_size'] = i32(read(4)) # read bmp header size @offset 14 (this is part of the header size) file_info['direction'] = -1 #---------------------- If requested, read header at a specific position - header_data = ImageFile._safe_read(self.fp, file_info['header_size'] - 4) # read the rest of the bmp header, without its size + header_data = ImageFile._safe_read(self.fp, file_info['header_size'] - 4) # read the rest of the bmp header, without its size #---------------------------------------------------- IBM OS/2 Bitmap v1 #------- This format has different offsets because of width/height types if file_info['header_size'] == 12: @@ -185,6 +175,19 @@ class BmpImageFile(ImageFile.ImageFile): (raw_mode, ((file_info['width'] * file_info['bits'] + 31) >> 3) & (~3), file_info['direction']) )] + def _open(self): + """ Open file, check magic number and read header """ + # read 14 bytes: magic number, filesize, reserved, header final offset + head_data = self.fp.read(14) + # choke if the file does not have the required magic bytes + if head_data[0:2] != b"BM": + raise SyntaxError("Not a BMP file") + # read the start position of the BMP image data (u32) + offset = i32(head_data[10:14]) + # load bitmap information (offset=raster info) + self._bitmap(offset=offset) + + #=============================================================================== # Image plugin for the DIB format (BMP alias) From 8c003e9b3f9b130f5b1e8d7fcbcf33851b82d6be Mon Sep 17 00:00:00 2001 From: artscoop Date: Thu, 5 Mar 2015 12:17:52 +0100 Subject: [PATCH 0075/1037] And finally ? Error happening in Python 3.x with P images: in original code, palette data was created from a list of bytestrings. Changed to a full bytestring. - `b"".join(list of bytestrings)` works in python 2.7 and 3.x - `b"".join(bytestring)` works in python 2.7 but fails in python 3.x No need to `join` anymore. Works in 3.x --- PIL/BmpImagePlugin.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index 88f940708..99159d569 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -126,7 +126,6 @@ class BmpImageFile(ImageFile.ImageFile): self.mode, raw_mode = BIT2MODE.get(file_info['bits'], (None, None)) if self.mode is None: raise IOError("Unsupported BMP pixel depth (%d)" % file_info['bits']) - #------------------ Process BMP with Bitfields compression (not palette) if file_info['compression'] == self.BITFIELDS: SUPPORTED = {32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0)], 24: [(0xff0000, 0xff00, 0xff)], 16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]} @@ -167,7 +166,7 @@ class BmpImageFile(ImageFile.ImageFile): raw_mode = self.mode else: self.mode = "P" - self.palette = ImagePalette.raw("BGRX" if padding == 4 else "BGR", b"".join(palette)) + self.palette = ImagePalette.raw("BGRX" if padding == 4 else "BGR", palette) #------------------------------ Finally set the tile data for the plugin self.info['compression'] = file_info['compression'] From 80d6b29b77fe5eba171dbeb517acbb1c7a17cddb Mon Sep 17 00:00:00 2001 From: artscoop Date: Tue, 10 Mar 2015 02:08:23 +0100 Subject: [PATCH 0076/1037] Fix bug with bitmasks on a 1x1 RGBA file Does not change testing on other files, but fixes a case which previously made PIL collapse. The Bitmap was a 1x1 RGBA and provoked an exception in PIL, but every Image viewer can load it. Fixed code with comparison of header size, compression type and loading type of masks and fixed it. --- PIL/BmpImagePlugin.py | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index 99159d569..f8a755812 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -104,18 +104,19 @@ class BmpImageFile(ImageFile.ImageFile): file_info['colors'] = i32(header_data[28:32]) file_info['palette_padding'] = 4 self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), file_info['pixels_per_meter'])) + if file_info['compression'] == self.BITFIELDS: + if len(header_data) >= 52: + for idx, mask in enumerate(['r_mask', 'g_mask', 'b_mask', 'a_mask']): + file_info[mask] = i32(header_data[36+idx*4:40+idx*4]) + else: + for mask in ['r_mask', 'g_mask', 'b_mask', 'a_mask']: + file_info[mask] = i32(read(4)) + file_info['rgb_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask']) + file_info['rgba_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'], file_info['a_mask']) else: raise IOError("Unsupported BMP header type (%d)" % file_info['header_size']) #------------------- Special case : header is reported 40, which #----------------------- is shorter than real size for bpp >= 16 - if file_info['compression'] == self.BITFIELDS: - file_info['r_mask'] = i32(read(4)) - file_info['g_mask'] = i32(read(4)) - file_info['b_mask'] = i32(read(4)) - file_info['a_mask'] = i32(read(4)) - file_info['rgb_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask']) - file_info['rgba_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'], file_info['a_mask']) - self.size = file_info['width'], file_info['height'] #--------- If color count was not found in the header, compute from bits file_info['colors'] = file_info['colors'] if file_info.get('colors', 0) else (1 << file_info['bits']) @@ -128,8 +129,14 @@ class BmpImageFile(ImageFile.ImageFile): raise IOError("Unsupported BMP pixel depth (%d)" % file_info['bits']) #------------------ Process BMP with Bitfields compression (not palette) if file_info['compression'] == self.BITFIELDS: - SUPPORTED = {32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0)], 24: [(0xff0000, 0xff00, 0xff)], 16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]} - MASK_MODES = {(32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX", (32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA", (32, (0x0, 0x0, 0x0, 0x0)): "BGRA", (24, (0xff0000, 0xff00, 0xff)): "BGR", (16, (0xf800, 0x7e0, 0x1f)): "BGR;16", (16, (0x7c00, 0x3e0, 0x1f)): "BGR;15"} + SUPPORTED = { + 32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0)], + 24: [(0xff0000, 0xff00, 0xff)], + 16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]} + MASK_MODES = { + (32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX", (32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA", (32, (0x0, 0x0, 0x0, 0x0)): "BGRA", + (24, (0xff0000, 0xff00, 0xff)): "BGR", + (16, (0xf800, 0x7e0, 0x1f)): "BGR;16", (16, (0x7c00, 0x3e0, 0x1f)): "BGR;15"} if file_info['bits'] in SUPPORTED: if file_info['bits'] == 32 and file_info['rgba_mask'] in SUPPORTED[file_info['bits']]: raw_mode = MASK_MODES[(file_info['bits'], file_info['rgba_mask'])] From 5a49375d4fb0ed2c7b9d37fab49aec7db21d76ad Mon Sep 17 00:00:00 2001 From: George Davaris Date: Wed, 11 Mar 2015 15:37:02 +0000 Subject: [PATCH 0077/1037] Fix UnboundLocalError in ImageFile --- PIL/ImageFile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index 82691af92..f81ddf2a0 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -202,6 +202,7 @@ class ImageFile(Image.Image): try: d.setimage(self.im, e) except ValueError: + t = None continue b = prefix t = len(b) From 84ec2af495a13076ca5d0bec2f9439e9ee639a3c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 27 Feb 2015 14:48:07 +1100 Subject: [PATCH 0078/1037] Allow truetype() to search for extensions other than .ttf --- PIL/ImageFont.py | 34 +++++++++++++++------------------- Tests/test_imagefont.py | 6 +++++- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index a9c50e560..61c552b1d 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -261,38 +261,34 @@ def truetype(font=None, size=10, index=0, encoding="", filename=None): try: return FreeTypeFont(font, size, index, encoding) except IOError: - if font.endswith(".ttf"): - ttf_filename = font - else: - ttf_filename = "%s.ttf" % font + ttf_filename = os.path.basename(font) + + dirs = [] if sys.platform == "win32": # check the windows font repository # NOTE: must use uppercase WINDIR, to work around bugs in # 1.5.2's os.environ.get() windir = os.environ.get("WINDIR") if windir: - filename = os.path.join(windir, "fonts", font) - return FreeTypeFont(filename, size, index, encoding) + dirs.append(os.path.join(windir, "fonts")) elif sys.platform in ('linux', 'linux2'): lindirs = os.environ.get("XDG_DATA_DIRS", "") if not lindirs: # According to the freedesktop spec, XDG_DATA_DIRS should # default to /usr/share lindirs = '/usr/share' - lindirs = lindirs.split(":") - for lindir in lindirs: - parentpath = os.path.join(lindir, "fonts") - for walkroot, walkdir, walkfilenames in os.walk(parentpath): - if ttf_filename in walkfilenames: - filepath = os.path.join(walkroot, ttf_filename) - return FreeTypeFont(filepath, size, index, encoding) + dirs += [os.path.join(lindir, "fonts") for lindir in lindirs.split(":")] elif sys.platform == 'darwin': - macdirs = ['/Library/Fonts/', '/System/Library/Fonts/', - os.path.expanduser('~/Library/Fonts/')] - for macdir in macdirs: - filepath = os.path.join(macdir, ttf_filename) - if os.path.exists(filepath): - return FreeTypeFont(filepath, size, index, encoding) + dirs += ['/Library/Fonts/', '/System/Library/Fonts/', + os.path.expanduser('~/Library/Fonts/')] + + ext = os.path.splitext(ttf_filename)[1] + for dir in dirs: + for walkroot, walkdir, walkfilenames in os.walk(dir): + for walkfilename in walkfilenames: + if (ext and walkfilename == ttf_filename) or (not ext and os.path.splitext(walkfilename)[0] == ttf_filename): + fontpath = os.path.join(walkroot, walkfilename) + return FreeTypeFont(fontpath, size, index, encoding) raise diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 1b03ed13b..a7d3e5ebb 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -269,7 +269,11 @@ try: #correctness. with SimplePatcher(sys, 'platform', 'darwin'): fake_font_path = '/System/Library/Fonts/Arial.ttf' - with SimplePatcher(os.path, 'exists', lambda x: x == fake_font_path): + def fake_walker(path): + if path == '/System/Library/Fonts/': + return [(path, [], ['Arial.ttf'], )] + return [(path, [], ['some_random_font.ttf'], )] + with SimplePatcher(os, 'walk', fake_walker): self._test_fake_loading_font(fake_font_path) From 3e9e95b00aa88226c8d4b2b6e600a98a751e1e5a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 27 Feb 2015 14:50:54 +1100 Subject: [PATCH 0079/1037] Changed truetype() to prefer .ttf extensions --- PIL/ImageFont.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index 61c552b1d..92722a7fe 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -283,12 +283,21 @@ def truetype(font=None, size=10, index=0, encoding="", filename=None): os.path.expanduser('~/Library/Fonts/')] ext = os.path.splitext(ttf_filename)[1] + firstFontWithADifferentExtension = None for dir in dirs: for walkroot, walkdir, walkfilenames in os.walk(dir): for walkfilename in walkfilenames: - if (ext and walkfilename == ttf_filename) or (not ext and os.path.splitext(walkfilename)[0] == ttf_filename): + if ext and walkfilename == ttf_filename: fontpath = os.path.join(walkroot, walkfilename) return FreeTypeFont(fontpath, size, index, encoding) + elif not ext and os.path.splitext(walkfilename)[0] == ttf_filename: + fontpath = os.path.join(walkroot, walkfilename) + if os.path.splitext(fontpath)[1] == '.ttf': + return FreeTypeFont(fontpath, size, index, encoding) + if not ext and firstFontWithADifferentExtension == None: + firstFontWithADifferentExtension = fontpath + if firstFontWithADifferentExtension: + return FreeTypeFont(firstFontWithADifferentExtension, size, index, encoding) raise From 6e36d5304d312d82b40518069231f62254933094 Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 26 Mar 2015 13:52:57 +0200 Subject: [PATCH 0080/1037] Move LICENSE from docs to make it more explicit [CI skip] --- docs/LICENSE => LICENSE | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/LICENSE => LICENSE (100%) diff --git a/docs/LICENSE b/LICENSE similarity index 100% rename from docs/LICENSE rename to LICENSE From 883858151d0a7f48e4c9acc444a65e21bc3cdbd9 Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 26 Mar 2015 14:05:17 +0200 Subject: [PATCH 0081/1037] Simple test for 1104 --- Tests/images/illu10_no_preview.eps | Bin 0 -> 392205 bytes Tests/images/illu10_preview.eps | Bin 0 -> 405470 bytes Tests/images/illuCS6_no_preview.eps | Bin 0 -> 411730 bytes Tests/images/illuCS6_preview.eps | Bin 0 -> 424558 bytes Tests/test_file_eps.py | 15 +++++++++++++++ 5 files changed, 15 insertions(+) create mode 100644 Tests/images/illu10_no_preview.eps create mode 100644 Tests/images/illu10_preview.eps create mode 100644 Tests/images/illuCS6_no_preview.eps create mode 100644 Tests/images/illuCS6_preview.eps diff --git a/Tests/images/illu10_no_preview.eps b/Tests/images/illu10_no_preview.eps new file mode 100644 index 0000000000000000000000000000000000000000..ffe8a537afd5ad941c1b8ce347846ae42aff138d GIT binary patch literal 392205 zcmeFa>vCI1vM%@=j_^BR&apeBIb$u}B{_V&n-|Gex8>G|>f3M>2oL}%v_OCx0A+C_ z^zQS0d8x`;YXPL(?lTj6wrr9>U9z&WGPAO>vZ}uR-(H=5b2yxxkG|P`wB3F7>hw82 zY<>B~*M}#6>Ybb(_nu7$v*CDp`EB>FwcGaJv#Mf`e%gN~5?szh}+1~0+ zXT9tB=)-vQ%cIftg89euQGYp`f7_J?x-U>}v79sM!Rv?J?E`7&>*p+yU`n0wY`WIx838(s6TytG+rVLq&ga1 zj;F_yG4fwb|1laY(S>ijUggJ8e>j?V|J&h;ZBJew~2F~39^9saTeB0fQg!*|j8jgk- zm)XsHz}%R5dUJgZa>eZH`E0NlEtOk(hd;dRy|@C{?)D>$HtYV6Zdv4bHk-V;y&m=c zyc|!)%iG?Yy`9e}{qpdS&;E4$95r@!?`Yt7HXn6&9_?62hlyd7cAXYc?brgNxn)?dG9W55ev&n3Z*1AWN{@{HakYmW^?8@_*bNuq}e+;QQ zey@KxYNU}_W>=IAh<*1z|G)oVbh3RI|EufmbotMn`d_2|pL=}Ud&fCIERU1XMgL|( z<^J{8e}R1mtK?5}nmTu52We;5rP zefh;g$_&SYWd*C_8q*>A)qOvm{Q~25HMp72(L+n_Eax|)ZZ7s|eDUQMHvlEo#k>`R zMW^`3`(37Yhd0;Va~g)->l@gK)%g&$R_8FIyTj4NC;xz)>2Ni@0hW@?Qq@N(JNh_y z7ZNAK3m8$W$*gY>r07tJqKQvvpd)o9Ww-myb{Djtu=$jC4NO)7*HWcS2 z|MG4$A2FqKJ-hDO559hqG(oNdB8(=B(U)I*0@{Ew7*3_#2*=1Mkn8rx2c^GP&=S@#~bSI zbT84_kiA=E?}lvD`JhHU0Icj|ckp5U2}aiS^=P>IW?Ohjp@Fe_mmjXsNu$^0WOm-4 zpt~4;Wa?MzI1Im3yqk()Mm*+|9Tz7e?*A?m_ft0ll*Gp1+T`-(R?z-A1msbvA=j%r_FHI zg@$dU_LWoCka2*GM-jqpzP#x*g3XhLmvRQox&; zkZt58xo#;S6fkja$^M7erxZ8yA;5s2g|}H^*zeXz3h_mqAq2Zw8nTMKD5uRbE}|%C zl3?Q5EDFg)sU(%n0u;y(A6cj-kj{-ugVOODALasaQ+h%T{) zuJqhxyxIJ_W%Z-U6+M7tLov6X!WK}oX?tsnpJ#KnCH9AD50}R#fLce|+L&Svhe06l zI$fDK8?{!Twm0JTUUvY0@jTC{ZfB@H-2lM>a}aDWTOGBc)E+2H&4F@JYh*D`)wSR&=N{yT#(#|Rp2G>JPfYm_fY!3;f zbyd|?hY1907)xHm%{|1{32i%0@wT8^zMD;kz03!MC+m`3eCR|rb3k79lN8`DF_M3* zi^?uULvodoJAvKmprn|xX{Y11D&YQA?5H0{TvvCmZYBn)^X45^21I-z%ZC-rwro?Y zvKNp#kO0)F$)z^vE5N|}qzYDC)qFU(nu^X>cDDK#mLfQsJSk16IA-yUYXAz_ z@ts>=bjC#!4%S2KjGLh@>ZFgY{j6Nz{o!YaaRw;38394b`mq^5-$(%>8JD)C8 zEnEM*oHnqP@M9A|0_G-GntiVHNk_U(@Ta0LfRh_Ozm-4bC`w1LxP)BMpMz3^59997 zSg0<#N=$NXmn+u=*0Ag~7aTv$X74fRLRcX{3DuyHH}-Um=OR&INKThxaS}W9C4XDX zMq3+rh%Tm1;1&6dg_J6aX)jxKJ@rD_NM8bN(xp<@lTs*2nG`itQLqZ!#yS`gqbXYg zA(&|7!pH7bbt%A!7yhl&ZNh>TbXfw{iG(gj!Nj+Qn2%9l&Q52$(t#pUGD7@d-BpBK znFur+19sC{42V_YK4WLdzLhx02nX)4jP0hLwFBF($UX#AblX|I9Ds}&DB6Y_d*yte zup*mR<4n^E^Sx)P2tY%tbhLip({T1%%6?42)Ojpo=nyvFJXMn1*yu?XRl?sz!< z;HyQc_X4^gIW}f+0drscRCboTsii6;k}Xs-RxJJ!4(#zYRCTJh2=&@>YXD5ghqhGd zhhi920S_&Zi)m+BN(AB(bci_*i^lunya;8q`uRZoKLHZK0LA6N)&Bg3E|jI z2AjJtCbLPl&mdBPbiqKeUUU+5;Nj0XK`gZyj*pIWrxU?2UA}YReOjY89NJ?oT2cWu z&zfvnhYiB6(P5Mbb-d>Z=R}OGpL?%PcZ5V4g~Yd=ZYQwOStb}2@3glVUH69IAq{Gv z^hrBL!A{jOI$aqYV@9Y|<)kyv^Q~7C!(B`r5Js&BC2NCrR^tUHEX;1!A!tju!gssV z83qkX!Tb3$B#X)x%OLG9XDQ>+#aW_%?tH1H+B&nA30Ij^ro2C`=;Y=eLH?v_T9I)h z(nQOck-pOpq6|1;E6Zpt^A+e9kc{N(a4HV0l)F-nW0RAt)rK0<(b}%mnp4xj?1--J zyks#C)WrCLJ^GVnV2@;@lsvM50cuKIUKZ&sm~{~)q@8AJbduW|9D8c`x0M={&gu29i8#99~1Y26CQd+h0=GDyv z5R~O@DbR{O+kjf;1k}(mNP{QQ^-0K<)fy~VsSK?vb6MPu0!-Q540Y7%7w;4@ebI}` z9Oru7PNO2`c$uDL;p=mq>{_s`0I#j8;Tgn9>ZDa8zcrXp*D1ogg`%|eRcbPzTGFMJ zEL;SH8YZ@}2dxANRW^_zQ#F^jzGwZxwqnok+3`@v?QWY{xqpvVa)BuOgUM{sl>KI# zQjV<9q78;4aEMsa?!KYN{$?Qy5ri!cZ(s<^+}X`>13$cc)wAVK4?dG$tbonpWNK`4 z!I5xPw55hj4vT#Wgbb5Gm0;#ct1N!7{QAZz;6zg}UoY zJ-H94C-lSK)!vis)ZY5$_V3f&{)Xm`xr3U?vT9{t|u{?w8AV zU1D?x@A~uWK6X$q#K+j>zER1FLuU8hP>Z2iRicCHx*&SF+-a0Ndsrl{N?y@n3DW%oW8JC{mZdvbItp$%M%a%rQ|N zGRgJ_w#yYU=MhA}zr*5+Ta&sl%rP6;r8HrfiG-kCUs+NipKYCQj&@II9S!b>Rjb4c>FjB53P8UUw~H zl~t1A9=)gUDh!#;?Cw>PuUlO>bJ0`uBlS&ut=u?rDv}zbkK?6vw$qV)O90;jEH6|P z_%xZ>C^;2TSC=W-Egwx^5mF(NlQWnbfh_l7r*(D~Nto6T5h+zP3# zaC+zBHtizG=B)X{HN?^PjAUKu{ifS)fB?(1oc6gwe0)2$O`yI2B7il3x@Lu$DST@o z6%)Z5i{f~GGnF$lY5}Pls2Z6B&apBZ(k6hgOB@Wm4w*p6*o4EV5W*=s1#Wf~9A=g- zlf(8Hs%h(?OtXPR80h@gUP$xz>c+-iuoD51IGY8uhQrJDefF{~5Lib}GVmANS6 z8JL+^(=0G--K7cFt*Woe$_DGzSy8sY#K3^RireZxGZdusktV=3SE>OQZ3eerhAz8o z+?y0`mnKLPiQBeaVSKNSPfw1p5-Sv$r-4bCPaB)@vz6|Hshx`Mx?baK?2e?M(NtZ; zOc2o`u+@p>G;+5u`ocw^wNu1&vpMy23KIOPGWLL#)3PS8kT&mlt!o;?12g^g_?n(_ zo57Nb7*Bh1&Cw`iv?`6VPtITba9?{eW6j!k40sy4fJJF{c3N0Ugf9)cEv1ZfY~IIA zP6%j%4@$~2=R;#C>rM&d^mDLJS=A$)3CHNX?r6w~|QyQ+4?31c2Zdtx#d6t>v(7{J1S3xaCq@+({G+J+Y)g zFi&GUF*b(K1f=*#g>ZS01prH!1+y!&dF*^|Uk;7vH2>iLo<1xyrLGTtvX7&>|41q- z8weU%z<=tWk8m_XR%nW0UyM#QIM0MwYE!6vT)jH{;n`1zN6&r=7;6=Di)o#+)th(Y z1l`tG~^?l(JfZ07s#iFAo-C$MPNjW)iMt0fU4s%{}qMsn5$f+O^ZS(imtViN57 z*MV?FvupPCzWtSDDE7$?99xOT#hOMXg#*S#kWfRqX*doSgr4mPJ+Wjny_XRTlAvHI zP%m%W12SOh2hghFunlZxJ3OURA;vN%iMIOs>u1w+{`+3X!^$((rlxQ_|BlmP+fSc9 z=IOA;^X9U@Bz`R$NcS+4hD7y<}cvxnaH zED|D3tWXT=0vyF!S@b_xgh!gn=Kbq;<3TfHfO%B>atZiVBjvV{^1hM6C^~q$V3X?N zp@g{@@Qlxy5mCS8P14jf*A#Vheg_&~V%d`7FQtb}6vlAm%zkOhk;v<|j2x5ap`>bB!aC%kD!M_-=En;OPNZp7X1tp%md(`u zhuL`8`)Nml7Fq~k6D1FR6bSHW4+DEfy?@Nk%Pif=Kn*ePYI$}dPga4n>(emi<8aNq z(e%T3KAZMt?@MT~lY4_c+=FBQx+6*kGR{e%?=i?CwTn%eW&dv_TuJ+TnKosJL_q?E zc`U9+gE3B^6d9vAPG!loyZ~#rBi2KqvX)^Bkh3LX1jhYEE573?&e!nLkJj7^z1Tt4 zA;&zRRkq>Tv+E6+jHWL7gIhq@=9}3oy&$5s}lODIYVj5au_P#|W>Je5$SWAlOA61{8OShDPii@-Gm7E*)vvzuk_2Kxkyf0ud& z#tu}vj@GYVesPB7*dfjv_J?E0btNg@A$@|f5Pu7CkcwVm6XMh(&pHj^LYc9&W_@z~@Sxo{}wLGNelW zMT?6C8tNpgp8z~sH7t$8M=_wxT{(rNzQXBE?me2BI+$Gdrq@fjD#1@<%!t=kb~FoF z{1P)+17~Xm99DzKvs$nzXc1vzThiCYaY_@3<%zYVzm$7A*tQ(V(HjYgMKPSXMxY8{ z*(8%2~=EYE^L?TW8t2+WV#bl zJi-Ws7{FfR+$XI{V+Lr<`}iCzV4no6fFrEEMZs^ZGh<_eE8v1@@Cnt3;oY?tkZ*dz()k zSXkVoV>;2i6wPT15T}}i*FL-C+T|0w8{vKnkk?`-`05qOcZyWdeKV z5N#ZHw3C;*pW-u1QM0J=cOg;sKLBIw$<#T{RE0Q4_OMiRmqj8YX9=DC6OHcbVwV9f z54sQeN4m>!0mscMo7pDfEM>eywQ_?0mWPo87Y~9+!y892Pl;_{JdHIz@){PBq<|Ak z6bGilq}cE}c_m)gW*Y#<>8^;y8Jan(P9Zyb5Wk{(-*!b<1Kwl&_X!4XQS5=-5BIQ9 zFmEx*+})`5cUc8D>Sdd9E7UV=&mLGUYxHfs)jgOjJf;(><_KK*&3TRX z_IzI*bRm$?Xu>xEFvM#ZX9)MH+>XVbZRiIGq0h(gPt-b`M=_x(rP{%7!J%k-g`tI7 z&Gz0YW$&!j-ma5oeRZd8ji%0`H59#cdu!{)PYILiw)WFRLffOHfY;yZR9^R?ibF5i zDW(!zcOfYAqRNBTtrgs~3nMCH_1$+6kR|RIJ%Sd)F?TU<%jKL{8`F_%)Ybw*q2Qw~=-cUh zC6y{H2IPZmQ+W?SeD%KYHZ7d{19y(-cQV7m55;|Q)<=CbK-b^L=aOUtX*)-pU!NygpAcsVrH0nbacr>tDDPAIA zkRmBP&xoC{J;&ieD^FXe)|X_Fm74@yGszHYyTX5ZI4i-;zTL8_I&Mi!t(zy)R71U< zE!8w5*R3IILcIPy8*MS6XyrHFWK*khVotT>6*Cy&7MKJ+duln%m3m>9;ifa$r7OEx zQwAopR`r3b?z*qwH$EL;Pkz~b)yJv$`9qZ%wqmP$_bmkzq}CC;^vby+xv`RdNZs)3 zu`H5N;p9<+VPrQ<6cm+m<>>+ZI(1LDA;>ulHtr1tbwXBuZI)y74Te_H6#@Fb;g#Pa^nJ(Um^T0)`xn24PEEX4lwga8q8aCY(N(|K57%%t|=+t#F{#@b^8yn=o)b0z3b5lg=Xu3h7zJVmF$Hhrs#eC@79JhjvB<- z=5StjuZjkE)^T1J9@+}oiBGQ#cJ`OpYJBjp4sjs&?+d0%sO+=yvBBoeY;uEbc_}r% z8et!L&>PHg^Awjmb%^s3_FKmv?pu7pM+M}v#`PetaXBOGrB%OG0B-3_>P$&shu*I>qr|!!8CtSY<9T95LfXg66Y}IH| zu`NNQvlvA!G$l%CPb2D`2|WBpZ}N4q&TTccF;NzGC|rE$U`c0JRjfy!(LYPaJZ%IV zfbzXVdNZ~NAo*&e9hlFokgW5|b75cGA^Idc)j9Ik6_j_YiMjVsBCul|pMydpaW)bA zqHLwemIaR2b>JZYNN+da>MpTmtl>8*q!PVT_vv310YY>TL`lVlCFaSIrn; z#EPtXuZlSUiG$bdyAHmLtgQyUOwms**4b7Luy0;6cFVFlh~NjW_zE+(96I7fJ95`u zEZo`C+F?PejF&7(2#n`QPC5>9u{<;Z+BLdWmXPwbr^i zz)p~N0(?$GF{<^!U;FbZVu8Nx{sB=~*l3p5dgKFdOTzt-*aYoQ;gx}6fJrSLeq68p z%NVC~XVY&O6V}BX@oqL;JnAyQHP!9o3UVy>Q3_y2T_Jl{nBo5r7m)oIx@@sbEIwfagT!D>4G7 zNqTLB^AQ0yosi1OLEVe%#T(2eq-)B>o|(Nk;)L@pr{rNt0?`sQ`lsoSjPb)Bz(N;V zE+Dyfs@@4sG|HJx$@!MaupgAdLBeJ}>-fS2A9I*TiUkji$^g2!6{TaLActjzkdZ&u zMnip8Dd^D2S+Iy)edIZL6nN;k1}()(dg(Rb0}#p>U8flmpE`qm02RW(iiM0P*`%JH zqbi(DY9%(a$r=>(_QW=N^$;SO9Dm-FP!4F0DJU>rl<|x!QfV`aURlTf1W&SD^?f>8 zG9=O1j`%9;eRloCQqH<-ARN<9*0@BvvQJ?}BKqz&`WIYu5^vXgu?|MU0aCrdTUM}U z*rS4Pshm79&m=((0%PYfa4|`lIm>k$bU0%zH41de=knxDu6!^@C5?9>w3W-GuSfSJ z%g`dR@H~SAnHr(x{FnIH#p*NrG}&r2=J1maezDMl!^tEc0`nmk9!?$16md3n(NgO9 zFpG=M2wI1f*13NT8p8gAed2UofFWHclrsnT zOvqPwz`Tx;p&29$v_hg6=QR=h4<|tEnHL79iiMw-lGdr5;I-RfmLrj6BZ@%^SyE7G zc5o)hvz)c%e2_HY-8zce+-fKI&6qZCQNp)*la_Zbuqfm_gt`?gG4qzhNMfi5SkGxV zX>`cD9=NNyqC%XHhoig=Zz_>fgSVXedS}(3iLy2Wwm<&+l(C%)2}V8zKG4VdfTG+d z!}p<`*l0D&~;-!FR8gmdoX%&y=mvQxe)%%;JdrglSJ$;`pOS!xI+_6{~^cW-yx~ay= znU7@)M#r}3rVp`BS%LW8)#}x|*>Z+ByX%u1e3*`Y!s+2`&KSt@=&;IwLy$hTaKUd) z6O#BhV1(JcAW&iimmhR)9S^C^&fXjIO}>#de={wot02;0w9rAg{+EY@|Y_Rvt28_QGj<#-gqm z%yaAJB>z(Ry30B_D>(`-H|*`!Bix9Gr?G{7Y?}X+uAG(hus3m3<2gNnr2L-23{=k?0fhsJT}Z~f^}W{$(kc*dg>QR z%PNjZS!}@{@;@P(5I7(ChXC5_z`oxXDEbS9}H`OS7Oj-%|@9kky z(H9o80Vegk8A1XPnLR-O2n}vc_M4*s@TP$A3B}CFuVcz$!DK+g;amI+i{A+%JpD;_ zSXHpHiwkCHI(#ZUaS}I3CN~KfHmcGRx?ZQ9Xsb4=DTMoz4M3W7b0Cp$DA=T2E*OZl zmUML*z2?(oqsZi9Y6`h@U|X(glea}^>`F10eF#Ox>5uYmS7;@pD9sq8^n;!Zz(W&6 zJz!?i6DtRSs@3ShT5ptvJOu|p0`x8 z0SyIMSAJ>G#iG#GTVOCM(=ruC7+xwzY+=fQbPm8~Na@%JaV9o-m<3SggUxW#%Rvq& zjkkbF?64WiUhEfjh?tm(w_SeWhEAb28$2hU=Q#PN{3fk#lQmuwj{~#;jJnw8FHEzp zE0vXdegc(;|8?DOtSQ>M)3K+8b1)yCX+crw?DYIX#}h=V5Xk}Wt<3F-msAwc_>?~M zToK!_weMQH$DJUDYU@L-8t$0)a7i!*Zq^h)wJPDXO)`FPp{5`xO^}6TZ#e+hH0z># z(M(KKex?cgT6)L{FM#bQ|Di|u0H1tpCpPEf;?z(?D#Ird6u#n&BzhrVLVxvepFCpX zZ(!h38H4XcNO0vqwZU-v3OJsz7g$5#96H@l$YFP2hJhydQo$uEi5ZoYheu>mF$Pw# zZZ1Zn&2WJWhSG+F2M?Ky*JS8=V1kv9)Yqe%MgM#{E?cF8a+hE}H<0zyK!IUY+g2?l>*7U5HvAk^MonM@# zPb){#2*aiVMMZe1*R#P@;cILe(bv*19(<}*8Z6-s#gQORieRhhR{ zC=4ahWCbkLY#DF|v2qWRvH|yTIeE$}go+@?%;;%+a!HxU8ip;-TcQ)dFC21=#*mFw zTa!PZRk{eM@S3OILsa$5D6e{IUl*t5K1E4P7$=}eZm8I(5JICjn!d@k?^aCC|67e zO0H#uoe&UAYm8h|{<8rnAlzRwogkLG&`z?NPNGV_*6d_0+tfAYTcD83DhKDI zpP7T5*1{U10>zhtUIpD|y>gMto)gRHKnZY{AhnrRelFLticF;u&DUDmWWOxDv!<}> zhqCPMnzDHjTP)PJ(lRYgx}+>!p4R)qSDElnYn<`r6rr#S7=t5enFdmm2(|gvz%=O# zMu|H@2@r8wuY;N+Xbm*vhTO!{@;pb%@vaaPVP{HH*Zf1HvJd`?#c^;mBsCi;3~xL< zyG_~8AhIqkT&qI)u)!0QSHMS1$IqIKoTb6lPAz;4lc4t+sb}GH2rCPz0Tof8AuH&I zkXpbjH8~_2b4Bn=0|WlsYauMBlJLG~Yyd97^zLU&-%ZntEQ9Q6`Z&*^t)_mDAXrgQ z;;)bqr(#T3fv!HxcSe;OST7$0I_|!sQ~(~;5`;o`@*G0Dps|@GOZFw~PZ!7sq1?p3 zQ3MiAungnUHhsU3D&fk;vEQsr@^;p`UZRieRvILHM{f+Bv6PkOQ!Y{w^oxHZlG@ zDPAMQ_mSa^BD_X|3jq%D>+E<>@%7?sr8f`Iuan)Nrq`@hFt$;?O%8l8bJv@*&xzhDe*nTGdA+tywK0NPCW- z&6>(7b3cjj((44mu`kwiYTg~cc*ds|hKE_@MvYmREX5s2y}@|ITlRx4 z#$|!urEM9V)c(=2k3*4u^6a^%TQiI}_7QUG7Lw2+yMZqq5c=$(nxQn%8WmdvygJx6RTV+?dZc z7g{Q#6V4SY&V~~;h$gUYJ6n>9K;&! zMR^()+)u|PN)i8Ue8Zjt_w*$susS-LeS~k8QQ_NftG}Htapm@Q^>_O#g{9-$)tfOc zKD?AKyWfAG23MZkNy`$Fyg}8x3=q}q{r_EgvfGeu9V%^2e|z7Zac6daIGavx z#ao9gKb%XX3r?0gB_BHrt>MqinFN*s;*{^a9kRRdDLaq7U64VJPK9X0^=BfL*~uHo%ee9cw|Jtp-q)c_-#Aa>2O*58%7$nl!N_22&EWh zUycu(;0NhTW$fIqs|Gz6oaq$yBMiN83KFj%;(y*;n{G*(j4Qja)~{F^eBfd8q7bZt zc%wYWQxTT4LeAodcaV7kM>PgZpU8bSRV+jcC)JnqNaIQog`RZT?|xSvf~bKp%(EtZ zJ%VF#FnY-YzC*EDg#{HVW2(i-7b7*h!fj>I{$#7vw@wF@%VQshe8=E$e!COp0t_D~ zd1*6S%*Db*K|4iJF>c97MPo!k6+V({(Qhm$x$I8qQM5U~<*-!hfz>59%Mm$8BBBvO zjoLtI>#i$B1=nXa!BJMPP&4f?3M!#IhPHV#b~IvKx!P?Y(gc3lv{}%ubj#wh`7}w0 zglC@{>f-?fk!?H$lWEcE7z-gYrq+>!_8UkvaVTWdnc&AEqD~3fY2IZ4h;Pb@pE*2s z4s|Hus0{wYV5e(yvswid(-!i`Ujk=5&sK=VdR8iV=_~=jy=X9Z1DLeO69%-%7D$P+ z$}D|^PqtcR!g5Q8`0-!tee-K8Jp!ht=)+jOEr=Dt2bIZ&^LQi_{DZ47F*Ir{ZoT{G zRSF^Ka{dLUH(X%Q@Smp1wsEvDi1miED2Y&ENGLXGwj~{W3YO1l@|KJcT3VKi#cE{D zO(hdM??WeqT&CCPZML4`7WVAYL%q?(LHJlj{O^QX;F=)VLcdo&;h2uZ)#oCw=UjsC z^q!lQ*dP9bC@L9DjUgm0T`Fbt(TMcf98bNmvs;gL^lEht43V$gBB#jQx5T)FX=pl@ zFaxw{AlS{fjHm3$IA^PUz;;vi;Dcr#c=WmTv$HXQ+oN`Eeg<_<+u{ixvfe&>0vXxD zC;Wc9`tG9k535e@9G95twquj#expvzNd^fo)hWPW{)z>)_pKmAtvT!YB$Wt9b355T zY@vjd^VG|^7LKt}F25000@Knh0nr=EtzW#YQUH^ zf?45)JUbJowk9&z{qb1SG@<6vgjm^75t5S;AK6}>AQBp7YrbO=;K^na4PJF`gO_tO zbb!)QQ=@%ampH_Y|MCin6SObSG%q4gQ(`pG0cCC&NG;qXQ)$=+LS$nfm3Y^6S}fr8 zFqpLVWHD?MK6sb?W@yNzp`~ELt%h3p72mX=fAGI9-F0y?HqB6~#q64km>ejgYnW>C z3zMmF^;rr}9(eMg4V+LQ>@uUT`Hq)YJ^SsOX zEwd=fo&gIdDk zCOr}fK0`K60E|`$S--+f9#`F+|Bm-)wzx3`nBBp^*#>~?1-JNejTOAQ@`6CAQ4l4) z;O(s!L}!`>x3<0D`}bbZWq~7PhR<=DpB92O6j5@6WsjwH75Z+=LAeFZ5hVz+gt;Kh z7_t$lIN-)TsBF3%oFF2BKT_S<3p3M{T%t47w8$F|aUOvF9QcMnlMq{@A9+}5~}=m zmnUDqVVLg7u0|Iq5)*#Uvl%cez4(++{E;tDyhI0a<92x9E9DF()0ATmLwOz^K0sgw z-uFrg7cT0+AhmcgA%-}i=EdfVc*Mg<=?n)6=)Fq;bMN|-3m%*|VI!fYBZiDOp4=6G zLCBZOBEfn^P4$%ItJ%WqzFJia)au&o!)8TEaJGa37~!gQ_cC&C_;cJw`K+=(@n+JZ zN+mlcdDaDYt}E+9a#Ur3mp&FMhW@pmZ?2Ac&?g-uQjG>|Wl}l?lnE~U5Z&cQ#F~j; z;G;|uVP9XI;%zM$2nh)8znk@k?o>{TF=>#VSBqOA@;Ny?1Um2&R{zf*$zd(U?Q5{AgGO9v24odWp6NlRX67x*-`}O8@r`L zje?|krr-mSm6x)Gy!hAYNC9=9%15(WPYE` z8f=AEyvHUmlTx>DW0gZi-MTse{YYl@>1f|bUk(GVkF9XCBpx|_D$g~1@~=CO^?RBk z-IlHv6I@mxl?g-+3jjD9;b)%DQvHI#RmfTaF+xk6vGzP7#5xGPB_0H&Z=*N@T6g;i z-r6JBG;p1kni{>^tVy>;t{LPujlREzM)C=rfQR@laBZ`!QDi_isR_{B4E% ztK`|f;$>C33Gknhzq-XW$cgtq#xJ)o^9jf5WB3w}t!fQsLOTd7sU{&TL|X?m)^LBK ztyr+(^l@d+MdGPZ$i^o;*b3v4pLBdpdtu0~##1i~-q=`{Q-;W5cB3~mNhc_bAY`kM zT+^8L7@!YK`F5a@G#p889kfn;)H>y`QSFfDR4#lwUxhCEpkt z$SE>JsH~3!VRY?6nXk=jBS8sc%S+&DM)n$_&nW|7>lr`~HP&VzyQraNuTnVq17!3h z1d@X|5jBBkZM=&?!;^0qHVm2#QioQweYQx1P>(87IV05*aq!CE0yLOh^|>Za?}8LpP1PaI#SqYLW{3lNzJ$*{_orEC1%{{1tJNysD`9%39Y?WoqQ+Z0t z74v+@Cb4aIy3MIk6^hI!&=d>wQZ(pbwrHT~v}aBJu-W)oIfb1QM3~(=Or#&{v1Fsb z>FG{J%azp~Xz3`kCVv#I{J90K219U&S+@-(Wk!FR6r@Z14=Vn&4I%zpW(jf;vG)Gi zM4d(!HFmUyGZh(4YiRTJ8c7;CXx3a-!W_y3vrGaiUr2;i_MNL^BM`;GPN<8O>aHol?`G#f;h$A4Hs|3QoT|3 zCDiFYmhm8@Eq>0ghV^eIOaGQYCP8e#qc9K(U~K*KP<+koe__k8MNr7sk=45~cDLOw z(=X^KQK+UjSJ&fgv*GIkv4;z;etm^&vQg0pdV!Vli|GLR#8WZ83oeDZjMei#mSd3V zA7e`@BLS@CQ@}VVCQc?w#7hyeVk!!{PsjKm4i=1t5$(3_4m+DfxIi>CsOmNWX2ZEo z?1n^Ac$RUR{i&q)ZkLm{+u&8Vq!AT6IdN^NIZNe$R}w`KJoOQ9ph7Kx7cZH7KWOFPg63-kr$}I|0N$A#BJDn1sw3ZG9O7V zf>qcESpEc;e61~0dTL)frKF5;jQeFeVI)_HUu^i~qWS3mL4OJq%dW!w1sxzA94I2} zOo0px#BEo|TW0u>*vK?$7R}Wy+h}w}aYGZ**YLK9?|Y$VzDq~<1dAy}%$_24n6S*e zHev|_gFV4~wH_y3?6Zu3NhTk8SuO&k>f)T}WigYUsONf20+))w@0HolS6D7M+yP>VV(PRx8t-<8u4C>Pcc8P~ha>AE%T z7IV{>_MFePrLzpuYWaLi+Q}V_=lHqBVqG^f(UzvP#$owvQ`*P&*ICku4Wcn!!$}#? z7yquigo?q4ly&P}mUL-IgLm#Qq+P>t#9Ga02Uu9qLEjR~&20T^Z0KKOLPxoXycm%U zpYf3r6&-2uv+XdJe{yyA|Nk)JV;N(NfRD%=&K?{{7XfOI3r_9lv(pM(p&TE zT<1wvvWt5MijRB4IVJeA|0OQ&76LaN+||d^WgkB3td^^f&#}8Ah%zD1Ir!{OY zfHX5!x7b3RhW&;(MdgKPSnT+mTC6nFk{I#J>(<1bgnDulhy)7$aJ|j@(*^c+H9R$i zdr1*NO8X_>Jy5qbGj=n3Eq)`n#c$-dz%hJ@;lMIp|27>L9rIGu-4q=x0O_@G--53} z!Vp=Bns>nq3cz3MU+5IW9@^aGU?`cQ@G#KJC8Own@3W7hI}<7hZauk0EP0|o=`w z8^u(4Qir%xH<6NwMoMUCAr7h1hK~=Q|BdRou(f>Cvi=tpZ7HuNmWDG{JS|Cl#2&M> zAwy?8|8gN=;`P(3DG-pC8h7QFT%(q%%@+i_xf(n=d=d!v^<%lOHS@_rJzL2jlsS+a zG1fJ|7TA`#xhmTdei90XvM}2u9Hp(5X5Pk%WgAuNyQNBQw*c5cI=N}FOA@ZhJtxpF z53>=rd6HQHVh&nMYdLUaZ%X;?Jbdb}m#^s2TbHa21OVgEC37)lFchC7>53E#if?K9 zkZX!5y>xDiC&_@IXC9xY01!yhJQUXU*mJT7c$^C)6)@+wJ?Vyzh(zW2VP9!$PA`_S z@t0r7NXrRPISa-Pgvc;lH7D6~W9j4x;M8>vC+Wpwuxh%ca&}wc8$05kI;~v>DPnk|HEc>`S0CUvpQ^+t&EBE|7azucbiLvJ2qwzd-mr|@K+b;J=~B6d!! zRZ|}AM-IJoLEFd&jKPy@s^deXA+d}&BepN)5!&jf{yA)6?f6bQRz$2Q2R{c8dQ)rk zt^P!FoO<5T^J#A9w6gHPaz~c1g99G?c52Gh+I-#=SmxJZKp-=<{5+D7L5$r@P z-GLFxIyfEuI`-;D3dPrhPE%3XgVXjTsLu{?#|C1$Ee;e5k~=x#{k%mQHgqdVWiiFo zbP-jqJYo6UvDps*+0v1ad}lx6oJ@o%2ciWPOD;P!o(|ol!Z?v|OS_kc(or7SVc@tu zlf-K-xl@yF2PmTKvFqhQ|3p^~!Pd2RpqMdcy@X0_rhii_iXzg!m{8VFqq2)hEp@1Z z*~dzNoQ1=Pgx1>XXy}7o_>m9&QAbH=h96G=?q0ag?Z+;mdNpoR6veb zu=3m1c@y`&*gY}Q0B>3-<;iO?+dOkBrx|16z#5~m_$Db z2|>k|_rFPV)bF4kI@ZU7RCxNA0%Hg%C0X5-thbi$yXkJGFojns;#Cu({#K13Ozu={ ziEPH!$QjSZm#in9Xc=4YCI*=AI27h|k$H19RmJ}Xq}qJ#weBfMo9_;El;uauQW9t}wEn#&zssVG``a|0xD##RFE0FEcLD-O*=|6@xCHePFa{sX5oR1cGV0(*>b6U z2^slg{sfeGDhfnZ1!OCb!v2AkIi}f&`}f4k03&p&fQ<0+Vc0m8L*0PDNz=L^iUM9rE#)cqoC1Fc{L3fXuZTsnB}r6~yCVVOhoY zg8!L)NFsDb=<@B2d#D9nmn@UkER2Qf4JYx{8=!*7#po1IOS~V2313Z32785lhib~z zt{!-{%|B7B9@6GdY*8I8y0I|V%q^OiecP}Uu0voqv-&}0ZAcdf;&n*(eCc{X3>9iE zYee~p=d{wY0Jpp2IwQL^sn6B8x;fOR5#O*Svovk*WHLN1X$77R#7bKvG_f@USj*&R z6eNXgm1C~l3TL8a3FD+>4G~-4F_99waC*aco7NYURX}e52T6g6(hp6OuT)w%cVGD} zNt&cr8v43HmQTm@HQJI*=|u?m$LhW`;CJ?+1w-9vh)~Q`gDNs~tefT#5-nJ>MT3nN z?`kp`#ks}YWN!2G;GU^3?1nD|_^-F^4dewqdKkadhg)fsyoYMSkWCWYDi3S^WFaaz za4kdPL=>*0(2}w`SnUmxJ985xeKCFo@jq*{3oubQiazDBaLgMcv5BYd7>LYUf%0(_ z3aO1qmSm-d@2e{)QyxnIJxGg#1$CuTY&2Dqn`I9VzRhoYl<7u))Kfv?nnu{!KY+hR z?_;*KPbre#4@!yNe)b5b(o*tUX?HQxF*+w@;yJ9DPNE5VR;9D)sp@X zQ{B6ZS-Qaj^$y1_iz+aH7*YKc`O=M$PDx-s&Ht2d+Lk5?M3nX0;!y5=V z`kAZa2BhexdGki6+!r{pE;^BmEg$|W=EC)#I^=_pw45c_A5~BKglj(wGYTZETLKO%3@?+{A)X@c4F8f2h&fkLu|3Gj$;)LV8d!AhgGQ%`M<+il zN(W17DQV_TCqE>daswh_GUkkXq0xp1rZdFFNB2&^CKzkfrJ49R{v`L>no+!l{7_|T zV#)fL%*KB*1ZzA>K_;|tmJ#`0e%+5~S9*P)Eznpx(ULsdWc=1H(9xw2&vT(Fyl+I# zUGcRmUOM6178Ma}q3qMt`;$wsAo(S4G zsCFo5%_e(^a|wN~I~gRM6qOG^x$xL%ZqjGLS;Y|r1znXEzCK0Jg8=gWzlvk)VjVQ$ zc&yaGzNaRJ1HEo7Lfgu?I*+(c7BeCFf*Wr_SBD>F<6#4u+j{!G?r8XL<@vBRD^*Vh zYnIyCC$rYZOjr4RISifXvv>YdT-deBv!Yr?Sf+M(SAnRrYc%IQCIQrwx zpG;Arvu!rsTC;gpj9MH9Tju8^s%7CR=bOBHsH~qF-ntHHhR|VVP*=6HNZDfp-%Q4^gE$CNfMSyPQ8_Y_x1dWBvm|_u$FsBhGEU&iAbI^%?Al{0F4}lkZ7^W0#Cu%63?r44A*V|L65SGpD6|>67BYMLuzIo zwpDc1s>uQeoMr1`;gSYKRVYPXAbA=$&NWsyAR6og5AmS&Yy~qhQT-F6oS-k<+Bbg)2ko zLCZ$&{hEp6<}8!C6&es|Hb!UmH%OncQWV}hc-dfp*eNie+oCHBFxOS4dm$RW0Mp$a z!#U%4%10n)&!HE;US$C4tK3|S}8=XcE)h76*r7@&Ml zq}nKwrakIYn0X--PosUXk5q|^2#qwl5C>eWQ|;S2MvPR7pWV%$Cm~yy3AoaS)zrc| z`{K;CM*f)yx@8NPDGXh}Io;bBQ_&BRgzZs;P02l)D(XJUc3g;*-UM#gNAE%qyHzx{2{o1zS0mRdTe(-A&~rgkZ!VJS5tA(E zkUGq53^aU-yD7mRb>zWlQVoMDa5g>uj+#} z98EG>GRj$>x(QBGDqt&<0nvq(gx$N@`KSlA)H_D}cng}GcSLu?2aZen@^-z+uQ$?J z`C%<77%&fY*mRBg9H5aI&E@{o5Vy*l;AN%6>?xddzB5Ktc$qmCWS%seE9bl3&E%Ip zp*2GXf3!pNvN_F}=!z@g#~a}7Xjo#0&&3D>qUX$nr`U`iMpT!X9J@HbxxiwN4|lT{ zSi0UI?uY|I6H3-ghLw1%Qk5wVf+oa5EfP*FBLL%PXFhz@#Vl@rmWM3S1h@mfcLOgB zo7>>B6lj|))STxge7L^@$AcIH4=*Ig1sf61k){fOH(|n*uU_M!N~H4YcPXm^TwUwc zw0>IOQgvi|i}8QpC9?y*hhQ0e?h>TB0F+opKU*O~>vA$X$NE8>ve1V16$G#*K?kwZ zlvLX!vJp@zlj?9he)_-uYRKc8@EJRS&q7^W$7Cn0^S*MVHOPjnq*_pElJn4^#DS34 z(7jLcegbV4>onT^M3kQ4rGw$fC~Uo7X{1shcc2+RDqx~6v=aj4;oVYB!ae)IMwFDP`AMl;ppRmHq|HAcGp{s|;!^m?Y%?3*Xbn-2Dc zs%Y0A^D3SW6X3gg6@ongP2J_wgk;;z*1A;28>v`)a6=Y9j-3P0m!m%qfT1Wsz`PS& zl}t-7I3k^dS+ZTijqv(_wd1MZfNk}0@os!!Gi12HD%=pShyA3Ob7ru#W|e6uE(ZprsLz34--2U^;%y;wk_AhfCLj!wZb+BobsLTY@?axK*5rW(R%R~xiX3EeQ73rK74_r$a2SD zHfr&>QsDwXQ+YvN;HWdU{YJ4QRvF1$Q-j1Qa1OqNrodo%WYEdTI{+kw&}#&UvT2JNsch@g*1NaXrG?=6ZS4pWLUi+nb-K)W@=PC|cLX-`|h=0q)oSXybwh$CZ=IBFG*R${_Y$1%9NVpMgldJ^(ST8T4ER)cP!H<@WdlPqBYn#L+x3)=S)->nsr3N=h zOlYVfHS549__+aZ0qc7cog|a!zX?A{Q5UYYZRAE`b#SSTcp+khoCTust;jNSMsKJV zH6FOZyk!!W6KbivauE^|wO7=N$^P&ON;X~7p2e&G^L0?>X&bIth?8CN253KbSF2CZ z*1qzyyB9QaMoJjQHQWkBUiP8%KW@SzmuNv@`w^E!-KQ|O=*i{fyPLi%-t|Iv0BNt( z4n)f`erUtbCfb;vtz&1PjXSedore>fPk!1A=8p5fXDa#{g-8yhfz4 z4Aase<@9>F3^<+HXI0wUup*fq-M*l2;f zv8k8KfbYQOt=X+mJBh>3wjMosy1(81X6w=8{ViNR{b+0V$y5B_2%TIBf48Jbn83>DE)$;+KQRyH9s=6K48JB!%-Wbdd0m8@tJb z^qY=QKo*ly(5z7!;S!ZXcK0dDcQs_g&-UsdR3Z_fWxYf-Pv6-x)%V|0POV_8*}=9_ z#7%*^^~?N}v3)Z%Lc5)I>ssdlQU}0%e<+&IVyO#L(&Jc(^T>RULQ_3yWSH32hHge` zw)~E${rT9GyX3$_iv*s@_~GU8pI$%fS<#!pJRsDOB^gG9p4YjVS$OBAuQ%*O@JB!B z)M0TLtLo^7CiL^mHsBI%c{zedTCSE5{Ip~8JMaatJBOo)!yb(ME+@xsy(j0FvQh*R zuW-@KXm~T1MH+p;83{tMUevJQ%#qekwY;*vEj8M)0LGt(MuXA?B8}XHN~Th24XdwWNxxl!hu{ZX!bdWaJo#Z@G6#$FePHXVVX(dAuxc{Z@q* zeKQjVTJQ+9zH+>FZ=+s;^9r;Ga1%%$KT#Vl%(GRXntSOe9gJfzWbQMx>k6m6&HBpen?uft4a!F&J#^3 z>|23i*|pTE0f749eM$zyzv&#neg!z33FTr#3D58K=Wt!#O8Hlfvx)dQM8hSA6v6+TY~nXSO_w? z-M0(8$Xvy+6}C1FV7?>RR*Fw~w54-rWNe{e1kqY1^*wKtpln;woowC;`qxGSdD#}N#W4F`I6ifw7)&i#JnFnT= z?jnXp^CT74c(eL%fPiW_+gj~ow+ijo8%{L3XM1^KN?qqRa0;)L<+kY6_VE@P$R=GL zCua-+WHT+SKM%a{zq&U~kAUu-Py>=fNL5MWW-UM2%<8meO12;+YROK$>y&BJIiUFo zrBvMR7Rf3`snp7sSu$8=(Z_C4;oKJhf}Hy;kp!?>8k+h)jk&@2#_yeB#nyYOndG z=7CSFQNp;s@g{U#o7lN=pR2i^)}GP=6^c7ZWto;pC}xV>iJ|Pay0duo9CH}Ld5&jc z3xEUP#4NqcjjcTRYDXVUNK_-ri_CU0Y=IB6h2g=j@)y@3y-)g7QoxAlmYBPC_acNr zLK(m;EX_O5t+i0Lfa(S+*Lu=x>VK}E3kLgadWZ~BB^&bIOuKBkOgjN;@lDd%h+J$I3_`=&{6p2-(OLizQwuw+I0Z3%LWv>{=nzCxq1< z^#;qU_Pt;yoArt&bJ3^TUVgQ>9%6j3Pj)%dLkGG;s7r}5X9Z60U80NFu|+lR4BA(1 z3=g)=c$M<%tI>QuoAdG>`CtKJj+yN*einHsW8%ev^@L5>?~?lU+7Ze))&3M1QykZRbGmMt`!SZgbytbLuO$Fl0;ffkW_9UOJ! zh=iJnkOvW`Vzh-<*lDyq#w3%&@nVVRtq2Z+o5LUPf`Ts96@QxOGluMVQXC?&g=ERJ zY-**``R={Q{G+| z|B?|+_Td@n#BzH44COj@%ps)JFG+}X0g&@ipiLYkZFpun^$D0Z;2qT5-n=c$uB)F z@@S87q1NLb{(L-?iKMz32J_gII?|He$e23Zr;9)`r35f7lUAl^u)lP5`WN)}<~k<< zjqvSyG0Iuji}U;i`{TGf^xf{hi)zwmL}Q6TAJGVq8S9x*{FO1vUvWN`QP-ivCPMWRPKQX;TGvHJoq}uY z8;S2{wl3lStFeu{18Xtg)ZNf4_$bOa>w+#4FwM?8t2T*XqJp^b!TUCW!7Zt; zV5KZhc*~@TGBjvXH6qnD9DI&&ZX#O}XOEB5L$~%#;~wsSP%zz^4H4<*=fKfexH~vp z3t>rz<=AJpur1xbqXjJ#TF^oAXoNd@R1opg~m;CnaV94YHrv&HgM<_ONF^C!s1!KP;JXS!i!I^*zGHgExj1TAwa)83xfnP}xGU`-LWh^hOO0%%Ee~C9~-_ zgwRFg*}K_r(QFL{B4&0wNT;o~w1n5ute`fEbq3ILXflg(P0~YGdhg)UNv~~a0`0AL z{mI1=?|7J+^3oRwS_|b}xR^Xe%sgxIv(}P^LYNdXfyaAV%%Xk(C?^TSef{;b>F~9k z3HkDiufIMTUE;oKOZc{n9iaiLbd?{EXS2y0Y)s1G4(=n%<0)T%eLTCqg>~|7`7QR1 z>|=KuJICMbY;7HAhYIR+ccvEifcA%^8Ot1h}|6~}RrNId>BnlEtU z{I{utrnTEW# z^xt7X^mxe~&%GpU*v4kq)Qmt678#ud8_{@dJ*Nv^dan!hdYeWuC7Aa7C*D@E^KZZ~ zFvSGNM{h9Gi(TM-)PP&+_ZVhZ7A4Az3#Jw6&&gX0lq}K#KYqiIorQ=?7K(r-%f04q zm9o;MOhwYZ)Q}JoJ8j?)ot5h#ExiHr^j&{G8a{_MI2ltNQGGcnN4G-k z?yk8)WQcPa%}j?_eowBxyK*X3R@ZZGp^qgAoI*;G^%VHwFE2@cySmw#WF51o(oMpw z#YA}_WUmAM(`TWQN^eA^8Z3K%d3gdtzq)bur;r*m*K~q+?~|F~Nm+PfP{hwy#CO0cfBx3x4b>QV6mc zExCb?>kj4jJf}#N#39;^vD6!bLfBQc-uR}%we`29!~b@^@V@e)B+R$U68- z5=*`ajY%B`#sSF~&?>NC9y?9htv#?DO2n3If&nhR!!3iACsbMSdz@)eNV=h8==k~( zjO3IG28HHjC6LEO_t|Ju*nQ8AO6mgVec6mtBG_K8Q9$?H1Z_1Q^l&!!_JnY*sYrE2 z*Lnvkdhf&%=-9o(W?{frXZ4fL3)l-fk}vM?79@n5;Y%<0rCWY0tpsiW)B0b?)^DQ6 znstNpIOpkVK@x2^s(NPqd(nCljzS2u6G0FmeQ*kJL*~~Ape>#h*E%U=S61x9Bx6EW zUuBi$T`rP%8yn4mosLG_<+YI%#ezE zyOgZdvHocjgOW95lb9(ZeF2x@{v(oK_G1_lL6PFOAk$t4|P&|q>O5d8XxAhb!yn8cUA zFhkn)Fj_Eb3MpD_qO~2O6wpjau8t|l6TT}=eLBT$3hN*#d6R>Ky>^30KXWscwcbvA#0DDOyM9DO>iE2wIK zeJgC^{=_>;xd=Li%(=Yl0P0xFZ=!}D`CJT(O=2JGt?dFw9+$D$J5E2zccH#aSJ(A=gkMb!@w$;$Nvo<+!hm1%h`D!@`bvl+!4&Gf? z12{J%*ez^q;rfza7Vq$DJY2pL-%gRqyfs;UR%_N!XArQRXl1JuY-4^Cx{ zoU)d9dn@@}!bAH_Q1ff7Kw;U`DE??#J%T2ubHkR!Hg^@8q{Z>Ec9V47<{4)5*6lM2 z0r{zJF=Cf=_*Wr*?RAyv_&9A5VAB#~C-dY)HY}IlPE*H|(l66Bm33d6pkHqg^t7W{gPV4rTnp6l(Wf)K$ZU5Vzg_yNzRfsKdjr5lpulV&yzZxm1r8(y~!1>UxS{%OYctQv>bcn7Oh8Q zCYaRd0!DNpRv!okTk)P__?{`o%u6j=SPCalRjU%sLH0%|9|G?wJK|~B+m}lcWrd(2 zzcGdMlA>so)nqSIiV?lE=4Mu`rhue`;LH3l$~8WUAs;9S<5sXwosp%Z^_V1+$LOV- zPCS*jb@fmM#By4tse%yL9O*3Mfx92CZ#@+`rFQhC5Hh@3-AOJ?6wX_A_#9U@%FwAZ zHgBTZksEi?a&OSQZ#rCgY}j!$=bUQIR?lDXE?UV*+#;NUf<~so=#ac@SOgRmxHB3| zVO9@oO{ZD0kYptrOyF1aKFTeL>4!);Y9%$TYs6&@70rjOUT^{f4B>=zs*(povdw|g zjaG>{6FY5+%AMtTt-9J&43dEZSJUO|5#saFE~1sBrVd12`(+umr4!p{#T@n0(sFx& zJK7}Vdv>k!PxP%5Iv0AU-OC(9LZojPXJRMB_wKrU*U5-pvaE{B$EO~pn%OiJ~ z5%o=F{qiv(nj9@v3rzso0tCI5uf}yrk`v*joRUD`pad&;&wUJ-*4*r4DkI);PrtO{ zwjDd=qv&c;!p>O=>VtpKkW3NQpdct16nX$9@o1#p4aHjPMxt8`whn|iq*|1R03o2O z{zp?Hwv%l7H7i&Tsz@r<2_rhz2f#Qn(+AElk35bl7oV-yIvkn(0bi4{7Vgy9-e)De5At`idQiGEPzREe&b8I)6BMEaFB!ag~wZ;Yt z8I_*{>eW})#261z1qDvfI%&iLLQ=_UG4CYZ*C}a3H?xvd&R-?6$N4JsgB+D7Qw7?R z68d0eK!ED27dYDz`XRtlI;D?$9L8jzzg4xd1NV4ui$E$su`14{$;8ZK=w-Z!(c5oB z(k9|SF9sJL=XS4gEjUsT4@Qz0l#1j^F0o{R#9!#wLQm||?f`X}i!?}vTB0dwjCI!w zo@xz4hP-7pX&XzUAf5j!rBgO!~x#k~?GY&`)Y>uZVtY zKML$R5j|0CxS#80KE`20pEQg&*f2i|A8z6SA3hm~G*&Wvc}@OR8auV4tz47w#tU^E z{AoJ5H8n!zLix!VS%{pW!wwn|XYuDJqwBv&1adBpRY*w2sIThnQHA=&=+S02)>6QS z`5hp(2BiS7n)q2Tb(JC@vN)(X=kGcQCaO_Jp~6p>x44cUifV!RRu+3eFfIJCem@3Prv!=?l(_%uRnbA`j4l7 z_~u8M_>#jjc)AHCO`%0~6)DY6Emp@bPB`#+`@o%lHJAZOo1YVm5=)2p>yfKo-`-rI z$3OARM8G-^l5`^Htk%@1RdCJC+Vw1x5CM!)=ua2zBk{-n`-^pT4{u&8C-sn3-~OFhC{@ z&5wWoVTLo9gO)-yrpfG+XO{LJtwa2Wqr0#^eRB$wRrcHQJ$(aRxg3ACa=K z-yGlLpHJ^X_~jq_;}+5-T2(KBZcBmR2v;$GWjM?PTNQ2;jbBPDb8*%)!ux188NSB7 zOH;9k8Dv0r5#>;q0>5zUEqM*(Y9?7a;96r@fba`F27t^hI4O?GxCa5hYZfL?o^CbT z5hqP-jCUy5rZHgn{Zrb3v_;E(+Vk@nmv1PJOYQiN!Ov6t9OCB)Kgakv!Ot`NJg)}ZsK1T+ z+aL~p1Hv{SYy-kJAZ!D|HXv*R!Zsjm1Hv{SYy-kJAZ!D|4j}9R!VWOlLH!-n-+@aI zzX4$f5Ox4z2M~4uVFwU)0AU9Zb^u`q5Ox7!7Z7#hFT8_zeiV zfUpY)yMV9@2)lr=3kZ9Fum=cxfUpM$dw{S92z!9A2MBw>U=Q{8P=D`;(Dndf4-obM zVGj`Y0bw5y_5ooZ5cUB95BCoC0bw5y_5ooZ5cUCK9~kVT{yysOKO?k#KsW$|13)+c zgabf00E7cTH~@qLKsW$|13)+cgabf00E7cTH~K~&1;US?N0>U9690I~2ARGe15g;4^ z!Vw@G0m2a=909@+ARGb05g;4^!VxeyLj5DuKRO|_BS1I;gkwNB283fkI0l4cKsW}3 zV?a0tgkwNB283fkI0l4cKsW{l$Ebgd`X^9e_zegrfN%l`CxCDQ2q%DW0thF7Z~_P? zfN%l`CxCDQ2q%DW0tnB5!86oRG!>z; zX&hdJc9V&M(~VHde6()P#C7e^)!hbQnn33b)cM?M&j~vWuTxEfAzkMK{ zC%Z<3wf{8Nm#iLmM?&5yE+UzX@pQmHo7lgo+yf-TQU+xM4KzB_09%#^oQDmpB59vpsxUK|6g$vaM<3M-$&H+4K&hh$yEUUU) zKyG!}q*@q|3caxsm)cWTWpkl{M?8uXdMC~342kJGZBK(TY5pS8t%##g;uwT76+DBX zeTD0&^ub8Tsp@`sPCm0YY@<^RZ?3Lx&q>aJIj zc+!EFRTF}Y3z6q&1$`?Gw{~027#9`Y>nf*av{{?118WJTP8v1_yQ#wJGi;n{lP)|X zq_@F%Q37Ryt!U}DfH*7NowWh*5y0^Q-8SM-hZ&UUWjT&?R>48H??vQlZ4b|rxma|4 z9i9b`D{5JX0$cyGM%>9RuF>*GeDd8}e6*%j<->E4;&9m0HIX%_25X5lNnGn{)L2y5 z`mgk{s7~Sjex={Z#r#J@2sH?J$QUWpv04a3i4m7?(z>CFh2^MU-2>aBbqF|Hv zxe`eB14~NzG`&eSML%QjT;ObrBe}dhd^CR!)+Wd{=JlKA?3(Klj`Pg>xUw*X&a-I_ zlU_UKoYx54z)={^s)-8}Tl;#y;8y2dR;|7Yq>t8%@t{z{m1|@pXQiN#!xUtsVGFT< z(>!V4T{mK-8waMu>+Q&Qp$m&iUxidjOJ>#=d^a(vNrLu$!N3xYXlbzIe01rHd0!~F zffZoTyM*|w1ja1=bmxU~Q!F9W2SZ7Zp$|fE%U7XI$!>)B8FPAdT@=Prb`hR2lka@h zxFuD`c(jKnyA-HGV9b>-VNgQ@{@D*fvZICg{lPc&pe~DGWJ3l)`k$*{vVo&!WH%y& z$S#G=5XDf~Rxs#5m_BQPY+BLA2y43>Qt8S%vx}t|S*?z{m}|;+tasR&$%=Ej zXlhx6dTg1k9!rx&1wCnE5F(i{5@a^O>QyGnFb4obG??n(bFiS0obHwM<1&b*4B8N? zX=h(OS~3%=rxj4#&gNm{q&w9i$)VGWCe%cY+{Vs#_y&ej$JfjJ>j8IypJXfTOole9 zLoOu|qF1rj&lo}d-8sirw8J9f6lu>51`8YKolXas;ZGLU3+a3lfVAo7H znuoXN-+_wvLu0(e?a2eUHL=dzId}xVw&kzPYC0kf+_5rdG&e)7(^*EpWh1V$1s_gR zyMKmun#+yR{||d_wjNcIcZt?Go*yPYD3GNQzjJz`xI4;aCj^WCO+?5aCXK=%K3RA(~QfY0|W#p<+Rs2d{)udraIC8%k?MyAQ?0T`I{(+$y9AdF-j z5H0}1{r2O$l*xgr++G-+4p!j}1q$f`7i2snWrd+~o27~7DzQu=P0?aF_epR_Ln7WR zlyGX)!Hdq4cCez>r+}%^ZcwHJLWmSi(-aq1GB;(* zu4S;!91^A*2MW*Ao4+8vu?c19FG*;g{vO?oKK`+GL#!=Drv7=UTmr z9cw%#(}Sy%cYZB3R%%8C>|}9Ex!2Pz(>bjd(C|?VMxRU3vdZ9-5aHK6J{B1Qk!~ z5)A#>Ub)K~_J~Gsd&}qIHGYDd%}TpAV&d3^IS&+2M=k)=gtCb+J}inGmn#RLKXvE zZfdIv0Om%woF7!V{d|3o7Hb38>{WS$T}Dl>AMFaTq?!Tp%l#AN?u=NyrdgN=bc?}Woi zgW)00A*LnM;i^<>BA{||2t$y?IQyLtEKjfC+c<9dmeY0tJU zsN8;rcaTDR-)1~6uZN#D7pzrzWx|Pa7LMB%IGnziJ2=}n?80DlrMF|H$rFjXnDwxW zP7$N+OFKHu(>)E)UU6zT@|Ht={>U((rOakzC_+W(SM|)yUwnL62GNfE1HaEgy4I!2 zZHQ|@Y$_W%!y1M}d#(x3IoA@4O>!9}F(44S6mYKUiRliXB!E|!#f_||qGz;5KX#nC zVA5jf9MJ?KGZqFqCimkdsPJuT1Z4%c$_SkkQ5gq7{%L%gr zTkp~mu*VYe3%%fqVKGLEWuyU#zW5~%lQ}S{1oz>NoZ-BqDR7(Ht5gNvm^ zzMNUOlnj;GyMu#`Z9Mjd+ec;1m%~PM9fiS$Ea>s_kd*c$Yd9*mUp`-fFU-L@=!XGH zoCi78?2a>Asmusd(R0rql4EYk5#&%4Y$)drx@pmH`ZO(_IrW*GOG~0-6+QCxq2O2- zl9Cf#X+<2lDwQ#LaObMMQ!N4fo^)+iFR zgRCu$WE3IhI8xX~!n$DPp+d&u;4#rfUARw=W~sbFu?aAte1^5j#yPhukS5z4;*1qJ z&Z#qNHRNFtQZ+JQk8fQaq4{bueUAW>aEe3Pm9SK^fcc?X_lHtn$!vJF_1bOs?g*lZ zM^We|cqbvK97eO15Z$lbCf2y%eTepj^4F>8I=w1F+#&g}UI!YI1lVRQ8ozC;d1gzR zH@k^;muw5Ut&4`mX62sSEpAt9$4fpyC?&0ymP>+YwUqvoaH`?V6NIpa&%-euM3fEe zB!;=Mjf&`HPem)bXxx>68KWix`b7YP^GGG7ya?a~@Q%N}G#pwMH8&oYqXvh;o0hAo zob}(^Z%Yfuu@T}{$JQZEw~9F1VDcSAC)QS}=BS3jP6bOCWO?irXoM*3ywIhn+@wA_ zghRDp&r~iz@Q??o5c(C6(XLSeZ}Wpy^iMHt8MvScSGk=u%mq=)t;mwP2QYMPQIw*> zvTmuFss=86_!v2p65lx;wm9tyr)6c~&I^u1&a1nrdiI=m?226=X0(bgk~7}sSrr

Qtu?uX*zB>OD52f4RsVgy29&y^AO#!-meYa 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 KLG2D`g8u=|++QF7 literal 0 HcmV?d00001 diff --git a/Tests/images/illu10_preview.eps b/Tests/images/illu10_preview.eps new file mode 100644 index 0000000000000000000000000000000000000000..74d2fec9353217fb17926548bfa1fdae328ae7b6 GIT binary patch literal 405470 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�qGJ;Lf?>KW9SiR&Zf#LN_<_VMnWj}K(Wx7)(8ChB!xT@$C3oRN2 zxZvUbhX3gkEn|YFZzV`mZl)T( z#STN6eYtqM;-xQ=19bT};DiCbe#>JOT_0j82!Dk-^Z!WCdaeAt^0H3HvdO`_t7-t} zh6KBXjV)YX^3&oSevOCAcjDVAGMTp~tIul98tP2qJwfmj6|mGh#Cw1mIqJcw%#l;p z5^rxMze{*%zX@u7g%v0)n;OL*POFE|ve@RXLX)&OKGtrMuG>7rY~H$kMj;?S z)h$Nsk`DhY#IL=sQXLV(jFevz=nIfmWTFrG>3p?!o0kx|;Hx2c*+3f(qQY zg%^v}0X(1DzSYr#)y0tPk5>adeb5}#(xr$a$i?Bk? zlD@L;YZLUV4T7F_G;46v?vrbQT7KF@^8B_fUC)c1l4}P!RcPm%zhmL+DJ24hSz+@= zISQ!KUt5fJEjP(o(&mSCTapqa5dC>lr?V1Gqq;Y_!u4y=^S9~UshpN$kKCg5h|C0& z8ePDMF2w2s!C))ia}3`z#h7`iMGH&e1gdIPqB+RkDCI-oJ!MBc4SV}?NusO}G~_p? zkX}+0jk22TWlAxkm)6|Oiq#a5ln{KGA4a*xM=|6BC1KnO_Ng*3zg^9v>%MPF8%0?MFb;jmR zR6BCxPFn5_n)gkID~}C3j^>dFwURqjiFK|nn zgnY-Yb^eaNbwcMt@3ecFV@Qbf4dYDgg!tZFm+v|m(My(fF(HdcbZNN!S9&TJgiu_C z^dblLxC zO2l@OO}}OZ>p>Ms#X4a`$NB&mCuaJ<8Rn74QRU*Z^;(A`vp?W#Qr5zqI@|j!M>2%5 z&}8(cp%2}bS4cndOtcScBBO@p*fkET!H20C56LeY%t~ok6+2!40B7h5EiJrWN)#2J zMaT~b~PJ}ISeIz7>4ozxsvcOk4hkB0fCUYcVPJ=}7cB$6bKp~^@ zb3ncN%93}0T8f0f2g?Px34WW4c09S47y zPHs$%P`OZkaz+*+XXvnlM#NeC*~#eYPZEKgi(?fMk}>M5dV5r%eldEqnT@p+@L_%j zh^;{>0IVi{7EE2G2#72WD$e=44uXkllu@Yg)8!4Wv z`d9twc54wJ-wi%&b_U(iUh!jfip>aDMg0E#2%EO!7vt%JKkq(xynFTG!K>e&{_er| zGVvvcXYh0rN}588>MBy2pIWSrpPz8x^Y(!||7tJ;k~Tjl7$uet@#iB~y}G%+M2~;q znTddP9wg~R&RMOgQLEsZo3-m%CLsbCqtKr&+DGE|{rA`3%^v)D|7QUE7uA-q zxu&R@VYj1M!!j4IWo%-$>J@^6hKUG;Rb=|%M2{tRMMt=KqItQ=z)Pmr3pG_%%POZK zF=fF((|d2BxEBR|qAc96Y4DEk_GZOR8mDWQ1b@Bk(KI|w+X8&(944}MM&Z`KTe?aT z6uq6M*|letsyh%lH06;5PA#8|e~(SL{@Te{=iBQ0zn;Fh8O^2_znhtIb1*`Vax1(yXtUZ;eh%?-gr8&loZ#mvex6l>ZPedJ{cR8j zzX4$z5Viqf8xXbuVH*&(0bv^uwgF)q5Viqf8xXbuVFwU)0AU9h?4bS*>hHiMh~I#) z0|+~SumcD?fUpAyJAkkQ2s?nV0|>i-unP#gfUpY)yMV9@40cg}7xi~RRs05oT|n3c zgk3<`1%zEd*ad_=K-dF>JwVt4ggrpm1B5+5*aL(;V6cb!d#Jy6L}+_}um=cxfUpM$ z`+%?y2>XDr4+#5!fQNer`+%?y2>XDr4+#5!un!FOQGXxx_n#8lJ|G+b!T}&00Kx$v z900-rARGX~0U#Uz!T}&00Kx$v900-rARGXL1Jr+n`j5aR_zeh;0O1iJJOYGAfba+q z9s$B5KzIZQj{xBjAUpzuM}Y7M5FP`pP>GeM}+nS5S{?S6F_(Z2u}dv2_QTH zgeQP-2ndIOa0m#8fN%&1hk$Sh2#3Jn5cLmH|L~B|4guj15Do$15D*Rl;Rq0p0O1G_ zjsW2Z5RL%h2oR0{;Rq0p0O1H29HIUZ>K~mD+7Tce0m3mL90S5JARGh2F(4cR!Z9Em z1Hv&N90S5JARGh2F(4cRgJaY`M*R~gF#HCD6F@itgcCqG0fZAkI01wcKsW(}6F@it zgcCqG0fZAkI01yGz~CwBKSlkgyM*=>5S{|UQ$TnM2u}gwDIh!rgr|V;6cC;Q!c#za z3JA{t;Ta%21B7S5;2G*aL;Yt5g!T*&o&mx$KzIfS&j8^WAUp$vXMphR*&CS;T+JWh zrYEk3MJaAu^lNgf%9@)R8h(45)g?)9wlA)>iP?OG3eR_P4B^})2%3se*)$F> zLc7UC!Rf}at4%^hj7#r)fC-|0kQ9)@nI!i#$n2DQ)#>!k(QVq2oTB)bC@@(;(#Np{ zZ$^17Dhe>eibixbX%`1fu5K(}H9Uc8CWCIg5o>!^Hm)^lrOg2t6g^DK()&wm3XKZ= zLjVJ>$(~s!&9#jQoP<3QC;jswZUPxEZytPaM{aY1M#?q3B4US|oeVT-8A}pZc=SA{ zXhb^tyms&vZP#x5vm@~XT!qPAcM0cIw(y@%-_FQ!@H>eikVg(?HLkM2xZm6p&y!sv z!rFhB>q}Pmydxp+6c>?9MsoK_av2KUjBC+0qUpF7QhDtM-#f=^reDN>`y|t&lEfMH zL_i=q>RATU3%(3Ik_$udnukV?bbo`YTi@aZTfAW_UB=M06mtOMVS80tQ4&XtP|#e0 zu$+|Mo5(6Usz5SCKbp;xf|AWLmw4y2(!fAV*IX#6GR%iYX74t&hUJ!YaYukq*^JBx z%Eq{1{??r!8B>s*$+sww9k76JL+|V#z-}Gt zCL2>W|Yg?h8U-O5Pil{V#Wg0H8ajeGfF~LUN>xvNwm(VQWxKYS*{YC_ed1XIR<$iXP+V7nqQZshf^nd`eCGh3F6VfCK$cZqE+DtM zY*H-@NQK^5iA(LNtFpP!z#|^T3B8kMbcV$AowlbznKXY9=~l#1C~*uznF^l4(7wcV zRQg~fL=sE#}6V?61= z%c==M#)Zgpw1U2shFiO>W{iu9?sb(@Guo_8)`7KzQYQ@?gWXhN^%*u!wMiGA5z?Dr zyeNUP!B({NOF*2J?#|i(_z2+mfNmRcsKX3O^s*dBI;-HI+jk;zwYG=n$y_YDz7EfV z#}&1#LxHV-StIUb7uRU{BR=`=Ek0V)s`BBvNO3sq>6*wIRD-odnk24uHEJv>Z2f2Y zSX8HQf4|c2f}%OUe0~x z$m`HY+V*g<=*PEcQ|T-B=J~WNp*1gXdL8@pvRaVbL_$>}7{SfhlMGZdZOgXY6#|Jp zF#;Q{tf^!_S>&4st|C;OQ5={Htb?G8j&~twdEW6%G#aLSj)e2E*#?qi`j($FIh_Mq zMpKi*_)0=Dok1~MFeOVn*-GVw7ykmIkP&kSg+W^V3yPx$?BXpU5-Dox8TWGRPzL(I z{#Rdd*d+$WWjo~zXQwy@#;B{Zj#2_L2mmQ`;}$f&Lcoi~ZV47_y9oQ#p`u`u_qh^C z_5({w`82&rHbp;U?_A()izB(bJbW~N2G%CXHszE6kGdxx8PRiT~@8W3Z#$Li}9dP!Zq+tuOfYUr_ z-(5Fir5gvP#Ov+IccBZ5NneFjNlRwd7koD{sY!zNeZjyIjc94G z1TDj2gTn90+BAKuDik3*Q)tDfm*;v)?d5DT9^61?Nx-1liD&hcw`0(VQ`CLLW5m60 zqO%AC^X2+c#ie$ANnZlwICEpmACH{isveVTr)Q_*10n4tI)iMrfzkX}fYG4{?97L4 zUHzc4&ZXFCT$f!2CQ1zrKaKH=l8j59Vag~o+I*ZEkjc7Hqc*>`)NK!g6tHWi8_mPp z^Y1{#yP+{&;`ZbL+?rTt{y*%!*>+sVu{L;JT1)RxN2YAjw6JR)Xo+-TA|VO{2!QjU za0;lxAdEx-Bq%hWr5~g(`l27GU!lL4G9&li1&}g*{`0RkZIQKeh>VOWBO|9qX9jYu z(_bFdgd+vqF*By!+_cp?oMp6IHvBxB@u6X=`3%v|ke#+g_4IZ5(;VdU#1MxjMNnA=F&COYc=c@IN0 zhKrM0L}DW;I#SU~BAm`aK-~6#>$Z34C-ly3?W8PUQKCgTLF! zE?~tpyG$gUQZv3hI7lxgVMxi5I9X7lBKV`@7XQwPO#z*X;O-F4$vZ=VLh0*WJH!pbDb6{Cfmcql9et60}#?Eue^a61`wpOp9$KR)sOjI z9TB6PU3JF^$o}7o>P*HO@cF)_Se+IObz=nK6&B321U2o~$TV3!00WbLx*^&MgprH` z!X-es-+r8zG6hiCt%u|D;Rd{+Kp|b=f{cfxyf9?9Sej_A63Zmg6fK5xp9F_AB;w6N z1*b+Gyyz@x2Q#%kB}|QWgEAcuLZob(rntD0g(+JuZ@M+|1$_q)ZMit6F>pkFn9nJ6 zEr)gPkTBgiPau5OKROMrQ+ zKC6^U@N%QW&#a|*@MuMk!K5Qo!u=>CNS@BkxPHY{EgjANAf2LHrxI=WSr>?5*1w!Z zywN6j+vJGtZM?l9tgTlrtHZ4cEE4@~w?sx~+A zuwm~p-g*cTNP<#eU*QBrIs#SRU|Xj>wRIbpo87_u^Zowzcy9UCV>~ZU|EtrO`{}Lg zAI)ZRPcCZrQoJ#WV1e&!_|Yd9P=r&5VtB}H%!7$*=MuZ_sz(>{(7d$prdw?$sCZge zVCXLnt3BSZM>K-lTfG#o@x6>o#etv(X3!Cp_wd$s?R9B~1bb?=vW!?Wz9Fr2fSY-U zO%$}R(1XeggTF;C)V!L(;<6Ih6AqjMIZ7y6f4!P4%>slAb+~fN7&!i45e7S;Y8rjdC79=zcPnPr{)>Y6>Rg z@J{-~sIXTaK;q368bUd-FnprlAv57(+6Xz^mpf&$I>J^5#L3uuk@M1l>NFh|MB{`!=nK$^O~?B_boo+e3Zc35S!0 zqZ6D%OiQN2Ri)BGK;`TNh9Hk|{yQO9kzT>KaoqAPr|klGaVk2+H%73HZkS0C1UR3N z-Fkv|kV1Q3W<0KLhMzVUtW|Y=%87Coj$0Nuoc*|Pd~syhg~8}rZ^ufLClYlr?_rmn zB1YR+c66Ahdm5m<=G1WHEr6w|o`uMO6qFwn1exHSOtxMT0 zh-*n~Djzz-8iquBZV1mg*Ak0Oav3EtAP~9|aIWcz=`NonfLE8tjjX4lXS7B?_MN$4 z(qiZw(*z0J!%7E9>xuIamk%FhGg( zAg7w$ab_!(8(}g%_xvU~=9U~m4mH7sYT=-p77eFQ)6%(9pUJtjBs!Mqk*_x;$GVV| zoZw0;;>cB{iphgJSA~z{2vu5v&OBkZ;m$SrBTPxz)FS8NYS}H?tXdxB_LEwpNYDG18K0TVH@(RTcz=ZNS)+QV0+^#^HY;%Y+R^&LR z&aBmtheb%$$bdb*b#;X1tI6~|0!+dw4ry1yQq2Muhics)N_{1>;nmh_x81uVh$bFI zshi-PgrIU5&DKM7Kf6V&al!i#?F;2^Qqe7XRfM=h@?pIWG$aYI%~&*k+g9`3mNaj6 z6YZ|p7IIq`4U5gnJ-1ujuGWs1e1K3&S}iS?1kq|K{U_m6!$0D*-b`O$PMK00!rgN=kVVz$xGze|>2S5rCb zzqj9(7LH>h#I264L!53EakjzaJBUuKty0ZV4TGHumN3Zj*elQoQQCQ-OIf*TeRK$i zYQdhVT!7#q4^koYDAU zl)AkIzt7c7r}8;`Lb;r)7w0Iq5{n$lJ1%jT`edO4bLgq0;1=*WjUN*`U}^}a=j?nLj`hVDkx zZIP&aobx@4*ZC1Dm6A~ksew~eB(dos&>ZhQq%!;`sf7paPPT@JgmJvx)zB=9qFy@+ z5d75KQ%b6xl;Q?x`bshSnz%&LQ#)VS*SO!ibjTx!CA}@{Zmz#K5u}*!fv((<~)Jy!+x zIG4gL$}?Oz317?Q7Vxi>*!p}Oo!jP<3$Uk@YhljFOCfG@Wn6%fFHEN$fglud^8>)d zk32FZlRInUGxUv%dL5OzR8KyPY0H`i|PPR@?@DkOrZfMMvDjNm_5mrW`uj^o$LKaPa5xuf9xgI38 z;MyijfTgeksO!KKp?DyLT$h>8b6Hne&a1^tCEgue;AL5i-!|uy@C3-=M^KUQ=z&^v#V>kPIdHkhEhWA3oveNywymH32e4^kjd7Cz<$=Mw`iF}5gci2|>H@t#HPoUo^lrMI`x5|!ZR37NZr>ol+$CK0X_Wlkn zUy(rHNvK=sXLQB5@!K>1d0a%pgK(1Tj1&46j@!D@`Q}_!N%351$KZU7Na6SjR$Bip zF@SoV(a25459n}10PmERL~JPxr`Xos`t zbkfLVXIye}7aR!oCGZHlE*K;IM|CzFVlbLj;?N*HzPYOf$P}cBrm}#mBaOF4fjs$J z*`2aUbSNHIx-}S$mh@e;AW)&p6?a%RC`)L@@6B+h4sxNBut@pQZA&aJPI@a|f`}yr z)B?FhoH2V~1i73PqM!VWi)Oy3$e~`)Xc1LDADQBQY78qbYs)QD5FL_~L$f6C$Q|oXG?_GaFcjR1RX3JmeCAVoXY)Io@yr|MmU%3&*21eIEMT zZxk85%7bTkv@@CGk>7sv{r9t2tUk8l_o}D3n-BTboIpLpA$(jVx4Arcys$F2Sgp@A zaE}kwpTl+u_+WeI_p0G%wchV_e`wTd9f4DoOW!8dB|O7*c8Vvf&&Q(~s;zgf-3tKS z{dQ$512DZHMDC7T3EPHV5rxA7ozYhm#SV-AQzKHr~jE_OQ$&G~w}(dxAEr`w(Hb!X?=_1eOGyS2bD>h<1Sv)}1=7YF^t z&irhiBnIt~WnuF0>aqgZdl;X$|HU=H_~H zjrnGO4v@QZ{Z^;etk0wE&RlcQnQaYf{lR>rzfkXWx`X?o4cR}=#@i>m$NRsBo8;Vi zQMyLNEC8#cR-!9B4zY>!0j`|uH9PfouhWNjj5|w?hlfmT5daEV&aGV>%f!Qsh#1*% zS&nOu7MI{!bBi$1rSzTAoaI#$6aI8 zt@ekpPRliB#nEP`+HLJ15>{`us$2EuR<+yx-1oW7J4(Z{N>bcc6JIX3_CA}sYH5iA zx$Ujw;^j`D8+4Y}V$3wYR&Q2>tcK0)qGK6nR<>Bj0Lcwwp!`s)zSWys{iiXS3=RKr zH_*^`c)%W$2aB#*RLn4+Mwit5h`;2{Zd#N6?Q=~t>k0%uNs`I>A8JcrsPT;Z&4bmZ zhtbEE=i_6#4=TLp1zugHIsAh9v(piVnfY0@5p=H$53eLS)wDdN1ZzxL569=peP~2M zmbegO&int$V3{3Z!w-8}OcF4Psu!C?nl*}_Byk+ivG|4aZB^J+un3yM1D;U*^NR@` z$Ix^F#HKd5;z3t}t0V8`G8mIkrwai>l-l@&0+Gi^Ym$Ly6QpsVB?B#i#)meT%^}o< zAzws`Z&sB2Fqsr5*UL9xQb4!7IVX42jNy^$gr!b{la$uNEJ$hhd@;Q}HQC0cX#hZs zPE0@PGM@uuZ%a$?h=CK_Plo6F@5gg^S80oVs|OlsqwxouKXZ5c>Ku(m8@gGGe6Xh8 zHWJ6q=fkr}xP&Ao^JS-4ccy!nC)JK_o8{Rg-{(bpG3njPd9+-`xZwxdAfeYhwLO}g zZ_2Jn)TR^Zddgkqs~HKSB;CFvNZ}#(iJT#vJV^F25WS>Gq~ld|?AQPX-lIs;GZMjw zE>y#E`2{aIf+KQzy^|@TOl^gIHp$Xx3YKKrghy%yq1WpKuc<5*0D;w?Z3+>ee(2>* z5O32hQcLbwaB$T1F z5hEBk#s!6!2BlPBZKZOT(`{;%$>$evPr#;=pWHk_is&WOL2y8{?ER6Tr_vgFg_tN4 zj0+b~&Tgf(K1<332{ag_TvD=Kq^0NwY;H|CJZTs)ga=o*WO&ibk}83gl#>^=kokWI zjl+vW;-mdix}*?hSR>hA`Jh-HgW<&)-bOxWYL?U%8boa-Bkws>iO{vEm-X^3 zLvwS%8?y)wDpe{Eeivq(7h`iwv5I&T_yyj zWjf6x2ZcOiJY7qtIxTQ2z~azMI0td1A=W>LK$&0p@fMh@uljro!TXvIxDdRr`GgC> z`|Ss_8#4&8_S^3-^;*P?f$#;0|p-_RFx>HO`F` zFuhR3nhfI-=uQ(i*?ZuQ?(#Gv z&ggW~5N^QXRXrIFxxS8!j5`EEM|*SkjflV}5m|Z*L-5Ji(^-nwl&VzGa**KW3WQ#0 zo`7%#^6i8>X)ryK!p1J15n1qqcx=sShY!Ok9U(NN%t`=@{60iC1&&0u^6<#=<+YJm z78p2o7&R4T1h)5;ePJ#V=Y_$=RG>!l*k$f(Dzp{a`ku$?OM5{98WR`g)wSJl7+Rf7(-CLjUy}o<+me5_UE*_bwW8>^NfaVKijA zv+iz`huGNcl@P$fC2i#N54MDHm~%@XN7I_b6u3yCg1v55cpj}%$-j@rk%i&|X%sdQ zBDwd^(%Nd5(GM&Ugg4Rzo<5RRsaU_v0+8g$9{Opb5!5OCFzI0K%SW`sb6D2cJvpQB zDNAi+auCUI6|!#MvPbkVv~d+9o2n6|aZ`qXt+Mt&1_fDh!J^qI;^NcVeFkzK>?UMivQAz+jw|{`I}7*Oh@fs z==SZ|{o~=;$2%p890tP*Uqx$VQY%DvYlWoL2_;pvCg>-v(9XWvSlFCfdG=)E_8mG^ zvWV*Q*#CoIIUr%uYSb?|U1zj+js?Kl1QI_(-IxZVhL|iZnj5O#qql0I7SV(D%Un;4 zFDC#mwv)hSgY~(kB{Y$A-Qj8|O{0QI{q6gA7#{U{vPU$kC>9b$z8MWT0r} zvuTFc?sQjLMjjWQQ^!*bW>J;uiddWjr4m8KlevG8ER!PJHkk;Lts*l`+-{U8x0ox+ z8xi5AfbhngVy1Vo#DTDcv=@J;v%hHu>Y6s6wSNQ(9)+a0e`wUbQI+5t%qz zGZ^aq0NIwnhLy_E$C8EX_J!v|HAE8cRF9;n#4h?1>egKgFoh6?LcO8;v_zF)VI%Vt zb{K}?tg5*pAutgG03zzTlRC(;mr_EGCMGLSHpH!$b$42TOe@vXy}vG)`DG_$$0{?ug-OPg0`&hHMyC-gG+52s?*irNXl)Z_NVM z&NAkX(0)!M)`@V{fejwzcGd(a>bC2mW_q6G9I9YEq*3|iS z=Dqy<3vZy;`&*Hyo#Cu%jbqo527ygrL^g-JvT65AZoep*!cY#gAS*Rx^C@227=q(^J!RZzD=p*UECq|oKi z;~*9xD|yzgAfE21I3|9ispsRPQ{HxGDjWHOSz>{cp}`zWh0oOKP@Bt3BI)kLf7w2#5tp`r(kBVS>N1vxC5g-;F^Oath!UtvLrBrmB-F{f$dF z@xqmkxPe?$j?2Ur`8zb1OJ^uPZGQIS`Pkn90Sfq#ry>&N5(&;)Q2065Kc4xyCTPma zMWhFX>}*|ds1|a9!l2ElM)Io6!Xc}s{ra8^J1eQF^Ej~%lF$v2TjH_s&h!_Zm z7q6yy4yBm!Hh!2Db|;czygHDiB+b*40%e^E%WaBM>^iGs0%Lk#Kc}-5YknPTgmU#^ zf^OP!l9e0G8PzfxOu|Bz^yV6bA^7Hks2{@)i}Mv3gctMlve3eHN4{vH5k7+kHus&F zUNji9;&G-Fj|kbv)^jdW)Il0?Fd?S+Wg6$x9gI-M(kL+BR~x}7Uddud65Wq;A)W!r zg0VePn=(xBwjVI2K1;3ysVpKJ8r$U!8ZK zjCN?#!5qbY%-hkHG@Bvhvk0gCvkC4Zga?E692951$)cg8jO6wVIE)AT($PmYZIq$6 zfQB1x#31kEtQwg6kwy=gZMKp=8c8jHp~@99R>65E{)%ypxsF*>)|uf&bv0nbQxDLg zMCFZOG}i5JPt8Qtm+sY`j;<@!$uy51#737I%eV@>`q1IxSG9m!y?yw-G=Y%6&M_`v_|4I%Et^ z_-={137EU0X}(;sr#ZfBZ(SYF16n9;uH)H5vXJW>T5?=~crDJeaWv&DcG;OBC7jYt zly389%yk;~s&LVJWzoyLL<%!cWSG9I(y0h9dNo;UV3r#EE53gr}p-%sm{y#Uv} zISpI_Mo?0>Gtt=M=m|SR2x}BbhLPy!%KVfKcvSb2nQ=(UjYB4SXD27;J2_Ew03-h< zwD3(sB6WHO`&(kE%8#R7tKR4~ws-2iopx&nd$jL6>nbuSqYZp=XP@$ZqnmM(5ei%j zBU^HX4mA;Oqz}$MMAr`elR*~BI=T}6zFVYODnIf0SK~1;Dh~5hlnj7&$eN5aTqb9r zCC{~ZgY`>B}D4_$K&%ojBMMp@Crqi zQzbh+!Kf!01~EQjidXT1DNhWb6)jr)3N^~_yuF~S#>tT8Ue`mf!Ygp93|9t^nl7+0 zZ*E%GnVZoLn;QUh+>D&$o=#J9k{73lBm*RP1S?TtN|Beba91|;L+OfC-B^tCY{hr8 zB5Zlfid8Ha~^#4;LsbA*Sa%-78B&7YsIt zFPAM8U?lzgEbJZm-PGOybKj;D4M7@X@Z9&4CR zu_vsCRG5Y$+hpWa^NlQx@Sgqr27eh$=~3?Ln;#l&c%AfD-TrzT;Dr~!ftWwd6kA*! z!ZF)lK;)a>c#7&Qpj) zx@i$l$NtonPPsuQw z7ra-gA7w1s511#cB(YJ!HP6*m4$VVmH+L4Hp z`NJ**b&mTMus4HNY5nR1v4Apm&)KuZ&*WCX;89E0D^ygbE+ho8ImC(V4l063T0y>$ zF%AoM0kcNCZAC4ySlmcRE3)I$=N$D4%^A*z=Ws6&bR&#&>wN_ow}hYtJQt$*m-fs_ zMc8MaV|+#9k<1PriX2PfdKI#btz0(8)EM}&YGK8`9BRlWsNrKO)VxV>Q!}H5j_1|p z?V5j)QwZYuYXl#^1Hr*+JEncHa6XYZ)*UWy&XAM3J34_uq(Y+XW%Xp9S;6aqcfXO`yUdGMA@s}!+k{F8+ zW^xqc@i}TpD3rdlZsQ7LNOqcWa-oc5NLA@5y3;PrL{cz{bm=e#OHt$PdR)f?E?wt+`W<7uyeGsm*G+%n)CZL?dxiaEm|RZ3q- z@m2halX|pnL}sYQ?9}DsEQ<~52l+4YTv0Ih$aR7(Wql^QJ*D;X)jY3h!_n#KAiQK$j#XaA;cCZr^9V5#~-^D!Qw$9R04>yvqs1_Tl5oP0@23+Ikd zCQ3lO>gM{RM*`Sn2=Mf$lGW4AT@NOOw2VrDKXoYivVBrrBEBTvjDh6CIvn9zF>~*W z;o-z|RS>9*!DKR26k}N$AvS|cVj1G+SSo2c47~94DOhNT`6DL5N|}h6xL<3We6*+R z7@jD(<61OCwx>7>C+AHwL!4PKLWJD4W+pL=*O-`sfr-L3)x1nGE=IVbVJR^yeAZv5 zvCfbLqY-`>TOh*I?1dL!6pJ;cR7lH5K1??e&=@zun?{$J2d~v4gi}p}fl1Zf12D5t zGzzInh&=mjgAi>%{H28R zjaCD9MI)AZLRE(=U%jUguEElX;)nw(5oNAumeEW?VsH!b(hC$iy zw5xvm4Y(}2R}G;JH_NYPg$SPD!6p+NRV-pW<_)do(OemSOAs1?u*8?g9WgOr;JY%} z!Ik$Z#Uou7fTxh#OD@ffZR#e`G)!{{DV?Rs6`zj%)ZVw>lviU>@myJ(|HD^{A;da9 zT|7AxNrIFFA%UBZj^Jed;m6ax_@*S1sywyUMQIM1Rhc9pB_^;`0C>|sI>zDW?cKwV zmv<-)$lJ`@6`_`HLugCOh_=fV9pXv@jLz3JO-vPRe#(@=Xap^z&@fkuJp+Jzw`k^# zWD`dj4aB}k^Zd8Ao=JD5vvFIkd~4to8Wg*qcG>~diK_rNi|bjtQIlTaek5$bLD$3F z*l(N6mdA)IZ%;U|Ax;Simq?WZS7s`&pCzjGq~n(Ns>{oZ!;4FjKa|`Kf$}mFplr}(tb0ORkD){7iTA8C=s+JgK35`WGK&MHhcP!&;xKX zc=&T=VHcQ#rFm>|tMX-x;kMyVdim}ry49Ku-zTvZ1n!~U@VaurtL6q&<0gPkM`Jav z8y4r0%*KNFvqQL0Y=x0gKZiq~X8%x{@h9P{pvgG|Hnj(s_JuXE@PmLU95GhB6NM^^ z&fzgYoLZ&@SxVD0gCH5&;X)GOcBY9jW4S;PBRFGVCNBh%P+(e&2w7xJZC4gP-w6Hj z3$Dz4zg10$fegu!J5woOBbys21ApRPHTiY`uQFV?^8WHu^(w=5eEG2*e!#`2B&u&L z1%wz!!N)e8G&w)v^=n*1Pj_WOvsvef0yNcNt0$Y-M3UYTSnd-QSzYIF7?%neTEq!Z zQVillU9YhC5I1_pDl%h~(1oz_)0BaBxJwzE;y4Yh5?gek@*I=V3Ho=05_Swg359X& zraJNHfYq4>9auSG@x!d&ZBylKat#D?Kk>Aj`onxR^$u&?r(!>}{nIwk!#VpebPmj5f_GIJ?WrzQcBnv@qcm zAAr@mtT?u+2I&%eV2yGRm{;s1rJYCtr+pGYS3CfRd6dtRp@NH{F#i#m!HNo%ga7Nc zZc>t#qWQ0*lI(&>a#z<^p*kz46K9xs(+R-BP7w4j=NqaJ$FyVkA_<}YYLZs1RtQ1Y zE$Twpszl}kyOgZIf&yVem2m$5(B(uJlo|D&)e?0?eXWnO-+^s(u){DfS7OPmP6dYM zim*99iAmd4dL`z4ns$oB!;{IF%#Rfns?m5Xt;>kPrnKbF!#Mn4GFeR})BBzd4QDEZzBGf-1DW&kY*J?`^N=Okl zxAK!%;)T*yu$e?ILx)-sTK&j@Ni1%`QuFrRLJgCJR5J2qU4OVcHf92xIq__0Y$S=- z2UoL_hlZG)9D+ACZ=B$KCEdSBJ(erw!~jCh$u-D%BcMDnTSt4$jj>tiYzz)?;baW6 zL~BVmXS00r&8v(IE$N=94e|@wOUrKEx(m1xa zzOl6NY-3?|upa85zrn#{dkXWRU7OO2@QQ*wy|j+;ae0}v2LH7&;lwrCWbyk|dW5E1 zYZ6f?uR`CQ22S|$2kso|;7Ons=8ROR*>3T_+mwk(5`Wa|UA$m}(Hh?^yf?#{*wsY8 z54m8w+Uz#ucfXtdMjbALcWj#W8<;g3eMzsQ)%by&4xZSl^%?ah?VmyyxWN{lXW;$- z3g+N#lxad>xEJ_c$1^mTb*J!mtc|6G7>hZtYE#Qa5R%D%Js%y)fMT`z;r~tAUi2y=;auSFXH}q3ff}@i( zhyL~(7n#g$D5a;E)#u7?t!y3czq=S$*YW;Nc-#}dG2I6DQRekG1U3f`mp7M=5d4eL zOkG@{x9~7qn>K&HJ$DBW>(xqL*ZUhiuQyXIpS8#FE<+!&UUonm-E>O~4KO-@Y7P>3 z%{!I_)oAya&HnWTz1R&XBnJSCP()P(A9{w$+bJEdpa!~cfkG3v$Zp&6%gYdDKbWlv%#yG`jS+%2l z!pD5VK|bLe_fB|Z7)4oK!684_DVI^7meH7&(VUjinwHT{GPdF5cw~0yuyT^ZBp=|= zq%U@Y82H&fPJ8&oV>+4b6Nn+}d^9;p-Z{LO?8$v$6Uo>=20YwRO^n5HX`E3SYYrrL z`{3jvmsjCd_K$X@IvoA*8^`31!^i&UGW;GPSWGe}`c48JRXs^wV)0_xM!HFWN~6;+ z#+D~kEGoH@ER6ZmoJdnJt>MJ^!yZl*5Pr6cCNvxi;kl?F{)Z#lp($I**a@a-A(U6d z_O#I1@?LFk3(c{2baFnyxyuXquSc#KMvKN~-R8s2HD=pRxmS)Ko#4LX^)V)^T%1bB z;u3=3v`^eWUw7$vZT~c1)pit-r|a(a)7o-r62qhJhwZ(|Z)qr0);#LRr!6Mj;rRXd zu))Q$GU+4u0wl@{lhD{g>aF*`>9lXefI%8bextc{77_j6*u3qw7ha)4xsXQ`R&e5E zhR$>sauglkmChs9Mpj;=A441|KK&!MBh6J)kkP+o`C=VF7aWitkIG7H zZlBY63%vKr5nDI;WZ9rG5GzDS%yU zv#?qO0rnckoiB^z?E;c%&5Wg%s7z>y>S$M~XR1(?Et@_9)s(Q>M%>{pQ5;If3nor^tg{6`wnv#T;2{y5Yend;RL`28VY(spctBjR=gXpwDQfA#Jp9d*?caZee z*M4EOd6Q}OqEPe2w3?5s=A$vKz+{gKRUb{Ox@=V$Wr;{<%Z2*O)9TO88Gu>5TCqCD zT@HAR@EDtWGGyiZj-)A_pEodKzTy6i@-JC5nQbp3rge zY^eZXX?R&vm`@X zMs;pT{53!LZ^Ek<18zQgrOd+P@?DzB*e#UK;qyw$8XfEgYd<>}CQ=4k+<(eSYy+mr z*ibn+Fi{{kX*`*5T2--$Iq`kL^K^7i}rie32 z*R53Gcu2dfqB?wdxd`_yC%6_!kRLp%Uj~nH0H@85cR#)C1dZiT##Q)K+{_Pw#hQqG zR@}j33(KP|?>NyUmfw=v>ZgBV2QTvblZq@n92fZm8+aM39;9teVdA{O$@BXtrl6l6_naumQQ$0F$KCAT%jH>qnXHNFjYP0T{xEv;Fh2hFQricC8DRK7nEYw~0J=j+27) ztZig1%&jhdd-l3uQaPguuOP+3BF2z5mpcTFm|Q&VF$P&%x(K4mW`WDfUzxAsf9tji zJ!!5rqHu5mMm^uWS~{2HM@|93n@&p6PcVzyVYFp&OG?lv)pO6JoL&=%q#IuNDK9!& z+*}O zffM8*)LP(xA3+4s zVne>AwEXTdhHggB#xpNG^t)c zs!9G^c`5QKne-@u@$g`igjBWzJG|cJ5smEUQV#EMzhuxS4>_TUiikO?+FlCUOP8-K zcCDZaN4TV+%bUu2a^Hx$_>|t_D;zu!58R0^BZ3hg6C<8GR%^G*A8g8Fo|tBWf>E8$ z(%Nd4NRD^UoJYpxOGL7869E_WlJDjpl;=XZY<6;3PFt{}ny^GT_#4q?izP zM9&J*cMZ!I(=poSXOPl+TiKE?lf7KPr%YmS5GFPEq;HC~gmDtI6U2*L=jMs^ZfcZ% zAhS#!xg!43ri~p)p}o~-SP>F2OSJ{u3MC`2N@in#Gj&3SJYP5GI2MI9wNI{UkP*aH zc*jU0CU(6-*gcy#_u9!g9j%5LMGV;aq0!pdyug~`B;K8q5Z=)zC)mCdKFj|u4_|tM zIM!q#9x73#@TvM*43GLaC?@@W@tb2H)e5hwN0Mx^2$*c|Vnhs)y}9)P!tAG;+Y^d) z*!)$SPF7tj#n6fC1x=CjkrUx?h?%f+Xh;m-pWq>sUm6s(*yNyu@Thdi!o68Mc>19W z8wxqmhA`$BbVN~4H^*?(xVzwVJfaSZ3nT#ojoXqglSa5MWp0s#Swm$Q(i2SIZ)0Yi zCSX&aPYXgVqo8<))P#`$dNsP(#uh)K0k%v@fPzYBV9BKjar|tmT5vuJe)t=LmAH}Q zWu%UnY6dywb}S++Dq-DAvtfC7rv-0@V5sy=9Uq({VIo#s!XM_{glLji@wwQiv^8?% z_+4wxyp|!_vg1K;?F4eboI5^|N6q7X}qqN+G2BNa0jJ$}poX zj?C=@P&h}G*w{_*xE|566&xP9<%4){)s2~XCnum|tkiMMD5*pECJB+i1in%}sd2rC-a$xY5aH{Ob~DUX zfJH&Y-?*^uTS!c7jkj~TPnL1#FpxC}G?_Ys#<1WltOLh8!c9b(LT^n>0)ctZPs&W; zQlX}=WWzo;l2buRbGqd1g@)-2>69C;j%zHaY))FWZT#b`hz|oICl#1rQP`Hy`0tu2 z%1TJ_*xK(hCGug)8>N#;7$&{0>qT-|n|Xw&cb2CBf)~zXRE1*u-^BuGGsO~CbI{4@g`jjf;?sTzM3xT<}Nm`!o#O-epHP$pT zh!uhLAMUsNSw44-1)Qw^!I^8akB235o$dx)z?Fh?bcZGhFJCO7O>vRr#tCvxacwBh z?&?hl4r%NR;8Oy!bDRtME2PVD{1UDUgVYVj7vnl>*utwdV$N+ame;aEjy0WQahqj7%-- zUQm@bd0&2)0~xY}9jDZMd%`qqs8Nz8ZnBai28$Vq*tA6gsWlC@bS9&f`!LD7H2KRg zpU1ommkJH}mKXs=VkoxjEggFZ(wSqEF=rZH#5G=cWL8kGiUJr(jU%lr9x6;>v#QPQ zg~Ftgjvs+GlpeM^K(EPil$GNOwA;5)jIN9?Pf?RH?cwWsv5?5mRTNgHERiUqF#!Kw z)jp|8XT(>ee8Tl%0TZYkbTENHX)sM8|SIZ>{XEEx4sYHt zMWGgsQ%e7Wo%ADo3A1&*sU#K%jiW&u`~I$%_G|-Ycq80bMdscBqWbY>I_5)( zoh>gn?d(JpkP!n%+b&f~NIXC%I2ZJHrBo4e_pWOIAO0G&*E@D^$hzW9)VhN$a4}Ty z5Bt~Ayx`EP+iNj_TvUx5@Uq={Dj^jL{)2k7+OfIyQNo=ivj;2gW9LTj%0IvK|KBfm zmDWEiXO$8`Gm?DiUWHOIDlD1WN)YW-b}Okaq{WY#hCza{p?UNa@sWUx&cp+UO^`4M zpWqbx1s1VR=@-tFvINW8l=aj~yXD~ksZ?107nm4aYkq^2uRWZlU~@B$Mt@K`j4Zf1 zv1o=u=I5~+h_jTzJ(-m zq#I>FkLqrMsG|+Q2=XNXNs0Vv-%5rh7FW&oW1_Q3%*%A*944Ot1y)iB@059QxIqp)&SL`ST5ZJCy2L@GW`r+Tv83AhwBK; z?S={-@RY+}v8;pmPeq`TIFv(eGmW-UKU|aXiF(m6HD*G zFhR6Vgv_aS@aBLbG9oXQ^s$(9R7|osPu#U%w)@&vi=MpUR&8EH_vyf@WOE;f$6!K| zM_21m-ZHl0;myx{HeIws*{{a9PW?kZmu}smTs~hCGT-U|Px~^;PnzhIL-De&06-HI zHT%kED{7kkF`uotY4$vPn3zRdcQlX_p{^MC?X@(wEh9=Yk=^|Wen6fykRgzDrbH2TlUQ&j7 z&t>eIS(Fi^3=ugC9kB&7G)pZLwy$^>CgS zIzOuN$$yp9(z9&&!H=LtCG$&@pLZlNo}`oVjNKb|SJIUI2GH5KZEdRdA_^*-A3&3< zGQCAG#p1~koS06kdvr3|-}#6o!o!_&oYUL9+!@Q^7IP>4;u62xl0Tc~{W-OeQ^+Ob zuUcz5+o6R${0Xeu9sm29I9?mpl+E+Vy> z;r*B}`7UnnFUE4nHL~ckqOZ>O=DZxZHy7@+f-$sCo19DL-m#sJIMZy8uf%kx#ZS(p z9dhxFd@KaC0gN}nD}@3Lj8CDv6l*YKD<#u>*}-MKrG^}*%VwY`;pX%7!QiQ;$%NY) z8k!E0!T85Ln*K(*)G|ey!RCTM%F)-1b>DA=*{G(^oDE1R{au4rn4(Q0IAi8(NiIzbKUz z9J}Ta8L&ea(B@B9(EreV;n8O-$5c-zq$cA-EaKxbW;%s|6sQ=zdxB^t81!F2}ohtKxVEmWWav6F7m*Lsbls^iIVdg>#ooZIAHa z8dthz&CV4{_j&N5&nK5HvCuby7UW!i6h%^&;MKf|qe(rXGiC*%g?(5Gjo2@RVW9X& z;5EM9I&@NLtcDyV5Jv-|qL=JB;?a@aU_egnC&(*k=o}jPq=-ytoSZAUAYB{HpDp4p z#ECt#P0tjm_2WmW$m^0JQJyr!16p1(E7>ovU+{x>bjXb^63AZA?LmwDTST(LThnGQ zb`W=f5$M<a{f>E@*JZ z+QsoNg5}J2#4-fZ;dSPXDG(3ezPcdA_dm0ZIW8!y-*?!5wBV6kB?;b$0ON7IZKQbmf z*`pDg_Pzx$E%JILAAYX+m9&s0P9ksP!aJjTC!=irVhgviZ=dhKXN{_G9&gDeDQn!*%Qn8CP-MSGb4v(kuRctrpgem(+m&)ns-pSBCf5^}Bn*g*@pNo(xq&%xiBCP4 z?K;Mhei$okl)&afi4y69(WcZ#L%er;Y3`+b0SuWjS-hn6HH6N1?#L4_>@lY#IVf)# z@lx90Jc^%dWwFs(C?{(N%mTc3P<5|MqCM&qwj0wg)6UCvT#fQT(lIhv^2aop=s*X+ zEFJwKl_hqCZ`2kN5EoA+Fp+0@gOvwsAYIuC)Gzb@u}nhV9|u zt3nSB4_}5fYLTcBDmoMt{XD)ooMhTH1^q9pTSWijtcd=_Swvq%uVQA0vXu7JN!lz= zywM~A7II&4ZuoTdUcE*9Yac*3M?C#H1Cnve zWw9frBEs{6FsBgelA;R0A7NU=UuukW`@3jfC8HA)8uo<(GGZvrSefdLrP#mvU@w`(wj=tbLn}dVy>7`tVRpb z`S6PF5%+CsQX!l~;^RTak&{0W_6w68D1xR)0ZXAt11eUCEV zXrT$*gim0AoFJwQ2AC0t5uqt6+(baUa^ODM2Z0J71m+WOn#SawNXUaE@Ba~=k|Y5V z!10uXv(qq4PMV^hAeI}w(%6LT^kn0pDR$inEFzswWCVB@8_c@$RsUgSp#2MH*mXch zQueE~bSP0TF<|EIR6hH4nAEFtU1Q?ua47v|)TXR1IF~0~V91do?X-2QQg&MiBLJ3$ zLS(rw8#^4R!E#tu{~1@(H5^8A_+8d*%(}wKnWAT&`G8smvlu1U>DP@vDbV`|I3^i}AVAN%d zvFyT7k!BYLpftNMtZ}w^C0}HwF$W5UNE`V~D_^iTUsk)WuNNfG=Bc3>PdW7L)sz8~=ExiEL5% zV@|xRQQzplSEU@*3%_7gcf=6h==hxLgI^#GyN4=QWtiptj6Z@^x+Fthjj2tg9I?D} zajn}a*Eg5p-sH0%Q+wdm@On#+ZKTAnvNq(llYea+vNHWIZ3BChz5@KI1LAbgBS?a2 zf2jkhPo3VtrRE>y(hXk2)pWo&u;z~gu2iP-;|8FAgbB`sCvHgUdC2m-Jy07AEE}DI z2NJTc3m3$wSaevXE8FJ^DVx6OV4(LHf<8~)ZvyoN>VFfcFR)e&>R&z9Cfw%091plE?aShNSadr)*`<%! z@$UZd`uLmzlKFZnIE`wfhHW4*moIf4O{%-NqDfNcPEHPKWtX2Q$Hf|M$tG4?q8%cf ziF#j#uOj&U_p1PL4^oDG57H5Q!<#a_FwySG+ zdw6^X^8eJV)hI?ttoW(ku2nb2m*+p#0QBtSVEj|PiiS_|&&ujgwQmXZ{0jK?CD=p) z3OAK2F`3zc%akKKAitYjINQeh{l(dMbNh7U@>6zE)v&@A!3w(+F5!I>9nmjOMXW69 zRqPW;;US(b#N%2=_T)MbJ5x$v+H^La#{=)rb-N>>*RFxkhv%QF{E8CEGVc+&s$%EV z8T+*vad2~WesOttcF?Y7eoG!7H>1UiTxr^Kfz|2Z2v=>zs#tEinC#L1h^hzjiLY^{ z{b8TJy{6sM(GDM~`@%M})n^-3ts7gahMvBW-PUw%+$ylECcXXkO}2Tm^&6t-)vMwD zCZ2ngE@1^^JUrU0H=3WS8Ew_5(mg3i!XB{MpYt%LRsB{O#T2PkZ+~B{{`t?aDAkwC zt5tp`{G8t{9iRRA&v(A1b^b-rF4^iLO_h@S+aDiXDiD_KpT7Hl|F8f1JM8EC>AM&0 z<=XP;+<0&Q(eG#D_1~XtZ2x|+-5-7T;Qlv1Uf#PrI;EagFOLq7C-*LY`ff-8=6m=} z$=P>RE^>bG(|0V^j4sJmwLR1PVY^nZS38}VdaK>()c;0Cu94O7|IQEf*1dM;UZY#p zKi}Q|2ETqh8|~a%n_txC@b%Mod*|n;_pWE&6Iku9nc+~L;A#raR)eMb$2Khm$!c4+7H;_Q%H8Et1{+#N_} zFRRbgvj7}*e;jS!!+m8~-oB5G$cOt_SEMKHPVrQqwE1HeOT_l5+WG$Z;g~xcaNu4K z4$sq6YKG80+$U<>gI(PH>ATAxAb&%==ltFM`Lm1dgC8>~l2(<}6e44kG%lqal?(_s z>(Xzc9%Lkt?|3r95;C)Wa+F;TPeHm`Rz`AEdbo6iE-8OYEAHYz`EE*i)VpWq1ou6R z??b5CKhzpO)VdoGsv6{~b+=Z(SF8P)70NnRa2_9)`z$E2WDdq0RQ;Tu*1i!FSF#Mo z$=L>m`1_(pONTIzh&h~NL2YimQ@#BH5rdNtlRMHW2Bj5pur~HCj<$}6a7v7wB4;-W z83_``j|sn+`qOvj_js|7606+1_kmWN@3&fv7V%S4B2vXVd;F~ZeQXYlwKVOzYN~HP zuP!`{^)&IidhFoe&-%Y*t-;{w`oZg;*LDZ9gWZL}`e1fxFqqG}PY07{5BJ`W9`=79 zKJ0&3nqR(H9t>Jf4-TIU?$!qUXLf+{^k-r4=RaKz{yun0mGS54U^ctk8q6Oo9&9y! zJ{&yU;N-zhwl!En=I+yl*}=1g+1+Q0v%3cmXP>_MdG5pR!`a>4U*-oVkLNy|JZ6CN zvxDXN!3X;9>HMIzx-jUi%+D@wEY3dNSh)PK_3(1C{phl{6aM?K@M!kK_LKQR?G;*h z^>FXm+Tz*?=QF@i&{z7*-&qeGeY%TI4<>^T3v1Hl!P45`>GB%7wlElS?;j1QcTX3j z%l!A##leTAhak(NxxoRthrHdT`FuG6_;fIb-hwXMOLI?8enHVWG%!EdeL9CUPy~RE zm%*&%HK4M%w6=8l^!dWWg~r1E_N#?w%cl?bW-nh{KHXoPWq{AlAMGt)zTWT6t$o~m zzi{~U_qC0KcaJ~5YOc;bdG_@9ptJJ(lfBWi*N-;lYZpsPKc6iBJh!*|dhchD?%jIr z`T3LQk7v(5wzAa)23TL&sJ(vqV)1bJqJBAgdDwsPy0LdUJUX9jy=cFG)m*D%Z*yz> z^0d42sD5{+xw5eLYWQe>>FDu(>*JH(PVUYfwAY^>o{nD~y=H*#wvK;pjE~!oe>;8r z;^?%qfBNp-#pG<*d4IAt7%ZXtOY^f&Audl}EzCh6wurWS3-hyc3+v0z78akaJ=t5@ zeSNw6df_7jJlT5mVR8A<-n*6GKYZLhWq`H2tH1C4ynb-Lyz#8Px^_5v z_2T*I*2~5E_+@?V@b%H?WccFrZ0lkDVykge_Zi^b`NN-Iyf|#{zdky>*gk&U**SQ( za&S5v9>05h^zL2f!D`)z%BFxUO~?9sc`$IZs+i?iN~>{+(>@%8TD;HZ{;fVn=KJb!wGy^u#Iot}B?+h4;NtT5-A-??zO}v6tGC{Dvq#P2_q{>qX!SV* zT)lhHXl3oQ%jZq0@$9m5n$?b)kFqD@$EU~bm%aXYbh@+LI9OZ1`)ag)@Y})rtFJ{~=P`t<3`=IrJ3{@qssaOdI6Y%qWLJo_-W@$w*R?`~~j#?|=nxVLlpyz%(r z^7)gU$H3_0;fvpPdYAR{?Bn?5;>*tRtLId8<@w9(px$1+`|4pMV+>pQy4mCB!zbDA zk6tvh!CYD0-r#8V{Qcc0lezbgdLkhkFFzh<9}gK|vE5&G+K1WU8U9*+|NG0)`1RI! z{Gs-+w)fj_liwe`>z)6;wfy_#>f^n~z3!{$XPvdx^IB(jclPH+2DrRE*}1#@Vs+)> z%Xl1axs4S+l%v+mvhZ0k9!;Ydy83rZg1!4c~;*(f0(r&HD6}0 zpX{H3At%p%>3zh%4DekW9sDr(t$UVDjxL{%X0zY-vk&t}k4_ew!{P6%}e;J;xPL`kT^?%Ms1>a!bL^ zpKRmBcEqy;?C1-?x~c`RE4Yz*|9E)l%3NFkc6+{2>-8J0esgwlezsok*IR>LyI-4c zBI4DX>lw-=^$TsRA#Siw6VkcE;bd|j%atc^?MFKXBbQ&OKR-Uh`GL{m*~t-4{cfHW z{K#@~Z@+{Ujhk3es2cGhCQJ6Hgd3j;*;OrrOc!1MF@^|cCH2diL40!xPXtUZKY}o( zIi|RYoeJwCMDk9~KDrDd9nI+0$M4cq%RpG+i)MVRu=nU_$;t73dVFv6W0s^vl+lFz z0rQ~cWn$5qYcF)>8iV=S`MHH=z1~}x>&o$`3?Nu=sp&u_Cp+gK5QZMmm2)@G!UbkpX*%%jJw$KDqx+fl z%M$Wc$_EKh(?UW23whSYOtaNl==A0m>cq2t15@Mn!t7jau{+viF&0iH_jkyCmxm{-=O;HVW}3fQWL%wJG!d`vgWV0o$p4v=_wJ(%M9 z2e7{PXgGPkKiS_p9IxOk>nRcM{^Ia(GG+moRw5!mj$>NNFT_s;l@uyMdqPY8ll%36 zgeDYAE)Oqxd%*thdohi*CS9XzAy>LFHx_^vURNsZm$kl*Jki_6E@ND$M;_z$>8wp0c zXLtfZ#M76H)RnfW`3Q<)3HN0sPb+cC)JlB1WQZl(l=2buG!~>KzOe7HGGjhriZc~n zjQ9Va|C7b$;XRjJf`1MM?(rPor=6r)nZIwYM*9!$udc0NwP0Oqa?;H;w{dggebyEz zj8^5eo2$~MBP_w^Yg_Wo06y|X-ue2PdUJJtHX~nK5~o%!*RsF_{P-g?u25e@*l~P) zdk?eA-~TNNfkgQqLb9(S3Ux92QIP*RVJNe`0ej`>bi-3FX=N}O6xs7KlPwk8t_-tq zET=C0rrjC{1eQA-$biSzOt)Jg@g7_>Je4&*)2Ilxe^n3nx6X#R;-n6XfzduGr8-xR zu%u#(4|W%2VSADFB~bNW@?)wo_?oT#yOA%V=D*4HZ*qMR1-_{8zoqNnIn!=2kWjZQ&DEvG^jbA~Ata-_K9l?Js(kZ1&O>dUjd3_d?pCQ+>7gss{PvqR zy3=hmr9X{6Bk9s=GhIQbja|yER*TBjXS#JpvR#|$bo*UNtF(@G()~ZvPe#h#xxla7X0UN6m%Jf@YM`3`yecukyZde_|g)pZw?OMMlM20-#yNTPg z!?(7BeGE)smRXd+?5wS20HZV0>!CdsJZq;j(`^GDrx7llnND9|bY=)w|84#8Y1HQk zqccM^iS2ZVHHurUChZt)>yR31*Jz*chw3QnI$(YiuRWnCehKT4*6sB>A&t1-X@Q+! zo0aR}YVkUhhQC2Vd^d5QCx6xjDY>?M9Vj|& zL{h)rquy{S5CEJcMDPi9Ap&(3FQap3rrqus?p->r(nGV=#~fofdW+(RXtO_K^DzS!y*^EQB{*m|H0WjDcFCUgthjx1_| zX23t9bG^fS;O`urIW0%$V0LQd4{F2rbFnH7H&(!-})ddV!_-@sD zsw=RhOou+Cw_y!&nSNJP4ko_Zt<5w$Vod0hY9OiAHSORM9V{~lS{M_M4Th;;pmD9z z;o$%}3cQHcC}y&Yv^G^UsQ7Mi!#(hhWd+}gYrl)LP?m=Nbm~nl(*Ys-&@UcWAx}m~ z&;UwDb(%8}ageOvlM#W(St4Q2@_Q|xO3I@S)-YTQG`1qQv#<^kO!T>ZE8$EQ)B0}LTaXeJUZ zV1gu}Ypq>ZdkJEqcL{H(Hzphy(%OPFvy<5t(;$Q5_;iuh>aooQ6M;Csn_^!ZxGI%C z)p;O!Vsc?HY{+O5L<~u|OPuus;|l8;OnaxvwvSj)?P9!ai~IxW9)0&)4VFr@qcYL~ z7#UXp8M+BO-hgTYE@)GQN84W+lOleUQh+diT{S>6-9Psf-a$cAVVv2wxgkpOq0E;3H>H`l?q+7J&< z$`C)q!-HB8doT--PQo+c8X+m-9|bC;sr>aYIya5ZpgqZ74{8c`gT(|%BqlM*WXV-5 zNrKNK_25)>gbUEwWPD7&mJo#Bf&|{M{91 zL6pTALm6zvV|jt|%Vk&d_+WiYJt?HfYdgWJ*+Apjdi z6_D1dNg42>A^4(RPXvLd0@PMtl#<;Zqcsv;TCPVHhH z=2r|Ty4a#3#(R(jWQyNyB7rtzWdg}nbov;{J0d5jE>qV&W`I5GF`6ZApj^8rt}yo) z-!+WGBwvC#+zxT-W9NEkzsnXuia?ucVwIpH?P?EmB~cV~MCnQsEg9_LB6g}hbh`}< z&%ZZ4OW$Gb`D4{_~=)1ZCknX5aR|>pmuDeW1~RrHicZl zZ~!$7dG27afemE>**9Eh6{=XK z4Gp3?5JToJ4JFhLomVFqJS}9R^iaFWW(#@&xG?TDo?oC0=^7-WVZy6EBGTZOu~p(S zAXBp`{bAk0cOs3{jtMH}Rc)DPF)7H#^?A~xMh=2UrU86A-fyB%74fKRf#CKN}ANEib8)^N9gf-v-pqUOtTX>CGb>Dx--o!GP zG68$Q&`E*HJ}pk4^YF+3swUTKO?YQax}eE*1Y0pjA&PSveYb_4 ztjYAPQIw#`^%{&ZW^=5%5LEis7#Vuq_1}S@t_-U(eRnu)0wzF}>ANpZDTGB-ncEgR zlPc3^M`re{yWAZy^+A^@t!vs3U9Q(!GwQ~(F4K2gf+RtgDXlA0f7WIC#^6WYV_kON zt~;R&>oUU-`^vgZ-z^F9u(czGQ(9e|aMoqU)dDskd7eEcB$O_iM`Iw#2SLJ!P8d=W zeRd!w#6x_5zTyy=E>n`qD+BhcecRBc>P-7;dO}84XZr4lsbh6^ z->x^I468GhL718;z-=~-FRISeZciqNtj>gIOM+ZNanBNC6tuZsgAz-yGib9*3sEbV zrt*T~gbqeUQU(yvJQ5uVBux#Bs6Qo%tOHs0bT~z50kxL7R=oya1w#}AN0;ckX=5`C z3VpYsft;^#D@yC}6dVEuIQ1F@dKiXu9{~@E9nhx;Hv%5QDJ#*HM)4^CG#-s_an%T@ zMjSv~30zTH*CxzxI_O)$q6|?6EsO8N-_~?pn$|Ly5t}2*rUed@c^ZM5aI9n!&4DW> zk~ldWxT0^JuaEZV`U&A4u)t|YzOFT?~tarKs%*QO$IR0Uh0l$ zMATlSY3MPOX@|)O!N4?1Mts)}G+M$PiVMCaqR9&?R406g{`O1C z$@6Hp2Zx7u@Ih~WBMTSTZEjD7n_C|@kFoC@8Sz$LWOFep!wbajsWjOX5Z+45Ka`d{ zVM#Bd`HRNi(DjJ?zqiPtr_a#LJl)GF&D04oPSTcfS@bEr-_m5I9fcYw)Q5XiC+`KrITf-rOyK)i464>)rQ2|&;cT@rj!0QdW9^&x#ARVh zEsg(0RKj4`x3xocLYl%i7~DRMWrPz?mw_X`S-iEZl`&im$MCKFF&RQv9q$DT+*(V$ zRWY$Nk_VJFL}d?yeHksaP!mfaZ45e++z=9LnrqHVBN9z6qDdaLZrdR&&5B~$FClE| zx&r3eE;;y;2G0u4JB$yJ1S4elRg zU8FklEu10OBc=(bh`S1dxl7;Dj?^KJa~diGE_0J$Kn5@##CGJH>jYf140DR{7Z~xI z`{Ovnv}R)YuCihUw2XkpEsBPLSP2J}s8-a>lBab@OK2^sN&Sr+q}@ab?}BP75vXYr z_cl-1q|;_;Kuqqm`wpFsX~KXALzouZiA)4^1a(MmBu%VOWMce|Omu0CON1e~#&+b} zF(hV1D#tVtU8ORSDRM0mQqr{0^!s43ON`FW&7B;dJUg@pJPkccvoux_ib~1rwGyF^J=xX99Zq z{kB@HcvJA3Y+*USmCoPOiWgndoqW?f;m|2*hqjiHPw@t0_glPaN?-Hg>n*nV9m?p= z2e;i5Tc_vK@BKwq$-TdFov>8%I|n1=(p#kiDPuXk@VVGbdjIgKSb#1tW;~KS>^NLm zBnmF|H~B;^-pRw=mP7FW+i$-8CdwBTz|EJVO*}i7d+M1VFbpO2F6C^v-`|`Z?4NFq z08X9`r7L`K892Z4;IF^U!@lCjn(?~k7+3Y;q7xh=|XWu+R4%I_)hgU+~@DV-=K?Vkw_=5>$T0(Gd$`#{xCB>op3h8!m(w# z+4Nquk##e=XtjzPRod0nWyWiG{2uoU;RG+W)a+41#5yAEI^Tedgoj(-{m_|v_V?fJ zt)4z@HvYW**Y8`)zs{_`8MRjCFAo3m>+s;+;p1N}9=!Z@?d8kt@1tKgoApWS&C0{S zKKOg*(J`*n{jq<0?f&Dp|B;=pAFs3yPw(%4|N5s%^VfH~w}$VI|GxDnG&B6qjlVoP zdp0{ae{gTO@t;fMh2ev<`pdb+udq*SXijU+({QbnCA>caN49w+|mI-+8zA(~H@yrJwKo?bhCZjo<$1esukl}hSwG$FFTDF{_jK}XYrc{F`efAm@$~zng=h1dbI)fx zz2;8#*B7gQeTOSL_kTH?>CZoW@bJNd=Wljiul%t8^u|4GXB*u znmJfK|3`D~)$*&Y-R1xKwKeJeWBtLq^Um5|9z3c2=fOXge(ByGG#~H1y4z^}_1(K? z?*_MCJvf)fN0jRzb5IRAe4O>67)tb4xMUOv3PdT005uZK&m`uTs% zJ$?Iq=WwI@=bd|hU0r^1dT%AW(^{_gM~kQT=6@LPE!?~Ju6h60H>1_oo#D>PM(ftQ zv%hct^#z*Q{p~M*wPVUS+T3dSg>)v4PU}g9J;fGl8BFVe)bx9 znX6Uad5_>Su~-I|UP^?_C|^s=8s++5QWF*vLxqG14!kC`Gaiz)6Usw%i=%(ox#VKQ z_8_keSum?D-eRvVondl!U(tudyM1zuWejJ-owdZhG1!~I`QeoWyWR7EpKY#w5=#eP zH-(}-1}=xddS2QRDRHa);Hh5Y%V9)Qa2>N-Yg$7{b#A=YY3uS^zfa+t>28?Gg3c0( zVIR+W<<0jIdNkjgr+CGIzMnqubb4CcT$tw^>&NBl9xPfZ;9VUzhIwnTcrIQ;`;v}% zXi*DkXx6!6qaeQAgW@ToMnZu`Fi^uPL2m10FNR-S9g<8HP zru`3Leyl^4CWU!S&T9pXw*&7qqb+N(lWug)A&=LOM@|X1H!1NqzsW`EXFy}q{PAjm z?{Np+tkv%p%)FYhXMAqo$_FZkpT0Y}|0ZtE*_~ct;XPDQ*A>~e@hV@nzo2z=d)4}z z7_WU-tJmQ-Y1fCV>)H@%g3u;+y?EAs{dBQUIbUMgJKc!Mdo_Erq ztyMk)k!9YSNq?};tG8x%Bq@n2p89rlSF)=Ao%>kOp^vBC1G(y=0-Z;B!?_h*7Hw?b zgVgj2m`;B?TW(t`(3sC@b$ zvdX_l4Isc$ro>J?W(6ozte0H|jZC1npGT zUWZ}(n>6F!b0(Dmw}A1+)I39d6&bt6MxEt(t+k*U*x)RM_Pxx zThe+rrh&#)> z4en>>HkqNn?rtS$8C&+)S2gMsOWK8V?A}+HY=YJc@!B(zY+rf;RN7mo^KLYpa$aS# z-AN?@sn+#b*;J0jC8w|$winH;?*~)6fY}U-rMTl2zG@iTv`J*0QLyw|RVuGu4*(<7 z)dd8dCav?T;*8=qJB@vErVOk{q5e0U<-=Y?GrF#w~Qa4JryD|@yKNk zTf1vjR@xml^qVes>f!w_rZr$1Vs6&E2)%G^q>@{Iv{stf;?6&_dUG|ReZtd8wfOF3r`3UeklI8l^I|{oRtZG>PcrMzO z7|f?$YqdKL8~O5i9v$d#T(h0K&+TC1@K=`=UtAK2J$_EOSpJlW!(hqAAaaMS{IH## zLk)g!I1w);J?aQ&l=2{u(&$ui=`Y_;48OZ?&wy#ARy%l=^ZomrP+w|h;a!44!@Oe8 zNph@j3mAo%T2NCUtxbE$9Dhdv&PLu#L5l6L$Jgqq!pa4Qk;62bN%SETkc5(2B!-J6*v3;B^w z*)uFPwiE!7J;+!O8oNIjL(z!mwFE2H=2U5Hra}_t`%$;-Us^R7UTM8~t(0gfVfBaOu=o(0Jx+h7kJ|7oeAsQ(0zRB|EYq#5YaH&i zJ!$JN-7n~MAm4q@?qnO+SDgH2Tp?p9w@p^_FN+`W7c;L@2s;b(>bp-S(AItjKbNPP z%|+|sQ!MrzHbB8xyI9b#_MHYheFu7JuT0b2yRYh3jQkKsG%a8<2P?43MWkTtinV%IBs9*i0Rc_hwDwjcl`<)&kA#6PB3eq^n^8ph zta=<@$A2|+ulFRHE?wyyH_wSL9=f$;Qk@9Dl)1Rm_&KIh_N76+-+s7XAD+8;#&5}w zQ;)zC>W5h6Zf9H~<0K3=?;b67R`**Lq0FbBJSXkXy-lU4qZKfhGRWOvJuuVC-aanC zlt>2#_&W?uAUn%m$>^;5!CCE(Hp>BFsvCdt9Hd<$?hzZFtPt5a(72mgWkP0(OhImNCjEE2$tO#UcEQ2aRtnBxPn{tOsQ4J7dEfsWA~2lsoHNL z!@OL7n%}`4dOVp}C3`FX5I(TqEw5)(SYG9`eCX^Eu%SO2$xp4WFK06&396(fN9b`H zw0W$S2F2RVzqIzGA!Wd1<#UHa$NL5{Tury>;#$#VJrNq6mUJZ=#}@r#%YFx>EB5Ju zc~TdiPk7$7-W&1rwu5zbxZ8l^l-m6^=5(vMv!Ju=rf*<<(r=wgqkw^|TjlKmT~>A8 zE5@A5dG$MAsO8;A_V%=Rz2A01r`&$svXVx@>)}c6I!(30VU+tXje z-e&62Uo>l9^zF^wx2@OvJ71<$v}Eq_4Vnkz2wXou)+qC-&+esaanflzXFt^k~W-Bp6Fa=C<5wl4vo{!1)1SQwA>E^K(1dcKj-~{9#H~cz*Mduxk8H8K3x08 zw&K;-&C9aSpttta`#9;doq=ol5^SpL<*w@t``)s-Q|!hgF_l(8?IxUUHh`AL=Iw0* z5eTGw75l^K?6=^wc|Av}pi}y*q02e)Y?j;Ehq(3dUA7I_s98=3r@Yg+VYo9#Y425Q zo{k=EPu9da0&NGJrY(>mzm0$@{n+Wch7HlR`C_E$)mgrQ=*lQWELylBPKl82_y?$cL9J`+nxT7e@^?I~PyPNHr8lK-3Wl4{-Vjs6ynVxk z&!grL1%OH(f_DWI)b0!TG`85H_TV5stMy6~xLK=7%pjs)1Tvfb$gy-|%C>%W^pq+^ z3HjcJ+ik@Db!$7<{{Z8o`PRI-4!EJAkGbU+IPYU6=4%M*RdBaF9Cc)6w_4&gW53N{w1UhXNaqSDQ?5?8i$q zoVjMjNUzkB212ctcvSXZuKqO@Wp0*v(@yjQwgphR>J>Y zTHunNp!M_S|G#KO5qs1dfv!9Q!6y^-h5c73K-Bnj2dBPkQ!RS{ZC z<04b3#5OjevmmT5P;NZH3~j2j;UG~ji(lvOPD8rgYDvv=nk}4uZ$%_Yw6Ex@2-Z$S z@7X@=zxA(U0Iq7ymzDT+s(RDi0NS9{5{hmB>YQOH79>*g13{#n&PaZZ@kAMwEA5@y zPKf-vb<)2Uw}j2pfb27pKfA)6i@b4(k7B=*I!@lYb z$(HQGTg*w`CbllY!^nVqo7*9Ga54uoGVfdgUs&2*LOsHZ(jN>#<5F`NGv$H5XBV?z zPOh-W{gMtf=L1FLnVMSCSl#g|e|^k_2kvb^o~No$@OEwU#?CHod#C}6*?#*p1Rj%` z6tUKBZdlt|@4>s?=XUAexpB%>(>`}I8x5*`PxkfmQodHfS950FMrpEpv7!g>=h)VK z+;4euLB~MTuf1OTR^NWh&bTwar^7MGwy;n3Um($}ii(Ss)r2l%Qiv`ffhV~@2i<>B|_ z-J6*<3pY=Izu*(&x?5kGuob)`Zz!Kz#9!6naJnT zV%I+V1=~^cj;$AEU)Qp`r1W%vImly-TUJ%vQ%=EhZxn7-I=Bt+g+A?XxoG@@>uj@_ ztIzV7Gzyq5f%Jw;o7F?Hy1bUe@ol%7&BqU(7TdJ|eU|U)t@(wA6L#M+C!^vHk{JdM z9(A|J@wiKsxY>b6%cu8{jak`mgG`I?=Xjgy_D2`i>*MG1;qTn=)v|FgLU@VB+V3{N zD2aA^l$LbluhHnmRmIybu&+7s8z94>$^N{&w}pa?T%?0HbPo0JZV+MK>|HB9%X?}q z{RbhY?0U8t`X?cYq&$S=0%j~XliZANrE{p>V0D5$cgc(%?~eBr93Pk2Yr9V9WJZ{K zce|jWx27WGY+;MH-P{g(ou)DI&Fe^6tc{mk8+Z;-+0gocy?a_2ntuRa?03ZiMrzN` zu>9ltpzLoOp4XIttv9s1;u5IEc-^gy$yh}z0>j;|*-ts?7SQp&-?jB?(qAQbrO%`1 z^(FZdHXMJcU|ha%W|!N#Msag_XU&G`-Z~U8m$lC%r>{{h50LNOM}LMN-Qlh?Tc>U( zhVd^)2JS%B?>l+1&^j%Typ`A!%^D|gm-sMq2;2`e4EM?tyxZ&j)U_w#`>Hzs?QA2{-Xy={ z@uRC^>}4sB?jn)fWEKd=+UPygr&gpb;r^~yle;+YXsc-Wmk2sCmqSq z1LUjBqYI9Hu%W0Q`9-Q0Fipitp-)#FVq<}x6~F8oY*f3`l3c~WFsBYHkXX`~tH0A`EiPb+CZjg_8NucG#lxDidM(GKg#^CM>l{e+87CLNM93+jYPlR(7uKb_ij$x*s=5Hm97>Xf-D4_xrsz^Rx;mTXnd|n?w}H zADv%jD0m9BCI&@qxZ9!cVtoUGXWFaCz^F8G+x1c|HY&!S;?e9>+w>cKKb>ivlJw4d zfHF01NiEkyMnnxYCRp3uY<5HS;WFwH#LIecc#Vy&mO)dLl9$T4tgBF%b+xy$eGztP zvI<|Q4qj?l9byOqQltXLKl+PisJkt(*}mPbtAJuX{yn>NlH1uYNCEG;U)s`Ugs}R2 zmRd)HpVBu&E^qh)o>9)dzN)^Ffzkc7jLT$_1OErnyTPVq>QxeFNDo$2%A zU_Mh*a4REec(@IS@^D?OLzan;hu6jXYB zDZZI5D7|%`#EOYvv+k_ZTA2*EpDC9fgubY4dpP1T$MyAwVo+n_w<|78Zr5w6;Ed75 z0mqtue41T}mK|mBd$Nx#dXPm~s0fdxO)f7#!sLC5k9=fmbV{95Kx5$%)E;yvlFrfM zKCI9WyXSRoVXsU?ukA~|VBb>;0O)K})W7z-{YJR`NN@hhME7Y6>UCS=O7~_iwq(E4 zqLgu=UU};me+?Huv*#4WujAoVmED?Efrt@owU+dK)Y~nZDr4RHy94h`e^26k z%F76YDzza8dQSnQT0kKwKlYIJPKE}y0Z)2 zY0&0)qu^xHZuEh|Nxm!hqSi#6S36~1{^49y91P&LNaJUoypvP2@hlr{R<+5+G zN5N@tU)gxEK#~c|4eu#`D~`|_v*X_x7(vLsRU4?K7m?kc1M=G(HKAUnG|(arXyZm0 zYT*>PzEe7`;ER%9|8bjJ4%7yEa@5oZTlhx+*MWx%23~j}(yd5Dym3tL+H_%ETCN}7 z`n_isiw>BD19>0G2;m*z2=Nffq6YeDfWoESabEoSAkA;vBM;Y_)R^wPhQP-eQsPeU z+ZDXLDPYFb@3%@@3pPo-qz2~o_1oz!npM$XKWpyI%L@G$iZ`7nh*5xKmL z9oMY~t@bOEL4}zK8h?aYqFxli++}DKR*GcHGFjKw%hB|Y>ukf{{LV-#3HO(WAiTX^ zDJjqBpbxwc<6faW|Ec?5w0}<}E7lGZ?xLA5jXP(J)nguSJGBQkb}Ne)dvl*|(@TL9NNz?#TK<_N4XTxyQ+PYo4GX4uAO6xpB$Spny)uSIC>x$6=0dm<# z7I!I^zvb*W8NsN-mO9&6|%=OqkLWUy%nx)Scr+gyA-{yG52J|%1elXp+a@?>U z{~q}+XT_h-Tq%<%o%dq`RIQr-piZ0ceU@7k+PpIdA?hy*m^HK57roDCny?4(CEp@^ z*C~z(jVJK*ZQAg;xne!=5&Pf}caYRO(K2N}4g6Gsrt!^~l$X=-vRjeKPo<$1(qiE9 zVX_XPPjEYzLx7(iHh-VM*k!+sYxe42vs-tv-PM+C_4?L?X?M7_v6@1nQ_)>`)A#4~ z^Dj%`SnBMdlCQ1J3|WV(cR6aKQDuFO+hR}1$I`sqGCv6|_Ga0KTyifY*+B)2IPc$Y zTBDV^@@weJv(iMbEjn3+l9>B)=5?nKwZsTc%051xzUOUDA9Br|V@da}EV)>uN%OnityxTKzzqKG!h-GNaKKA^>ADkK#OdoLUGb z2+y&3;Lx`xUs~}A64|iMf~Nhg9}X{M)E-DTw>m0Z3)UZPH}>D1Huo}**lvg|J7_y@ zw_&Y&y-u#pfSnDeb1?G1)U#1W>nou>b74H?;7Y~ZFUnYd>3GRmw!&B-evVeJ^#9HnjAN0@5fm+9I)|{n4$yIvI{eVo&{^DL3VlXrE$@{EbnZHqv3_Wb% zuA;r0!RrL$!G!=2_+$2==;qUb?kw~h&{^aqa$j@Yue)@=isH>@^31us{z7M?)309& zuh4K}3J+tS%@$6Zv9t}dLKUBSKk?-wLjB9zhC9=D#x|<3zZ_icvr)PQadGW4d_DVa zLBCBja2yGDTOTTIfT(4$F@y^-;!`KdzlMDyPJ}TN=3a7h>jU>aniX^$7tY{z-um>y zA06{a8Vs#!Xs`g(3ZN6aRH~@a4c3-49ZI5Bl85Nqw;V#&%bb@#V_^4jUmj;maM_v%y)4?OvbYP+WNKS?LV@p%Pm5st^b1Zf4~$ujbB ze$^OlP&$PLk6Ck0bGUP89_~ggu%vB?)C_A~XW~i*gjL~P+HSh3^AxpojeHHki{2fW zzj(K0rTY9RwDJIpr%@Vyb3nLfwL3d|CVa<-Czde3%3;wE^W|egR@fB?-Bpp8Z9}d{ z)Q?H&RUPTX+m)tTs7S~gt#VHbth8?B-`8w9wr3yacG03A#@F2BZL?ayfFMXE*G|KK zDj%g#^Y|)h#Z}(O&XX^6Ik`o`M888dZb{RHi>PZrTm|Y=#{g*i+BE9ljQy?l2Z%Gy zb!VZHowyI)AC1?d5$!+i-F7KQ-M^+VINM%6Xzf44XdA=z8v3f-aOd599G*bjlNK$B zi+K**z1-DR@Y?q(Nd8{&e7Oi||AY}igxvRfP1VWWn+TG$ar7Lt4_TeF18Humfy zf5tHz^coG?ZmnvSBQ^5uTdV>Ey_qlW-Tp?s63J*W8{$vS4uR3P3fwQE$1Zl*c4G$n zPN{bKM4cvW4y2BMUzHZ}t+x*yU1D*C1>^*^Z<0v-vQ&ulw=T zz?QEq z_)}SgN44R2P^*LXQJg()SfMpy(;AfI^y5*)CEpCEbo^Y{gXQ*Y(-otiCx=hHUfWEq zt~<~5$7L;B_mb`%AVo9$wSQZFls~uKx|up{;n>}^`#%=yLkS~&-bEjDS}mo!GcrDD ztIG79#pN@Z!mgl@)NU@WKqmxCR(F^mNycuJXz?^|)+k6yCR7(-V$ColbjmfBS z2LB5you)sLp2m8F=1q%zq7Ygt1=#-Bzf9VyWS@{u->j_6P1a`T4{$VDl962fm6o4D z@yq>6=V(XLQbm%vh`CG`%j<@ug;PX5>c%6&_9jqC{>(|W`@OucufgJb0y!)evM^6d z2oREcT+f(FFi=?eo2@zi++Z&AhM{ooL-LvT+si-Zt2{3sTgIU&Z*%FozbEx!con~& z>iyHkZ_4p=Iv=0z9|QF)53a%dajfGDF70@WQQj(TXUzRcx^;oQ3jNs903Y_E=xjU> z1L3;i^CdMd^H;YjnYfrafH!MjaPv*~r?=67FZimVe6!$CM})|Hu@3gvMlE*7n_HQ) zt-cn{`@h^kS2z$RVD>tcE@9ggz{O1|O`7GUgl_2?(OoDZWjLCgNxjY%u}=Mud4w(! zQ|cGw>q;p^YIw`LzS6B^STw5Vz~ZlsBED0rW(OqAm+&#Hm1s_}KpzcRZZ8AYlLW&dII@z% zbymP6<|llwPxZ~aR61BOCq3)K-lM8*7L$xb=KHb@FZ$!;5-O{srJKrK?I^zyf zdR|SNxdYnW=8&Naahp~(*aQ9b>C@K8y^K&S;g^E0`}fi$Dvv0LyZY(sG|=k8Dd3C& z;JZI#^wjXi{=z7?&zH;#wO%1GTuzJ*XW$m3_5;9QJHa(r;b*Koe)$`;uF? zIy|&@0L_+(15$mlJOx?R{?>Vv$C5NJV30z1GakPA7*yL)HkqI7O%O>_OHCBY2u^GC zX`DgoEH$gkMF33@xTTj~$cb1QOQ^H{nB97$+sR(KHa@kDDh=AB_HRTQ2Xb(m97k;7 zv-}pGdcg0>#0FX#4;Qt>?Qf5hRB_cjJO30@NiW)UhC}ryVWwM5XNh7!K$mE!nM=drkOHfZ{Lzi6gRBDx6FE@ zr#f*sVIjUnqoqytk$6(feF6s6-Z|UasoVPdckvV^R~FMpyVme`g5S;TQzu%{b~mn( zO14>d@R9FmwYV)o-q5PP4Nr>1^vD@+-N4TL-1=aOsBJNkj)Ij=`UPGoGl< zTDWWmaR10Gr#f

-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≤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

Ub0eJb?Y?_#+Zr-#1yt+9LqS~Ih8 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 F^$&Tk?Na~% literal 0 HcmV?d00001 diff --git a/Tests/images/illuCS6_no_preview.eps b/Tests/images/illuCS6_no_preview.eps new file mode 100644 index 0000000000000000000000000000000000000000..4be1ea9a8ce10acd1e02c9788c5e663f3bd2b0a6 GIT binary patch literal 411730 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�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⩔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≪}3P~(;nMr*}@}EYo^2!eU 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

$^9S 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ℑ&) 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

+zzY6M5hLDLpk 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 XsQkSLkN5O{8}9wVp?rS3;6v~qadTke literal 0 HcmV?d00001 diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index 71c40116b..861cf264a 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -223,6 +223,21 @@ class TestFileEps(PillowTestCase): self._test_readline_file_psfile(s, ending) + def test_open_eps(self): + # https://github.com/python-pillow/Pillow/issues/1104 + # Arrange + FILES = ["Tests/images/illu10_no_preview.eps", + "Tests/images/illu10_preview.eps", + "Tests/images/illuCS6_no_preview.eps", + "Tests/images/illuCS6_preview.eps"] + + # Act + for filename in FILES: + img = Image.open(filename) + + # Assert + self.assertEqual(img.mode, "RGB") + if __name__ == '__main__': unittest.main() From b885c04388e27aea85f2385d7a948fff25618618 Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 26 Mar 2015 14:06:54 +0200 Subject: [PATCH 0082/1037] EPS parser: don't index blank lines --- PIL/EpsImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py index d077ca1ab..83024b63f 100644 --- a/PIL/EpsImagePlugin.py +++ b/PIL/EpsImagePlugin.py @@ -275,13 +275,13 @@ class EpsImageFile(ImageFile.ImageFile): s = fp.readline().strip('\r\n') - if s[0] != "%": + if s[:1] != "%": break # # Scan for an "ImageData" descriptor - while s[0] == "%": + while s[:1] == "%": if len(s) > 255: raise SyntaxError("not an EPS file") From 735d34260814f9180b773723fd741a7da73047ed Mon Sep 17 00:00:00 2001 From: Martin Fitzpatrick Date: Thu, 26 Mar 2015 13:25:26 +0100 Subject: [PATCH 0083/1037] Add support for HTTP response objects to Image.open() HTTP response objects returned from `urllib2.urlopen(url)` or `requests.get(url, stream=True).raw` are 'file-like' but do not support `.seek()` operations. As a result PIL is unable to open them as images, requiring a wrap in `cStringIO` or `BytesIO`. This commit adds this functionality to `Image.open()` by way of an `.seek(0)` check and catch on exception `AttributeError` or `io.UnsupportedOperation`. If this is caught we attempt to wrap the object using `io.BytesIO` (which will only work on buffer-file-like objects). This allows opening of files using both `urllib2` and `requests`, e.g. Image.open(urllib2.urlopen(url)) Image.open(requests.get(url, stream=True).raw) --- PIL/Image.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/PIL/Image.py b/PIL/Image.py index 2f304a9a8..58944f891 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -109,6 +109,7 @@ from PIL._util import deferred_error import os import sys +import io # type stuff import collections @@ -2248,6 +2249,11 @@ def open(fp, mode="r"): else: filename = "" + try: + fp.seek(0) + except (AttributeError, io.UnsupportedOperation): + fp = io.BytesIO(fp.read()) + prefix = fp.read(16) preinit() From 508c8dbe002c4dac7356bf41456b2ab374689066 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 27 Mar 2015 06:56:34 -0400 Subject: [PATCH 0084/1037] Fix manifest [ci skip] --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index b808ad17b..4fb47c637 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -9,6 +9,7 @@ include *.yaml include .coveragerc include .gitattributes include .travis.yml +include LICENSE include Makefile include tox.ini recursive-include PIL *.md From 860a1dedf1e530ef21fc1052c8229ff8ae1492be Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 27 Mar 2015 07:02:02 -0400 Subject: [PATCH 0085/1037] Prep 2.8.0 No ci skip here because let's see CI run w/new version numbers. I'm starting to think (again) this bump should happen immediately after the release instead of right before the next? But I know @wiredfool had some objection to this at some point. As a compromise, maybe we could change to 2.9.0dev immediately following the release of 2.8.0. --- PIL/__init__.py | 2 +- _imaging.c | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PIL/__init__.py b/PIL/__init__.py index 4d5d7b35b..d1537769e 100644 --- a/PIL/__init__.py +++ b/PIL/__init__.py @@ -12,7 +12,7 @@ # ;-) VERSION = '1.1.7' # PIL version -PILLOW_VERSION = '2.7.0' # Pillow +PILLOW_VERSION = '2.8.0' # Pillow _plugins = ['BmpImagePlugin', 'BufrStubImagePlugin', diff --git a/_imaging.c b/_imaging.c index 6ae120688..c3be9beab 100644 --- a/_imaging.c +++ b/_imaging.c @@ -71,7 +71,7 @@ * See the README file for information on usage and redistribution. */ -#define PILLOW_VERSION "2.7.0" +#define PILLOW_VERSION "2.8.0" #include "Python.h" diff --git a/setup.py b/setup.py index 7262db39e..558c4aed1 100644 --- a/setup.py +++ b/setup.py @@ -90,7 +90,7 @@ except (ImportError, OSError): NAME = 'Pillow' -PILLOW_VERSION = '2.7.0' +PILLOW_VERSION = '2.8.0' TCL_ROOT = None JPEG_ROOT = None JPEG2K_ROOT = None From b3ea34fb88b4b7b5dbde2a988c5bc99df9f9179c Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 28 Mar 2015 07:22:14 -0400 Subject: [PATCH 0086/1037] Document #1151 [ci skip] --- docs/releasenotes/2.7.0.rst | 4 ++-- docs/releasenotes/2.8.0.rst | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 docs/releasenotes/2.8.0.rst diff --git a/docs/releasenotes/2.7.0.rst b/docs/releasenotes/2.7.0.rst index 8359bd7aa..65a8f2d11 100644 --- a/docs/releasenotes/2.7.0.rst +++ b/docs/releasenotes/2.7.0.rst @@ -1,5 +1,5 @@ -Pillow 2.7.0 -============ +2.7.0 +===== Sane Plugin ----------- diff --git a/docs/releasenotes/2.8.0.rst b/docs/releasenotes/2.8.0.rst new file mode 100644 index 000000000..5fe485875 --- /dev/null +++ b/docs/releasenotes/2.8.0.rst @@ -0,0 +1,14 @@ +2.8.0 +===== + +Open HTTP response objects with Image.open +------------------------------------------ + +HTTP response objects returned from `urllib2.urlopen(url)` or `requests.get(url, stream=True).raw` are 'file-like' but do not support `.seek()` operations. As a result PIL was unable to open them as images, requiring a wrap in `cStringIO` or `BytesIO`. + +Now new functionality has been added to `Image.open()` by way of an `.seek(0)` check and catch on exception `AttributeError` or `io.UnsupportedOperation`. If this is caught we attempt to wrap the object using `io.BytesIO` (which will only work on buffer-file-like objects). + +This allows opening of files using both `urllib2` and `requests`, e.g.:: + + Image.open(urllib2.urlopen(url)) + Image.open(requests.get(url, stream=True).raw) From 144e45cbb72c92ecaa3d47f4c04ac66492180a45 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 28 Mar 2015 07:27:45 -0400 Subject: [PATCH 0087/1037] Update CHANGES [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index dea818960..5fea75209 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.8.0 (unreleased) ------------------ +- Add support for HTTP response objects to Image.open() + [mfitzp] + - Improve reference docs for PIL.ImageDraw.Draw.pieslice() #1145 [audreyr] From 6151cfd411c0e0790f15825b93cbc028ef584f1b Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 28 Mar 2015 07:34:47 -0400 Subject: [PATCH 0088/1037] Add 2.8.0 release notes to index [ci skip] --- docs/releasenotes/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index c2f95f670..3ffb9c4f1 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -5,3 +5,4 @@ Release Notes :maxdepth: 2 2.7.0 + 2.8.0 From 174d9ac083327723c82274ee4af228dd7f9dfa33 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 29 Mar 2015 15:02:53 -0400 Subject: [PATCH 0089/1037] Add Image.open(url) tip from @mjpieters [ci skip] --- docs/releasenotes/2.8.0.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/releasenotes/2.8.0.rst b/docs/releasenotes/2.8.0.rst index 5fe485875..85235d72a 100644 --- a/docs/releasenotes/2.8.0.rst +++ b/docs/releasenotes/2.8.0.rst @@ -12,3 +12,11 @@ This allows opening of files using both `urllib2` and `requests`, e.g.:: Image.open(urllib2.urlopen(url)) Image.open(requests.get(url, stream=True).raw) + +If the response uses content-encoding (compression, either gzip or deflate) then this will fail as both the urllib2 and requests raw file object will produce compressed data in that case. Using Content-Encoding on images is rather non-sensical as most images are already compressed, but it can still happen. + +For requests the work-around is to set the decode_content attribute on the raw object to True:: + + response = requests.get(url, stream=True) + response.raw.decode_content = True + image = Image.open(response.raw) From 75a760e5d6abe351e159fb1ce78a889d4a698d36 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 1 Apr 2015 09:35:45 +1100 Subject: [PATCH 0090/1037] Fixed unused imports and removed or commented unused variables from FpximagePlugin --- PIL/FpxImagePlugin.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PIL/FpxImagePlugin.py b/PIL/FpxImagePlugin.py index 7e1d09dc8..e435cb447 100644 --- a/PIL/FpxImagePlugin.py +++ b/PIL/FpxImagePlugin.py @@ -20,7 +20,7 @@ __version__ = "0.1" from PIL import Image, ImageFile -from PIL.OleFileIO import * +from PIL.OleFileIO import i8, i32, MAGIC # we map from colour field tuples to (mode, rawmode) descriptors @@ -130,15 +130,15 @@ class FpxImageFile(ImageFile.ImageFile): fp = self.ole.openstream(stream) # skip prefix - p = fp.read(28) + fp.read(28) # header stream s = fp.read(36) size = i32(s, 4), i32(s, 8) - tilecount = i32(s, 12) + #tilecount = i32(s, 12) tilesize = i32(s, 16), i32(s, 20) - channels = i32(s, 24) + #channels = i32(s, 24) offset = i32(s, 28) length = i32(s, 32) From d88a7b778fcebee6afabf1d939a9fd9d48853e32 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 1 Apr 2015 08:58:10 +1100 Subject: [PATCH 0091/1037] Added missing self argument in PyAccess --- PIL/PyAccess.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/PyAccess.py b/PIL/PyAccess.py index 28829051d..a3f1c3909 100644 --- a/PIL/PyAccess.py +++ b/PIL/PyAccess.py @@ -54,7 +54,7 @@ class PyAccess(object): print (vals) self._post_init() - def _post_init(): + def _post_init(self): pass def __setitem__(self, xy, color): From 0ca14909eddd9d158b692015f763c95782150a36 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 1 Apr 2015 09:04:28 +1100 Subject: [PATCH 0092/1037] Fixed duplicate import in pilprint --- Scripts/pilprint.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Scripts/pilprint.py b/Scripts/pilprint.py index be42e0a75..efa2b9862 100644 --- a/Scripts/pilprint.py +++ b/Scripts/pilprint.py @@ -11,6 +11,7 @@ # 0.3 2003-05-06 fl Fixed a typo or two. # +import getopt, os, sys from __future__ import print_function VERSION = "pilprint 0.3/2003-05-05" @@ -21,15 +22,12 @@ from PIL import PSDraw letter = ( 1.0*72, 1.0*72, 7.5*72, 10.0*72 ) def description(file, image): - import os title = os.path.splitext(os.path.split(file)[1])[0] format = " (%dx%d " if image.format: format = " (" + image.format + " %dx%d " return title + format % image.size + image.mode + ")" -import getopt, os, sys - if len(sys.argv) == 1: print("PIL Print 0.2a1/96-10-04 -- print image files") print("Usage: pilprint files...") From 128280e5b85147c7c64ee1225b7f4f7e64bfec8e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 1 Apr 2015 09:08:39 +1100 Subject: [PATCH 0093/1037] Fixed duplicate import in Tests helper --- Tests/helper.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/helper.py b/Tests/helper.py index 563f42060..d8f88656b 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -165,7 +165,6 @@ class PillowTestCase(unittest.TestCase): # helpers -import sys py3 = (sys.version_info >= (3, 0)) From 192fa39fb72ee88ad5039f8f217b769502f0ac0d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 1 Apr 2015 09:10:12 +1100 Subject: [PATCH 0094/1037] Fixed duplicate import in test_imagecms --- Tests/test_imagecms.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index d669b4de5..93bdda44c 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -240,7 +240,6 @@ class TestImageCms(PillowTestCase): self.assert_image_similar(hopper(), out, 2) def test_profile_tobytes(self): - from io import BytesIO i = Image.open("Tests/images/rgb.jpg") p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"])) From 444d9d5731d622019f5c1ad51a792dce976e8412 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 1 Apr 2015 10:29:26 +1100 Subject: [PATCH 0095/1037] Changed variable name foo since it has been blacklisted in health --- Tests/test_image_transform.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_image_transform.py b/Tests/test_image_transform.py index 4306e9b43..26ff8822c 100644 --- a/Tests/test_image_transform.py +++ b/Tests/test_image_transform.py @@ -128,13 +128,13 @@ class TestImageTransform(PillowTestCase): # Running by default, but I'd totally understand not doing it in # the future - foo = [ + pattern = [ Image.new('RGBA', (1024, 1024), (a, a, a, a)) for a in range(1, 65) ] # Yeah. Watch some JIT optimize this out. - foo = None + pattern = None self.test_mesh() From d3cab99d318ccc7ea4effabaf49b583b093ef843 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 1 Apr 2015 10:33:44 +1100 Subject: [PATCH 0096/1037] Removed unused import in pilconvert script --- Scripts/pilconvert.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Scripts/pilconvert.py b/Scripts/pilconvert.py index 934167351..0341994c9 100644 --- a/Scripts/pilconvert.py +++ b/Scripts/pilconvert.py @@ -15,7 +15,6 @@ from __future__ import print_function -import site import getopt, string, sys from PIL import Image From 5114425f6bfb96cadea2cb0dc237ee637eaa9c69 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 31 Mar 2015 20:13:37 -0400 Subject: [PATCH 0097/1037] Update CHANGES [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 5fea75209..263ae86b6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.8.0 (unreleased) ------------------ +- Various code health fixes + [radarhere] + - Add support for HTTP response objects to Image.open() [mfitzp] From 120982574a0f9a255fbe18f78d4017b5dee8d95c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 1 Apr 2015 11:11:49 +1100 Subject: [PATCH 0098/1037] Moved __future__ import to the top of pilprint script --- Scripts/pilprint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/pilprint.py b/Scripts/pilprint.py index efa2b9862..889944de7 100644 --- a/Scripts/pilprint.py +++ b/Scripts/pilprint.py @@ -11,8 +11,8 @@ # 0.3 2003-05-06 fl Fixed a typo or two. # -import getopt, os, sys from __future__ import print_function +import getopt, os, sys VERSION = "pilprint 0.3/2003-05-05" From 440022e59f41a47318986d9dd6a3470a059d7033 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 1 Apr 2015 11:14:27 +1100 Subject: [PATCH 0099/1037] Added missing import in FpxImagePlugin --- PIL/FpxImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/FpxImagePlugin.py b/PIL/FpxImagePlugin.py index e435cb447..e92ee89eb 100644 --- a/PIL/FpxImagePlugin.py +++ b/PIL/FpxImagePlugin.py @@ -19,7 +19,7 @@ __version__ = "0.1" -from PIL import Image, ImageFile +from PIL import Image, ImageFile, OleFileIO from PIL.OleFileIO import i8, i32, MAGIC From 0d64171481c29ec5355ccc29032ff1477815bf98 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 31 Mar 2015 20:22:09 -0400 Subject: [PATCH 0100/1037] Configure setuptools to run nosetests, fixes #729 http://nose.readthedocs.org/en/latest/setuptools_integration.html --- CHANGES.rst | 3 +++ setup.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 263ae86b6..cafc92077 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.8.0 (unreleased) ------------------ +- Configure setuptools to run nosetests, fixes #729 + [aclark] + - Various code health fixes [radarhere] diff --git a/setup.py b/setup.py index 558c4aed1..f0d2e9399 100644 --- a/setup.py +++ b/setup.py @@ -754,7 +754,7 @@ setup( include_package_data=True, packages=find_packages(), scripts=glob.glob("Scripts/pil*.py"), - test_suite='PIL.tests', + test_suite='nose.collector', keywords=["Imaging", ], license='Standard PIL License', zip_safe= not debug_build(), From b1517e717624f9638fd8a0f22e3ad68d4862b4df Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 31 Mar 2015 21:14:32 -0400 Subject: [PATCH 0101/1037] Update CHANGES [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index cafc92077..40e727870 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.8.0 (unreleased) ------------------ +- Fix: Cannot identify EPS images, fixes #1104 + [hugovk] + - Configure setuptools to run nosetests, fixes #729 [aclark] From 1f6f358132a4569694cddccc1c4efb3a5aeac5aa Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 31 Mar 2015 21:36:52 -0400 Subject: [PATCH 0102/1037] Update CHANGES [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 40e727870..5048271ff 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.8.0 (unreleased) ------------------ +- Re-enable test image caching + [hugovk, homm] + - Fix: Cannot identify EPS images, fixes #1104 [hugovk] From 6572728275b96194a908a1a03d0595eee5f995a5 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 31 Mar 2015 21:44:07 -0400 Subject: [PATCH 0103/1037] We're going downtown [ci skip] https://www.youtube.com/watch?v=QCz9CdOCh7w (Set release date for 2.8.0 happening in < 8 hours) --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 5048271ff..0ce3a43b0 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,7 +1,7 @@ Changelog (Pillow) ================== -2.8.0 (unreleased) +2.8.0 (2015-04-01) ------------------ - Re-enable test image caching From 1f5aeea463f1c5e436f2757ba53829a8c2911d81 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 1 Apr 2015 12:21:58 +1100 Subject: [PATCH 0104/1037] Fixed incorrect import in FpxImagePlugin --- PIL/FpxImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PIL/FpxImagePlugin.py b/PIL/FpxImagePlugin.py index e92ee89eb..ed0c20c4e 100644 --- a/PIL/FpxImagePlugin.py +++ b/PIL/FpxImagePlugin.py @@ -19,8 +19,8 @@ __version__ = "0.1" -from PIL import Image, ImageFile, OleFileIO -from PIL.OleFileIO import i8, i32, MAGIC +from PIL import Image, ImageFile +from PIL.OleFileIO import i8, i32, MAGIC, OleFileIO # we map from colour field tuples to (mode, rawmode) descriptors From 1199665033c93755653be3ebbe5e9467aa18f642 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 1 Apr 2015 12:29:20 +1100 Subject: [PATCH 0105/1037] Removed duplicate import from OleFileIO --- PIL/OleFileIO.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index c1cc5d5b6..6a6b644bb 100755 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -1983,8 +1983,6 @@ class OleFileIO: if __name__ == "__main__": - import sys - # [PL] display quick usage info if launched from command-line if len(sys.argv) <= 1: print(__doc__) From 68d9860921ce7b9b4d1204cd62a97b31af11c297 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 1 Apr 2015 12:42:39 +1100 Subject: [PATCH 0106/1037] Changed list comprehension variable name to avoid redefinition --- PIL/BdfFontFile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PIL/BdfFontFile.py b/PIL/BdfFontFile.py index c52812450..0c1614e0f 100644 --- a/PIL/BdfFontFile.py +++ b/PIL/BdfFontFile.py @@ -69,8 +69,8 @@ def bdf_char(f): bitmap.append(s[:-1]) bitmap = b"".join(bitmap) - [x, y, l, d] = [int(s) for s in props["BBX"].split()] - [dx, dy] = [int(s) for s in props["DWIDTH"].split()] + [x, y, l, d] = [int(p) for p in props["BBX"].split()] + [dx, dy] = [int(p) for p in props["DWIDTH"].split()] bbox = (dx, dy), (l, -d-y, x+l, -d), (0, 0, x, y) From 094a2ca59290da4fcd2372d734a09924a9a62dec Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 31 Mar 2015 21:48:45 -0400 Subject: [PATCH 0107/1037] I am aclark4life on GitHub [ci skip] --- CHANGES.rst | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 0ce3a43b0..8a9cadd67 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -11,7 +11,7 @@ Changelog (Pillow) [hugovk] - Configure setuptools to run nosetests, fixes #729 - [aclark] + [aclark4life] - Various code health fixes [radarhere] @@ -152,7 +152,7 @@ Changelog (Pillow) [wiredfool] - Fix manifest to include all test files. - [aclark] + [aclark4life] 2.6.0 (2014-10-01) ------------------ @@ -322,7 +322,7 @@ Changelog (Pillow) [wirefool] - Top level flake8 fixes #741 - [aclark] + [aclark4life] - Remove obsolete Animated Raster Graphics (ARG) support [hugovk] @@ -451,7 +451,7 @@ Changelog (Pillow) [larsmans] - Avoid conflicting _expand functions in PIL & MINGW, fixes #538 - [aclark] + [aclark4life] - Merge from Philippe Lagadec’s OleFileIO_PL fork [vadmium] @@ -866,13 +866,13 @@ Changelog (Pillow) [blueyed] - Package cleanup and additional documentation - [aclark] + [aclark4life] 1.7.4 (2011-07-21) ------------------ - Fix brown bag release - [aclark] + [aclark4life] 1.7.3 (2011-07-20) ------------------ @@ -884,19 +884,19 @@ Changelog (Pillow) ------------------ - Bug fix: Python 2.4 compat - [aclark] + [aclark4life] 1.7.1 (2011-05-31) ------------------ - More multi-arch support - [SteveM, regebro, barry, aclark] + [SteveM, regebro, barry, aclark4life] 1.7.0 (2011-05-27) ------------------ - Add support for multi-arch library directory /usr/lib/x86_64-linux-gnu - [aclark] + [aclark4life] 1.6 (12/01/2010) ---------------- @@ -905,28 +905,28 @@ Changelog (Pillow) [elro] - Doc fixes - [aclark] + [aclark4life] 1.5 (11/28/2010) ---------------- - Module and package fixes - [aclark] + [aclark4life] 1.4 (11/28/2010) ---------------- - Doc fixes - [aclark] + [aclark4life] 1.3 (11/28/2010) ---------------- - Add support for /lib64 and /usr/lib64 library directories on Linux - [aclark] + [aclark4life] - Doc fixes - [aclark] + [aclark4life] 1.2 (08/02/2010) ---------------- @@ -935,23 +935,23 @@ Changelog (Pillow) [jezdez] - Doc fixes - [aclark] + [aclark4life] 1.1 (07/31/2010) ---------------- - Removed setuptools_hg requirement - [aclark] + [aclark4life] - Doc fixes - [aclark] + [aclark4life] 1.0 (07/30/2010) ---------------- - Remove support for ``import Image``, etc. from the standard namespace. ``from PIL import Image`` etc. now required. - Forked PIL based on `Hanno Schlichting's re-packaging `_ - [aclark] + [aclark4life] .. Note:: What follows is the original PIL 1.1.7 CHANGES From c562c557887cbdeca41eff888cd9c03fe53e0ea4 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 31 Mar 2015 22:00:53 -0400 Subject: [PATCH 0108/1037] Camel case nit [ci skip] --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index f0d2e9399..a5f114d88 100644 --- a/setup.py +++ b/setup.py @@ -730,7 +730,7 @@ setup( version=PILLOW_VERSION, description='Python Imaging Library (Fork)', long_description=_read('README.rst').decode('utf-8'), - author='Alex Clark (fork author)', + author='Alex Clark (Fork Author)', author_email='aclark@aclark.net', url='http://python-pillow.github.io/', classifiers=[ From d8c69ef5aa534bd06b1eefcd6c59b8c73c1ffe81 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 31 Mar 2015 22:01:05 -0400 Subject: [PATCH 0109/1037] Fix link; wording nit [ci skip] --- docs/about.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/about.rst b/docs/about.rst index 90da5a609..547d4dac3 100644 --- a/docs/about.rst +++ b/docs/about.rst @@ -17,7 +17,7 @@ The fork authors' goal is to foster active development of PIL through: License ------- -Like PIL itself, Pillow is licensed under the MIT-like `PIL Software License `:: +Like PIL, Pillow is licensed under the MIT-a-like `PIL Software License `_:: Software License From 3cd7f0e6f29fe4547c8f995d0463be5c2f6f67e4 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 1 Apr 2015 15:14:56 +1100 Subject: [PATCH 0110/1037] Removed unused variable in Jpeg2KEncode --- libImaging/Jpeg2KEncode.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libImaging/Jpeg2KEncode.c b/libImaging/Jpeg2KEncode.c index ea4bca2f2..868cfdb41 100644 --- a/libImaging/Jpeg2KEncode.c +++ b/libImaging/Jpeg2KEncode.c @@ -257,7 +257,7 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, opj_image_cmptparm_t image_params[4]; unsigned xsiz, ysiz; unsigned tile_width, tile_height; - unsigned tiles_x, tiles_y, num_tiles; + unsigned tiles_x, tiles_y; unsigned x, y, tile_ndx; unsigned n; j2k_pack_tile_t pack; @@ -471,8 +471,6 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, tiles_y = (im->ysize + (params.image_offset_y0 - params.cp_ty0) + tile_height - 1) / tile_height; - num_tiles = tiles_x * tiles_y; - state->buffer = malloc (tile_width * tile_height * components * prec / 8); tile_ndx = 0; From b75ee4c5b618c36e6fa5c7618405010081a14ea0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 1 Apr 2015 15:26:00 +1100 Subject: [PATCH 0111/1037] Added tests for font paths without extensions and for preferring ttf extensions --- PIL/ImageFont.py | 4 ++-- Tests/test_imagefont.py | 32 +++++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index 92722a7fe..9aa163c68 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -279,8 +279,8 @@ def truetype(font=None, size=10, index=0, encoding="", filename=None): lindirs = '/usr/share' dirs += [os.path.join(lindir, "fonts") for lindir in lindirs.split(":")] elif sys.platform == 'darwin': - dirs += ['/Library/Fonts/', '/System/Library/Fonts/', - os.path.expanduser('~/Library/Fonts/')] + dirs += ['/Library/Fonts', '/System/Library/Fonts', + os.path.expanduser('~/Library/Fonts')] ext = os.path.splitext(ttf_filename)[1] firstFontWithADifferentExtension = None diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index a7d3e5ebb..dff1bce2a 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -233,7 +233,7 @@ try: # Assert self.assert_image_equal(im, target_img) - def _test_fake_loading_font(self, path_to_fake): + def _test_fake_loading_font(self, path_to_fake, fontname): #Make a copy of FreeTypeFont so we can patch the original free_type_font = copy.deepcopy(ImageFont.FreeTypeFont) with SimplePatcher(ImageFont, '_FreeTypeFont', free_type_font): @@ -242,7 +242,7 @@ try: return ImageFont._FreeTypeFont(FONT_PATH, size, index, encoding) return ImageFont._FreeTypeFont(filepath, size, index, encoding) with SimplePatcher(ImageFont, 'FreeTypeFont', loadable_font): - font = ImageFont.truetype('Arial') + font = ImageFont.truetype(fontname) #Make sure it's loaded name = font.getname() self.assertEqual(('FreeMono', 'Regular'), name) @@ -252,29 +252,43 @@ try: def test_find_linux_font(self): #A lot of mocking here - this is more for hitting code and catching #syntax like errors + fontDirectory = '/usr/local/share/fonts' with SimplePatcher(sys, 'platform', 'linux'): patched_env = copy.deepcopy(os.environ) patched_env['XDG_DATA_DIRS'] = '/usr/share/:/usr/local/share/' with SimplePatcher(os, 'environ', patched_env): def fake_walker(path): - if path == '/usr/local/share/fonts': - return [(path, [], ['Arial.ttf'], )] + if path == fontDirectory: + return [(path, [], ['Arial.ttf', 'Single.otf', 'Duplicate.otf', 'Duplicate.ttf'], )] return [(path, [], ['some_random_font.ttf'], )] with SimplePatcher(os, 'walk', fake_walker): - self._test_fake_loading_font('/usr/local/share/fonts/Arial.ttf') + # Test that the font loads both with and without the extension + self._test_fake_loading_font(fontDirectory+'/Arial.ttf', 'Arial.ttf') + self._test_fake_loading_font(fontDirectory+'/Arial.ttf', 'Arial') + + # Test that non-ttf fonts can be found without the extension + self._test_fake_loading_font(fontDirectory+'/Single.otf', 'Single') + + # Test that ttf fonts are preferred if the extension is not specified + self._test_fake_loading_font(fontDirectory+'/Duplicate.ttf', 'Duplicate') @unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or MacOS") def test_find_osx_font(self): #Like the linux test, more cover hitting code rather than testing #correctness. + fontDirectory = '/System/Library/Fonts' with SimplePatcher(sys, 'platform', 'darwin'): - fake_font_path = '/System/Library/Fonts/Arial.ttf' def fake_walker(path): - if path == '/System/Library/Fonts/': - return [(path, [], ['Arial.ttf'], )] + if path == fontDirectory: + return [(path, [], ['Arial.ttf', 'Single.otf', 'Duplicate.otf', 'Duplicate.ttf'], )] return [(path, [], ['some_random_font.ttf'], )] with SimplePatcher(os, 'walk', fake_walker): - self._test_fake_loading_font(fake_font_path) + self._test_fake_loading_font(fontDirectory+'/Arial.ttf', 'Arial.ttf') + self._test_fake_loading_font(fontDirectory+'/Arial.ttf', 'Arial') + + self._test_fake_loading_font(fontDirectory+'/Single.otf', 'Single') + + self._test_fake_loading_font(fontDirectory+'/Duplicate.ttf', 'Duplicate') except ImportError: From 2834b789820f419f3cd214956158372735fe9723 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 1 Apr 2015 17:22:44 +1100 Subject: [PATCH 0112/1037] Removed or commented unused variables from OleFileIO --- PIL/OleFileIO.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index 6a6b644bb..ebdde80ca 100755 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -1243,7 +1243,7 @@ class OleFileIO: debug( "Number of sectors in the file: %d" % self.nb_sect ) # file clsid (probably never used, so we don't store it) - clsid = _clsid(header[8:24]) + #clsid = _clsid(header[8:24]) self.sectorsize = self.SectorSize #1 << i16(header, 30) self.minisectorsize = self.MiniSectorSize #1 << i16(header, 32) self.minisectorcutoff = self.MiniSectorCutoff # i32(header, 56) @@ -1561,7 +1561,7 @@ class OleFileIO: ## break ## self.direntries.append(_OleDirectoryEntry(entry, sid, self)) # load root entry: - root_entry = self._load_direntry(0) + self._load_direntry(0) # Root entry is the first entry: self.root = self.direntries[0] # read and build all storage trees, starting from the root: @@ -1786,7 +1786,7 @@ class OleFileIO: :returns: True if object exist, else False. """ try: - sid = self._find(filename) + self._find(filename) return True except: return False @@ -1842,11 +1842,11 @@ class OleFileIO: try: # header s = fp.read(28) - clsid = _clsid(s[8:24]) + #clsid = _clsid(s[8:24]) # format id s = fp.read(20) - fmtid = _clsid(s[:16]) + #fmtid = _clsid(s[:16]) fp.seek(i32(s, 16)) # get section From bd3f036a5685e970d698d54770e05df37d2830b8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 1 Apr 2015 17:48:26 +1100 Subject: [PATCH 0113/1037] Fixed many style problems in OleFileIO resulting from indentation --- PIL/OleFileIO.py | 158 +++++++++++++++++++++++------------------------ 1 file changed, 79 insertions(+), 79 deletions(-) diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index ebdde80ca..a08ae0ee3 100755 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -1999,89 +1999,89 @@ Options: check_streams = False for filename in sys.argv[1:]: -## try: - # OPTIONS: - if filename == '-d': - # option to switch debug mode on: - set_debug_mode(True) - continue - if filename == '-c': - # option to switch check streams mode on: - check_streams = True - continue + #try: + # OPTIONS: + if filename == '-d': + # option to switch debug mode on: + set_debug_mode(True) + continue + if filename == '-c': + # option to switch check streams mode on: + check_streams = True + continue - ole = OleFileIO(filename)#, raise_defects=DEFECT_INCORRECT) - print("-" * 68) - print(filename) - print("-" * 68) - ole.dumpdirectory() + ole = OleFileIO(filename)#, raise_defects=DEFECT_INCORRECT) + print("-" * 68) + print(filename) + print("-" * 68) + ole.dumpdirectory() + for streamname in ole.listdir(): + if streamname[-1][0] == "\005": + print(streamname, ": properties") + props = ole.getproperties(streamname, convert_time=True) + props = sorted(props.items()) + for k, v in props: + #[PL]: avoid to display too large or binary values: + if isinstance(v, (basestring, bytes)): + if len(v) > 50: + v = v[:50] + if isinstance(v, bytes): + # quick and dirty binary check: + for c in (1,2,3,4,5,6,7,11,12,14,15,16,17,18,19,20, + 21,22,23,24,25,26,27,28,29,30,31): + if c in bytearray(v): + v = '(binary data)' + break + print(" ", k, v) + + if check_streams: + # Read all streams to check if there are errors: + print('\nChecking streams...') for streamname in ole.listdir(): - if streamname[-1][0] == "\005": - print(streamname, ": properties") - props = ole.getproperties(streamname, convert_time=True) - props = sorted(props.items()) - for k, v in props: - #[PL]: avoid to display too large or binary values: - if isinstance(v, (basestring, bytes)): - if len(v) > 50: - v = v[:50] - if isinstance(v, bytes): - # quick and dirty binary check: - for c in (1,2,3,4,5,6,7,11,12,14,15,16,17,18,19,20, - 21,22,23,24,25,26,27,28,29,30,31): - if c in bytearray(v): - v = '(binary data)' - break - print(" ", k, v) - - if check_streams: - # Read all streams to check if there are errors: - print('\nChecking streams...') - for streamname in ole.listdir(): - # print name using repr() to convert binary chars to \xNN: - print('-', repr('/'.join(streamname)),'-', end=' ') - st_type = ole.get_type(streamname) - if st_type == STGTY_STREAM: - print('size %d' % ole.get_size(streamname)) - # just try to read stream in memory: - ole.openstream(streamname) - else: - print('NOT a stream : type=%d' % st_type) - print() - -## for streamname in ole.listdir(): -## # print name using repr() to convert binary chars to \xNN: -## print('-', repr('/'.join(streamname)),'-', end=' ') -## print(ole.getmtime(streamname)) -## print() - - print('Modification/Creation times of all directory entries:') - for entry in ole.direntries: - if entry is not None: - print('- %s: mtime=%s ctime=%s' % (entry.name, - entry.getmtime(), entry.getctime())) + # print name using repr() to convert binary chars to \xNN: + print('-', repr('/'.join(streamname)),'-', end=' ') + st_type = ole.get_type(streamname) + if st_type == STGTY_STREAM: + print('size %d' % ole.get_size(streamname)) + # just try to read stream in memory: + ole.openstream(streamname) + else: + print('NOT a stream : type=%d' % st_type) print() - # parse and display metadata: - meta = ole.get_metadata() - meta.dump() - print() - #[PL] Test a few new methods: - root = ole.get_rootentry_name() - print('Root entry name: "%s"' % root) - if ole.exists('worddocument'): - print("This is a Word document.") - print("type of stream 'WordDocument':", ole.get_type('worddocument')) - print("size :", ole.get_size('worddocument')) - if ole.exists('macros/vba'): - print("This document may contain VBA macros.") +## for streamname in ole.listdir(): +## # print name using repr() to convert binary chars to \xNN: +## print('-', repr('/'.join(streamname)),'-', end=' ') +## print(ole.getmtime(streamname)) +## print() - # print parsing issues: - print('\nNon-fatal issues raised during parsing:') - if ole.parsing_issues: - for exctype, msg in ole.parsing_issues: - print('- %s: %s' % (exctype.__name__, msg)) - else: - print('None') + print('Modification/Creation times of all directory entries:') + for entry in ole.direntries: + if entry is not None: + print('- %s: mtime=%s ctime=%s' % (entry.name, + entry.getmtime(), entry.getctime())) + print() + + # parse and display metadata: + meta = ole.get_metadata() + meta.dump() + print() + #[PL] Test a few new methods: + root = ole.get_rootentry_name() + print('Root entry name: "%s"' % root) + if ole.exists('worddocument'): + print("This is a Word document.") + print("type of stream 'WordDocument':", ole.get_type('worddocument')) + print("size :", ole.get_size('worddocument')) + if ole.exists('macros/vba'): + print("This document may contain VBA macros.") + + # print parsing issues: + print('\nNon-fatal issues raised during parsing:') + if ole.parsing_issues: + for exctype, msg in ole.parsing_issues: + print('- %s: %s' % (exctype.__name__, msg)) + else: + print('None') ## except IOError as v: ## print("***", "cannot read", file, "-", v) From 68663ad3529611e3a24d39efd43dfe810fcf7561 Mon Sep 17 00:00:00 2001 From: George Davaris Date: Wed, 1 Apr 2015 10:48:28 +0100 Subject: [PATCH 0114/1037] Define t variable in a more appropriate place --- PIL/ImageFile.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index f81ddf2a0..79faff797 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -196,13 +196,15 @@ class ImageFile(Image.Image): except AttributeError: prefix = b"" + # Buffer length read; assign a default value + t = 0 + for d, e, o, a in self.tile: d = Image._getdecoder(self.mode, d, a, self.decoderconfig) seek(o) try: d.setimage(self.im, e) except ValueError: - t = None continue b = prefix t = len(b) From d20038e8455f60f98c68afe2f7642a1c4a2cb481 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Wed, 1 Apr 2015 06:10:17 -0400 Subject: [PATCH 0115/1037] You don't need to register every release [ci skip] Only once per project --- RELEASING.md | 1 - 1 file changed, 1 deletion(-) diff --git a/RELEASING.md b/RELEASING.md index 1bd8a28ae..5e6d4649e 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -47,7 +47,6 @@ git push origin --tags * [ ] Ping cgohlke for Windows binaries * [ ] From a clean source directory with no extra temp files: ``` -python setup.py register python setup.py sdist --format=zip upload python setup.py sdist upload ``` From 54956f57c60be90c847c80653bf7d605cbd9b1b7 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Wed, 1 Apr 2015 06:15:10 -0400 Subject: [PATCH 0116/1037] No separate upload required [ci skip] --- RELEASING.md | 1 - 1 file changed, 1 deletion(-) diff --git a/RELEASING.md b/RELEASING.md index 5e6d4649e..2c3422ef9 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -48,7 +48,6 @@ git push origin --tags * [ ] From a clean source directory with no extra temp files: ``` python setup.py sdist --format=zip upload -python setup.py sdist upload ``` (Debian requests a tarball, everyone else would just prefer that we choose one and stick to it. So both it is) * [ ] Push a commit to https://github.com/python-pillow/pillow-wheels to build OSX versions (UNDONE latest tag or specific release???) From d58e6f873ccb243a847c1b4a3b9c862a9d322f67 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Wed, 1 Apr 2015 06:16:36 -0400 Subject: [PATCH 0117/1037] Add sdist helpers to makefile [ci skip] Document in RELEASING --- Makefile | 15 ++++++++++++++- RELEASING.md | 4 ++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 98e0c647a..b4c6b044d 100644 --- a/Makefile +++ b/Makefile @@ -58,4 +58,17 @@ docs: $(MAKE) -C docs html docserver: - cd docs/_build/html && python -mSimpleHTTPServer 2> /dev/null& \ No newline at end of file + cd docs/_build/html && python -mSimpleHTTPServer 2> /dev/null& + +# Test sdist upload via test.pythonpackages.com, no creds required +# .pypirc: +# [test] +# username: +# password: +# repository = http://test.pythonpackages.com +sdisttest: + python setup.py sdist --format=zip upload -r test +sdistup: + python setup.py sdist --format=zip upload +sdist: + python setup.py sdist --format=zip diff --git a/RELEASING.md b/RELEASING.md index 2c3422ef9..065d951d8 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -49,6 +49,10 @@ git push origin --tags ``` python setup.py sdist --format=zip upload ``` +Or +``` +make sdistup +``` (Debian requests a tarball, everyone else would just prefer that we choose one and stick to it. So both it is) * [ ] Push a commit to https://github.com/python-pillow/pillow-wheels to build OSX versions (UNDONE latest tag or specific release???) * [ ] Retrieve the OS X Wheels from Rackspace files, upload to PyPi (Twine?) From 6dabce59c730a82dd9e950f8c1392c1689561fea Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Wed, 1 Apr 2015 06:19:12 -0400 Subject: [PATCH 0118/1037] Source dists aren't binary [ci skip] --- RELEASING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 065d951d8..18564e3c1 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -20,7 +20,7 @@ Released as required for security or installation fixes. * [ ] Run pre-release check via `make pre` * [ ] Push to release branch in personal repo. Let Travis run cleanly. * [ ] Tag and push to release branch in python-pillow repo. -* [ ] Upload binaries. +* [ ] Upload source and binaries. ## Embargoed Release @@ -42,7 +42,7 @@ git push origin --tags * [ ] Upload binaries -## Binary Upload Process +## Upload Process * [ ] Ping cgohlke for Windows binaries * [ ] From a clean source directory with no extra temp files: From da4469d392b702487c87ab5c3baacaed3e435354 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Wed, 1 Apr 2015 06:50:57 -0400 Subject: [PATCH 0119/1037] Add link to issues [ci skip] --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index ce1c707c3..7c2563387 100644 --- a/README.rst +++ b/README.rst @@ -41,3 +41,5 @@ More Information - `Guides `_ - `Installation `_ - `Reference `_ + +- `Issues Date: Wed, 1 Apr 2015 06:52:12 -0400 Subject: [PATCH 0120/1037] Fix syntax [ci skip] --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 7c2563387..5a7105455 100644 --- a/README.rst +++ b/README.rst @@ -42,4 +42,4 @@ More Information - `Installation `_ - `Reference `_ -- `Issues `_ From 20fd366eb957debf738dc7c8cfdaf88be8a8c6eb Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 1 Apr 2015 14:18:01 +0300 Subject: [PATCH 0121/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 8a9cadd67..9bf7f0889 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.8.0 (2015-04-01) ------------------ +- Fix UnboundLocalError in ImageFile #1131 + [davarisg] + - Re-enable test image caching [hugovk, homm] From 2879819ce7a4f13d46eb3af1b7f343d14bc85746 Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 1 Apr 2015 16:47:01 +0300 Subject: [PATCH 0122/1037] Style/health fixes --- PIL/GifImagePlugin.py | 107 +++++++++++++++++++-------------------- PIL/ImageFont.py | 25 ++++----- PIL/Jpeg2KImagePlugin.py | 9 ++-- Tests/test_imagefont.py | 88 +++++++++++++++++++------------- Tests/test_pyroma.py | 8 +-- 5 files changed, 128 insertions(+), 109 deletions(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 55aece38c..8db42e8e8 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -24,13 +24,11 @@ # See the README file for information on usage and redistribution. # +from PIL import Image, ImageFile, ImagePalette, _binary __version__ = "0.9" -from PIL import Image, ImageFile, ImagePalette, _binary - - # -------------------------------------------------------------------- # Helpers @@ -271,7 +269,7 @@ def _save(im, fp, filename): pass # write uncompressed file if im.mode in RAWMODE: - imOut = im + im_out = im else: # convert on the fly (EXPERIMENTAL -- I'm not sure PIL # should automatically convert images on save...) @@ -279,9 +277,9 @@ def _save(im, fp, filename): palette_size = 256 if im.palette: palette_size = len(im.palette.getdata()[1]) // 3 - imOut = im.convert("P", palette=1, colors=palette_size) + im_out = im.convert("P", palette=1, colors=palette_size) else: - imOut = im.convert("L") + im_out = im.convert("L") # header try: @@ -290,7 +288,7 @@ def _save(im, fp, filename): palette = None im.encoderinfo["optimize"] = im.encoderinfo.get("optimize", True) - header, usedPaletteColors = getheader(imOut, palette, im.encoderinfo) + header, used_palette_colors = getheader(im_out, palette, im.encoderinfo) for s in header: fp.write(s) @@ -315,26 +313,26 @@ def _save(im, fp, filename): else: transparency = int(transparency) # optimize the block away if transparent color is not used - transparentColorExists = True + transparent_color_exists = True # adjust the transparency index after optimize - if usedPaletteColors is not None and len(usedPaletteColors) < 256: - for i in range(len(usedPaletteColors)): - if usedPaletteColors[i] == transparency: + if used_palette_colors is not None and len(used_palette_colors) < 256: + for i in range(len(used_palette_colors)): + if used_palette_colors[i] == transparency: transparency = i - transparentColorExists = True + transparent_color_exists = True break else: - transparentColorExists = False + transparent_color_exists = False # transparency extension block - if transparentColorExists: + if transparent_color_exists: fp.write(b"!" + o8(249) + # extension intro o8(4) + # length o8(1) + # transparency info present o16(0) + # duration - o8(transparency) # transparency index - + o8(0)) + o8(transparency) + # transparency index + o8(0)) # local image header fp.write(b"," + @@ -344,9 +342,9 @@ def _save(im, fp, filename): o8(flags) + # flags o8(8)) # bits - imOut.encoderconfig = (8, interlace) - ImageFile._save(imOut, fp, [("gif", (0, 0)+im.size, 0, - RAWMODE[imOut.mode])]) + im_out.encoderconfig = (8, interlace) + ImageFile._save(im_out, fp, [("gif", (0, 0)+im.size, 0, + RAWMODE[im_out.mode])]) fp.write(b"\0") # end of image data @@ -422,74 +420,75 @@ def getheader(im, palette=None, info=None): if im.mode == "P": if palette and isinstance(palette, bytes): - sourcePalette = palette[:768] + source_palette = palette[:768] else: - sourcePalette = im.im.getpalette("RGB")[:768] + source_palette = im.im.getpalette("RGB")[:768] else: # L-mode if palette and isinstance(palette, bytes): - sourcePalette = palette[:768] + source_palette = palette[:768] else: - sourcePalette = bytearray([i//3 for i in range(768)]) + source_palette = bytearray([i//3 for i in range(768)]) - usedPaletteColors = paletteBytes = None + used_palette_colors = palette_bytes = None if im.mode in ("P", "L") and optimize: - usedPaletteColors = [] + used_palette_colors = [] # check which colors are used i = 0 for count in im.histogram(): if count: - usedPaletteColors.append(i) + used_palette_colors.append(i) i += 1 # create the new palette if not every color is used - if len(usedPaletteColors) < 256: - paletteBytes = b"" - newPositions = {} + if len(used_palette_colors) < 256: + palette_bytes = b"" + new_positions = {} i = 0 # pick only the used colors from the palette - for oldPosition in usedPaletteColors: - paletteBytes += sourcePalette[oldPosition*3:oldPosition*3+3] - newPositions[oldPosition] = i + for oldPosition in used_palette_colors: + palette_bytes += source_palette[oldPosition*3:oldPosition*3+3] + new_positions[oldPosition] = i i += 1 # replace the palette color id of all pixel with the new id - imageBytes = bytearray(im.tobytes()) - for i in range(len(imageBytes)): - imageBytes[i] = newPositions[imageBytes[i]] - im.frombytes(bytes(imageBytes)) - newPaletteBytes = (paletteBytes + - (768 - len(paletteBytes)) * b'\x00') - im.putpalette(newPaletteBytes) - im.palette = ImagePalette.ImagePalette("RGB", palette=paletteBytes, - size=len(paletteBytes)) + image_bytes = bytearray(im.tobytes()) + for i in range(len(image_bytes)): + image_bytes[i] = new_positions[image_bytes[i]] + im.frombytes(bytes(image_bytes)) + new_palette_bytes = (palette_bytes + + (768 - len(palette_bytes)) * b'\x00') + im.putpalette(new_palette_bytes) + im.palette = ImagePalette.ImagePalette("RGB", + palette=palette_bytes, + size=len(palette_bytes)) - if not paletteBytes: - paletteBytes = sourcePalette + if not palette_bytes: + palette_bytes = source_palette # Logical Screen Descriptor # calculate the palette size for the header import math - colorTableSize = int(math.ceil(math.log(len(paletteBytes)//3, 2)))-1 - if colorTableSize < 0: - colorTableSize = 0 + color_table_size = int(math.ceil(math.log(len(palette_bytes)//3, 2)))-1 + if color_table_size < 0: + color_table_size = 0 # size of global color table + global color table flag - header.append(o8(colorTableSize + 128)) + header.append(o8(color_table_size + 128)) # background + reserved/aspect header.append(o8(0) + o8(0)) # end of Logical Screen Descriptor # add the missing amount of bytes # the palette has to be 2< 0: - paletteBytes += o8(0) * 3 * actualTargetSizeDiff + actual_target_size_diff = (2 << color_table_size) - len(palette_bytes)//3 + if actual_target_size_diff > 0: + palette_bytes += o8(0) * 3 * actual_target_size_diff # Header + Logical Screen Descriptor + Global Color Table - header.append(paletteBytes) - return header, usedPaletteColors + header.append(palette_bytes) + return header, used_palette_colors def getdata(im, offset=(0, 0), **params): @@ -497,7 +496,7 @@ def getdata(im, offset=(0, 0), **params): The first string is a local image header, the rest contains encoded image data.""" - class collector: + class Collector: data = [] def write(self, data): @@ -505,7 +504,7 @@ def getdata(im, offset=(0, 0), **params): im.load() # make sure raster data is available - fp = collector() + fp = Collector() try: im.encoderinfo = params diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index 9aa163c68..1e5a27f7b 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -262,7 +262,7 @@ def truetype(font=None, size=10, index=0, encoding="", filename=None): return FreeTypeFont(font, size, index, encoding) except IOError: ttf_filename = os.path.basename(font) - + dirs = [] if sys.platform == "win32": # check the windows font repository @@ -281,11 +281,11 @@ def truetype(font=None, size=10, index=0, encoding="", filename=None): elif sys.platform == 'darwin': dirs += ['/Library/Fonts', '/System/Library/Fonts', os.path.expanduser('~/Library/Fonts')] - + ext = os.path.splitext(ttf_filename)[1] - firstFontWithADifferentExtension = None - for dir in dirs: - for walkroot, walkdir, walkfilenames in os.walk(dir): + first_font_with_a_different_extension = None + for directory in dirs: + for walkroot, walkdir, walkfilenames in os.walk(directory): for walkfilename in walkfilenames: if ext and walkfilename == ttf_filename: fontpath = os.path.join(walkroot, walkfilename) @@ -294,10 +294,11 @@ def truetype(font=None, size=10, index=0, encoding="", filename=None): fontpath = os.path.join(walkroot, walkfilename) if os.path.splitext(fontpath)[1] == '.ttf': return FreeTypeFont(fontpath, size, index, encoding) - if not ext and firstFontWithADifferentExtension == None: - firstFontWithADifferentExtension = fontpath - if firstFontWithADifferentExtension: - return FreeTypeFont(firstFontWithADifferentExtension, size, index, encoding) + if not ext and first_font_with_a_different_extension is None: + first_font_with_a_different_extension = fontpath + if first_font_with_a_different_extension: + return FreeTypeFont(first_font_with_a_different_extension, size, + index, encoding) raise @@ -310,15 +311,15 @@ def load_path(filename): :return: A font object. :exception IOError: If the file could not be read. """ - for dir in sys.path: - if isDirectory(dir): + for directory in sys.path: + if isDirectory(directory): if not isinstance(filename, str): if bytes is str: filename = filename.encode("utf-8") else: filename = filename.decode("utf-8") try: - return load(os.path.join(dir, filename)) + return load(os.path.join(directory, filename)) except IOError: pass raise IOError("cannot find font file") diff --git a/PIL/Jpeg2KImagePlugin.py b/PIL/Jpeg2KImagePlugin.py index 446699fdb..ed3e1530a 100644 --- a/PIL/Jpeg2KImagePlugin.py +++ b/PIL/Jpeg2KImagePlugin.py @@ -12,14 +12,13 @@ # # See the README file for information on usage and redistribution. # - -__version__ = "0.1" - from PIL import Image, ImageFile import struct import os import io +__version__ = "0.1" + def _parse_codestream(fp): """Parse the JPEG 2000 codestream to extract the size and component @@ -208,8 +207,8 @@ class Jpeg2KImageFile(ImageFile.ImageFile): def _accept(prefix): - return (prefix[:4] == b'\xff\x4f\xff\x51' - or prefix[:12] == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a') + return (prefix[:4] == b'\xff\x4f\xff\x51' or + prefix[:12] == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a') # ------------------------------------------------------------ diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index dff1bce2a..627b28c67 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -22,6 +22,7 @@ try: self._saved = None self._is_saved = False self._value = value + def __enter__(self): # Patch the attr on the object if hasattr(self._parent_obj, self._attr_name): @@ -31,6 +32,7 @@ try: else: setattr(self._parent_obj, self._attr_name, self._value) self._is_saved = False + def __exit__(self, type, value, traceback): # Restore the original value if self._is_saved: @@ -48,14 +50,14 @@ try: ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) self.assertEqual(ttf.path, FONT_PATH) self.assertEqual(ttf.size, FONT_SIZE) - + ttf_copy = ttf.font_variant() self.assertEqual(ttf_copy.path, FONT_PATH) self.assertEqual(ttf_copy.size, FONT_SIZE) - + ttf_copy = ttf.font_variant(size=FONT_SIZE+1) self.assertEqual(ttf_copy.size, FONT_SIZE+1) - + second_font_path = "Tests/fonts/DejaVuSans.ttf" ttf_copy = ttf.font_variant(font=second_font_path) self.assertEqual(ttf_copy.path, second_font_path) @@ -234,61 +236,77 @@ try: self.assert_image_equal(im, target_img) def _test_fake_loading_font(self, path_to_fake, fontname): - #Make a copy of FreeTypeFont so we can patch the original + # Make a copy of FreeTypeFont so we can patch the original free_type_font = copy.deepcopy(ImageFont.FreeTypeFont) with SimplePatcher(ImageFont, '_FreeTypeFont', free_type_font): def loadable_font(filepath, size, index, encoding): if filepath == path_to_fake: - return ImageFont._FreeTypeFont(FONT_PATH, size, index, encoding) - return ImageFont._FreeTypeFont(filepath, size, index, encoding) + return ImageFont._FreeTypeFont(FONT_PATH, size, index, + encoding) + return ImageFont._FreeTypeFont(filepath, size, index, + encoding) with SimplePatcher(ImageFont, 'FreeTypeFont', loadable_font): font = ImageFont.truetype(fontname) - #Make sure it's loaded + # Make sure it's loaded name = font.getname() self.assertEqual(('FreeMono', 'Regular'), name) - - @unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or MacOS") + @unittest.skipIf(sys.platform.startswith('win32'), + "requires Unix or MacOS") def test_find_linux_font(self): - #A lot of mocking here - this is more for hitting code and catching - #syntax like errors - fontDirectory = '/usr/local/share/fonts' + # A lot of mocking here - this is more for hitting code and + # catching syntax like errors + font_directory = '/usr/local/share/fonts' with SimplePatcher(sys, 'platform', 'linux'): patched_env = copy.deepcopy(os.environ) patched_env['XDG_DATA_DIRS'] = '/usr/share/:/usr/local/share/' with SimplePatcher(os, 'environ', patched_env): def fake_walker(path): - if path == fontDirectory: - return [(path, [], ['Arial.ttf', 'Single.otf', 'Duplicate.otf', 'Duplicate.ttf'], )] + if path == font_directory: + return [(path, [], [ + 'Arial.ttf', 'Single.otf', 'Duplicate.otf', + 'Duplicate.ttf'], )] return [(path, [], ['some_random_font.ttf'], )] with SimplePatcher(os, 'walk', fake_walker): - # Test that the font loads both with and without the extension - self._test_fake_loading_font(fontDirectory+'/Arial.ttf', 'Arial.ttf') - self._test_fake_loading_font(fontDirectory+'/Arial.ttf', 'Arial') - - # Test that non-ttf fonts can be found without the extension - self._test_fake_loading_font(fontDirectory+'/Single.otf', 'Single') - - # Test that ttf fonts are preferred if the extension is not specified - self._test_fake_loading_font(fontDirectory+'/Duplicate.ttf', 'Duplicate') + # Test that the font loads both with and without the + # extension + self._test_fake_loading_font( + font_directory+'/Arial.ttf', 'Arial.ttf') + self._test_fake_loading_font( + font_directory+'/Arial.ttf', 'Arial') - @unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or MacOS") + # Test that non-ttf fonts can be found without the + # extension + self._test_fake_loading_font( + font_directory+'/Single.otf', 'Single') + + # Test that ttf fonts are preferred if the extension is + # not specified + self._test_fake_loading_font( + font_directory+'/Duplicate.ttf', 'Duplicate') + + @unittest.skipIf(sys.platform.startswith('win32'), + "requires Unix or MacOS") def test_find_osx_font(self): - #Like the linux test, more cover hitting code rather than testing - #correctness. - fontDirectory = '/System/Library/Fonts' + # Like the linux test, more cover hitting code rather than testing + # correctness. + font_directory = '/System/Library/Fonts' with SimplePatcher(sys, 'platform', 'darwin'): def fake_walker(path): - if path == fontDirectory: - return [(path, [], ['Arial.ttf', 'Single.otf', 'Duplicate.otf', 'Duplicate.ttf'], )] + if path == font_directory: + return [(path, [], + ['Arial.ttf', 'Single.otf', + 'Duplicate.otf', 'Duplicate.ttf'], )] return [(path, [], ['some_random_font.ttf'], )] with SimplePatcher(os, 'walk', fake_walker): - self._test_fake_loading_font(fontDirectory+'/Arial.ttf', 'Arial.ttf') - self._test_fake_loading_font(fontDirectory+'/Arial.ttf', 'Arial') - - self._test_fake_loading_font(fontDirectory+'/Single.otf', 'Single') - - self._test_fake_loading_font(fontDirectory+'/Duplicate.ttf', 'Duplicate') + self._test_fake_loading_font( + font_directory+'/Arial.ttf', 'Arial.ttf') + self._test_fake_loading_font( + font_directory+'/Arial.ttf', 'Arial') + self._test_fake_loading_font( + font_directory+'/Single.otf', 'Single') + self._test_fake_loading_font( + font_directory+'/Duplicate.ttf', 'Duplicate') except ImportError: diff --git a/Tests/test_pyroma.py b/Tests/test_pyroma.py index 59aa3810e..0875cc972 100644 --- a/Tests/test_pyroma.py +++ b/Tests/test_pyroma.py @@ -14,6 +14,7 @@ class TestPyroma(PillowTestCase): def setUp(self): try: import pyroma + assert pyroma # Ignore warning except ImportError: self.skipTest("ImportError") @@ -26,9 +27,10 @@ class TestPyroma(PillowTestCase): # Assert if 'rc' in PILLOW_VERSION: - #Pyroma needs to chill about RC versions and not kill all our tests. - self.assertEqual(rating, (9, - ['The packages version number does not comply with PEP-386.'])) + # Pyroma needs to chill about RC versions + # and not kill all our tests. + self.assertEqual(rating, (9, [ + 'The packages version number does not comply with PEP-386.'])) else: # Should have a perfect score From 358ddc3286d6fcf2acbe4a2e8fced94c69b06303 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Wed, 1 Apr 2015 10:22:42 -0400 Subject: [PATCH 0123/1037] Update CHANGES [ci skip] --- CHANGES.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 9bf7f0889..3484b6fda 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -16,8 +16,8 @@ Changelog (Pillow) - Configure setuptools to run nosetests, fixes #729 [aclark4life] -- Various code health fixes - [radarhere] +- Style/health fixes + [radarhere, hugovk] - Add support for HTTP response objects to Image.open() [mfitzp] From 53d1b823518bda72d2c7f04e79f83ed32b776099 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Wed, 1 Apr 2015 10:29:01 -0400 Subject: [PATCH 0124/1037] Add header to orig readme [ci skip] So we can link to it from README --- CHANGES.rst | 3 +++ README.rst | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 3484b6fda..bc5e1af96 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -958,6 +958,9 @@ Changelog (Pillow) .. Note:: What follows is the original PIL 1.1.7 CHANGES +0.2b5 - 1.1.7 (1995-2010) +------------------------- + :: -*- coding: utf-8 -*- diff --git a/README.rst b/README.rst index 5a7105455..fe3f69aa0 100644 --- a/README.rst +++ b/README.rst @@ -35,11 +35,12 @@ More Information - `Changelog `_ - `Contribute `_ + + - `Issues `_ + - `Documentation `_ - `About `_ - `Guides `_ - `Installation `_ - `Reference `_ - -- `Issues `_ From 12ee04a06fc3d2269470105714f77e8bd5f6bd56 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Wed, 1 Apr 2015 10:33:11 -0400 Subject: [PATCH 0125/1037] Include link to pre-fork changelog [ci skip] --- README.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.rst b/README.rst index fe3f69aa0..de4377900 100644 --- a/README.rst +++ b/README.rst @@ -34,6 +34,9 @@ More Information ---------------- - `Changelog `_ + + - `Pre-fork `_ + - `Contribute `_ - `Issues `_ From 071df8d33d4653fed24265dab441f04748a792c9 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Wed, 1 Apr 2015 15:38:18 -0400 Subject: [PATCH 0126/1037] Some Flake 8 fixes --- PIL/BmpImagePlugin.py | 65 +++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index f8a755812..30ca10971 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -57,16 +57,16 @@ def _accept(prefix): return prefix[:2] == b"BM" -#=============================================================================== +# ============================================================================== # Image plugin for the Windows BMP format. -#=============================================================================== +# ============================================================================== class BmpImageFile(ImageFile.ImageFile): """ Image plugin for the Windows Bitmap format (BMP) """ - - #--------------------------------------------------------------- Description + + # -------------------------------------------------------------- Description format_description = "Windows Bitmap" format = "BMP" - #---------------------------------------------------- BMP Compression values + # --------------------------------------------------- BMP Compression values COMPRESSIONS = {'RAW': 0, 'RLE8': 1, 'RLE4': 2, 'BITFIELDS': 3, 'JPEG': 4, 'PNG': 5} RAW, RLE8, RLE4, BITFIELDS, JPEG, PNG = 0, 1, 2, 3, 4, 5 @@ -78,10 +78,10 @@ class BmpImageFile(ImageFile.ImageFile): file_info = dict() file_info['header_size'] = i32(read(4)) # read bmp header size @offset 14 (this is part of the header size) file_info['direction'] = -1 - #---------------------- If requested, read header at a specific position + # --------------------- If requested, read header at a specific position header_data = ImageFile._safe_read(self.fp, file_info['header_size'] - 4) # read the rest of the bmp header, without its size - #---------------------------------------------------- IBM OS/2 Bitmap v1 - #------- This format has different offsets because of width/height types + # --------------------------------------------------- IBM OS/2 Bitmap v1 + # ------ This format has different offsets because of width/height types if file_info['header_size'] == 12: file_info['width'] = i16(header_data[0:2]) file_info['height'] = i16(header_data[2:4]) @@ -89,8 +89,8 @@ class BmpImageFile(ImageFile.ImageFile): file_info['bits'] = i16(header_data[6:8]) file_info['compression'] = self.RAW file_info['palette_padding'] = 3 - #----------------------------------------------- Windows Bitmap v2 to v5 - elif file_info['header_size'] in (40, 64, 108, 124): # v3, OS/2 v2, v4, v5 + # ---------------------------------------------- Windows Bitmap v2 to v5 + elif file_info['header_size'] in (40, 64, 108, 124): # v3, OS/2 v2, v4, v5 if file_info['header_size'] >= 40: # v3 and OS/2 file_info['y_flip'] = i8(header_data[7]) == 0xff file_info['direction'] = 1 if file_info['y_flip'] else -1 @@ -100,7 +100,7 @@ class BmpImageFile(ImageFile.ImageFile): file_info['bits'] = i16(header_data[10:12]) file_info['compression'] = i32(header_data[12:16]) file_info['data_size'] = i32(header_data[16:20]) # byte size of pixel data - file_info['pixels_per_meter'] = (i32(header_data[20:24]), i32(header_data[24:28])) + file_info['pixels_per_meter'] = (i32(header_data[20:24]), i32(header_data[24:28])) file_info['colors'] = i32(header_data[28:32]) file_info['palette_padding'] = 4 self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), file_info['pixels_per_meter'])) @@ -115,27 +115,27 @@ class BmpImageFile(ImageFile.ImageFile): file_info['rgba_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'], file_info['a_mask']) else: raise IOError("Unsupported BMP header type (%d)" % file_info['header_size']) - #------------------- Special case : header is reported 40, which - #----------------------- is shorter than real size for bpp >= 16 + # ------------------ Special case : header is reported 40, which + # ---------------------- is shorter than real size for bpp >= 16 self.size = file_info['width'], file_info['height'] - #--------- If color count was not found in the header, compute from bits + # -------- If color count was not found in the header, compute from bits file_info['colors'] = file_info['colors'] if file_info.get('colors', 0) else (1 << file_info['bits']) - #--------------------------------- Check abnormal values for DOS attacks + # -------------------------------- Check abnormal values for DOS attacks if file_info['width'] * file_info['height'] > 2**31: raise IOError("Unsupported BMP Size: (%dx%d)" % self.size) - #------------------------ Check bit depth for unusual unsupported values + # ----------------------- Check bit depth for unusual unsupported values self.mode, raw_mode = BIT2MODE.get(file_info['bits'], (None, None)) if self.mode is None: raise IOError("Unsupported BMP pixel depth (%d)" % file_info['bits']) - #------------------ Process BMP with Bitfields compression (not palette) + # ----------------- Process BMP with Bitfields compression (not palette) if file_info['compression'] == self.BITFIELDS: SUPPORTED = { - 32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0)], - 24: [(0xff0000, 0xff00, 0xff)], + 32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0)], + 24: [(0xff0000, 0xff00, 0xff)], 16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]} MASK_MODES = { - (32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX", (32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA", (32, (0x0, 0x0, 0x0, 0x0)): "BGRA", - (24, (0xff0000, 0xff00, 0xff)): "BGR", + (32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX", (32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA", (32, (0x0, 0x0, 0x0, 0x0)): "BGRA", + (24, (0xff0000, 0xff00, 0xff)): "BGR", (16, (0xf800, 0x7e0, 0x1f)): "BGR;16", (16, (0x7c00, 0x3e0, 0x1f)): "BGR;15"} if file_info['bits'] in SUPPORTED: if file_info['bits'] == 32 and file_info['rgba_mask'] in SUPPORTED[file_info['bits']]: @@ -152,9 +152,9 @@ class BmpImageFile(ImageFile.ImageFile): raw_mode, self.mode = "BGRA", "RGBA" else: raise IOError("Unsupported BMP compression (%d)" % file_info['compression']) - #----------------- Once the header is processed, process the palette/LUT + # ---------------- Once the header is processed, process the palette/LUT if self.mode == "P": # Paletted for 1, 4 and 8 bit images - #------------------------------------------------------ 1-bit images + # ----------------------------------------------------- 1-bit images if not (0 < file_info['colors'] <= 65536): raise IOError("Unsupported BMP Palette size (%d)" % file_info['colors']) else: @@ -162,23 +162,23 @@ class BmpImageFile(ImageFile.ImageFile): palette = read(padding * file_info['colors']) greyscale = True indices = (0, 255) if file_info['colors'] == 2 else list(range(file_info['colors'])) - #------------------- Check if greyscale and ignore palette if so + # ------------------ Check if greyscale and ignore palette if so for ind, val in enumerate(indices): rgb = palette[ind*padding:ind*padding + 3] if rgb != o8(val) * 3: greyscale = False - #--------- If all colors are grey, white or black, ditch palette + # -------- If all colors are grey, white or black, ditch palette if greyscale: self.mode = "1" if file_info['colors'] == 2 else "L" raw_mode = self.mode else: self.mode = "P" self.palette = ImagePalette.raw("BGRX" if padding == 4 else "BGR", palette) - - #------------------------------ Finally set the tile data for the plugin + + # ----------------------------- Finally set the tile data for the plugin self.info['compression'] = file_info['compression'] self.tile = [('raw', (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']) + (raw_mode, ((file_info['width'] * file_info['bits'] + 31) >> 3) & (~3), file_info['direction']) )] def _open(self): @@ -188,16 +188,15 @@ class BmpImageFile(ImageFile.ImageFile): # choke if the file does not have the required magic bytes if head_data[0:2] != b"BM": raise SyntaxError("Not a BMP file") - # read the start position of the BMP image data (u32) + # read the start position of the BMP image data (u32) offset = i32(head_data[10:14]) # load bitmap information (offset=raster info) self._bitmap(offset=offset) - -#=============================================================================== +# ============================================================================== # Image plugin for the DIB format (BMP alias) -#=============================================================================== +# ============================================================================== class DibImageFile(BmpImageFile): format = "DIB" @@ -206,8 +205,6 @@ class DibImageFile(BmpImageFile): def _open(self): self._bitmap() - - # # -------------------------------------------------------------------- # Write BMP file From d754598f146f868e8cd7d247b3af6cf3f3c8d510 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Wed, 1 Apr 2015 15:39:41 -0400 Subject: [PATCH 0127/1037] Update CHANGES [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index bc5e1af96..88720be67 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.8.0 (2015-04-01) ------------------ +- Fix 32-bit BMP loading (RGBA or RGBX) + [artscoop] + - Fix UnboundLocalError in ImageFile #1131 [davarisg] From 66e86f325fb01a3061ebded1995e614a5e8fe475 Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 2 Apr 2015 08:29:18 +0300 Subject: [PATCH 0128/1037] Catch struct.error on invalid JPEG (#1163) --- PIL/Image.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 58944f891..7cfa72e6f 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -110,6 +110,7 @@ from PIL._util import deferred_error import os import sys import io +import struct # type stuff import collections @@ -601,7 +602,7 @@ class Image: def _repr_png_(self): """ iPython display hook support - + :returns: png version of the image as bytes """ from io import BytesIO @@ -893,12 +894,11 @@ class Image: if isinstance(t, bytes): self.im.putpalettealphas(t) elif isinstance(t, int): - self.im.putpalettealpha(t,0) + self.im.putpalettealpha(t, 0) else: raise ValueError("Transparency for P mode should" + " be bytes or int") - if mode == "P" and palette == ADAPTIVE: im = self.im.quantize(colors) new = self._new(im) @@ -1546,7 +1546,7 @@ class Image: self.load() - size=tuple(size) + size = tuple(size) if self.size == size: return self._new(self.im) @@ -2266,7 +2266,7 @@ def open(fp, mode="r"): im = factory(fp, filename) _decompression_bomb_check(im.size) return im - except (SyntaxError, IndexError, TypeError): + except (SyntaxError, IndexError, TypeError, struct.error): # import traceback # traceback.print_exc() pass @@ -2281,7 +2281,7 @@ def open(fp, mode="r"): im = factory(fp, filename) _decompression_bomb_check(im.size) return im - except (SyntaxError, IndexError, TypeError): + except (SyntaxError, IndexError, TypeError, struct.error): # import traceback # traceback.print_exc() pass From 3f09b8f1715b018e8249337f1432070301c61e18 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Thu, 2 Apr 2015 10:59:03 -0400 Subject: [PATCH 0129/1037] Prep 2.8.1 --- CHANGES.rst | 6 ++++++ PIL/__init__.py | 2 +- _imaging.c | 2 +- setup.py | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 88720be67..88c9a7793 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,12 @@ Changelog (Pillow) ================== +2.8.1 (2015-04-02) +------------------ + +- Bug fix: Catch struct.error on invalid JPEG, fixes #1163 + [wiredfool, hugovk] + 2.8.0 (2015-04-01) ------------------ diff --git a/PIL/__init__.py b/PIL/__init__.py index d1537769e..4dce12d33 100644 --- a/PIL/__init__.py +++ b/PIL/__init__.py @@ -12,7 +12,7 @@ # ;-) VERSION = '1.1.7' # PIL version -PILLOW_VERSION = '2.8.0' # Pillow +PILLOW_VERSION = '2.8.1' # Pillow _plugins = ['BmpImagePlugin', 'BufrStubImagePlugin', diff --git a/_imaging.c b/_imaging.c index c3be9beab..08e003c80 100644 --- a/_imaging.c +++ b/_imaging.c @@ -71,7 +71,7 @@ * See the README file for information on usage and redistribution. */ -#define PILLOW_VERSION "2.8.0" +#define PILLOW_VERSION "2.8.1" #include "Python.h" diff --git a/setup.py b/setup.py index a5f114d88..3887b421d 100644 --- a/setup.py +++ b/setup.py @@ -90,7 +90,7 @@ except (ImportError, OSError): NAME = 'Pillow' -PILLOW_VERSION = '2.8.0' +PILLOW_VERSION = '2.8.1' TCL_ROOT = None JPEG_ROOT = None JPEG2K_ROOT = None From 5cfc34b2874a246a50deda6658e86209b27c5540 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Thu, 2 Apr 2015 14:53:49 -0400 Subject: [PATCH 0130/1037] Begin 2.9.0 development with 2.9.0.dev0 Use PEP440 syntax to identify development branch and keep pyroma happy: https://www.python.org/dev/peps/pep-0440/#developmental-releases. --- PIL/__init__.py | 2 +- _imaging.c | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PIL/__init__.py b/PIL/__init__.py index 4dce12d33..6d51a5dcb 100644 --- a/PIL/__init__.py +++ b/PIL/__init__.py @@ -12,7 +12,7 @@ # ;-) VERSION = '1.1.7' # PIL version -PILLOW_VERSION = '2.8.1' # Pillow +PILLOW_VERSION = '2.9.0.dev0' # Pillow _plugins = ['BmpImagePlugin', 'BufrStubImagePlugin', diff --git a/_imaging.c b/_imaging.c index 08e003c80..786363e24 100644 --- a/_imaging.c +++ b/_imaging.c @@ -71,7 +71,7 @@ * See the README file for information on usage and redistribution. */ -#define PILLOW_VERSION "2.8.1" +#define PILLOW_VERSION "2.9.0.dev0" #include "Python.h" diff --git a/setup.py b/setup.py index 3887b421d..5886de07b 100644 --- a/setup.py +++ b/setup.py @@ -90,7 +90,7 @@ except (ImportError, OSError): NAME = 'Pillow' -PILLOW_VERSION = '2.8.1' +PILLOW_VERSION = '2.9.0.dev0' TCL_ROOT = None JPEG_ROOT = None JPEG2K_ROOT = None From 2db66c6d89a8d29feee2274e03e8540012b4f7b5 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Thu, 2 Apr 2015 14:59:49 -0400 Subject: [PATCH 0131/1037] Update changes [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 88c9a7793..7cda23b6b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,9 @@ Changelog (Pillow) ================== +2.9.0 (Unreleased) +------------------ + 2.8.1 (2015-04-02) ------------------ From 7fcfebdd22ad3769eef05fc688885501ccbba701 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Thu, 2 Apr 2015 15:11:39 -0400 Subject: [PATCH 0132/1037] Update release documentation [ci skip] - Format files that receive version number update for easy cut/paste. - Wording nits - Clarify steps --- RELEASING.md | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 18564e3c1..181f4e077 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -4,23 +4,34 @@ Released quarterly. -* [ ] Get master to the appropriate code release state. [Travis CI](https://travis-ci.org/python-pillow/Pillow) should be running cleanly for all merges to master. -* [ ] Update version in `PIL/__init__.py`, `setup.py`, `_imaging.c`, Update date in `CHANGES.rst`. +* [ ] Get master to appropriate code release state. [Travis CI](https://travis-ci.org/python-pillow/Pillow) should be running cleanly for all merges to master. +* [ ] Update version in: + +``` + PIL/__init__.py setup.py _imaging.c +``` + +* [ ] Update `CHANGES.rst`. * [ ] Run pre-release check via `make pre` * [ ] Tag and push to release branch in python-pillow repo. -* [ ] Upload binaries. +* [ ] Upload source and binary distributions. ## Point Release -Released as required for security or installation fixes. +Released as required for security, installation or critical bug fixes. * [ ] Make necessary changes in master. -* [ ] Cherry pick individual commits. Touch up `CHANGES.rst` to reflect reality. -* [ ] Update version in `PIL/__init__.py`, `setup.py`, `_imaging.c` +* [ ] Cherry pick individual commits. +* [ ] Update version in: + +``` + PIL/__init__.py setup.py _imaging.c +``` +* [ ] Update `CHANGES.rst`. * [ ] Run pre-release check via `make pre` * [ ] Push to release branch in personal repo. Let Travis run cleanly. * [ ] Tag and push to release branch in python-pillow repo. -* [ ] Upload source and binaries. +* [ ] Upload source and binary distributions. ## Embargoed Release @@ -39,12 +50,12 @@ git tag 2.5.3 git push origin 2.5.x git push origin --tags ``` -* [ ] Upload binaries +* [ ] Upload source and binary distributions. ## Upload Process -* [ ] Ping cgohlke for Windows binaries +* [ ] Contact @cgohlke for Windows binaries. * [ ] From a clean source directory with no extra temp files: ``` python setup.py sdist --format=zip upload @@ -58,4 +69,3 @@ make sdistup * [ ] Retrieve the OS X Wheels from Rackspace files, upload to PyPi (Twine?) * [ ] Grab Windows binaries, `twine upload dist/*.[whl|egg]`. Manually upload .exe installers. * [ ] Announce release availability. [Twitter](https://twitter.com/pythonpillow), web. - From 5c233d12c631dcaab90e1032f4941300ca07062b Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Thu, 2 Apr 2015 15:17:47 -0400 Subject: [PATCH 0133/1037] Make sure we upload a tar.gz too --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index b4c6b044d..a729e4088 100644 --- a/Makefile +++ b/Makefile @@ -70,5 +70,6 @@ sdisttest: python setup.py sdist --format=zip upload -r test sdistup: python setup.py sdist --format=zip upload + python setup.py sdist upload sdist: python setup.py sdist --format=zip From dcf399b9a0021a129ed9e1d38c5c7e589f8cacba Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Thu, 2 Apr 2015 22:49:15 +0200 Subject: [PATCH 0134/1037] pcd: minor cleanup; remove draft method It's not supported, and the implementation does nothing. --- PIL/PcdImagePlugin.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/PIL/PcdImagePlugin.py b/PIL/PcdImagePlugin.py index 5ce7aa48c..06db5d628 100644 --- a/PIL/PcdImagePlugin.py +++ b/PIL/PcdImagePlugin.py @@ -52,25 +52,6 @@ class PcdImageFile(ImageFile.ImageFile): self.size = 768, 512 # FIXME: not correct for rotated images! self.tile = [("pcd", (0, 0)+self.size, 96*2048, None)] - def draft(self, mode, size): - - if len(self.tile) != 1: - return - - d, e, o, a = self.tile[0] - - if size: - scale = max(self.size[0] / size[0], self.size[1] / size[1]) - for s, o in [(4, 0*2048), (2, 0*2048), (1, 96*2048)]: - if scale >= s: - break - # e = e[0], e[1], (e[2]-e[0]+s-1)/s+e[0], (e[3]-e[1]+s-1)/s+e[1] - # self.size = ((self.size[0]+s-1)/s, (self.size[1]+s-1)/s) - - self.tile = [(d, e, o, a)] - - return self - # # registry From c2c586dcc1635e3f9d73263c8417e515d6f0bb77 Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 3 Apr 2015 00:28:25 +0300 Subject: [PATCH 0135/1037] This is not so since #1029 --- docs/handbook/image-file-formats.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index a85a917b8..ed0c3ae5b 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -114,8 +114,7 @@ PIL reads JPEG, JFIF, and Adobe JPEG files containing ``L``, ``RGB``, or Using the :py:meth:`~PIL.Image.Image.draft` method, you can speed things up by converting ``RGB`` images to ``L``, and resize images to 1/2, 1/4 or 1/8 of -their original size while loading them. The :py:meth:`~PIL.Image.Image.draft` -method also configures the JPEG decoder to trade some quality for speed. +their original size while loading them. The :py:meth:`~PIL.Image.Image.open` method may set the following :py:attr:`~PIL.Image.Image.info` properties if available: From c0d818153ee108925bf8949b7721707d096f82dd Mon Sep 17 00:00:00 2001 From: John Kirkham Date: Fri, 3 Apr 2015 08:37:44 -0400 Subject: [PATCH 0136/1037] docs/installation.rst: Fix typo. [ci skip] --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index d9027bda6..9bc3d3f0f 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -118,7 +118,7 @@ Build Options Sample Usage:: - $ MAX_CONCURRENCY=1 python setup.py build-ext --enable-[feature] install + $ MAX_CONCURRENCY=1 python setup.py build_ext --enable-[feature] install Linux Installation From 2934b11affad8b7a05e3223e4ec3cf5adefdcfff Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 3 Apr 2015 09:25:58 -0400 Subject: [PATCH 0137/1037] Release guide wording nits [ci skip] --- RELEASING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASING.md b/RELEASING.md index 181f4e077..47b05fd01 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -4,7 +4,8 @@ Released quarterly. -* [ ] Get master to appropriate code release state. [Travis CI](https://travis-ci.org/python-pillow/Pillow) should be running cleanly for all merges to master. +* [ ] Develop and prepare release in ``master`` branch. +* [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in ``master`` branch. * [ ] Update version in: ``` From eacc3c93d6b9fd88a6fc85b98d403b395e8924bd Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 3 Apr 2015 09:30:37 -0400 Subject: [PATCH 0138/1037] Release guide wording [ci skip] --- RELEASING.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 47b05fd01..aae0dce62 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -7,15 +7,23 @@ Released quarterly. * [ ] Develop and prepare release in ``master`` branch. * [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in ``master`` branch. * [ ] Update version in: - ``` PIL/__init__.py setup.py _imaging.c ``` - * [ ] Update `CHANGES.rst`. -* [ ] Run pre-release check via `make pre` -* [ ] Tag and push to release branch in python-pillow repo. -* [ ] Upload source and binary distributions. +* [ ] Run pre-release check via `make pre`. +* [ ] Create branch and tag for release e.g.: +``` + $ git branch 2.9.x + $ git tag 2.9.0 + $ git push --all + $ git push --tags +``` +* [ ] Create and upload source distributions e.g.: +``` + $ make sdistup + +* [ ] Create and upload binary distributions (see below). ## Point Release From 3123db17a5c1fe647cbeff0dd70cad513bed345d Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 3 Apr 2015 09:46:44 -0400 Subject: [PATCH 0139/1037] Fix syntax [ci skip] --- RELEASING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASING.md b/RELEASING.md index aae0dce62..eb463742b 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -22,7 +22,7 @@ Released quarterly. * [ ] Create and upload source distributions e.g.: ``` $ make sdistup - +``` * [ ] Create and upload binary distributions (see below). ## Point Release From 8afc57adb7c2e74fc82f25a5ff33eb178ba0aeba Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 3 Apr 2015 09:48:15 -0400 Subject: [PATCH 0140/1037] Experiment with footnotes [ci skip] I can't seem to find a compiler for markdown that works like GitHub so I'm making a branch to experiment. Also want to discuss some of the releasing practices in a PR. --- RELEASING.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/RELEASING.md b/RELEASING.md index eb463742b..089c6b7a4 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -78,3 +78,9 @@ make sdistup * [ ] Retrieve the OS X Wheels from Rackspace files, upload to PyPi (Twine?) * [ ] Grab Windows binaries, `twine upload dist/*.[whl|egg]`. Manually upload .exe installers. * [ ] Announce release availability. [Twitter](https://twitter.com/pythonpillow), web. + + +Footnotes[^1] have a label[^@#$%] and the footnote's content. + +[^1]: This is a footnote content. +[^@#$%]: A footnote on the label: "@#$%". From 88f2ce13475d1bcbd681703b5f2eacc301c7dc42 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 3 Apr 2015 09:49:41 -0400 Subject: [PATCH 0141/1037] Footnote syntax fail --- RELEASING.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 089c6b7a4..eb463742b 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -78,9 +78,3 @@ make sdistup * [ ] Retrieve the OS X Wheels from Rackspace files, upload to PyPi (Twine?) * [ ] Grab Windows binaries, `twine upload dist/*.[whl|egg]`. Manually upload .exe installers. * [ ] Announce release availability. [Twitter](https://twitter.com/pythonpillow), web. - - -Footnotes[^1] have a label[^@#$%] and the footnote's content. - -[^1]: This is a footnote content. -[^@#$%]: A footnote on the label: "@#$%". From bb5247a8bf4cfebe9f3d2bca05def727a00d01a2 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 3 Apr 2015 09:54:55 -0400 Subject: [PATCH 0142/1037] Add quarterly release date detail --- RELEASING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASING.md b/RELEASING.md index eb463742b..21e810a1e 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -2,7 +2,7 @@ ## Main Release -Released quarterly. +Released quarterly on the first day of January, April, July, October. * [ ] Develop and prepare release in ``master`` branch. * [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in ``master`` branch. From ade0f3b91df191e7f342fcb490b85610f0ef2b2f Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 3 Apr 2015 09:55:43 -0400 Subject: [PATCH 0143/1037] Indent --- RELEASING.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 21e810a1e..bfcccb796 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -54,10 +54,10 @@ Security fixes that need to be pushed to the distros prior to public release. * [ ] Amend any commits with the CVE # * [ ] On release date, tag and push to GitHub. ``` -git checkout 2.5.x -git tag 2.5.3 -git push origin 2.5.x -git push origin --tags + git checkout 2.5.x + git tag 2.5.3 + git push origin 2.5.x + git push origin --tags ``` * [ ] Upload source and binary distributions. @@ -71,7 +71,7 @@ python setup.py sdist --format=zip upload ``` Or ``` -make sdistup + make sdistup ``` (Debian requests a tarball, everyone else would just prefer that we choose one and stick to it. So both it is) * [ ] Push a commit to https://github.com/python-pillow/pillow-wheels to build OSX versions (UNDONE latest tag or specific release???) From 8df74228036820f089a8596db09969cf8e1a80ad Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 3 Apr 2015 10:12:13 -0400 Subject: [PATCH 0144/1037] Wording, add link to PEP 440 https://www.python.org/dev/peps/pep-0440/ --- RELEASING.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index bfcccb796..73a67b9fb 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -6,7 +6,7 @@ Released quarterly on the first day of January, April, July, October. * [ ] Develop and prepare release in ``master`` branch. * [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in ``master`` branch. -* [ ] Update version in: +* [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in: ``` PIL/__init__.py setup.py _imaging.c ``` @@ -27,21 +27,23 @@ Released quarterly on the first day of January, April, July, October. ## Point Release -Released as required for security, installation or critical bug fixes. - -* [ ] Make necessary changes in master. -* [ ] Cherry pick individual commits. -* [ ] Update version in: +Released as needed for security, installation or critical bug fixes. +* [ ] Make necessary changes in ``master`` branch. +* [ ] Cherry pick individual commits from ``master`` branch to release branch e.g. ``2.8.x``. +* [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in: ``` PIL/__init__.py setup.py _imaging.c ``` * [ ] Update `CHANGES.rst`. -* [ ] Run pre-release check via `make pre` +* [ ] Run pre-release check via `make pre`. * [ ] Push to release branch in personal repo. Let Travis run cleanly. * [ ] Tag and push to release branch in python-pillow repo. -* [ ] Upload source and binary distributions. - +* [ ] Create and upload source distributions e.g.: +``` + $ make sdistup +``` +* [ ] Create and upload binary distributions (see below). ## Embargoed Release Security fixes that need to be pushed to the distros prior to public release. @@ -62,7 +64,7 @@ Security fixes that need to be pushed to the distros prior to public release. * [ ] Upload source and binary distributions. -## Upload Process +## Binary Distributions * [ ] Contact @cgohlke for Windows binaries. * [ ] From a clean source directory with no extra temp files: From 16fcf9985f6baf135b6b89bbf75e28ccceb37755 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 3 Apr 2015 10:13:55 -0400 Subject: [PATCH 0145/1037] Is this an anchor? --- RELEASING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 73a67b9fb..52bf3df81 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -23,7 +23,7 @@ Released quarterly on the first day of January, April, July, October. ``` $ make sdistup ``` -* [ ] Create and upload binary distributions (see below). +* [ ] Create and upload [binary distributions](#binary-distributions) ## Point Release @@ -43,7 +43,7 @@ Released as needed for security, installation or critical bug fixes. ``` $ make sdistup ``` -* [ ] Create and upload binary distributions (see below). +* [ ] Create and upload [binary distributions](#binary-distributions) ## Embargoed Release Security fixes that need to be pushed to the distros prior to public release. From 95047fe206808a106301890764e4a980d01ea7c1 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 3 Apr 2015 10:19:08 -0400 Subject: [PATCH 0146/1037] Recommend org branches We have Travis-CI for org branches so why personal branch? --- RELEASING.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 52bf3df81..e994c6922 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -30,20 +30,25 @@ Released quarterly on the first day of January, April, July, October. Released as needed for security, installation or critical bug fixes. * [ ] Make necessary changes in ``master`` branch. -* [ ] Cherry pick individual commits from ``master`` branch to release branch e.g. ``2.8.x``. +* [ ] Update `CHANGES.rst`. +* [ ] Cherry pick individual commits from ``master`` branch to release branch e.g. ``2.9.x``. +* [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in release branch e.g. ``2.9.x``. * [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in: ``` PIL/__init__.py setup.py _imaging.c ``` -* [ ] Update `CHANGES.rst`. * [ ] Run pre-release check via `make pre`. -* [ ] Push to release branch in personal repo. Let Travis run cleanly. -* [ ] Tag and push to release branch in python-pillow repo. +* [ ] Create tag for release e.g.: +``` + $ git tag 2.9.0 + $ git push --tags +``` * [ ] Create and upload source distributions e.g.: ``` $ make sdistup ``` * [ ] Create and upload [binary distributions](#binary-distributions) + ## Embargoed Release Security fixes that need to be pushed to the distros prior to public release. From f52f37341597d51e530ef01b374aab0bb4c951af Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 3 Apr 2015 10:22:15 -0400 Subject: [PATCH 0147/1037] Specify release branch --- RELEASING.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/RELEASING.md b/RELEASING.md index e994c6922..6e5cead18 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -33,6 +33,10 @@ Released as needed for security, installation or critical bug fixes. * [ ] Update `CHANGES.rst`. * [ ] Cherry pick individual commits from ``master`` branch to release branch e.g. ``2.9.x``. * [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in release branch e.g. ``2.9.x``. +* [ ] Checkout release branch e.g.: +``` + git checkout -t remotes/origin/2.9.x +``` * [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in: ``` PIL/__init__.py setup.py _imaging.c @@ -40,7 +44,7 @@ Released as needed for security, installation or critical bug fixes. * [ ] Run pre-release check via `make pre`. * [ ] Create tag for release e.g.: ``` - $ git tag 2.9.0 + $ git tag 2.9.1 $ git push --tags ``` * [ ] Create and upload source distributions e.g.: From eed5daa66790e7fdbfab771cd39e951bfb47aa8e Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 3 Apr 2015 10:26:35 -0400 Subject: [PATCH 0148/1037] Slight tweaks to embargoed release procedure --- RELEASING.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 6e5cead18..4a20d4b32 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -55,7 +55,7 @@ Released as needed for security, installation or critical bug fixes. ## Embargoed Release -Security fixes that need to be pushed to the distros prior to public release. +Released as needed to vendors before public for critical security-related bug fixes. * [ ] Prepare patch for all versions that will get a fix. Test against local installations. * [ ] Commit against master, cherry pick to affected release branches. @@ -70,8 +70,11 @@ Security fixes that need to be pushed to the distros prior to public release. git push origin 2.5.x git push origin --tags ``` -* [ ] Upload source and binary distributions. - +* [ ] Create and upload source distributions e.g.: +``` + $ make sdistup +``` +* [ ] Create and upload [binary distributions](#binary-distributions) ## Binary Distributions From cd32baedc0d19de0d134db6b96d4e36a65ea30be Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 3 Apr 2015 10:29:55 -0400 Subject: [PATCH 0149/1037] Wording --- RELEASING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASING.md b/RELEASING.md index 4a20d4b32..c79aa002f 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -55,7 +55,7 @@ Released as needed for security, installation or critical bug fixes. ## Embargoed Release -Released as needed to vendors before public for critical security-related bug fixes. +Released as needed privately to individual vendors before public for critical security-related bug fixes. * [ ] Prepare patch for all versions that will get a fix. Test against local installations. * [ ] Commit against master, cherry pick to affected release branches. From 150c7f7206473ea7edb367181902e976d991f9f9 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 3 Apr 2015 10:31:40 -0400 Subject: [PATCH 0150/1037] Wording --- RELEASING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASING.md b/RELEASING.md index c79aa002f..35f3f0ec6 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -55,7 +55,7 @@ Released as needed for security, installation or critical bug fixes. ## Embargoed Release -Released as needed privately to individual vendors before public for critical security-related bug fixes. +Released as needed privately to individual vendors for critical security-related bug fixes. * [ ] Prepare patch for all versions that will get a fix. Test against local installations. * [ ] Commit against master, cherry pick to affected release branches. From 2ca2ecb792844edef806629c9bdb7b6383cad2e8 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 3 Apr 2015 10:51:57 -0400 Subject: [PATCH 0151/1037] Source dists moved above Also dir does not necessarily have to be clean since the manifest determines what is included. --- RELEASING.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 35f3f0ec6..8a58390d0 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -4,6 +4,7 @@ Released quarterly on the first day of January, April, July, October. +* [ ] Open a release ticket e.g. https://github.com/python-pillow/Pillow/issues/1174 * [ ] Develop and prepare release in ``master`` branch. * [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in ``master`` branch. * [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in: @@ -79,14 +80,7 @@ Released as needed privately to individual vendors for critical security-related ## Binary Distributions * [ ] Contact @cgohlke for Windows binaries. -* [ ] From a clean source directory with no extra temp files: -``` -python setup.py sdist --format=zip upload -``` -Or -``` - make sdistup -``` + (Debian requests a tarball, everyone else would just prefer that we choose one and stick to it. So both it is) * [ ] Push a commit to https://github.com/python-pillow/pillow-wheels to build OSX versions (UNDONE latest tag or specific release???) * [ ] Retrieve the OS X Wheels from Rackspace files, upload to PyPi (Twine?) From b9af6253c522e1ad5717c8bd0c840cb0ad46f3f8 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 3 Apr 2015 11:00:47 -0400 Subject: [PATCH 0152/1037] Such updates to binaries! --- RELEASING.md | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 8a58390d0..29d77ab2d 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -79,10 +79,25 @@ Released as needed privately to individual vendors for critical security-related ## Binary Distributions -* [ ] Contact @cgohlke for Windows binaries. +### Windows +* [ ] Contact @cgohlke for Windows binaries via release ticket e.g. https://github.com/python-pillow/Pillow/issues/1174. +* [ ] Download and extract tarball from @cgohlke and ``twine upload *``. + +### OS X +* [ ] Use the [Pillow OS X Wheel Builder](https://github.com/python-pillow/pillow-wheels): +``` + $ git checkout https://github.com/python-pillow/pillow-wheels + $ cd pillow-wheels + $ git submodule init + $ git submodule update + $ cd Pillow + $ git fetch --all + $ git commit -a -m "Pillow -> 2.9.0" + $ git push +* [ ] Download distributions from the [Pillow OS X Wheel Builder container](http://cdf58691c5cf45771290-6a3b6a0f5f6ab91aadc447b2a897dd9a.r50.cf2.rackcdn.com/) and ``twine upload *``. + +### Linux + +## Publicize Release -(Debian requests a tarball, everyone else would just prefer that we choose one and stick to it. So both it is) -* [ ] Push a commit to https://github.com/python-pillow/pillow-wheels to build OSX versions (UNDONE latest tag or specific release???) -* [ ] Retrieve the OS X Wheels from Rackspace files, upload to PyPi (Twine?) -* [ ] Grab Windows binaries, `twine upload dist/*.[whl|egg]`. Manually upload .exe installers. * [ ] Announce release availability. [Twitter](https://twitter.com/pythonpillow), web. From 7129f65bedbc3552c47dd18beec8d0215bac22b9 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 3 Apr 2015 11:01:47 -0400 Subject: [PATCH 0153/1037] Fix syntax --- RELEASING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASING.md b/RELEASING.md index 29d77ab2d..ace76bfeb 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -94,6 +94,7 @@ Released as needed privately to individual vendors for critical security-related $ git fetch --all $ git commit -a -m "Pillow -> 2.9.0" $ git push +``` * [ ] Download distributions from the [Pillow OS X Wheel Builder container](http://cdf58691c5cf45771290-6a3b6a0f5f6ab91aadc447b2a897dd9a.r50.cf2.rackcdn.com/) and ``twine upload *``. ### Linux From 8a767db2ca0a77d9348a8bca7b316d761d4fb63a Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 3 Apr 2015 15:27:54 -0400 Subject: [PATCH 0154/1037] Wording [ci skip] --- RELEASING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASING.md b/RELEASING.md index ace76bfeb..88bc4315c 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -101,4 +101,4 @@ Released as needed privately to individual vendors for critical security-related ## Publicize Release -* [ ] Announce release availability. [Twitter](https://twitter.com/pythonpillow), web. +* [ ] Announce release availability via [Twitter](https://twitter.com/pythonpillow) e.g. https://twitter.com/aclark4life/status/583366798302691328. From 935d6949e9fb9761956cd49ee9c8a57ae70f1599 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 3 Apr 2015 15:29:28 -0400 Subject: [PATCH 0155/1037] Update icons in docs [ci skip] --- docs/index.rst | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 4351438de..4be131198 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -5,8 +5,12 @@ Pillow is the 'friendly' PIL fork by Alex Clark and Contributors. PIL is the Pyt .. image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master :target: https://travis-ci.org/python-pillow/Pillow - :alt: Travis CI build status + :alt: Travis CI build status (Linux) +.. image:: https://travis-ci.org/python-pillow/pillow-wheels.svg?branch=latest + :target: https://travis-ci.org/python-pillow/pillow-wheels + :alt: Travis CI build status (OS X) + .. image:: https://pypip.in/v/Pillow/badge.png :target: https://pypi.python.org/pypi/Pillow/ :alt: Latest PyPI version @@ -16,8 +20,12 @@ Pillow is the 'friendly' PIL fork by Alex Clark and Contributors. PIL is the Pyt :alt: Number of PyPI downloads .. image:: https://coveralls.io/repos/python-pillow/Pillow/badge.png?branch=master - :target: https://coveralls.io/r/python-pillow/Pillow?branch=master - :alt: Test coverage + :target: https://coveralls.io/r/python-pillow/Pillow?branch=master + :alt: Code coverage + +.. image:: https://landscape.io/github/python-pillow/Pillow/master/landscape.png + :target: https://landscape.io/github/python-pillow/Pillow/master + :alt: Code health To install Pillow, please follow the :doc:`installation instructions `. To download source and/or contribute to development of Pillow please see: https://github.com/python-pillow/Pillow. From efec85804c44032912365f9f9922d3bf125d5b26 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 3 Apr 2015 16:50:38 -0400 Subject: [PATCH 0156/1037] Clean up reqs Add Sphinx; remove long list of reqs we disabled a while back because RTD was broken. --- docs/requirements.txt | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index f08f7633e..0229cfb8f 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,19 +1,2 @@ +Sphinx sphinx-better-theme - -## requirements for working on docs -# -## install pillow from master if you're into that, but RtD needs this -#pillow>=2.4.0 -# -#Jinja2==2.7.1 -#MarkupSafe==0.18 -#Pygments==1.6 -#Sphinx==1.1.3 -#docopt==0.6.1 -#docutils==0.11 -#wsgiref==0.1.2 -#sphinx-better-theme==0.1.5 -# -## livereload not strictly necessary but really useful (make livehtml) -#tornado==3.1.1 -#livereload==1.0.1 From 02f3685b2d0f96cbe7028d45c0564efb0c5349ad Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 3 Apr 2015 16:51:45 -0400 Subject: [PATCH 0157/1037] Not using sphinx-better-theme [ci skip] As far as I know --- docs/requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 0229cfb8f..2806c1649 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,2 +1 @@ Sphinx -sphinx-better-theme From e36e7dd7a2061f287ac093421b9029b34c68a30c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 3 Apr 2015 23:22:13 +1100 Subject: [PATCH 0158/1037] Added duration set to GifImagePlugin --- PIL/GifImagePlugin.py | 80 ++++++++++++++++++++++++------------------- Scripts/gifmaker.py | 4 +-- 2 files changed, 46 insertions(+), 38 deletions(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 8db42e8e8..b414a128b 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -292,8 +292,23 @@ def _save(im, fp, filename): for s in header: fp.write(s) - flags = 0 + # local image header + get_local_header(fp, im) + im_out.encoderconfig = (8, get_interlace(im)) + ImageFile._save(im_out, fp, [("gif", (0, 0)+im.size, 0, + RAWMODE[im_out.mode])]) + + fp.write(b"\0") # end of image data + + fp.write(b";") # end of file + + try: + fp.flush() + except: + pass + +def get_interlace(im): try: interlace = im.encoderinfo["interlace"] except KeyError: @@ -302,10 +317,11 @@ def _save(im, fp, filename): # workaround for @PIL153 if min(im.size) < 16: interlace = 0 + + return interlace - if interlace: - flags = flags | 64 - +def get_local_header(fp, im, offset=(0, 0)): + transparent_color_exists = False try: transparency = im.encoderinfo["transparency"] except KeyError: @@ -324,38 +340,36 @@ def _save(im, fp, filename): else: transparent_color_exists = False - # transparency extension block - if transparent_color_exists: - fp.write(b"!" + - o8(249) + # extension intro - o8(4) + # length - o8(1) + # transparency info present - o16(0) + # duration - o8(transparency) + # transparency index - o8(0)) + if 'duration' in im.encoderinfo: + duration = im.encoderinfo["duration"] / 10 + else: + duration = 0 + if transparent_color_exists or duration != 0: + transparency_flag = 1 if transparent_color_exists else 0 + if not transparent_color_exists: + transparency = 0 + + fp.write(b"!" + + o8(249) + # extension intro + o8(4) + # length + o8(transparency_flag) + # transparency info present + o16(duration) + # duration + o8(transparency) + # transparency index + o8(0)) - # local image header + flags = 0 + + if get_interlace(im): + flags = flags | 64 + fp.write(b"," + - o16(0) + o16(0) + # bounding box + o16(offset[0]) + # offset + o16(offset[1]) + o16(im.size[0]) + # size o16(im.size[1]) + o8(flags) + # flags o8(8)) # bits - im_out.encoderconfig = (8, interlace) - ImageFile._save(im_out, fp, [("gif", (0, 0)+im.size, 0, - RAWMODE[im_out.mode])]) - - fp.write(b"\0") # end of image data - - fp.write(b";") # end of file - - try: - fp.flush() - except: - pass - - def _save_netpbm(im, fp, filename): # @@ -510,13 +524,7 @@ def getdata(im, offset=(0, 0), **params): im.encoderinfo = params # local image header - fp.write(b"," + - o16(offset[0]) + # offset - o16(offset[1]) + - o16(im.size[0]) + # size - o16(im.size[1]) + - o8(0) + # flags - o8(8)) # bits + get_local_header(fp, im, offset) ImageFile._save(im, fp, [("gif", (0, 0)+im.size, 0, RAWMODE[im.mode])]) diff --git a/Scripts/gifmaker.py b/Scripts/gifmaker.py index bd4de99c1..420140303 100644 --- a/Scripts/gifmaker.py +++ b/Scripts/gifmaker.py @@ -72,8 +72,8 @@ def makedelta(fp, sequence): for im in sequence: - # - # FIXME: write graphics control block before each frame + # To specify duration, add the time in milliseconds to getdata(), + # e.g. getdata(im, duration=1000) if not previous: From ecebedba7f2d7f8d7b6921886da58fb3239246f1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 4 Apr 2015 10:32:17 +1100 Subject: [PATCH 0159/1037] Added loop set to GifImagePlugin --- PIL/GifImagePlugin.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index b414a128b..6c1d53a1c 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -340,7 +340,7 @@ def get_local_header(fp, im, offset=(0, 0)): else: transparent_color_exists = False - if 'duration' in im.encoderinfo: + if "duration" in im.encoderinfo: duration = im.encoderinfo["duration"] / 10 else: duration = 0 @@ -357,6 +357,16 @@ def get_local_header(fp, im, offset=(0, 0)): o8(transparency) + # transparency index o8(0)) + if "loop" in im.encoderinfo: + number_of_loops = im.encoderinfo["loop"] + fp.write(b"!" + + o8(255) + o8(11) + # extension intro + b"NETSCAPE2.0" + + o8(3) + + o8(1) + + o16(number_of_loops) + # number of loops + o8(0)) + flags = 0 if get_interlace(im): From a5917b3fa36fb2128a5451fe355b287056042f7f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 4 Apr 2015 11:45:30 +1100 Subject: [PATCH 0160/1037] Added GifImagePlugin tests --- PIL/GifImagePlugin.py | 5 +++-- Tests/test_file_gif.py | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 6c1d53a1c..69df28b30 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -341,7 +341,7 @@ def get_local_header(fp, im, offset=(0, 0)): transparent_color_exists = False if "duration" in im.encoderinfo: - duration = im.encoderinfo["duration"] / 10 + duration = int(im.encoderinfo["duration"] / 10) else: duration = 0 if transparent_color_exists or duration != 0: @@ -360,7 +360,8 @@ def get_local_header(fp, im, offset=(0, 0)): if "loop" in im.encoderinfo: number_of_loops = im.encoderinfo["loop"] fp.write(b"!" + - o8(255) + o8(11) + # extension intro + o8(255) + # extension intro + o8(11) + b"NETSCAPE2.0" + o8(3) + o8(1) + diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 2ce728801..1378fb3f6 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -169,6 +169,33 @@ class TestFileGif(PillowTestCase): # first frame self.assertEqual(img.histogram()[img.info['transparency']], 0) + def test_duration(self): + duration = 1000 + + out = self.tempfile('temp.gif') + fp = open(out, "wb") + im = Image.new('L',(100,100),'#000') + for s in GifImagePlugin.getheader(im)[0] + GifImagePlugin.getdata(im, duration=duration): + fp.write(s) + fp.write(b";") + fp.close() + reread = Image.open(out) + + self.assertEqual(reread.info['duration'], duration) + + def test_number_of_loops(self): + number_of_loops = 2 + + out = self.tempfile('temp.gif') + fp = open(out, "wb") + im = Image.new('L',(100,100),'#000') + for s in GifImagePlugin.getheader(im)[0] + GifImagePlugin.getdata(im, loop=number_of_loops): + fp.write(s) + fp.write(b";") + fp.close() + reread = Image.open(out) + + self.assertEqual(reread.info['loop'], number_of_loops) if __name__ == '__main__': unittest.main() From dcefb9351a8f829240836a45f532b50a7d5d405b Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 08:00:34 -0400 Subject: [PATCH 0161/1037] Regen conf from quickstart --- docs/_build/.gitignore | 1 - docs/conf.py | 291 +++++++++++++++++++++++++++++++++++------ 2 files changed, 253 insertions(+), 39 deletions(-) delete mode 100644 docs/_build/.gitignore diff --git a/docs/_build/.gitignore b/docs/_build/.gitignore deleted file mode 100644 index b1f9a2ade..000000000 --- a/docs/_build/.gitignore +++ /dev/null @@ -1 +0,0 @@ -# Empty file, to make the directory available in the repository diff --git a/docs/conf.py b/docs/conf.py index ecc7f6722..0b9ee4db6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,72 +1,287 @@ # -*- coding: utf-8 -*- -import PIL +# +# Pillow (PIL Fork) documentation build configuration file, created by +# sphinx-quickstart on Sat Apr 4 07:54:11 2015. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. -### general configuration ### +import sys +import os +import shlex -needs_sphinx = '1.0' +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', - 'sphinx.ext.intersphinx'] -intersphinx_mapping = {'http://docs.python.org/2/': None} +# -- General configuration ------------------------------------------------ -source_suffix = '.rst' +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The encoding of source files. #source_encoding = 'utf-8-sig' + +# The master toctree document. master_doc = 'index' -project = u'Pillow (PIL fork)' -copyright = (u'1997-2011 by Secret Labs AB,' - u' 1995-2011 by Fredrik Lundh, 2010-2013 Alex Clark') +# General information about the project. +project = u'Pillow (PIL Fork)' +copyright = u'2015, Alex Clark' +author = u'Alex Clark' +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# # The short X.Y version. -version = PIL.PILLOW_VERSION +version = '0' # The full version, including alpha/beta/rc tags. -release = version +release = '0' -# currently excluding autodoc'd plugs -exclude_patterns = ['_build', 'plugins.rst'] +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' -### HTML output ### +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] -#from better import better_theme_path -#html_theme_path = [better_theme_path] -#html_theme = 'better' +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False -html_title = "Pillow v{release} (PIL fork)".format(release=release) -html_short_title = "Home" +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. + +import sphinx_rtd_theme +html_theme = "sphinx_rtd_theme" +html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] -html_theme_options = {} +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] -html_sidebars = { - '**': ['localtoc.html', 'sourcelink.html', 'sidebarhelp.html', - 'searchbox.html'], - 'index': ['globaltoc.html', 'sidebarhelp.html', 'searchbox.html'], -} +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# Now only 'ja' uses this config value +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'Pillowdoc' +htmlhelp_basename = 'PillowPILForkdoc' +# -- Options for LaTeX output --------------------------------------------- -### LaTeX output (RtD PDF output as well) ### +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', -latex_elements = {} +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', +# Additional stuff for the LaTeX preamble. +#'preamble': '', + +# Latex figure (float) alignment +#'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'Pillow.tex', u'Pillow (PIL fork) Documentation', u'Author', - 'manual'), + (master_doc, 'PillowPILFork.tex', u'Pillow (PIL Fork) Documentation', + u'Alex Clark', 'manual'), ] +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None -# skip_api_docs setting will skip PIL.rst if True. Used for working on the -# guides; makes livereload basically instantaneous. -def setup(app): - app.add_config_value('skip_api_docs', False, True) +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False -skip_api_docs = False +# If true, show page references after internal links. +#latex_show_pagerefs = False -if skip_api_docs: - exclude_patterns += ['PIL.rst'] +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'pillowpilfork', u'Pillow (PIL Fork) Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'PillowPILFork', u'Pillow (PIL Fork) Documentation', + author, 'PillowPILFork', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False From 15e9c54b9c3dadc1677ab437b16135d765e19303 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 08:02:33 -0400 Subject: [PATCH 0162/1037] Duplicate readme wording [ci skip] --- docs/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index 4be131198..4602f7bdf 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,7 +1,7 @@ Pillow ====== -Pillow is the 'friendly' PIL fork by Alex Clark and Contributors. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. +Pillow is the "friendly PIL fork" by `Alex Clark and Contributors `_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. .. image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master :target: https://travis-ci.org/python-pillow/Pillow From a4fb3767abbc3aea5ca9dc3c5239d9dffc370026 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 08:04:44 -0400 Subject: [PATCH 0163/1037] Docs don't need this blurb [ci skip] --- docs/index.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 4602f7bdf..318c55821 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -27,8 +27,6 @@ Pillow is the "friendly PIL fork" by `Alex Clark and Contributors `. To download source and/or contribute to development of Pillow please see: https://github.com/python-pillow/Pillow. - .. toctree:: :maxdepth: 2 From f1639df79984065b0ca7b7526085d8970a950afc Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 08:11:18 -0400 Subject: [PATCH 0164/1037] Adjust warnings and notes on install [ci skip] --- docs/installation.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 9bc3d3f0f..43ed9d5f0 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -1,17 +1,16 @@ Installation ============ -.. warning:: Pillow >= 2.1.0 no longer supports "import _imaging". Please use "from PIL.Image import core as _imaging" instead. +.. warning:: Pillow and PIL cannot co-exist in the same environment. Before installing Pillow, please uninstall PIL. .. warning:: Pillow >= 1.0 no longer supports "import Image". Please use "from PIL import Image" instead. -.. warning:: PIL and Pillow currently cannot co-exist in the same environment. - If you want to use Pillow, please remove PIL first. - -.. note:: Pillow >= 2.0.0 supports Python versions 2.6, 2.7, 3.2, 3.3, 3.4 +.. warning:: Pillow >= 2.1.0 no longer supports "import _imaging". Please use "from PIL.Image import core as _imaging" instead. .. note:: Pillow < 2.0.0 supports Python versions 2.4, 2.5, 2.6, 2.7. +.. note:: Pillow >= 2.0.0 supports Python versions 2.6, 2.7, 3.2, 3.3, 3.4 + Simple Installation ------------------- From 178849247603e30375735bcf2991968a9377faf4 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 08:12:15 -0400 Subject: [PATCH 0165/1037] s/Simple/Basic/ Basic, but not necessarily simple :-) --- docs/installation.rst | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 43ed9d5f0..a530ab64b 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -11,28 +11,22 @@ Installation .. note:: Pillow >= 2.0.0 supports Python versions 2.6, 2.7, 3.2, 3.3, 3.4 -Simple Installation -------------------- +Basic Installation +------------------ .. note:: - The following instructions will install Pillow with support for most formats. - See :ref:`external-libraries` for the features you would gain by installing - the external libraries first. This page probably also include specific - instructions for your platform. + The following instructions will install Pillow with support for most formats. See :ref:`external-libraries` for the features you would gain by installing the external libraries first. This page probably also include specific instructions for your platform. You can install Pillow with :command:`pip`:: $ pip install Pillow -Or :command:`easy_install` (for installing `Python Eggs -`_, as :command:`pip` does -not support them):: +Or :command:`easy_install` (for installing `Python Eggs `_, as :command:`pip` does not support them):: $ easy_install Pillow -Or download the `compressed archive from PyPI`_, extract it, and inside it -run:: +Or download the `compressed archive from PyPI`_, extract it, and inside it run:: $ python setup.py install From a7625a7ea00854009de032adf665eb44f84aeafa Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 08:15:42 -0400 Subject: [PATCH 0166/1037] Wording [ci skip] --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index a530ab64b..6aaf39e59 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -16,7 +16,7 @@ Basic Installation .. note:: - The following instructions will install Pillow with support for most formats. See :ref:`external-libraries` for the features you would gain by installing the external libraries first. This page probably also include specific instructions for your platform. + The following instructions will install Pillow with support for most image formats. See :ref:`external-libraries` for additional libraries and corresponding feature support. You can install Pillow with :command:`pip`:: From 1b0bd98b2128f43044d3643722acfb568d5b8eb6 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 08:17:29 -0400 Subject: [PATCH 0167/1037] Wording [ci skip] --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index 6aaf39e59..83e2d1435 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -16,7 +16,7 @@ Basic Installation .. note:: - The following instructions will install Pillow with support for most image formats. See :ref:`external-libraries` for additional libraries and corresponding feature support. + The following instructions will install Pillow with support for most common image formats. See :ref:`external-libraries` for a full list of external libraries supported. You can install Pillow with :command:`pip`:: From 2de5988257a934c0b9c5e7aaef5dc7c48d1e3086 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 08:18:45 -0400 Subject: [PATCH 0168/1037] Wording [ci skip] --- docs/installation.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 83e2d1435..5df61375e 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -18,15 +18,15 @@ Basic Installation The following instructions will install Pillow with support for most common image formats. See :ref:`external-libraries` for a full list of external libraries supported. -You can install Pillow with :command:`pip`:: +Install Pillow with :command:`pip`:: $ pip install Pillow -Or :command:`easy_install` (for installing `Python Eggs `_, as :command:`pip` does not support them):: +Or use :command:`easy_install` for installing `Python Eggs `_, as :command:`pip` does not support them:: $ easy_install Pillow -Or download the `compressed archive from PyPI`_, extract it, and inside it run:: +Or download and extract the `compressed archive from PyPI`_ and inside it run:: $ python setup.py install From c2cf3289f5090decb7c664469b692c93d43bafc2 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 08:20:48 -0400 Subject: [PATCH 0169/1037] Wording [ci skip] --- docs/installation.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 5df61375e..7bbd416b7 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -231,8 +231,7 @@ current versions of Linux, OS X, and Windows. .. note:: - Contributors please test on your platform, edit this document, and send a - pull request. + Contributors please test Pillow on your platform then update this document and send a pull request. +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ |**Operating system** |**Supported**|**Tested Python versions** |**Tested Pillow versions** |**Tested processors** | From a5d46aa0455a73a210e0369ca2b81277ae0a2635 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 08:24:13 -0400 Subject: [PATCH 0170/1037] Wording [ci skip] --- docs/installation.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 7bbd416b7..2632cca8c 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -119,8 +119,7 @@ Linux Installation .. note:: - Fedora, Debian/Ubuntu, and ArchLinux include Pillow (instead of PIL) with - their distributions. Consider using those instead of installing manually. + Most major Linux distributions, including Fedora, Debian/Ubuntu and ArchLinux include Pillow in packages that previously contained PIL e.g. ``python-imaging``. Please consider using native operating system packages first to avoid installation problems and/or missing library support later. **We do not provide binaries for Linux.** If you didn't build Python from source, make sure you have Python's development libraries installed. In Debian From e7d004f88bc12814e40752a63856d604a4e27f09 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 08:24:41 -0400 Subject: [PATCH 0171/1037] Wording [ci skip] --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index 2632cca8c..052653e9f 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -22,7 +22,7 @@ Install Pillow with :command:`pip`:: $ pip install Pillow -Or use :command:`easy_install` for installing `Python Eggs `_, as :command:`pip` does not support them:: +Or use :command:`easy_install` for installing `Python Eggs `_ as :command:`pip` does not support them:: $ easy_install Pillow From c7fd9ee7d18774a87b7a5b85034466b651e7c2a9 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 08:26:34 -0400 Subject: [PATCH 0172/1037] Move linux to bottom since it's easiest --- docs/installation.rst | 79 +++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 052653e9f..fc3b015f9 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -113,45 +113,6 @@ Sample Usage:: $ MAX_CONCURRENCY=1 python setup.py build_ext --enable-[feature] install - -Linux Installation ------------------- - -.. note:: - - Most major Linux distributions, including Fedora, Debian/Ubuntu and ArchLinux include Pillow in packages that previously contained PIL e.g. ``python-imaging``. Please consider using native operating system packages first to avoid installation problems and/or missing library support later. - -**We do not provide binaries for 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 python-dev python-setuptools - -Or for Python 3:: - - $ sudo apt-get install python3-dev python3-setuptools - -In Fedora, the command is:: - - $ sudo yum install python-devel - -Prerequisites are installed on **Ubuntu 12.04 LTS** or **Raspian Wheezy -7.0** with:: - - $ sudo apt-get install libtiff4-dev libjpeg8-dev zlib1g-dev \ - libfreetype6-dev liblcms2-dev libwebp-dev tcl8.5-dev tk8.5-dev python-tk - -Prerequisites are installed on **Ubuntu 14.04 LTS** with:: - - $ sudo apt-get install libtiff5-dev libjpeg8-dev zlib1g-dev \ - libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk - -Prerequisites are installed on **Fedora 20** with:: - - $ sudo yum install libtiff-devel libjpeg-devel libzip-devel freetype-devel \ - lcms2-devel libwebp-devel tcl-devel tk-devel - - OS X Installation ----------------- @@ -205,7 +166,6 @@ FreeBSD Installation .. Note:: Only FreeBSD 10 tested - Make sure you have Python's development libraries installed.:: $ sudo pkg install python2 @@ -218,6 +178,45 @@ Prerequisites are installed on **FreeBSD 10** with:: $ sudo pkg install jpeg tiff webp lcms2 freetype2 +Linux Installation +------------------ + +.. note:: + + Most major Linux distributions, including Fedora, Debian/Ubuntu and ArchLinux include Pillow in packages that previously contained PIL e.g. ``python-imaging``. Please consider using native operating system packages first to avoid installation problems and/or missing library support later. + +**We do not provide binaries for 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 python-dev python-setuptools + +Or for Python 3:: + + $ sudo apt-get install python3-dev python3-setuptools + +In Fedora, the command is:: + + $ sudo yum install python-devel + +Prerequisites are installed on **Ubuntu 12.04 LTS** or **Raspian Wheezy +7.0** with:: + + $ sudo apt-get install libtiff4-dev libjpeg8-dev zlib1g-dev \ + libfreetype6-dev liblcms2-dev libwebp-dev tcl8.5-dev tk8.5-dev python-tk + +Prerequisites are installed on **Ubuntu 14.04 LTS** with:: + + $ sudo apt-get install libtiff5-dev libjpeg8-dev zlib1g-dev \ + libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk + +Prerequisites are installed on **Fedora 20** with:: + + $ sudo yum install libtiff-devel libjpeg-devel libzip-devel freetype-devel \ + lcms2-devel libwebp-devel tcl-devel tk-devel + + + Platform support From 95d423e3915fd91599922a1a02048f090c04c95d Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 08:27:45 -0400 Subject: [PATCH 0173/1037] Clarify "do not need to do" something --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index fc3b015f9..b5208c857 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -39,7 +39,7 @@ External libraries .. note:: - You *do not* need to install all of the external libraries to use Pillow's basic features. + You **do not need to install all external libraries supported** to use Pillow's basic features. Many of Pillow's features require external libraries: From 0b515268bd63d7b5598817fb660ab1f69444e8a9 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 08:31:29 -0400 Subject: [PATCH 0174/1037] Make note about hiding releases on PyPI [ci skip] --- RELEASING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASING.md b/RELEASING.md index 88bc4315c..d9c4d761e 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -25,6 +25,7 @@ Released quarterly on the first day of January, April, July, October. $ make sdistup ``` * [ ] Create and upload [binary distributions](#binary-distributions) +* [ ] Manually hide old versions on PyPI as needed, such that only the latest main release is visible when viewing https://pypi.python.org/pypi/Pillow ## Point Release From dd3c4ad7ee3fbd0e9e4670b2da0e4f8fb204099c Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 08:33:58 -0400 Subject: [PATCH 0175/1037] Move about pillow even lower [ci skip] --- docs/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index 318c55821..9ddb96a91 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -31,11 +31,11 @@ Pillow is the "friendly PIL fork" by `Alex Clark and Contributors Date: Sat, 4 Apr 2015 08:41:03 -0400 Subject: [PATCH 0176/1037] Wording [ci skip] --- docs/about.rst | 19 ++++++------------- docs/index.rst | 2 +- ...riginal-readme.rst => pre-fork-readme.rst} | 6 +++--- 3 files changed, 10 insertions(+), 17 deletions(-) rename docs/{original-readme.rst => pre-fork-readme.rst} (99%) diff --git a/docs/about.rst b/docs/about.rst index 547d4dac3..3579515cf 100644 --- a/docs/about.rst +++ b/docs/about.rst @@ -4,7 +4,7 @@ About Pillow Goals ----- -The fork authors' goal is to foster active development of PIL through: +The fork author's goal is to foster active development of PIL through: - Continuous integration testing via `Travis CI`_ - Publicized development activity on `GitHub`_ @@ -17,7 +17,7 @@ The fork authors' goal is to foster active development of PIL through: License ------- -Like PIL, Pillow is licensed under the MIT-a-like `PIL Software License `_:: +Like PIL, Pillow is licensed under the MIT-like open source `PIL Software License `_:: Software License @@ -35,10 +35,7 @@ Like PIL, Pillow is licensed under the MIT-a-like `PIL Software License Date: Sat, 4 Apr 2015 08:41:58 -0400 Subject: [PATCH 0177/1037] Wording [ci skip] --- docs/about.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/about.rst b/docs/about.rst index 3579515cf..67bf20481 100644 --- a/docs/about.rst +++ b/docs/about.rst @@ -4,7 +4,7 @@ About Pillow Goals ----- -The fork author's goal is to foster active development of PIL through: +The fork author's goal is to foster and support active development of PIL through: - Continuous integration testing via `Travis CI`_ - Publicized development activity on `GitHub`_ From 5269a7991e8f859ee66fa217db6b45723344d89a Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 08:45:16 -0400 Subject: [PATCH 0178/1037] Add note about more release notes [ci skip] --- docs/releasenotes/index.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index 3ffb9c4f1..b4a592937 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -4,5 +4,7 @@ Release Notes .. toctree:: :maxdepth: 2 +.. note:: Contributors please include release notes as needed or appropriate with your bug fixes feature additions and tests. + 2.7.0 2.8.0 From 472a17d89c8a347a51ae984e891dfd9154543c50 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 08:46:13 -0400 Subject: [PATCH 0179/1037] Fix syntax [ci skip] --- docs/releasenotes/index.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index b4a592937..7a27f6f6e 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -1,10 +1,10 @@ Release Notes ============= +.. note:: Contributors please include release notes as needed or appropriate with your bug fixes feature additions and tests. + .. toctree:: :maxdepth: 2 -.. note:: Contributors please include release notes as needed or appropriate with your bug fixes feature additions and tests. - 2.7.0 2.8.0 From fe78a7a7d0052f5821a6a299340c8333a31067eb Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 08:46:58 -0400 Subject: [PATCH 0180/1037] Wording [ci skip] --- docs/releasenotes/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index 7a27f6f6e..d70edc54d 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -1,7 +1,7 @@ Release Notes ============= -.. note:: Contributors please include release notes as needed or appropriate with your bug fixes feature additions and tests. +.. note:: Contributors please include release notes as needed or appropriate with your bug fixes, feature additions and tests. .. toctree:: :maxdepth: 2 From 583dd007d37c333d0a719cfac16ceadd464423ff Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 08:49:18 -0400 Subject: [PATCH 0181/1037] import PIL --- docs/conf.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 0b9ee4db6..f1057b06a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -55,9 +55,10 @@ author = u'Alex Clark' # built documents. # # The short X.Y version. -version = '0' +import PIL +version = PIL.PILLOW_VERSION # The full version, including alpha/beta/rc tags. -release = '0' +release = PIL.PILLOW_VERSION # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. From 97cd5e1ea1d4c91ab3da9f5fe7c9d0f27ce602ba Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 08:50:47 -0400 Subject: [PATCH 0182/1037] Add note about appendices [ci skip] --- docs/handbook/appendices.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/handbook/appendices.rst b/docs/handbook/appendices.rst index f5482ffcd..e6c415cc6 100644 --- a/docs/handbook/appendices.rst +++ b/docs/handbook/appendices.rst @@ -1,6 +1,8 @@ Appendices ========== +.. note:: Contributors please include appendices as needed or appropriate with your bug fixes, feature additions and tests. + .. toctree:: :maxdepth: 2 From fd1202da062471e3a6b11e9a05fe33b231f5a8f8 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 09:03:28 -0400 Subject: [PATCH 0183/1037] Add sphinx-rtd-theme to doc reqs [ci skip] --- docs/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/requirements.txt b/docs/requirements.txt index 2806c1649..6b104fb01 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1 +1,2 @@ Sphinx +sphinx-rtd-theme From b09c861d878bd02c786c47e4db304937e8f788b6 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 09:12:42 -0400 Subject: [PATCH 0184/1037] Don't show pillow wheel building status [ci skip] I don't think we need to show this, and comparing builds of the source to builds of the wheels is confusing/misleading IMO --- README.rst | 4 ---- docs/index.rst | 4 ---- 2 files changed, 8 deletions(-) diff --git a/README.rst b/README.rst index de4377900..430810f0e 100644 --- a/README.rst +++ b/README.rst @@ -10,10 +10,6 @@ Pillow is the "friendly PIL fork" by `Alex Clark and Contributors Date: Sat, 4 Apr 2015 09:37:53 -0400 Subject: [PATCH 0185/1037] Update copyright & authorship [ci skip] To reflect PIL author, Pillow author and many contributors over the years. --- docs/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index f1057b06a..77eebca85 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -47,8 +47,8 @@ master_doc = 'index' # General information about the project. project = u'Pillow (PIL Fork)' -copyright = u'2015, Alex Clark' -author = u'Alex Clark' +copyright = u'1995-2015, Fredrik Lundh and Contributors. Alex Clark and Contributors AKA Pillow Fighters.' +author = u'Fredrik Lundh and Contributors. Alex Clark and Contributors AKA Pillow Fighters.' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the From 1d327690a796bd543c002fff7e7a8a6646fda197 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 09:39:52 -0400 Subject: [PATCH 0186/1037] Wording [ci skip] --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 77eebca85..2ff645370 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -47,7 +47,7 @@ master_doc = 'index' # General information about the project. project = u'Pillow (PIL Fork)' -copyright = u'1995-2015, Fredrik Lundh and Contributors. Alex Clark and Contributors AKA Pillow Fighters.' +copyright = u'1995-2015, Fredrik Lundh and Contributors. Alex Clark and Contributors' author = u'Fredrik Lundh and Contributors. Alex Clark and Contributors AKA Pillow Fighters.' # The version info for the project you're documenting, acts as replacement for From 8779b701199d9ad7205151e4720103ada83f7806 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 09:40:56 -0400 Subject: [PATCH 0187/1037] Wording [ci skip] --- docs/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 2ff645370..cd583937c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -47,8 +47,8 @@ master_doc = 'index' # General information about the project. project = u'Pillow (PIL Fork)' -copyright = u'1995-2015, Fredrik Lundh and Contributors. Alex Clark and Contributors' -author = u'Fredrik Lundh and Contributors. Alex Clark and Contributors AKA Pillow Fighters.' +copyright = u'1995-2015, Fredrik Lundh and Contributors, Alex Clark and Contributors' +author = u'Fredrik Lundh and Contributors, Alex Clark and Contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the From e934a739a2ae63f695d9c41a414096f0782714f9 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 20:36:53 -0400 Subject: [PATCH 0188/1037] Add intro [ci skip] --- CONTRIBUTING.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8351625fc..0dcaf86da 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,6 @@ -# Contributing +# Contributing to Pillow + +Contributions of bug fixes, feature additions, tests, documentation and more can be made via [issues](https://github.com/python-pillow/Pillow/issues) and/or [pull requests](https://github.com/python-pillow/Pillow/issues). ## Fixes, Features and Changes From 843ee095769385b117363ebb27213f51f4459d91 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 20:45:47 -0400 Subject: [PATCH 0189/1037] Wording, headers [ci skip] --- CONTRIBUTING.md | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0dcaf86da..511c3ce3d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,26 +2,28 @@ Contributions of bug fixes, feature additions, tests, documentation and more can be made via [issues](https://github.com/python-pillow/Pillow/issues) and/or [pull requests](https://github.com/python-pillow/Pillow/issues). -## Fixes, Features and Changes +## Bug fixes, feature additions, etc. -Send a pull request to the master branch. We'll generally want documentation and [tests](Tests/README.rst) for new features. Tests or documentation on their own are also welcomed. Feel free to ask questions as an [issue](https://github.com/python-pillow/Pillow/issues/new) or on IRC (irc.freenode.net, #pil) +Please send a pull request to the master branch. Please include documentation and [tests](Tests/README.rst) for new features. Tests or documentation on their own are also welcomed. Feel free to ask questions as an [issue](https://github.com/python-pillow/Pillow/issues/new) or on IRC (irc.freenode.net, #pil) -- Fork the repo -- Make a branch from master -- Add your changes + Tests -- Run the test suite. Try to run on both Python 2.x and 3.x, or you'll get tripped up. You can enable [Travis CI on your repo](https://travis-ci.org/profile/) to catch test failures prior to the pull request, and [Coveralls](https://coveralls.io/repos/new) to see if the changed code is covered by tests. -- Push to your fork, and make a pull request onto master. +- Fork the Pillow repository. +- Create a branch from master. +- Develop bug fixes, features, tests, etc. +- Run the test suite on both Python 2.x and 3.x. You can enable [Travis CI on your repo](https://travis-ci.org/profile/) to catch test failures prior to the pull request, and [Coveralls](https://coveralls.io/repos/new) to see if the changed code is covered by tests. +- Create a pull request to pull the changes from your branch to the Pillow master. + +### Guidelines -A few guidelines: - Try to keep any code commits clean and separate from reformatting commits. -- All new code is going to need tests. -- Try to follow PEP8. +- Provide tests for any newly added code. +- Follow PEP8. -## Bugs +## Reporting Issues -When reporting bugs, please include example code that reproduces the issue, and if possible a problem image. The best reproductions are self-contained scripts that pull in as few dependencies as possible. An entire Django stack is harder to handle. +When reporting issues, please include example code that reproduces the issue, and if possible a problem image. The best reproductions are self-contained scripts that pull in as few dependencies as possible. An entire Django stack is harder to reproduce. + +### Provide details -Let us know: - What did you do? - What did you expect to happen? - What actually happened? From 69f81ec089c1aaa70db4d71e21ebd3e6f97904a1 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 20:57:53 -0400 Subject: [PATCH 0190/1037] Wording [ci skip] --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 511c3ce3d..66a8e2bbc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing to Pillow -Contributions of bug fixes, feature additions, tests, documentation and more can be made via [issues](https://github.com/python-pillow/Pillow/issues) and/or [pull requests](https://github.com/python-pillow/Pillow/issues). +Bug fixes, feature additions, tests, documentation and more can be contributed via [issues](https://github.com/python-pillow/Pillow/issues) and/or [pull requests](https://github.com/python-pillow/Pillow/issues). All contributions are welcome. ## Bug fixes, feature additions, etc. From 5c6e778dbe1668eab14d976e6ca59b94ea3de02c Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 20:59:15 -0400 Subject: [PATCH 0191/1037] Wording [ci skip] --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 66a8e2bbc..5149f2a7d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ Bug fixes, feature additions, tests, documentation and more can be contributed v ## Bug fixes, feature additions, etc. -Please send a pull request to the master branch. Please include documentation and [tests](Tests/README.rst) for new features. Tests or documentation on their own are also welcomed. Feel free to ask questions as an [issue](https://github.com/python-pillow/Pillow/issues/new) or on IRC (irc.freenode.net, #pil) +Please send a pull request to the master branch. Please include documentation and [tests](Tests/README.rst) for new features. Tests or documentation without bug fixes or feature additions are also welcome. Feel free to ask questions as an [issue](https://github.com/python-pillow/Pillow/issues/new) or on IRC (irc.freenode.net, #pil) - Fork the Pillow repository. - Create a branch from master. From dafcd946e736f9d6a84e16a13ab9581a0571eee3 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 21:00:42 -0400 Subject: [PATCH 0192/1037] Update --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5149f2a7d..1006ef531 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ Bug fixes, feature additions, tests, documentation and more can be contributed v ## Bug fixes, feature additions, etc. -Please send a pull request to the master branch. Please include documentation and [tests](Tests/README.rst) for new features. Tests or documentation without bug fixes or feature additions are also welcome. Feel free to ask questions as an [issue](https://github.com/python-pillow/Pillow/issues/new) or on IRC (irc.freenode.net, #pil) +Please send a pull request to the master branch. Please include [documentation](http://pillow.readthedocs.org) and [tests](Tests/README.rst) for new features. Tests or documentation without bug fixes or feature additions are also welcome. Feel free to ask questions as an [issue](https://github.com/python-pillow/Pillow/issues/new) or on IRC (irc.freenode.net, #pil) - Fork the Pillow repository. - Create a branch from master. From 7ebf4ee90ea82435d86d82c82026dca2b9becfd3 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 21:01:30 -0400 Subject: [PATCH 0193/1037] Wording [ci skip] --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1006ef531..ca6e53564 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ Bug fixes, feature additions, tests, documentation and more can be contributed v ## Bug fixes, feature additions, etc. -Please send a pull request to the master branch. Please include [documentation](http://pillow.readthedocs.org) and [tests](Tests/README.rst) for new features. Tests or documentation without bug fixes or feature additions are also welcome. Feel free to ask questions as an [issue](https://github.com/python-pillow/Pillow/issues/new) or on IRC (irc.freenode.net, #pil) +Please send a pull request to the master branch. Please include [documentation](http://pillow.readthedocs.org) and [tests](Tests/README.rst) for new features. Tests or documentation without bug fixes or feature additions are welcome too. Feel free to ask questions as an [issue](https://github.com/python-pillow/Pillow/issues/new) or on IRC (irc.freenode.net, #pil) - Fork the Pillow repository. - Create a branch from master. From 3c8795e60bb30a72dd861c7e99c15398e8f1e396 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 21:02:55 -0400 Subject: [PATCH 0194/1037] Update --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ca6e53564..cc675a99c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ Bug fixes, feature additions, tests, documentation and more can be contributed v ## Bug fixes, feature additions, etc. -Please send a pull request to the master branch. Please include [documentation](http://pillow.readthedocs.org) and [tests](Tests/README.rst) for new features. Tests or documentation without bug fixes or feature additions are welcome too. Feel free to ask questions as an [issue](https://github.com/python-pillow/Pillow/issues/new) or on IRC (irc.freenode.net, #pil) +Please send a pull request to the master branch. Please include [documentation](http://pillow.readthedocs.org) and [tests](Tests/README.rst) for new features. Tests or documentation without bug fixes or feature additions are welcome too. Feel free to ask questions [via issues](https://github.com/python-pillow/Pillow/issues/new) or [IRC](irc://irc.freenode.net#pil) - Fork the Pillow repository. - Create a branch from master. From 6b7f42f21423f9600ecaedd74e5f1609f4af76b3 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 4 Apr 2015 21:03:40 -0400 Subject: [PATCH 0195/1037] Update [ci skip] --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cc675a99c..7a71cdd3d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ Bug fixes, feature additions, tests, documentation and more can be contributed v ## Bug fixes, feature additions, etc. -Please send a pull request to the master branch. Please include [documentation](http://pillow.readthedocs.org) and [tests](Tests/README.rst) for new features. Tests or documentation without bug fixes or feature additions are welcome too. Feel free to ask questions [via issues](https://github.com/python-pillow/Pillow/issues/new) or [IRC](irc://irc.freenode.net#pil) +Please send a pull request to the master branch. Please include [documentation](http://pillow.readthedocs.org) and [tests](Tests/README.rst) for new features. Tests or documentation without bug fixes or feature additions are welcome too. Feel free to ask questions [via issues](https://github.com/python-pillow/Pillow/issues/new) or irc://irc.freenode.net#pil - Fork the Pillow repository. - Create a branch from master. From 4efdb41fe73337e557c62ad05b8b674584c67163 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 5 Apr 2015 06:59:09 -0400 Subject: [PATCH 0196/1037] Wording [ci skip] --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7a71cdd3d..54ade800a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,7 +14,7 @@ Please send a pull request to the master branch. Please include [documentation]( ### Guidelines -- Try to keep any code commits clean and separate from reformatting commits. +- Separate code commits from reformatting commits. - Provide tests for any newly added code. - Follow PEP8. From 3f3a890c212e9655ef7d38c74cc6f0cb38e8fb80 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 5 Apr 2015 07:01:33 -0400 Subject: [PATCH 0197/1037] Wording [ci skip] --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 54ade800a..5a49ab2d7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,7 +20,7 @@ Please send a pull request to the master branch. Please include [documentation]( ## Reporting Issues -When reporting issues, please include example code that reproduces the issue, and if possible a problem image. The best reproductions are self-contained scripts that pull in as few dependencies as possible. An entire Django stack is harder to reproduce. +When reporting issues, please include code that reproduces the issue and whenever possible, an image that demonstrates the issue. The best reproductions are self-contained scripts with minimal dependencies. ### Provide details From 7f4cfdf36b519fbfe405c870728f602b6ddb5d9a Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Wed, 8 Apr 2015 11:07:59 -0400 Subject: [PATCH 0198/1037] Re-add extensions [ci skip] Fixes #1183. Thanks @joekilner. This should resolve. --- docs/conf.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index cd583937c..85be12f30 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -29,7 +29,8 @@ import shlex # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = [] +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', + 'sphinx.ext.intersphinx'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] From bb01e845ea4ccdd84acedaa82818d17034c212d0 Mon Sep 17 00:00:00 2001 From: Allen Li Date: Wed, 8 Apr 2015 15:57:17 -0400 Subject: [PATCH 0199/1037] Clarify docstring about mask values [ci skip] The behavior of paste() with regard to intermediate mask values was unclear, so this commit clarifies how it works. --- PIL/Image.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/PIL/Image.py b/PIL/Image.py index 7cfa72e6f..e6f5327be 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1288,7 +1288,13 @@ class Image: images (in the latter case, the alpha band is used as mask). Where the mask is 255, the given image is copied as is. Where the mask is 0, the current value is preserved. Intermediate - values can be used for transparency effects. + values will mix the two images together. If this image has an + alpha channel, intermediate values will also set the alpha + channel to themselves. + + See :py:meth:`~PIL.Image.Image.alpha_composite` if you want to + combine images with a partial mask without setting the alpha + channel. Note that if you paste an "RGBA" image, the alpha band is ignored. You can work around this by using the same image as From 7674694185c4899d679a1c16b5118d397e213204 Mon Sep 17 00:00:00 2001 From: Allen Li Date: Fri, 10 Apr 2015 23:23:26 -0400 Subject: [PATCH 0200/1037] Correct description of paste() behavior [ci skip] --- PIL/Image.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index e6f5327be..a2f9be15c 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1288,9 +1288,8 @@ class Image: images (in the latter case, the alpha band is used as mask). Where the mask is 255, the given image is copied as is. Where the mask is 0, the current value is preserved. Intermediate - values will mix the two images together. If this image has an - alpha channel, intermediate values will also set the alpha - channel to themselves. + values will mix the two images together, including their alpha + channels if they have them. See :py:meth:`~PIL.Image.Image.alpha_composite` if you want to combine images with a partial mask without setting the alpha From c6d410ff3b13ecf25381178ca35c149b31d6c519 Mon Sep 17 00:00:00 2001 From: Allen Li Date: Fri, 10 Apr 2015 23:24:30 -0400 Subject: [PATCH 0201/1037] Correct description of alpha-composite [ci skip] --- PIL/Image.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index a2f9be15c..be27bd99b 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1292,8 +1292,7 @@ class Image: channels if they have them. See :py:meth:`~PIL.Image.Image.alpha_composite` if you want to - combine images with a partial mask without setting the alpha - channel. + combine images with respect to their alpha channels. Note that if you paste an "RGBA" image, the alpha band is ignored. You can work around this by using the same image as From e8107e3bbbd48f5713f6c2de2539718e243fe7c2 Mon Sep 17 00:00:00 2001 From: Allen Li Date: Fri, 10 Apr 2015 23:25:15 -0400 Subject: [PATCH 0202/1037] Remove wrong docstring about RGBA paste [ci skip] Pasting RGBA images does preserve the alpha channel if the current image has an alpha channel. --- PIL/Image.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index be27bd99b..36ff2eebf 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1294,10 +1294,6 @@ class Image: See :py:meth:`~PIL.Image.Image.alpha_composite` if you want to combine images with respect to their alpha channels. - Note that if you paste an "RGBA" image, the alpha band is - ignored. You can work around this by using the same image as - both source image and mask. - :param im: Source image or pixel value (integer or tuple). :param box: An optional 4-tuple giving the region to paste into. If a 2-tuple is used instead, it's treated as the upper left From 689f28aae7c9cc3fddb73e7ec2a07f169b91d4ba Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 12 Apr 2015 12:58:46 +1000 Subject: [PATCH 0203/1037] Added icns save --- PIL/IcnsImagePlugin.py | 51 ++++++++++++++++++++++++++++++++++++++--- Tests/test_file_icns.py | 18 ++++++++++++++- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/PIL/IcnsImagePlugin.py b/PIL/IcnsImagePlugin.py index e88b84985..26735cc95 100644 --- a/PIL/IcnsImagePlugin.py +++ b/PIL/IcnsImagePlugin.py @@ -18,6 +18,7 @@ from PIL import Image, ImageFile, PngImagePlugin, _binary import io import struct +import tempfile, shutil, os, sys enable_jpeg2k = hasattr(Image.core, 'jp2klib_version') if enable_jpeg2k: @@ -293,12 +294,56 @@ class IcnsImageFile(ImageFile.ImageFile): self.tile = () self.load_end() +def _save(im, fp, filename): + try: + fp.flush() + except: + pass + + # create the temporary set of pngs + iconset = tempfile.mkdtemp('.iconset') + last_w = None + last_im = None + for w in [16,32,128,256,512]: + prefix = 'icon_{}x{}'.format(w,w) + + if last_w == w: + im_scaled = last_im + else: + im_scaled = im.resize((w,w), Image.LANCZOS) + im_scaled.save(os.path.join(iconset, prefix+'.png')) + + im_scaled = im.resize((w*2,w*2), Image.LANCZOS) + im_scaled.save(os.path.join(iconset, prefix+'@2x.png')) + last_im = im_scaled + + # iconutil -c icns -o {} {} + from subprocess import Popen, PIPE, CalledProcessError + + convert_cmd = ["iconutil","-c","icns","-o",filename,iconset] + stderr = tempfile.TemporaryFile() + convert_proc = Popen(convert_cmd, stdout=PIPE, stderr=stderr) + + convert_proc.stdout.close() + + retcode = convert_proc.wait() + + # remove the temporary files + shutil.rmtree(iconset) + + if retcode: + raise CalledProcessError(retcode, convert_cmd) + Image.register_open("ICNS", IcnsImageFile, lambda x: x[:4] == b'icns') Image.register_extension("ICNS", '.icns') +if sys.platform == 'darwin': + Image.register_save("ICNS", _save) + + Image.register_mime("ICNS", "image/icns") + + if __name__ == '__main__': - import os - import sys imf = IcnsImageFile(open(sys.argv[1], 'rb')) for size in imf.info['sizes']: imf.size = size @@ -308,4 +353,4 @@ if __name__ == '__main__': im = Image.open(open(sys.argv[1], "rb")) im.save("out.png") if sys.platform == 'windows': - os.startfile("out.png") + os.startfile("out.png") \ No newline at end of file diff --git a/Tests/test_file_icns.py b/Tests/test_file_icns.py index 99f6da9e3..ddbed3cd2 100644 --- a/Tests/test_file_icns.py +++ b/Tests/test_file_icns.py @@ -2,6 +2,8 @@ from helper import unittest, PillowTestCase from PIL import Image +import sys + # sample icon file file = "Tests/images/pillow.icns" data = open(file, "rb").read() @@ -20,6 +22,20 @@ class TestFileIcns(PillowTestCase): self.assertEqual(im.size, (1024, 1024)) self.assertEqual(im.format, "ICNS") + @unittest.skipIf(sys.platform != 'darwin', + "requires MacOS") + def test_save(self): + im = Image.open(file) + + test_file = self.tempfile("temp.icns") + im.save(test_file) + + reread = Image.open(test_file) + + self.assertEqual(reread.mode, "RGBA") + self.assertEqual(reread.size, (1024, 1024)) + self.assertEqual(reread.format, "ICNS") + def test_sizes(self): # Check that we can load all of the sizes, and that the final pixel # dimensions are as expected @@ -71,4 +87,4 @@ class TestFileIcns(PillowTestCase): if __name__ == '__main__': unittest.main() -# End of file +# End of file \ No newline at end of file From 5be6f810a57cbf1280c2571c937d3766f5038c37 Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 14 Apr 2015 11:51:04 +0300 Subject: [PATCH 0204/1037] Up --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 7cda23b6b..e831b9f1d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Tiff: allow writing floating point tag values #1113 + [bpedersen2] + 2.8.1 (2015-04-02) ------------------ From 740ab40c6da4200ad2387c53fe6c13067e7c5747 Mon Sep 17 00:00:00 2001 From: Karim Bahgat Date: Tue, 14 Apr 2015 20:17:57 +0200 Subject: [PATCH 0205/1037] Keep user-specified ordering of icon sizes Some consumers of icon files such as py2exe care about the order that icon sizes are added, so the user should be fully in charge of this, instead of force sorting them. --- PIL/IcoImagePlugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/PIL/IcoImagePlugin.py b/PIL/IcoImagePlugin.py index b4817db27..c4e24d99c 100644 --- a/PIL/IcoImagePlugin.py +++ b/PIL/IcoImagePlugin.py @@ -48,7 +48,6 @@ def _save(im, fp, filename): width, height = im.size filter(lambda x: False if (x[0] > width or x[1] > height or x[0] > 255 or x[1] > 255) else True, sizes) - sizes = sorted(sizes, key=lambda x: x[0]) fp.write(struct.pack("H", len(sizes))) # idCount(2) offset = fp.tell() + len(sizes)*16 for size in sizes: From 4f6c366e751e1e43fa82c0ffb70e5b7b42f9858d Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Thu, 16 Apr 2015 16:27:57 +0200 Subject: [PATCH 0206/1037] fix imaging leaks in putdata and getlist --- Tests/check_imaging_leaks.py | 43 ++++++++++++++++++++++++++++++++++++ _imaging.c | 6 ++++- 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 Tests/check_imaging_leaks.py diff --git a/Tests/check_imaging_leaks.py b/Tests/check_imaging_leaks.py new file mode 100644 index 000000000..3a7dcaa2b --- /dev/null +++ b/Tests/check_imaging_leaks.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python + +from __future__ import division +from helper import unittest, PillowTestCase +import sys +from PIL import Image, ImageFilter + +min_iterations = 100 +max_iterations = 10000 + +@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or MacOS") +class TestImagingLeaks(PillowTestCase): + + def _get_mem_usage(self): + from resource import getpagesize, getrusage, RUSAGE_SELF + mem = getrusage(RUSAGE_SELF).ru_maxrss + return mem * getpagesize() / 1024 / 1024 + + def _test_leak(self, min_iterations, max_iterations, fn, *args, **kwargs): + mem_limit = None + for i in range(max_iterations): + fn(*args, **kwargs) + mem = self._get_mem_usage() + if i < min_iterations: + mem_limit = mem + 1 + continue + self.assertLessEqual(mem, mem_limit, + msg='memory usage limit exceeded after %d iterations' + % (i + 1)) + + def test_leak_putdata(self): + im = Image.new('RGB', (25, 25)) + self._test_leak(min_iterations, max_iterations, im.putdata, im.getdata()) + + def test_leak_getlist(self): + im = Image.new('P', (25, 25)) + self._test_leak(min_iterations, max_iterations, + # Pass a new list at each iteration. + lambda: im.point(range(256))) + +if __name__ == '__main__': + unittest.main() + diff --git a/_imaging.c b/_imaging.c index 786363e24..09345c0dd 100644 --- a/_imaging.c +++ b/_imaging.c @@ -421,6 +421,7 @@ getlist(PyObject* arg, int* length, const char* wrong_length, int type) *length = n; PyErr_Clear(); + Py_DECREF(seq); return list; } @@ -1221,7 +1222,7 @@ _putdata(ImagingObject* self, PyObject* args) Py_ssize_t n, i, x, y; PyObject* data; - PyObject* seq; + PyObject* seq = NULL; PyObject* op; double scale = 1.0; double offset = 0.0; @@ -1329,6 +1330,7 @@ _putdata(ImagingObject* self, PyObject* args) op = PySequence_Fast_GET_ITEM(seq, i); if (!op || !getink(op, image, u.ink)) { + Py_DECREF(seq); return NULL; } /* FIXME: what about scale and offset? */ @@ -1342,6 +1344,8 @@ _putdata(ImagingObject* self, PyObject* args) } } + Py_XDECREF(seq); + Py_INCREF(Py_None); return Py_None; } From a649757f03c25213f54d58b3220372f83c43373b Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Thu, 16 Apr 2015 17:38:15 +0200 Subject: [PATCH 0207/1037] tests: add missing "from __future__ import division" --- Tests/check_webp_leaks.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/check_webp_leaks.py b/Tests/check_webp_leaks.py index b5875c8ab..95162ad4a 100644 --- a/Tests/check_webp_leaks.py +++ b/Tests/check_webp_leaks.py @@ -1,3 +1,4 @@ +from __future__ import division from helper import unittest, PillowTestCase import sys from PIL import Image From 12583664d0de0f4397d6cc64e722a0fa775eb7b8 Mon Sep 17 00:00:00 2001 From: Hugo Date: Sat, 18 Apr 2015 00:54:07 +0300 Subject: [PATCH 0208/1037] Tests pass on OS X 10.10 [CI skip] Running selftest: --- 57 tests passed. Ran 567 tests in 66.265s OK (SKIP=34) --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index b5208c857..d1beaf389 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -234,7 +234,7 @@ current versions of Linux, OS X, and Windows. +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ |**Operating system** |**Supported**|**Tested Python versions** |**Tested Pillow versions** |**Tested processors** | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Mac OS X 10.10 Yosemite | | | |x86-64 | +| Mac OS X 10.10 Yosemite |Yes | 2.7 | 2.8.1 |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Mac OS X 10.9 Mavericks |Yes | 2.7,3.4 | 2.6.1 |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ From 74f81f43ea2a7c2e06c7926077792822dcff2792 Mon Sep 17 00:00:00 2001 From: Hugo Date: Sat, 18 Apr 2015 01:00:53 +0300 Subject: [PATCH 0209/1037] Consistent title case [CI skip] --- docs/installation.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index d1beaf389..847ed401f 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -34,7 +34,7 @@ Or download and extract the `compressed archive from PyPI`_ and inside it run:: .. _external-libraries: -External libraries +External Libraries ------------------ .. note:: @@ -219,7 +219,7 @@ Prerequisites are installed on **Fedora 20** with:: -Platform support +Platform Support ---------------- Current platform support for Pillow. Binary distributions are contributed for From 8e063caa664eaba20974920b42cf66337a5c4595 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 22 Apr 2015 00:52:57 +1000 Subject: [PATCH 0210/1037] Updated OS X Platform Support [CI skip] --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index 847ed401f..7634a77fc 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -234,7 +234,7 @@ current versions of Linux, OS X, and Windows. +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ |**Operating system** |**Supported**|**Tested Python versions** |**Tested Pillow versions** |**Tested processors** | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Mac OS X 10.10 Yosemite |Yes | 2.7 | 2.8.1 |x86-64 | +| Mac OS X 10.10 Yosemite |Yes | 2.7,3.3,3.4 | 2.8.1 |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Mac OS X 10.9 Mavericks |Yes | 2.7,3.4 | 2.6.1 |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ From f6333cc36f091fe25a9ca1904e38d0cd68d09172 Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 23 Apr 2015 08:30:49 +0300 Subject: [PATCH 0211/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index e831b9f1d..4d13c5841 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,8 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Keep user-specified ordering of icon sizes #1193 + [karimbahgat] - Tiff: allow writing floating point tag values #1113 [bpedersen2] From 6ae1218f4bc6c7aebec5c782bc050856f3d44d74 Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 23 Apr 2015 08:33:26 +0300 Subject: [PATCH 0212/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 4d13c5841..0a22c33ea 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,8 +4,12 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Fix putdata memory leak #1196 + [benoit-pierre] + - Keep user-specified ordering of icon sizes #1193 [karimbahgat] + - Tiff: allow writing floating point tag values #1113 [bpedersen2] From 1bcda962d28bcc3434fd863a64bc0925fcae18c5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 23 Apr 2015 17:00:21 +1000 Subject: [PATCH 0213/1037] Updated IcnsImagePlugin documentation --- PIL/IcnsImagePlugin.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/PIL/IcnsImagePlugin.py b/PIL/IcnsImagePlugin.py index 26735cc95..c185277ba 100644 --- a/PIL/IcnsImagePlugin.py +++ b/PIL/IcnsImagePlugin.py @@ -248,7 +248,7 @@ class IcnsFile: class IcnsImageFile(ImageFile.ImageFile): """ - PIL read-only image support for Mac OS .icns files. + PIL image support for Mac OS .icns files. Chooses the best resolution, but will possibly load a different size image if you mutate the size attribute before calling 'load'. @@ -295,6 +295,13 @@ class IcnsImageFile(ImageFile.ImageFile): self.load_end() def _save(im, fp, filename): + """ + Saves the image as a series of PNG files, + that are then converted to a .icns file + using the OS X command line utility 'iconutil'. + + OS X only. + """ try: fp.flush() except: From 6d4b9b6d6169bb61b8ba43e0841856e230a38ea5 Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 23 Apr 2015 10:48:53 +0300 Subject: [PATCH 0214/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 0a22c33ea..f96f86fdf 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Added icns save #1185 + [radarhere] + - Fix putdata memory leak #1196 [benoit-pierre] From 63f6e94be40bab534070840222b228db4efc133c Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 23 Apr 2015 13:25:45 +0300 Subject: [PATCH 0215/1037] Flake8 after PR #1185 --- PIL/IcnsImagePlugin.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/PIL/IcnsImagePlugin.py b/PIL/IcnsImagePlugin.py index c185277ba..a1ebee704 100644 --- a/PIL/IcnsImagePlugin.py +++ b/PIL/IcnsImagePlugin.py @@ -17,8 +17,11 @@ from PIL import Image, ImageFile, PngImagePlugin, _binary import io +import os +import shutil import struct -import tempfile, shutil, os, sys +import sys +import tempfile enable_jpeg2k = hasattr(Image.core, 'jp2klib_version') if enable_jpeg2k: @@ -294,40 +297,41 @@ class IcnsImageFile(ImageFile.ImageFile): self.tile = () self.load_end() + def _save(im, fp, filename): """ Saves the image as a series of PNG files, that are then converted to a .icns file using the OS X command line utility 'iconutil'. - + OS X only. """ try: fp.flush() except: pass - + # create the temporary set of pngs iconset = tempfile.mkdtemp('.iconset') last_w = None last_im = None - for w in [16,32,128,256,512]: - prefix = 'icon_{}x{}'.format(w,w) - + for w in [16, 32, 128, 256, 512]: + prefix = 'icon_{}x{}'.format(w, w) + if last_w == w: im_scaled = last_im else: - im_scaled = im.resize((w,w), Image.LANCZOS) + im_scaled = im.resize((w, w), Image.LANCZOS) im_scaled.save(os.path.join(iconset, prefix+'.png')) - - im_scaled = im.resize((w*2,w*2), Image.LANCZOS) + + im_scaled = im.resize((w*2, w*2), Image.LANCZOS) im_scaled.save(os.path.join(iconset, prefix+'@2x.png')) last_im = im_scaled - + # iconutil -c icns -o {} {} from subprocess import Popen, PIPE, CalledProcessError - convert_cmd = ["iconutil","-c","icns","-o",filename,iconset] + convert_cmd = ["iconutil", "-c", "icns", "-o", filename, iconset] stderr = tempfile.TemporaryFile() convert_proc = Popen(convert_cmd, stdout=PIPE, stderr=stderr) @@ -337,7 +341,7 @@ def _save(im, fp, filename): # remove the temporary files shutil.rmtree(iconset) - + if retcode: raise CalledProcessError(retcode, convert_cmd) @@ -360,4 +364,4 @@ if __name__ == '__main__': im = Image.open(open(sys.argv[1], "rb")) im.save("out.png") if sys.platform == 'windows': - os.startfile("out.png") \ No newline at end of file + os.startfile("out.png") From aa1368f55115b6c33b9a741b55ee805bb567f209 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 23 Apr 2015 23:40:42 +1000 Subject: [PATCH 0216/1037] Flake8 fixes --- PIL/GifImagePlugin.py | 31 +++++++++++++++++-------------- Tests/test_file_gif.py | 4 ++-- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 69df28b30..cc41da949 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -308,6 +308,7 @@ def _save(im, fp, filename): except: pass + def get_interlace(im): try: interlace = im.encoderinfo["interlace"] @@ -317,9 +318,10 @@ def get_interlace(im): # workaround for @PIL153 if min(im.size) < 16: interlace = 0 - + return interlace + def get_local_header(fp, im, offset=(0, 0)): transparent_color_exists = False try: @@ -348,38 +350,39 @@ def get_local_header(fp, im, offset=(0, 0)): transparency_flag = 1 if transparent_color_exists else 0 if not transparent_color_exists: transparency = 0 - + fp.write(b"!" + - o8(249) + # extension intro - o8(4) + # length - o8(transparency_flag) + # transparency info present - o16(duration) + # duration - o8(transparency) + # transparency index + o8(249) + # extension intro + o8(4) + # length + o8(transparency_flag) + # transparency info present + o16(duration) + # duration + o8(transparency) + # transparency index o8(0)) if "loop" in im.encoderinfo: number_of_loops = im.encoderinfo["loop"] fp.write(b"!" + - o8(255) + # extension intro + o8(255) + # extension intro o8(11) + b"NETSCAPE2.0" + o8(3) + o8(1) + - o16(number_of_loops) + # number of loops + o16(number_of_loops) + # number of loops o8(0)) flags = 0 if get_interlace(im): flags = flags | 64 - + fp.write(b"," + - o16(offset[0]) + # offset + o16(offset[0]) + # offset o16(offset[1]) + - o16(im.size[0]) + # size + o16(im.size[0]) + # size o16(im.size[1]) + - o8(flags) + # flags - o8(8)) # bits + o8(flags) + # flags + o8(8)) # bits + def _save_netpbm(im, fp, filename): diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 1378fb3f6..3b682f86b 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -174,7 +174,7 @@ class TestFileGif(PillowTestCase): out = self.tempfile('temp.gif') fp = open(out, "wb") - im = Image.new('L',(100,100),'#000') + im = Image.new('L', (100, 100), '#000') for s in GifImagePlugin.getheader(im)[0] + GifImagePlugin.getdata(im, duration=duration): fp.write(s) fp.write(b";") @@ -188,7 +188,7 @@ class TestFileGif(PillowTestCase): out = self.tempfile('temp.gif') fp = open(out, "wb") - im = Image.new('L',(100,100),'#000') + im = Image.new('L', (100, 100), '#000') for s in GifImagePlugin.getheader(im)[0] + GifImagePlugin.getdata(im, loop=number_of_loops): fp.write(s) fp.write(b";") From f028928b5acf10eed759827a6900a52542b254e2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 24 Apr 2015 00:44:27 +1000 Subject: [PATCH 0217/1037] Rearranged used_palette_colors to fix get_local_header --- PIL/GifImagePlugin.py | 52 ++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index cc41da949..4c59b612b 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -332,15 +332,19 @@ def get_local_header(fp, im, offset=(0, 0)): transparency = int(transparency) # optimize the block away if transparent color is not used transparent_color_exists = True - # adjust the transparency index after optimize - if used_palette_colors is not None and len(used_palette_colors) < 256: - for i in range(len(used_palette_colors)): - if used_palette_colors[i] == transparency: - transparency = i - transparent_color_exists = True - break - else: - transparent_color_exists = False + + if _get_optimize(im, im.encoderinfo): + used_palette_colors = _get_used_palette_colors(im) + + # adjust the transparency index after optimize + if len(used_palette_colors) < 256: + for i in range(len(used_palette_colors)): + if used_palette_colors[i] == transparency: + transparency = i + transparent_color_exists = True + break + else: + transparent_color_exists = False if "duration" in im.encoderinfo: duration = int(im.encoderinfo["duration"] / 10) @@ -433,11 +437,26 @@ def _save_netpbm(im, fp, filename): # -------------------------------------------------------------------- # GIF utilities +def _get_optimize(im, info): + return im.mode in ("P", "L") and info and info.get("optimize", 0) + + +def _get_used_palette_colors(im): + used_palette_colors = [] + + # check which colors are used + i = 0 + for count in im.histogram(): + if count: + used_palette_colors.append(i) + i += 1 + + return used_palette_colors + + def getheader(im, palette=None, info=None): """Return a list of strings representing a GIF header""" - optimize = info and info.get("optimize", 0) - # Header Block # http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp header = [ @@ -459,15 +478,8 @@ def getheader(im, palette=None, info=None): used_palette_colors = palette_bytes = None - if im.mode in ("P", "L") and optimize: - used_palette_colors = [] - - # check which colors are used - i = 0 - for count in im.histogram(): - if count: - used_palette_colors.append(i) - i += 1 + if _get_optimize(im, info): + used_palette_colors = _get_used_palette_colors(im) # create the new palette if not every color is used if len(used_palette_colors) < 256: From d1c182cadcf903b347c89f5e2a0796167e726c87 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 24 Apr 2015 09:26:52 +1000 Subject: [PATCH 0218/1037] Various Flake8 fixes --- PIL/FitsStubImagePlugin.py | 3 + PIL/FpxImagePlugin.py | 4 +- PIL/Image.py | 4 +- PIL/ImageEnhance.py | 1 + PIL/ImageQt.py | 3 +- PIL/JpegImagePlugin.py | 2 +- PIL/JpegPresets.py | 18 ++-- PIL/OleFileIO.py | 151 +++++++++++++------------------ PIL/PcxImagePlugin.py | 10 +- PIL/PngImagePlugin.py | 1 + PIL/PyAccess.py | 2 +- PIL/TiffImagePlugin.py | 22 ++--- Tests/bench_cffi_access.py | 2 +- Tests/check_imaging_leaks.py | 2 +- Tests/check_jpeg_leaks.py | 7 +- Tests/check_png_dos.py | 11 ++- Tests/check_webp_leaks.py | 2 +- Tests/helper.py | 2 +- Tests/test_binary.py | 7 +- Tests/test_file_eps.py | 26 +++--- Tests/test_file_icns.py | 8 +- Tests/test_file_ico.py | 9 +- Tests/test_file_jpeg.py | 11 +-- Tests/test_file_libtiff.py | 14 +-- Tests/test_file_png.py | 3 +- Tests/test_file_tiff.py | 12 +-- Tests/test_file_tiff_metadata.py | 2 +- Tests/test_file_webp_metadata.py | 1 - Tests/test_file_xpm.py | 2 +- Tests/test_image_putdata.py | 6 +- Tests/test_image_resize.py | 6 +- Tests/test_image_transpose.py | 2 +- Tests/test_imagecms.py | 5 +- Tests/test_imagedraw.py | 8 +- Tests/test_imageenhance.py | 15 ++- Tests/test_imagefile.py | 2 +- Tests/test_imagefont_bitmap.py | 7 +- Tests/test_imagesequence.py | 8 +- Tests/test_numpy.py | 4 +- Tests/test_scipy.py | 26 +++--- 40 files changed, 201 insertions(+), 230 deletions(-) diff --git a/PIL/FitsStubImagePlugin.py b/PIL/FitsStubImagePlugin.py index 0b851ae59..7aefff212 100644 --- a/PIL/FitsStubImagePlugin.py +++ b/PIL/FitsStubImagePlugin.py @@ -18,6 +18,7 @@ _handler = None # # @param handler Handler object. + def register_handler(handler): global _handler _handler = handler @@ -25,9 +26,11 @@ def register_handler(handler): # -------------------------------------------------------------------- # Image adapter + def _accept(prefix): return prefix[:6] == b"SIMPLE" + class FITSStubImageFile(ImageFile.StubImageFile): format = "FITS" diff --git a/PIL/FpxImagePlugin.py b/PIL/FpxImagePlugin.py index ed0c20c4e..9d338d9da 100644 --- a/PIL/FpxImagePlugin.py +++ b/PIL/FpxImagePlugin.py @@ -136,9 +136,9 @@ class FpxImageFile(ImageFile.ImageFile): s = fp.read(36) size = i32(s, 4), i32(s, 8) - #tilecount = i32(s, 12) + # tilecount = i32(s, 12) tilesize = i32(s, 16), i32(s, 20) - #channels = i32(s, 24) + # channels = i32(s, 24) offset = i32(s, 28) length = i32(s, 32) diff --git a/PIL/Image.py b/PIL/Image.py index 7cfa72e6f..9c459dfb1 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -387,7 +387,7 @@ def init(): for plugin in _plugins: try: if DEBUG: - print ("Importing %s" % plugin) + print("Importing %s" % plugin) __import__("PIL.%s" % plugin, globals(), locals(), []) except ImportError: if DEBUG: @@ -546,7 +546,7 @@ class Image: self.fp.close() except Exception as msg: if DEBUG: - print ("Error closing: %s" % msg) + print("Error closing: %s" % msg) # Instead of simply setting to None, we're setting up a # deferred error that will better explain that the core image diff --git a/PIL/ImageEnhance.py b/PIL/ImageEnhance.py index a196d5b09..8c0f166f3 100644 --- a/PIL/ImageEnhance.py +++ b/PIL/ImageEnhance.py @@ -53,6 +53,7 @@ class Color(_Enhance): self.degenerate = image.convert(self.intermediate_mode).convert(image.mode) + class Contrast(_Enhance): """Adjust image contrast. diff --git a/PIL/ImageQt.py b/PIL/ImageQt.py index 22ee2ea8f..1723e3226 100644 --- a/PIL/ImageQt.py +++ b/PIL/ImageQt.py @@ -29,12 +29,13 @@ if 'PyQt4.QtGui' not in sys.modules: except: from PySide.QtGui import QImage, qRgba -else: #PyQt4 is used +else: #PyQt4 is used from PyQt4.QtGui import QImage, qRgba ## # (Internal) Turns an RGB color into a Qt compatible color integer. + def rgb(r, g, b, a=255): # use qRgb to pack the colors, and then turn the resulting long # into a negative integer with the same bitpattern. diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 8c20f5863..0ecc902e4 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -684,7 +684,7 @@ def _save(im, fp, filename): # https://github.com/jdriscoll/django-imagekit/issues/50 bufsize = 0 if "optimize" in info or "progressive" in info or "progression" in info: - # keep sets quality to 0, but the actual value may be high. + # keep sets quality to 0, but the actual value may be high. if quality >= 95 or quality == 0: bufsize = 2 * im.size[0] * im.size[1] else: diff --git a/PIL/JpegPresets.py b/PIL/JpegPresets.py index 6ca46d0cd..67af9ac9a 100644 --- a/PIL/JpegPresets.py +++ b/PIL/JpegPresets.py @@ -67,7 +67,7 @@ Libjpeg ref.: http://www.jpegcameras.com/libjpeg/libjpeg-3.html """ presets = { - 'web_low': {'subsampling': 2, # "4:1:1" + 'web_low': {'subsampling': 2, # "4:1:1" 'quantization': [ [20, 16, 25, 39, 50, 46, 62, 68, 16, 18, 23, 38, 38, 53, 65, 68, @@ -86,7 +86,7 @@ presets = { 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68] ]}, - 'web_medium': {'subsampling': 2, # "4:1:1" + 'web_medium': {'subsampling': 2, # "4:1:1" 'quantization': [ [16, 11, 11, 16, 23, 27, 31, 30, 11, 12, 12, 15, 20, 23, 23, 30, @@ -105,7 +105,7 @@ presets = { 38, 35, 46, 53, 64, 64, 64, 64, 48, 43, 53, 64, 64, 64, 64, 64] ]}, - 'web_high': {'subsampling': 0, # "4:4:4" + 'web_high': {'subsampling': 0, # "4:4:4" 'quantization': [ [ 6, 4, 4, 6, 9, 11, 12, 16, 4, 5, 5, 6, 8, 10, 12, 12, @@ -124,7 +124,7 @@ presets = { 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31] ]}, - 'web_very_high': {'subsampling': 0, # "4:4:4" + 'web_very_high': {'subsampling': 0, # "4:4:4" 'quantization': [ [ 2, 2, 2, 2, 3, 4, 5, 6, 2, 2, 2, 2, 3, 4, 5, 6, @@ -143,7 +143,7 @@ presets = { 15, 12, 12, 12, 12, 12, 12, 12, 15, 12, 12, 12, 12, 12, 12, 12] ]}, - 'web_maximum': {'subsampling': 0, # "4:4:4" + 'web_maximum': {'subsampling': 0, # "4:4:4" 'quantization': [ [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -162,7 +162,7 @@ presets = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3] ]}, - 'low': {'subsampling': 2, # "4:1:1" + 'low': {'subsampling': 2, # "4:1:1" 'quantization': [ [18, 14, 14, 21, 30, 35, 34, 17, 14, 16, 16, 19, 26, 23, 12, 12, @@ -181,7 +181,7 @@ presets = { 17, 12, 12, 12, 12, 12, 12, 12, 17, 12, 12, 12, 12, 12, 12, 12] ]}, - 'medium': {'subsampling': 2, # "4:1:1" + 'medium': {'subsampling': 2, # "4:1:1" 'quantization': [ [12, 8, 8, 12, 17, 21, 24, 17, 8, 9, 9, 11, 15, 19, 12, 12, @@ -200,7 +200,7 @@ presets = { 17, 12, 12, 12, 12, 12, 12, 12, 17, 12, 12, 12, 12, 12, 12, 12] ]}, - 'high': {'subsampling': 0, # "4:4:4" + 'high': {'subsampling': 0, # "4:4:4" 'quantization': [ [ 6, 4, 4, 6, 9, 11, 12, 16, 4, 5, 5, 6, 8, 10, 12, 12, @@ -219,7 +219,7 @@ presets = { 17, 12, 12, 12, 12, 12, 12, 12, 17, 12, 12, 12, 12, 12, 12, 12] ]}, - 'maximum': {'subsampling': 0, # "4:4:4" + 'maximum': {'subsampling': 0, # "4:4:4" 'quantization': [ [ 2, 2, 2, 2, 3, 4, 5, 6, 2, 2, 2, 2, 3, 4, 5, 6, diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index a08ae0ee3..a94b760c8 100755 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -28,7 +28,7 @@ # This import enables print() as a function rather than a keyword # (main requirement to be compatible with Python 3.x) # The comment on the line below should be printed on Python 2.5 or older: -from __future__ import print_function # This version of OleFileIO_PL requires Python 2.6+ or 3.x. +from __future__ import print_function # This version of OleFileIO_PL requires Python 2.6+ or 3.x. __author__ = "Philippe Lagadec, Fredrik Lundh (Secret Labs AB)" @@ -234,7 +234,10 @@ __version__ = '0.30' import io import sys -import struct, array, os.path, datetime +import struct +import array +import os.path +import datetime #[PL] Define explicitly the public API to avoid private objects in pydoc: __all__ = ['OleFileIO', 'isOleFile', 'MAGIC'] @@ -283,12 +286,17 @@ KEEP_UNICODE_NAMES = False #[PL] DEBUG display mode: False by default, use set_debug_mode() or "-d" on # command line to change it. DEBUG_MODE = False + + def debug_print(msg): print(msg) + + def debug_pass(msg): pass debug = debug_pass + def set_debug_mode(debug_mode): """ Set debug mode on or off, to control display of debugging messages. @@ -303,24 +311,24 @@ def set_debug_mode(debug_mode): MAGIC = b'\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1' -#[PL]: added constants for Sector IDs (from AAF specifications) +# [PL]: added constants for Sector IDs (from AAF specifications) MAXREGSECT = 0xFFFFFFFA; # maximum SECT DIFSECT = 0xFFFFFFFC; # (-4) denotes a DIFAT sector in a FAT FATSECT = 0xFFFFFFFD; # (-3) denotes a FAT sector in a FAT ENDOFCHAIN = 0xFFFFFFFE; # (-2) end of a virtual stream chain FREESECT = 0xFFFFFFFF; # (-1) unallocated sector -#[PL]: added constants for Directory Entry IDs (from AAF specifications) +# [PL]: added constants for Directory Entry IDs (from AAF specifications) MAXREGSID = 0xFFFFFFFA; # maximum directory entry ID NOSTREAM = 0xFFFFFFFF; # (-1) unallocated directory entry -#[PL] object types in storage (from AAF specifications) -STGTY_EMPTY = 0 # empty directory entry (according to OpenOffice.org doc) -STGTY_STORAGE = 1 # element is a storage object -STGTY_STREAM = 2 # element is a stream object -STGTY_LOCKBYTES = 3 # element is an ILockBytes object -STGTY_PROPERTY = 4 # element is an IPropertyStorage object -STGTY_ROOT = 5 # element is a root storage +# [PL] object types in storage (from AAF specifications) +STGTY_EMPTY = 0 # empty directory entry (according to OpenOffice.org doc) +STGTY_STORAGE = 1 # element is a storage object +STGTY_STREAM = 2 # element is a stream object +STGTY_LOCKBYTES = 3 # element is an ILockBytes object +STGTY_PROPERTY = 4 # element is an IPropertyStorage object +STGTY_ROOT = 5 # element is a root storage # @@ -370,7 +378,7 @@ for key in list(vars().keys()): def isOleFile (filename): """ Test if file is an OLE container (according to its header). - + :param filename: file name or path (str, unicode) :returns: True if OLE, False otherwise. """ @@ -433,7 +441,6 @@ def _clsid(clsid): tuple(map(i8, clsid[8:16])))) - # UNICODE support: # (necessary to handle storages/streams names which use Unicode) @@ -471,7 +478,6 @@ def filetime2datetime(filetime): return _FILETIME_null_date + datetime.timedelta(microseconds=filetime//10) - #=== CLASSES ================================================================== class OleMetadata: @@ -579,7 +585,6 @@ class OleMetadata: self.language = None self.doc_version = None - def parse_properties(self, olefile): """ Parse standard properties of an OLE file, from the streams @@ -663,7 +668,7 @@ class _OleStream(io.BytesIO): """ debug('_OleStream.__init__:') debug(' sect=%d (%X), size=%d, offset=%d, sectorsize=%d, len(fat)=%d, fp=%s' - %(sect,sect,size,offset,sectorsize,len(fat), repr(fp))) + %(sect, sect, size, offset, sectorsize, len(fat), repr(fp))) #[PL] To detect malformed documents with FAT loops, we compute the # expected number of sectors in the stream: unknown_size = False @@ -789,7 +794,6 @@ class _OleDirectoryEntry: DIRENTRY_SIZE = 128 assert struct.calcsize(STRUCT_DIRENTRY) == DIRENTRY_SIZE - def __init__(self, entry, sid, olefile): """ Constructor for an _OleDirectoryEntry object. @@ -882,8 +886,6 @@ class _OleDirectoryEntry: minifat = False olefile._check_duplicate_stream(self.isectStart, minifat) - - def build_storage_tree(self): """ Read and build the red-black tree attached to this _OleDirectoryEntry @@ -907,7 +909,6 @@ class _OleDirectoryEntry: # (see rich comparison methods in this class) self.kids.sort() - def append_kids(self, child_sid): """ Walk through red-black tree of children of this directory entry to add @@ -916,7 +917,7 @@ class _OleDirectoryEntry: child_sid : index of child directory entry to use, or None when called first time for the root. (only used during recursion) """ - #[PL] this method was added to use simple recursion instead of a complex + # [PL] this method was added to use simple recursion instead of a complex # algorithm. # if this is not a storage or a leaf of the tree, nothing to do: if child_sid == NOSTREAM: @@ -951,7 +952,6 @@ class _OleDirectoryEntry: # Afterwards build kid's own tree if it's also a storage: child.build_storage_tree() - def __eq__(self, other): "Compare entries by name" return self.name == other.name @@ -971,7 +971,6 @@ class _OleDirectoryEntry: #TODO: replace by the same function as MS implementation ? # (order by name length first, then case-insensitive order) - def dump(self, tab = 0): "Dump this entry, and all its subentries (for debug purposes only)" TYPES = ["(invalid)", "(storage)", "(stream)", "(lockbytes)", @@ -986,7 +985,6 @@ class _OleDirectoryEntry: for kid in self.kids: kid.dump(tab + 2) - def getmtime(self): """ Return modification time of a directory entry. @@ -1000,7 +998,6 @@ class _OleDirectoryEntry: return None return filetime2datetime(self.modifyTime) - def getctime(self): """ Return creation time of a directory entry. @@ -1064,7 +1061,6 @@ class OleFileIO: if filename: self.open(filename) - def _raise_defect(self, defect_level, message, exception_type=IOError): """ This method should be called for any defect found during file parsing. @@ -1086,7 +1082,6 @@ class OleFileIO: # just record the issue, no exception raised: self.parsing_issues.append((exception_type, message)) - def open(self, filename): """ Open an OLE2 file. @@ -1189,7 +1184,7 @@ class OleFileIO: self.sectDifStart, self.csectDif ) = struct.unpack(fmt_header, header1) - debug( struct.unpack(fmt_header, header1)) + debug(struct.unpack(fmt_header, header1)) if self.Sig != MAGIC: # OLE signature should always be present @@ -1231,11 +1226,11 @@ class OleFileIO: # rule => only a potential defect: if self.signature != 0: self._raise_defect(DEFECT_POTENTIAL, "incorrect OLE header (signature>0)") - debug( "MiniSectorCutoff = %d" % self.MiniSectorCutoff ) - debug( "MiniFatStart = %X" % self.MiniFatStart ) - debug( "csectMiniFat = %d" % self.csectMiniFat ) - debug( "sectDifStart = %X" % self.sectDifStart ) - debug( "csectDif = %d" % self.csectDif ) + debug("MiniSectorCutoff = %d" % self.MiniSectorCutoff) + debug("MiniFatStart = %X" % self.MiniFatStart) + debug("csectMiniFat = %d" % self.csectMiniFat) + debug("sectDifStart = %X" % self.sectDifStart) + debug("csectDif = %d" % self.csectDif) # calculate the number of sectors in the file # (-1 because header doesn't count) @@ -1266,14 +1261,12 @@ class OleFileIO: self.ministream = None self.minifatsect = self.MiniFatStart #i32(header, 60) - def close(self): """ close the OLE file, to release the file object """ self.fp.close() - def _check_duplicate_stream(self, first_sect, minifat=False): """ Checks if a stream has not been already referenced elsewhere. @@ -1298,7 +1291,6 @@ class OleFileIO: else: used_streams.append(first_sect) - def dumpfat(self, fat, firstindex=0): "Displays a part of FAT in human-readable form for debugging purpose" # [PL] added only for debug @@ -1335,7 +1327,6 @@ class OleFileIO: print(nom, end=" ") print() - def dumpsect(self, sector, firstindex=0): "Displays a sector in a human-readable form, for debugging purpose." if not DEBUG_MODE: @@ -1370,11 +1361,10 @@ class OleFileIO: a.byteswap() return a - def loadfat_sect(self, sect): """ Adds the indexes of the given sector to the FAT - + :param sect: string containing the first FAT sector, or array of long integers :returns: index of last FAT sector. """ @@ -1400,7 +1390,6 @@ class OleFileIO: self.fat = self.fat + nextfat return isect - def loadfat(self, header): """ Load the FAT table. @@ -1436,16 +1425,16 @@ class OleFileIO: if self.sectDifStart >= self.nb_sect: # initial DIFAT block index must be valid self._raise_defect(DEFECT_FATAL, 'incorrect DIFAT, first index out of range') - debug( "DIFAT analysis..." ) + debug("DIFAT analysis...") # We compute the necessary number of DIFAT sectors : # (each DIFAT sector = 127 pointers + 1 towards next DIFAT sector) nb_difat = (self.csectFat-109 + 126)//127 - debug( "nb_difat = %d" % nb_difat ) + debug("nb_difat = %d" % nb_difat) if self.csectDif != nb_difat: raise IOError('incorrect DIFAT') isect_difat = self.sectDifStart for i in iterrange(nb_difat): - debug( "DIFAT block %d, sector %X" % (i, isect_difat) ) + debug("DIFAT block %d, sector %X" % (i, isect_difat)) #TODO: check if corresponding FAT SID = DIFSECT sector_difat = self.getsect(isect_difat) difat = self.sect2array(sector_difat) @@ -1453,7 +1442,7 @@ class OleFileIO: self.loadfat_sect(difat[:127]) # last DIFAT pointer is next DIFAT sector: isect_difat = difat[127] - debug( "next DIFAT sector: %X" % isect_difat ) + debug("next DIFAT sector: %X" % isect_difat) # checks: if isect_difat not in [ENDOFCHAIN, FREESECT]: # last DIFAT pointer value must be ENDOFCHAIN or FREESECT @@ -1471,7 +1460,6 @@ class OleFileIO: debug('\nFAT:') self.dumpfat(self.fat) - def loadminifat(self): """ Load the MiniFAT table. @@ -1509,7 +1497,7 @@ class OleFileIO: def getsect(self, sect): """ Read given sector from file on disk. - + :param sect: sector index :returns: a string containing the sector data. """ @@ -1531,11 +1519,10 @@ class OleFileIO: self._raise_defect(DEFECT_FATAL, 'incomplete OLE sector') return sector - def loaddirectory(self, sect): """ Load the directory. - + :param sect: sector index of directory stream. """ # The directory is stored in a standard @@ -1567,7 +1554,6 @@ class OleFileIO: # read and build all storage trees, starting from the root: self.root.build_storage_tree() - def _load_direntry (self, sid): """ Load a directory entry from the directory. @@ -1592,14 +1578,12 @@ class OleFileIO: self.direntries[sid] = _OleDirectoryEntry(entry, sid, self) return self.direntries[sid] - def dumpdirectory(self): """ Dump directory (for debugging only) """ self.root.dump() - def _open(self, start, size = 0x7FFFFFFF, force_FAT=False): """ Open a stream, either in FAT or MiniFAT according to its size. @@ -1633,7 +1617,6 @@ class OleFileIO: return _OleStream(self.fp, start, size, 512, self.sectorsize, self.fat, self._filesize) - def _list(self, files, prefix, node, streams=True, storages=False): """ (listdir helper) @@ -1659,7 +1642,6 @@ class OleFileIO: # add it to the list files.append(prefix[1:] + [entry.name]) - def listdir(self, streams=True, storages=False): """ Return a list of streams stored in this file @@ -1672,14 +1654,13 @@ class OleFileIO: self._list(files, [], self.root, streams, storages) return files - def _find(self, filename): """ Returns directory entry of given filename. (openstream helper) Note: this method is case-insensitive. :param filename: path of stream in storage tree (except root entry), either: - + - a string using Unix path syntax, for example: 'storage_1/storage_1.2/stream' - a list of storage filenames, path to the desired stream/storage. @@ -1703,18 +1684,17 @@ class OleFileIO: node = kid return node.sid - def openstream(self, filename): """ Open a stream as a read-only file object (BytesIO). :param filename: path of stream in storage tree (except root entry), either: - + - a string using Unix path syntax, for example: 'storage_1/storage_1.2/stream' - a list of storage filenames, path to the desired stream/storage. Example: ['storage_1', 'storage_1.2', 'stream'] - + :returns: file object (read-only) :exception IOError: if filename not found, or if this is not a stream. """ @@ -1724,7 +1704,6 @@ class OleFileIO: raise IOError("this file is not a stream") return self._open(entry.isectStart, entry.size) - def get_type(self, filename): """ Test if given filename exists as a stream or a storage in the OLE @@ -1732,7 +1711,7 @@ class OleFileIO: :param filename: path of stream in storage tree. (see openstream for syntax) :returns: False if object does not exist, its entry type (>0) otherwise: - + - STGTY_STREAM: a stream - STGTY_STORAGE: a storage - STGTY_ROOT: the root entry @@ -1744,7 +1723,6 @@ class OleFileIO: except: return False - def getmtime(self, filename): """ Return modification time of a stream/storage. @@ -1760,7 +1738,6 @@ class OleFileIO: entry = self.direntries[sid] return entry.getmtime() - def getctime(self, filename): """ Return creation time of a stream/storage. @@ -1776,7 +1753,6 @@ class OleFileIO: entry = self.direntries[sid] return entry.getctime() - def exists(self, filename): """ Test if given filename exists as a stream or a storage in the OLE @@ -1791,7 +1767,6 @@ class OleFileIO: except: return False - def get_size(self, filename): """ Return size of a stream in the OLE container, in bytes. @@ -1808,7 +1783,6 @@ class OleFileIO: raise TypeError('object is not an OLE stream') return entry.size - def get_rootentry_name(self): """ Return root entry name. Should usually be 'Root Entry' or 'R' in most @@ -1816,7 +1790,6 @@ class OleFileIO: """ return self.root.name - def getproperties(self, filename, convert_time=False, no_conversion=None): """ Return properties described in substream. @@ -1828,7 +1801,7 @@ class OleFileIO: :returns: a dictionary of values indexed by id (integer) """ # make sure no_conversion is a list, just to simplify code below: - if no_conversion == None: + if no_conversion is None: no_conversion = [] # stream path as a string to report exceptions: streampath = filename @@ -1842,11 +1815,11 @@ class OleFileIO: try: # header s = fp.read(28) - #clsid = _clsid(s[8:24]) + # clsid = _clsid(s[8:24]) # format id s = fp.read(20) - #fmtid = _clsid(s[:16]) + # fmtid = _clsid(s[:16]) fp.seek(i32(s, 16)) # get section @@ -1864,34 +1837,34 @@ class OleFileIO: for i in range(num_props): try: - id = 0 # just in case of an exception + id = 0 # just in case of an exception id = i32(s, 8+i*8) offset = i32(s, 12+i*8) type = i32(s, offset) - debug ('property id=%d: type=%d offset=%X' % (id, type, offset)) + debug('property id=%d: type=%d offset=%X' % (id, type, offset)) # test for common types first (should perhaps use # a dictionary instead?) - if type == VT_I2: # 16-bit signed integer + if type == VT_I2: # 16-bit signed integer value = i16(s, offset+4) if value >= 32768: value = value - 65536 - elif type == VT_UI2: # 2-byte unsigned integer + elif type == VT_UI2: # 2-byte unsigned integer value = i16(s, offset+4) elif type in (VT_I4, VT_INT, VT_ERROR): # VT_I4: 32-bit signed integer # VT_ERROR: HRESULT, similar to 32-bit signed integer, # see http://msdn.microsoft.com/en-us/library/cc230330.aspx value = i32(s, offset+4) - elif type in (VT_UI4, VT_UINT): # 4-byte unsigned integer - value = i32(s, offset+4) # FIXME + elif type in (VT_UI4, VT_UINT): # 4-byte unsigned integer + value = i32(s, offset+4) # FIXME elif type in (VT_BSTR, VT_LPSTR): # CodePageString, see http://msdn.microsoft.com/en-us/library/dd942354.aspx # size is a 32 bits integer, including the null terminator, and # possibly trailing or embedded null chars - #TODO: if codepage is unicode, the string should be converted as such + # TODO: if codepage is unicode, the string should be converted as such count = i32(s, offset+4) value = s[offset+8:offset+8+count-1] # remove all null chars: @@ -1909,7 +1882,7 @@ class OleFileIO: count = i32(s, offset+4) value = _unicode(s[offset+8:offset+8+count*2]) elif type == VT_FILETIME: - value = long(i32(s, offset+4)) + (long(i32(s, offset+8))<<32) + value = long(i32(s, offset+4)) + (long(i32(s, offset+8)) << 32) # FILETIME is a 64-bit int: "number of 100ns periods # since Jan 1,1601". if convert_time and id not in no_conversion: @@ -1923,8 +1896,8 @@ class OleFileIO: else: # legacy code kept for backward compatibility: returns a # number of seconds since Jan 1,1601 - value = value // 10000000 # seconds - elif type == VT_UI1: # 1-byte unsigned integer + value = value // 10000000 # seconds + elif type == VT_UI1: # 1-byte unsigned integer value = i8(s[offset+4]) elif type == VT_CLSID: value = _clsid(s[offset+4:offset+20]) @@ -1938,8 +1911,8 @@ class OleFileIO: # see http://msdn.microsoft.com/en-us/library/cc237864.aspx value = bool(i16(s, offset+4)) else: - value = None # everything else yields "None" - debug ('property id=%d: type=%d not implemented in parser yet' % (id, type)) + value = None # everything else yields "None" + debug('property id=%d: type=%d not implemented in parser yet' % (id, type)) # missing: VT_EMPTY, VT_NULL, VT_R4, VT_R8, VT_CY, VT_DATE, # VT_DECIMAL, VT_I1, VT_I8, VT_UI8, @@ -1951,8 +1924,8 @@ class OleFileIO: # type of items, e.g. VT_VECTOR|VT_BSTR # see http://msdn.microsoft.com/en-us/library/dd942011.aspx - #print("%08x" % id, repr(value), end=" ") - #print("(%s)" % VT[i32(s, offset) & 0xFFF]) + # print("%08x" % id, repr(value), end=" ") + # print("(%s)" % VT[i32(s, offset) & 0xFFF]) data[id] = value except BaseException as exc: @@ -1999,7 +1972,7 @@ Options: check_streams = False for filename in sys.argv[1:]: - #try: + # try: # OPTIONS: if filename == '-d': # option to switch debug mode on: @@ -2010,7 +1983,7 @@ Options: check_streams = True continue - ole = OleFileIO(filename)#, raise_defects=DEFECT_INCORRECT) + ole = OleFileIO(filename) #, raise_defects=DEFECT_INCORRECT) print("-" * 68) print(filename) print("-" * 68) @@ -2027,8 +2000,8 @@ Options: v = v[:50] if isinstance(v, bytes): # quick and dirty binary check: - for c in (1,2,3,4,5,6,7,11,12,14,15,16,17,18,19,20, - 21,22,23,24,25,26,27,28,29,30,31): + for c in (1, 2, 3, 4, 5, 6, 7, 11, 12, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31): if c in bytearray(v): v = '(binary data)' break @@ -2039,7 +2012,7 @@ Options: print('\nChecking streams...') for streamname in ole.listdir(): # print name using repr() to convert binary chars to \xNN: - print('-', repr('/'.join(streamname)),'-', end=' ') + print('-', repr('/'.join(streamname)), '-', end=' ') st_type = ole.get_type(streamname) if st_type == STGTY_STREAM: print('size %d' % ole.get_size(streamname)) @@ -2066,7 +2039,7 @@ Options: meta = ole.get_metadata() meta.dump() print() - #[PL] Test a few new methods: + # [PL] Test a few new methods: root = ole.get_rootentry_name() print('Root entry name: "%s"' % root) if ole.exists('worddocument'): diff --git a/PIL/PcxImagePlugin.py b/PIL/PcxImagePlugin.py index 0765f099e..8eac6a594 100644 --- a/PIL/PcxImagePlugin.py +++ b/PIL/PcxImagePlugin.py @@ -58,7 +58,7 @@ class PcxImageFile(ImageFile.ImageFile): if bbox[2] <= bbox[0] or bbox[3] <= bbox[1]: raise SyntaxError("bad PCX image size") if Image.DEBUG: - print ("BBox: %s %s %s %s" % bbox) + print("BBox: %s %s %s %s" % bbox) # format version = i8(s[1]) @@ -66,8 +66,8 @@ class PcxImageFile(ImageFile.ImageFile): planes = i8(s[65]) stride = i16(s, 66) if Image.DEBUG: - print ("PCX version %s, bits %s, planes %s, stride %s" % - (version, bits, planes, stride)) + print("PCX version %s, bits %s, planes %s, stride %s" % + (version, bits, planes, stride)) self.info["dpi"] = i16(s, 12), i16(s, 14) @@ -106,7 +106,7 @@ class PcxImageFile(ImageFile.ImageFile): bbox = (0, 0) + self.size if Image.DEBUG: - print ("size: %sx%s" % self.size) + print("size: %sx%s" % self.size) self.tile = [("pcx", bbox, self.fp.tell(), (rawmode, planes * stride))] @@ -143,7 +143,7 @@ def _save(im, fp, filename, check=0): # gets overwritten. if Image.DEBUG: - print ("PcxImagePlugin._save: xwidth: %d, bits: %d, stride: %d" % ( + print("PcxImagePlugin._save: xwidth: %d, bits: %d, stride: %d" % ( im.size[0], bits, stride)) # under windows, we could determine the current screen size with diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index d8593f90d..398a01f33 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -78,6 +78,7 @@ MAX_TEXT_CHUNK = ImageFile.SAFEBLOCK # Set the maximum total text chunk size. MAX_TEXT_MEMORY = 64 * MAX_TEXT_CHUNK + def _safe_zlib_decompress(s): dobj = zlib.decompressobj() plaintext = dobj.decompress(s, MAX_TEXT_CHUNK) diff --git a/PIL/PyAccess.py b/PIL/PyAccess.py index a3f1c3909..87a6d4915 100644 --- a/PIL/PyAccess.py +++ b/PIL/PyAccess.py @@ -51,7 +51,7 @@ class PyAccess(object): self.ysize = vals['ysize'] if DEBUG: - print (vals) + print(vals) self._post_init() def _post_init(self): diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index d7291e87c..e4028fdbb 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -506,7 +506,7 @@ class ImageFileDirectory(collections.MutableMapping): typ = self.tagtype[tag] if Image.DEBUG: - print ("Tag %s, Type: %s, Value: %s" % (tag, typ, value)) + print("Tag %s, Type: %s, Value: %s" % (tag, typ, value)) if typ == 1: # byte data @@ -638,9 +638,9 @@ class TiffImageFile(ImageFile.ImageFile): self.__fp = self.fp if Image.DEBUG: - print ("*** TiffImageFile._open ***") - print ("- __first:", self.__first) - print ("- ifh: ", ifh) + print("*** TiffImageFile._open ***") + print("- __first:", self.__first) + print("- ifh: ", ifh) # and load the first frame self._seek(0) @@ -751,19 +751,19 @@ class TiffImageFile(ImageFile.ImageFile): # that returns an IOError if there's no underlying fp. Easier to # dea. with here by reordering. if Image.DEBUG: - print ("have getvalue. just sending in a string from getvalue") + print("have getvalue. just sending in a string from getvalue") n, err = decoder.decode(self.fp.getvalue()) elif hasattr(self.fp, "fileno"): # we've got a actual file on disk, pass in the fp. if Image.DEBUG: - print ("have fileno, calling fileno version of the decoder.") + print("have fileno, calling fileno version of the decoder.") self.fp.seek(0) # 4 bytes, otherwise the trace might error out n, err = decoder.decode(b"fpfp") else: # we have something else. if Image.DEBUG: - print ("don't have fileno or getvalue. just reading") + print("don't have fileno or getvalue. just reading") # UNDONE -- so much for that buffer size thing. n, err = decoder.decode(self.fp.read()) @@ -943,7 +943,7 @@ class TiffImageFile(ImageFile.ImageFile): (0, min(y, ysize), w, min(y+h, ysize)), offsets[i], a)) if Image.DEBUG: - print ("tiles: ", self.tile) + print("tiles: ", self.tile) y = y + h if y >= self.size[1]: x = y = 0 @@ -1128,8 +1128,8 @@ def _save(im, fp, filename): if libtiff: if Image.DEBUG: - print ("Saving using libtiff encoder") - print (ifd.items()) + print("Saving using libtiff encoder") + print(ifd.items()) _fp = 0 if hasattr(fp, "fileno"): try: @@ -1186,7 +1186,7 @@ def _save(im, fp, filename): atts[k] = v if Image.DEBUG: - print (atts) + print(atts) # libtiff always expects the bytes in native order. # we're storing image byte order. So, if the rawmode diff --git a/Tests/bench_cffi_access.py b/Tests/bench_cffi_access.py index 769396f53..5b64813e4 100644 --- a/Tests/bench_cffi_access.py +++ b/Tests/bench_cffi_access.py @@ -47,7 +47,7 @@ class BenchCffiAccess(PillowTestCase): self.assertEqual(caccess[(0, 0)], access[(0, 0)]) - print ("Size: %sx%s" % im.size) + print("Size: %sx%s" % im.size) timer(iterate_get, 'PyAccess - get', im.size, access) timer(iterate_set, 'PyAccess - set', im.size, access) timer(iterate_get, 'C-api - get', im.size, caccess) diff --git a/Tests/check_imaging_leaks.py b/Tests/check_imaging_leaks.py index 3a7dcaa2b..c6d99b8d1 100644 --- a/Tests/check_imaging_leaks.py +++ b/Tests/check_imaging_leaks.py @@ -8,6 +8,7 @@ from PIL import Image, ImageFilter min_iterations = 100 max_iterations = 10000 + @unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or MacOS") class TestImagingLeaks(PillowTestCase): @@ -40,4 +41,3 @@ class TestImagingLeaks(PillowTestCase): if __name__ == '__main__': unittest.main() - diff --git a/Tests/check_jpeg_leaks.py b/Tests/check_jpeg_leaks.py index adf7652cf..290bac4d8 100644 --- a/Tests/check_jpeg_leaks.py +++ b/Tests/check_jpeg_leaks.py @@ -71,9 +71,8 @@ post-patch: | :@:@@: :#:::@ :::@::::@ : :: : @ :::::: :@:: ::: :::: @:: @:::::::@:::: 0 +----------------------------------------------------------------------->Gi 0 8.421 - -""" +""" def test_qtables_leak(self): im = hopper('RGB') @@ -103,7 +102,6 @@ post-patch: qtables = [standard_l_qtable, standard_chrominance_qtable] - for count in range(iterations): test_output = BytesIO() im.save(test_output, "JPEG", qtables=qtables) @@ -135,7 +133,7 @@ pre patch: 0 +----------------------------------------------------------------------->Gi 0 11.37 - + post patch: MB @@ -172,7 +170,6 @@ post patch: test_output = BytesIO() im.save(test_output, "JPEG", exif=exif) - """ base case: MB diff --git a/Tests/check_png_dos.py b/Tests/check_png_dos.py index c74990a8c..762c9607a 100644 --- a/Tests/check_png_dos.py +++ b/Tests/check_png_dos.py @@ -5,6 +5,7 @@ import zlib TEST_FILE = "Tests/images/png_decompression_dos.png" + class TestPngDos(PillowTestCase): def test_dos_text(self): @@ -19,19 +20,19 @@ class TestPngDos(PillowTestCase): self.assertLess(len(s), 1024*1024, "Text chunk larger than 1M") def test_dos_total_memory(self): - im = Image.new('L',(1,1)) + im = Image.new('L', (1, 1)) compressed_data = zlib.compress('a'*1024*1023) info = PngImagePlugin.PngInfo() for x in range(64): - info.add_text('t%s'%x, compressed_data, 1) - info.add_itxt('i%s'%x, compressed_data, zip=True) + info.add_text('t%s' % x, compressed_data, 1) + info.add_itxt('i%s' % x, compressed_data, zip=True) b = BytesIO() im.save(b, 'PNG', pnginfo=info) b.seek(0) - + try: im2 = Image.open(b) except ValueError as msg: @@ -42,6 +43,6 @@ class TestPngDos(PillowTestCase): for txt in im2.text.values(): total_len += len(txt) self.assertLess(total_len, 64*1024*1024, "Total text chunks greater than 64M") - + if __name__ == '__main__': unittest.main() diff --git a/Tests/check_webp_leaks.py b/Tests/check_webp_leaks.py index 95162ad4a..79e22328b 100644 --- a/Tests/check_webp_leaks.py +++ b/Tests/check_webp_leaks.py @@ -5,7 +5,7 @@ from PIL import Image from io import BytesIO # Limits for testing the leak -mem_limit = 16 # max increase in MB +mem_limit = 16 # max increase in MB iterations = 5000 test_file = "Tests/images/hopper.webp" diff --git a/Tests/helper.py b/Tests/helper.py index c59caa2f5..1255b1819 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -130,7 +130,7 @@ class PillowTestCase(unittest.TestCase): # Skip if platform/travis matches, and # PILLOW_RUN_KNOWN_BAD is not true in the environment. if bool(os.environ.get('PILLOW_RUN_KNOWN_BAD', False)): - print (os.environ.get('PILLOW_RUN_KNOWN_BAD', False)) + print(os.environ.get('PILLOW_RUN_KNOWN_BAD', False)) return skip = True diff --git a/Tests/test_binary.py b/Tests/test_binary.py index 4d3fb5914..1e44d9641 100644 --- a/Tests/test_binary.py +++ b/Tests/test_binary.py @@ -2,6 +2,7 @@ from helper import unittest, PillowTestCase from PIL import _binary + class TestBinary(PillowTestCase): def test_standard(self): @@ -11,18 +12,18 @@ class TestBinary(PillowTestCase): def test_little_endian(self): self.assertEqual(_binary.i16le(b'\xff\xff\x00\x00'), 65535) self.assertEqual(_binary.i32le(b'\xff\xff\x00\x00'), 65535) - + self.assertEqual(_binary.o16le(65535), b'\xff\xff') self.assertEqual(_binary.o32le(65535), b'\xff\xff\x00\x00') def test_big_endian(self): self.assertEqual(_binary.i16be(b'\x00\x00\xff\xff'), 0) self.assertEqual(_binary.i32be(b'\x00\x00\xff\xff'), 65535) - + self.assertEqual(_binary.o16be(65535), b'\xff\xff') self.assertEqual(_binary.o32be(65535), b'\x00\x00\xff\xff') if __name__ == '__main__': unittest.main() -# End of file \ No newline at end of file +# End of file diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index 861cf264a..f1fbac922 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -148,8 +148,8 @@ class TestFileEps(PillowTestCase): # open image with binary preview Image.open(file3) - def _test_readline(self,t, ending): - ending = "Failure with line ending: %s" %("".join("%s" %ord(s) for s in ending)) + def _test_readline(self, t, ending): + ending = "Failure with line ending: %s" % ("".join("%s" % ord(s) for s in ending)) self.assertEqual(t.readline().strip('\r\n'), 'something', ending) self.assertEqual(t.readline().strip('\r\n'), 'else', ending) self.assertEqual(t.readline().strip('\r\n'), 'baz', ending) @@ -174,30 +174,30 @@ class TestFileEps(PillowTestCase): def _test_readline_file_universal(self, test_string, ending): f = self.tempfile('temp.txt') - with open(f,'wb') as w: + with open(f, 'wb') as w: if str is bytes: w.write(test_string) else: w.write(test_string.encode('UTF-8')) - with open(f,'rU') as t: + with open(f, 'rU') as t: self._test_readline(t, ending) def _test_readline_file_psfile(self, test_string, ending): f = self.tempfile('temp.txt') - with open(f,'wb') as w: + with open(f, 'wb') as w: if str is bytes: w.write(test_string) else: w.write(test_string.encode('UTF-8')) - with open(f,'rb') as r: + with open(f, 'rb') as r: t = EpsImagePlugin.PSFile(r) self._test_readline(t, ending) def test_readline(self): # check all the freaking line endings possible from the spec - #test_string = u'something\r\nelse\n\rbaz\rbif\n' + # test_string = u'something\r\nelse\n\rbaz\rbif\n' line_endings = ['\r\n', '\n'] not_working_endings = ['\n\r', '\r'] strings = ['something', 'else', 'baz', 'bif'] @@ -205,9 +205,9 @@ class TestFileEps(PillowTestCase): for ending in line_endings: s = ending.join(strings) # Native Python versions will pass these endings. - #self._test_readline_stringio(s, ending) - #self._test_readline_io(s, ending) - #self._test_readline_file_universal(s, ending) + # self._test_readline_stringio(s, ending) + # self._test_readline_io(s, ending) + # self._test_readline_file_universal(s, ending) self._test_readline_file_psfile(s, ending) @@ -217,9 +217,9 @@ class TestFileEps(PillowTestCase): s = ending.join(strings) # Native Python versions may fail on these endings. - #self._test_readline_stringio(s, ending) - #self._test_readline_io(s, ending) - #self._test_readline_file_universal(s, ending) + # self._test_readline_stringio(s, ending) + # self._test_readline_io(s, ending) + # self._test_readline_file_universal(s, ending) self._test_readline_file_psfile(s, ending) diff --git a/Tests/test_file_icns.py b/Tests/test_file_icns.py index ddbed3cd2..23d22df1b 100644 --- a/Tests/test_file_icns.py +++ b/Tests/test_file_icns.py @@ -26,12 +26,12 @@ class TestFileIcns(PillowTestCase): "requires MacOS") def test_save(self): im = Image.open(file) - + test_file = self.tempfile("temp.icns") im.save(test_file) - + reread = Image.open(test_file) - + self.assertEqual(reread.mode, "RGBA") self.assertEqual(reread.size, (1024, 1024)) self.assertEqual(reread.format, "ICNS") @@ -87,4 +87,4 @@ class TestFileIcns(PillowTestCase): if __name__ == '__main__': unittest.main() -# End of file \ No newline at end of file +# End of file diff --git a/Tests/test_file_ico.py b/Tests/test_file_ico.py index 4f53fe1c1..f7b52b124 100644 --- a/Tests/test_file_ico.py +++ b/Tests/test_file_ico.py @@ -25,23 +25,22 @@ class TestFileIco(PillowTestCase): # the default image output.seek(0) reloaded = Image.open(output) - self.assertEqual(reloaded.info['sizes'],set([(32, 32), (64, 64)])) + self.assertEqual(reloaded.info['sizes'], set([(32, 32), (64, 64)])) self.assertEqual(im.mode, reloaded.mode) self.assertEqual((64, 64), reloaded.size) self.assertEqual(reloaded.format, "ICO") - self.assert_image_equal(reloaded, hopper().resize((64,64), Image.LANCZOS)) + self.assert_image_equal(reloaded, hopper().resize((64, 64), Image.LANCZOS)) # the other one output.seek(0) reloaded = Image.open(output) - reloaded.size = (32,32) + reloaded.size = (32, 32) self.assertEqual(im.mode, reloaded.mode) self.assertEqual((32, 32), reloaded.size) self.assertEqual(reloaded.format, "ICO") - self.assert_image_equal(reloaded, hopper().resize((32,32), Image.LANCZOS)) - + self.assert_image_equal(reloaded, hopper().resize((32, 32), Image.LANCZOS)) if __name__ == '__main__': diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 64e8b74af..aa24582cc 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -301,15 +301,12 @@ class TestFileJpeg(PillowTestCase): # sequence wrong length self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[])) # sequence wrong length - self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[1,2,3,4,5])) + self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[1, 2, 3, 4, 5])) # qtable entry not a sequence self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[1])) # qtable entry has wrong number of items - self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[[1,2,3,4]])) - - - + self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[[1, 2, 3, 4]])) @unittest.skipUnless(djpeg_available(), "djpeg not available") def test_load_djpeg(self): @@ -340,9 +337,9 @@ class TestFileJpeg(PillowTestCase): """ Generates a very hard to compress file :param size: tuple """ - return Image.frombytes('RGB',size, os.urandom(size[0]*size[1] *3)) + return Image.frombytes('RGB', size, os.urandom(size[0]*size[1] * 3)) - im = gen_random_image((512,512)) + im = gen_random_image((512, 512)) f = self.tempfile("temp.jpeg") im.save(f, quality=100, optimize=True) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index a9377bf63..07850f8cf 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -27,7 +27,7 @@ class LibTiffTestCase(PillowTestCase): self.assertEqual(im._compression, 'group4') except: print("No _compression") - print (dir(im)) + print(dir(im)) # can we write it back out, in a different form. out = self.tempfile("temp.png") @@ -243,13 +243,13 @@ class TestFileLibTiff(LibTiffTestCase): im2 = Image.open('Tests/images/12in16bit.tif') if Image.DEBUG: - print (im.getpixel((0, 0))) - print (im.getpixel((0, 1))) - print (im.getpixel((0, 2))) + print(im.getpixel((0, 0))) + print(im.getpixel((0, 1))) + print(im.getpixel((0, 2))) - print (im2.getpixel((0, 0))) - print (im2.getpixel((0, 1))) - print (im2.getpixel((0, 2))) + print(im2.getpixel((0, 0))) + print(im2.getpixel((0, 1))) + print(im2.getpixel((0, 2))) self.assert_image_equal(im, im2) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 4cd5dc703..dbbe72afa 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -153,7 +153,7 @@ class TestFilePng(PillowTestCase): im = load(HEAD + chunk(b'iTXt', b'spam\0\1\0en\0Spam\0' + zlib.compress(b"egg")[:1]) + TAIL) - self.assertEqual(im.info, {'spam':''}) + self.assertEqual(im.info, {'spam': ''}) im = load(HEAD + chunk(b'iTXt', b'spam\0\1\1en\0Spam\0' + zlib.compress(b"egg")) + TAIL) @@ -380,7 +380,6 @@ class TestFilePng(PillowTestCase): repr_png = Image.open(BytesIO(im._repr_png_())) self.assertEqual(repr_png.format, 'PNG') self.assert_image_equal(im, repr_png) - if __name__ == '__main__': diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 32aef075e..b885780d6 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -121,13 +121,13 @@ class TestFileTiff(PillowTestCase): im2 = Image.open('Tests/images/12in16bit.tif') if Image.DEBUG: - print (im.getpixel((0, 0))) - print (im.getpixel((0, 1))) - print (im.getpixel((0, 2))) + print(im.getpixel((0, 0))) + print(im.getpixel((0, 1))) + print(im.getpixel((0, 2))) - print (im2.getpixel((0, 0))) - print (im2.getpixel((0, 1))) - print (im2.getpixel((0, 2))) + print(im2.getpixel((0, 0))) + print(im2.getpixel((0, 1))) + print(im2.getpixel((0, 2))) self.assert_image_equal(im, im2) diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 2954c0879..dfc16682b 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -37,7 +37,7 @@ class TestFileTiffMetadata(PillowTestCase): self.assertEqual(loaded.tag[50838], (len(textdata),)) self.assertEqual(loaded.tag[50839], textdata) - self.assertAlmostEqual(loaded.tag[tag_ids['RollAngle']][0], floatdata, + self.assertAlmostEqual(loaded.tag[tag_ids['RollAngle']][0], floatdata, places=5) self.assertAlmostEqual(loaded.tag[tag_ids['YawAngle']][0], doubledata) diff --git a/Tests/test_file_webp_metadata.py b/Tests/test_file_webp_metadata.py index f2f18d713..08d42d7c6 100644 --- a/Tests/test_file_webp_metadata.py +++ b/Tests/test_file_webp_metadata.py @@ -15,7 +15,6 @@ class TestFileWebpMetadata(PillowTestCase): if not _webp.HAVE_WEBPMUX: self.skipTest('WebPMux support not installed') - def test_read_exif_metadata(self): file_path = "Tests/images/flower.webp" diff --git a/Tests/test_file_xpm.py b/Tests/test_file_xpm.py index 9a666a705..6a6817048 100644 --- a/Tests/test_file_xpm.py +++ b/Tests/test_file_xpm.py @@ -15,7 +15,7 @@ class TestFileXpm(PillowTestCase): self.assertEqual(im.size, (128, 128)) self.assertEqual(im.format, "XPM") - #large error due to quantization->44 colors. + # large error due to quantization->44 colors. self.assert_image_similar(im.convert('RGB'), hopper('RGB'), 60) def test_load_read(self): diff --git a/Tests/test_image_putdata.py b/Tests/test_image_putdata.py index 3b7dc836a..b46456eba 100644 --- a/Tests/test_image_putdata.py +++ b/Tests/test_image_putdata.py @@ -72,8 +72,8 @@ class TestImagePutData(PillowTestCase): im = Image.new('L', (150, 100)) im.putdata(arr) - self.assertEqual(len(im.getdata()),len(arr)) - + self.assertEqual(len(im.getdata()), len(arr)) + def test_array_F(self): # shouldn't segfault # see https://github.com/python-pillow/Pillow/issues/1008 @@ -82,7 +82,7 @@ class TestImagePutData(PillowTestCase): arr = array('f', [0.0])*15000 im.putdata(arr) - self.assertEqual(len(im.getdata()),len(arr)) + self.assertEqual(len(im.getdata()), len(arr)) if __name__ == '__main__': unittest.main() diff --git a/Tests/test_image_resize.py b/Tests/test_image_resize.py index 9cd85d1b1..414758529 100644 --- a/Tests/test_image_resize.py +++ b/Tests/test_image_resize.py @@ -17,11 +17,11 @@ class TestImagingCoreResize(PillowTestCase): def test_nearest_mode(self): for mode in ["1", "P", "L", "I", "F", "RGB", "RGBA", "CMYK", "YCbCr", - "I;16"]: # exotic mode + "I;16"]: # exotic mode im = hopper(mode) r = self.resize(im, (15, 12), Image.NEAREST) self.assertEqual(r.mode, mode) - self.assertEqual(r.size, (15, 12) ) + self.assertEqual(r.size, (15, 12)) self.assertEqual(r.im.bands, im.im.bands) def test_convolution_modes(self): @@ -35,7 +35,7 @@ class TestImagingCoreResize(PillowTestCase): im = hopper(mode) r = self.resize(im, (15, 12), Image.BILINEAR) self.assertEqual(r.mode, mode) - self.assertEqual(r.size, (15, 12) ) + self.assertEqual(r.size, (15, 12)) self.assertEqual(r.im.bands, im.im.bands) def test_reduce_filters(self): diff --git a/Tests/test_image_transpose.py b/Tests/test_image_transpose.py index 3183ceadd..3069df61c 100644 --- a/Tests/test_image_transpose.py +++ b/Tests/test_image_transpose.py @@ -1,7 +1,7 @@ from helper import unittest, PillowTestCase, hopper from PIL.Image import (FLIP_LEFT_RIGHT, FLIP_TOP_BOTTOM, ROTATE_90, ROTATE_180, - ROTATE_270, TRANSPOSE) + ROTATE_270, TRANSPOSE) class TestImageTranspose(PillowTestCase): diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 93bdda44c..7a2a57151 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -4,7 +4,7 @@ from PIL import Image from io import BytesIO import os - + try: from PIL import ImageCms from PIL.ImageCms import ImageCmsProfile @@ -17,6 +17,7 @@ except ImportError as v: SRGB = "Tests/icc/sRGB_IEC61966-2-1_black_scaled.icc" HAVE_PROFILE = os.path.exists(SRGB) + class TestImageCms(PillowTestCase): def setUp(self): @@ -32,7 +33,7 @@ class TestImageCms(PillowTestCase): self.skipTest("SRGB profile not available") def test_sanity(self): - + # basic smoke test. # this mostly follows the cms_test outline. diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 6adc6c1f2..3b9919834 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -64,7 +64,7 @@ class TestImageDraw(PillowTestCase): # Assert self.assert_image_similar( - im, Image.open("Tests/images/imagedraw_arc.png"),1) + im, Image.open("Tests/images/imagedraw_arc.png"), 1) def test_arc1(self): self.helper_arc(BBOX1) @@ -97,7 +97,7 @@ class TestImageDraw(PillowTestCase): # Assert self.assert_image_similar( - im, Image.open("Tests/images/imagedraw_chord.png"),1) + im, Image.open("Tests/images/imagedraw_chord.png"), 1) def test_chord1(self): self.helper_chord(BBOX1) @@ -116,7 +116,7 @@ class TestImageDraw(PillowTestCase): # Assert self.assert_image_similar( - im, Image.open("Tests/images/imagedraw_ellipse.png"),1) + im, Image.open("Tests/images/imagedraw_ellipse.png"), 1) def test_ellipse1(self): self.helper_ellipse(BBOX1) @@ -154,7 +154,7 @@ class TestImageDraw(PillowTestCase): # Assert self.assert_image_similar( - im, Image.open("Tests/images/imagedraw_pieslice.png"),1) + im, Image.open("Tests/images/imagedraw_pieslice.png"), 1) def test_pieslice1(self): self.helper_pieslice(BBOX1) diff --git a/Tests/test_imageenhance.py b/Tests/test_imageenhance.py index 58042db85..82ba2e5db 100644 --- a/Tests/test_imageenhance.py +++ b/Tests/test_imageenhance.py @@ -21,23 +21,22 @@ class TestImageEnhance(PillowTestCase): im = Image.new("RGB", (1, 1)) ImageEnhance.Sharpness(im).enhance(0.5) - def _half_transparent_image(self): # returns an image, half transparent, half solid im = hopper('RGB') - + transparent = Image.new('L', im.size, 0) solid = Image.new('L', (im.size[0]//2, im.size[1]), 255) - transparent.paste(solid, (0,0)) + transparent.paste(solid, (0, 0)) im.putalpha(transparent) return im - def _check_alpha(self,im, original, op, amount): + def _check_alpha(self, im, original, op, amount): self.assertEqual(im.getbands(), original.getbands()) self.assert_image_equal(im.split()[-1], original.split()[-1], "Diff on %s: %s" % (op, amount)) - + def test_alpha(self): # Issue https://github.com/python-pillow/Pillow/issues/899 # Is alpha preserved through image enhancement? @@ -45,10 +44,10 @@ class TestImageEnhance(PillowTestCase): original = self._half_transparent_image() for op in ['Color', 'Brightness', 'Contrast', 'Sharpness']: - for amount in [0,0.5,1.0]: - self._check_alpha(getattr(ImageEnhance,op)(original).enhance(amount), + for amount in [0, 0.5, 1.0]: + self._check_alpha(getattr(ImageEnhance, op)(original).enhance(amount), original, op, amount) - + if __name__ == '__main__': unittest.main() diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index 662a3bfb0..01503dff7 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -58,7 +58,7 @@ class TestImageFile(PillowTestCase): # This test fails on Ubuntu 12.04, PPC (Bigendian) It # appears to be a ghostscript 9.05 bug, since the # ghostscript rendering is wonky and the file is identical - # to that written on ubuntu 12.04 x64 + # to that written on ubuntu 12.04 x64 # md5sum: ba974835ff2d6f3f2fd0053a23521d4a # EPS comes back in RGB: diff --git a/Tests/test_imagefont_bitmap.py b/Tests/test_imagefont_bitmap.py index 9da3d1776..27141f4b3 100644 --- a/Tests/test_imagefont_bitmap.py +++ b/Tests/test_imagefont_bitmap.py @@ -1,6 +1,7 @@ from helper import unittest, PillowTestCase from PIL import Image, ImageFont, ImageDraw + class TestImageFontBitmap(PillowTestCase): def test_similar(self): text = 'EmbeddedBitmap' @@ -13,10 +14,10 @@ class TestImageFontBitmap(PillowTestCase): draw_bitmap, draw_outline = ImageDraw.Draw(im_bitmap), ImageDraw.Draw(im_outline) # Metrics are different on the bitmap and ttf fonts, more so on some platforms - # and versions of freetype than others. Mac has a 1px difference, linux doesn't. - draw_bitmap.text((0, size_final[1] - size_bitmap[1]), + # and versions of freetype than others. Mac has a 1px difference, linux doesn't. + draw_bitmap.text((0, size_final[1] - size_bitmap[1]), text, fill=(0, 0, 0), font=font_bitmap) - draw_outline.text((0, size_final[1] - size_outline[1]), + draw_outline.text((0, size_final[1] - size_outline[1]), text, fill=(0, 0, 0), font=font_outline) self.assert_image_similar(im_bitmap, im_outline, 20) diff --git a/Tests/test_imagesequence.py b/Tests/test_imagesequence.py index 62a83cda6..459a053d8 100644 --- a/Tests/test_imagesequence.py +++ b/Tests/test_imagesequence.py @@ -24,16 +24,16 @@ class TestImageSequence(PillowTestCase): def _test_multipage_tiff(self, dbg=False): # debug had side effect of calling fp.tell. - Image.DEBUG=dbg + Image.DEBUG = dbg im = Image.open('Tests/images/multipage.tiff') for index, frame in enumerate(ImageSequence.Iterator(im)): frame.load() self.assertEqual(index, im.tell()) frame.convert('RGB') - Image.DEBUG=False + Image.DEBUG = False def test_tiff(self): - #self._test_multipage_tiff(True) + # self._test_multipage_tiff(True) self._test_multipage_tiff(False) def test_libtiff(self): @@ -43,7 +43,7 @@ class TestImageSequence(PillowTestCase): self.skipTest("tiff support not available") TiffImagePlugin.READ_LIBTIFF = True - #self._test_multipage_tiff(True) + # self._test_multipage_tiff(True) self._test_multipage_tiff(False) TiffImagePlugin.READ_LIBTIFF = False diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py index 7b6f22b2b..f010e0df5 100644 --- a/Tests/test_numpy.py +++ b/Tests/test_numpy.py @@ -129,8 +129,8 @@ class TestNumpy(PillowTestCase): arr = numpy.zeros((15000,), numpy.float32) im.putdata(arr) - self.assertEqual(len(im.getdata()),len(arr)) - + self.assertEqual(len(im.getdata()), len(arr)) + if __name__ == '__main__': unittest.main() diff --git a/Tests/test_scipy.py b/Tests/test_scipy.py index 926bbb186..60f1a1b1e 100644 --- a/Tests/test_scipy.py +++ b/Tests/test_scipy.py @@ -8,12 +8,12 @@ try: HAS_SCIPY = True except: HAS_SCIPY = False - + class Test_scipy_resize(PillowTestCase): """ Tests for scipy regression in 2.6.0 - Tests from https://github.com/scipy/scipy/blob/master/scipy/misc/pilutil.py + Tests from https://github.com/scipy/scipy/blob/master/scipy/misc/pilutil.py """ def setUp(self): @@ -21,25 +21,23 @@ class Test_scipy_resize(PillowTestCase): self.skipTest("Scipy Required") def test_imresize(self): - im = np.random.random((10,20)) + im = np.random.random((10, 20)) for T in np.sctypes['float'] + [float]: # 1.1 rounds to below 1.1 for float16, 1.101 works - im1 = misc.imresize(im,T(1.101)) - self.assertEqual(im1.shape,(11,22)) + im1 = misc.imresize(im, T(1.101)) + self.assertEqual(im1.shape, (11, 22)) def test_imresize4(self): - im = np.array([[1,2], - [3,4]]) - res = np.array([[ 1. , 1.25, 1.75, 2. ], - [ 1.5 , 1.75, 2.25, 2.5 ], - [ 2.5 , 2.75, 3.25, 3.5 ], - [ 3. , 3.25, 3.75, 4. ]], dtype=np.float32) + im = np.array([[1, 2], + [3, 4]]) + res = np.array([[1. , 1.25, 1.75, 2. ], + [1.5, 1.75, 2.25, 2.5], + [2.5, 2.75, 3.25, 3.5], + [3. , 3.25, 3.75, 4. ]], dtype=np.float32) # Check that resizing by target size, float and int are the same - im2 = misc.imresize(im, (4,4), mode='F') # output size + im2 = misc.imresize(im, (4, 4), mode='F') # output size im3 = misc.imresize(im, 2., mode='F') # fraction im4 = misc.imresize(im, 200, mode='F') # percentage assert_equal(im2, res) assert_equal(im3, res) assert_equal(im4, res) - - From 86be744d1fe75726c461d7d8cffa726bebb874e2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 24 Apr 2015 09:41:33 +1000 Subject: [PATCH 0219/1037] Various Flake8 Scripts fixes --- Scripts/enhancer.py | 3 ++- Scripts/explode.py | 6 ++++-- Scripts/gifmaker.py | 8 ++++++-- Scripts/painter.py | 5 +++-- Scripts/pilconvert.py | 7 +++++-- Scripts/pildriver.py | 3 ++- Scripts/pilfile.py | 5 ++++- Scripts/pilfont.py | 3 ++- Scripts/pilprint.py | 11 +++++++---- Scripts/player.py | 4 ++-- Scripts/thresholder.py | 9 +++++---- Scripts/viewer.py | 1 + 12 files changed, 43 insertions(+), 22 deletions(-) diff --git a/Scripts/enhancer.py b/Scripts/enhancer.py index fe250c9f8..34becf873 100644 --- a/Scripts/enhancer.py +++ b/Scripts/enhancer.py @@ -18,6 +18,7 @@ import sys # # enhancer widget + class Enhance(Frame): def __init__(self, master, image, name, enhancer, lo, hi): Frame.__init__(self, master) @@ -25,7 +26,7 @@ class Enhance(Frame): # set up the image self.tkim = ImageTk.PhotoImage(image.mode, image.size) self.enhancer = enhancer(image) - self.update("1.0") # normalize + self.update("1.0") # normalize # image window Label(self, image=self.tkim).pack() diff --git a/Scripts/explode.py b/Scripts/explode.py index b8680f631..0460fa020 100644 --- a/Scripts/explode.py +++ b/Scripts/explode.py @@ -9,11 +9,13 @@ from __future__ import print_function from PIL import Image -import os, sys +import os +import sys + class Interval: - def __init__(self, interval = "0"): + def __init__(self, interval="0"): self.setinterval(interval) diff --git a/Scripts/gifmaker.py b/Scripts/gifmaker.py index bd4de99c1..8777f74f6 100644 --- a/Scripts/gifmaker.py +++ b/Scripts/gifmaker.py @@ -49,20 +49,23 @@ from PIL.GifImagePlugin import getheader, getdata # -------------------------------------------------------------------- # sequence iterator + class image_sequence: def __init__(self, im): self.im = im + def __getitem__(self, ix): try: if ix: self.im.seek(ix) return self.im except EOFError: - raise IndexError # end of sequence + raise IndexError # end of sequence # -------------------------------------------------------------------- # straightforward delta encoding + def makedelta(fp, sequence): """Convert list of image frames to a GIF animation file""" @@ -91,7 +94,7 @@ def makedelta(fp, sequence): if bbox: # compress difference - for s in getdata(im.crop(bbox), offset = bbox[:2]): + for s in getdata(im.crop(bbox), offset=bbox[:2]): fp.write(s) else: @@ -109,6 +112,7 @@ def makedelta(fp, sequence): # -------------------------------------------------------------------- # main hack + def compress(infile, outfile): # open input image, and force loading of first frame diff --git a/Scripts/painter.py b/Scripts/painter.py index 80c4db6a0..6f1c19484 100644 --- a/Scripts/painter.py +++ b/Scripts/painter.py @@ -20,6 +20,7 @@ import sys # # painter widget + class PaintCanvas(Canvas): def __init__(self, master, image): Canvas.__init__(self, master, width=image.size[0], height=image.size[1]) @@ -33,7 +34,7 @@ class PaintCanvas(Canvas): box = x, y, min(xsize, x+tilesize), min(ysize, y+tilesize) tile = ImageTk.PhotoImage(image.crop(box)) self.create_image(x, y, image=tile, anchor=NW) - self.tile[(x,y)] = box, tile + self.tile[(x, y)] = box, tile self.image = image @@ -59,7 +60,7 @@ class PaintCanvas(Canvas): xy, tile = self.tile[(x, y)] tile.paste(self.image.crop(xy)) except KeyError: - pass # outside the image + pass # outside the image self.update_idletasks() # diff --git a/Scripts/pilconvert.py b/Scripts/pilconvert.py index 0341994c9..66b2e5ac2 100644 --- a/Scripts/pilconvert.py +++ b/Scripts/pilconvert.py @@ -15,10 +15,13 @@ from __future__ import print_function -import getopt, string, sys +import getopt +import string +import sys from PIL import Image + def usage(): print("PIL Convert 0.5/1998-12-30 -- convert image files") print("Usage: pilconvert [option] infile outfile") @@ -49,7 +52,7 @@ except getopt.error as v: format = None convert = None -options = { } +options = {} for o, a in opt: diff --git a/Scripts/pildriver.py b/Scripts/pildriver.py index e45b05008..ba2a8c66f 100644 --- a/Scripts/pildriver.py +++ b/Scripts/pildriver.py @@ -52,6 +52,7 @@ from __future__ import print_function from PIL import Image + class PILDriver: verbose = 0 @@ -500,7 +501,7 @@ if __name__ == '__main__': try: import readline except ImportError: - pass # not available on all platforms + pass # not available on all platforms # If we see command-line arguments, interpret them as a stack state # and execute. Otherwise go interactive. diff --git a/Scripts/pilfile.py b/Scripts/pilfile.py index 1b77b0e78..b4fe4942c 100644 --- a/Scripts/pilfile.py +++ b/Scripts/pilfile.py @@ -20,7 +20,9 @@ from __future__ import print_function import site -import getopt, glob, sys +import getopt +import glob +import sys from PIL import Image @@ -59,6 +61,7 @@ for o, a in opt: elif o == "-D": Image.DEBUG += 1 + def globfix(files): # expand wildcards where necessary if sys.platform == "win32": diff --git a/Scripts/pilfont.py b/Scripts/pilfont.py index ec25e7a71..4425c072c 100644 --- a/Scripts/pilfont.py +++ b/Scripts/pilfont.py @@ -14,7 +14,8 @@ from __future__ import print_function VERSION = "0.4" -import glob, sys +import glob +import sys # drivers from PIL import BdfFontFile diff --git a/Scripts/pilprint.py b/Scripts/pilprint.py index 889944de7..d13a2a526 100644 --- a/Scripts/pilprint.py +++ b/Scripts/pilprint.py @@ -12,14 +12,17 @@ # from __future__ import print_function -import getopt, os, sys +import getopt +import os +import sys VERSION = "pilprint 0.3/2003-05-05" from PIL import Image from PIL import PSDraw -letter = ( 1.0*72, 1.0*72, 7.5*72, 10.0*72 ) +letter = (1.0*72, 1.0*72, 7.5*72, 10.0*72) + def description(file, image): title = os.path.splitext(os.path.split(file)[1])[0] @@ -43,8 +46,8 @@ except getopt.error as v: print(v) sys.exit(1) -printer = None # print to stdout -monochrome = 1 # reduce file size for most common case +printer = None # print to stdout +monochrome = 1 # reduce file size for most common case for o, a in opt: if o == "-d": diff --git a/Scripts/player.py b/Scripts/player.py index 0c90286c5..43877415a 100644 --- a/Scripts/player.py +++ b/Scripts/player.py @@ -56,7 +56,7 @@ class UI(Label): del self.im[0] self.image.paste(im) except IndexError: - return # end of list + return # end of list else: @@ -65,7 +65,7 @@ class UI(Label): im.seek(im.tell() + 1) self.image.paste(im) except EOFError: - return # end of file + return # end of file try: duration = im.info["duration"] diff --git a/Scripts/thresholder.py b/Scripts/thresholder.py index 29d4592d9..1e55c5a32 100644 --- a/Scripts/thresholder.py +++ b/Scripts/thresholder.py @@ -18,8 +18,9 @@ import sys # # an image viewer + class UI(Frame): - def __init__(self, master, im, value = 128): + def __init__(self, master, im, value=128): Frame.__init__(self, master) self.image = im @@ -45,16 +46,16 @@ class UI(Frame): self.redraw() - def redraw(self, event = None): + def redraw(self, event=None): # create overlay (note the explicit conversion to mode "1") - im = self.image.point(lambda v,t=self.value: v>=t, "1") + im = self.image.point(lambda v, t=self.value: v >= t, "1") self.overlay = ImageTk.BitmapImage(im, foreground="green") # update canvas self.canvas.delete("overlay") self.canvas.create_image(0, 0, image=self.overlay, anchor=NW, - tags="overlay") + tags="overlay") # -------------------------------------------------------------------- # main diff --git a/Scripts/viewer.py b/Scripts/viewer.py index 86b2526cd..c0fc59d96 100644 --- a/Scripts/viewer.py +++ b/Scripts/viewer.py @@ -16,6 +16,7 @@ from PIL import Image, ImageTk # # an image viewer + class UI(Label): def __init__(self, master, im): From 87c4c6a72d9103373386a9103a18d88289444c0e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 24 Apr 2015 10:15:14 +1000 Subject: [PATCH 0220/1037] Various Flake8 fixes to base scripts --- mp_compile.py | 5 +++-- setup.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/mp_compile.py b/mp_compile.py index 955cd9c1b..892452558 100644 --- a/mp_compile.py +++ b/mp_compile.py @@ -3,7 +3,8 @@ from multiprocessing import Pool, cpu_count from distutils.ccompiler import CCompiler -import os, sys +import os +import sys try: MAX_PROCS = int(os.environ.get('MAX_CONCURRENCY', min(4, cpu_count()))) @@ -38,7 +39,7 @@ def _mp_compile(self, sources, output_dir=None, macros=None, pool = Pool(MAX_PROCS) try: - print ("Building using %d processes" % pool._processes) + print("Building using %d processes" % pool._processes) except: pass arr = [(self, obj, build, cc_args, extra_postargs, pp_opts) diff --git a/setup.py b/setup.py index 5886de07b..216322ec2 100644 --- a/setup.py +++ b/setup.py @@ -570,7 +570,7 @@ class pil_build_ext(build_ext): if feature.webpmux: defs.append(("HAVE_WEBPMUX", None)) libs.append(feature.webpmux) - libs.append(feature.webpmux.replace('pmux','pdemux')) + libs.append(feature.webpmux.replace('pmux', 'pdemux')) exts.append(Extension( "PIL._webp", ["_webp.c"], libraries=libs, define_macros=defs)) @@ -757,6 +757,6 @@ setup( test_suite='nose.collector', keywords=["Imaging", ], license='Standard PIL License', - zip_safe= not debug_build(), + zip_safe=not debug_build(), ) # End of file From ebc6996ba5790744724ecdd81dfb24920aca7ef0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 2 Apr 2015 16:35:52 +1100 Subject: [PATCH 0221/1037] Changed list comprehension variable name to avoid redefinition --- PIL/EpsImagePlugin.py | 2 +- PIL/TiffImagePlugin.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py index 83024b63f..62e438f0d 100644 --- a/PIL/EpsImagePlugin.py +++ b/PIL/EpsImagePlugin.py @@ -248,7 +248,7 @@ class EpsImageFile(ImageFile.ImageFile): # Note: The DSC spec says that BoundingBox # fields should be integers, but some drivers # put floating point values there anyway. - box = [int(float(s)) for s in v.split()] + box = [int(float(i)) for i in v.split()] self.size = box[2] - box[0], box[3] - box[1] self.tile = [("eps", (0, 0) + self.size, offset, (length, box))] diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index e4028fdbb..ec1c84220 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -978,7 +978,7 @@ class TiffImageFile(ImageFile.ImageFile): # fixup palette descriptor if self.mode == "P": - palette = [o8(a // 256) for a in self.tag[COLORMAP]] + palette = [o8(b // 256) for b in self.tag[COLORMAP]] self.palette = ImagePalette.raw("RGB;L", b"".join(palette)) # # -------------------------------------------------------------------- From e22e4239e502ece06848d53de2a804983c374e55 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 2 Apr 2015 16:37:04 +1100 Subject: [PATCH 0222/1037] Removed unused imports --- PIL/FontFile.py | 5 ----- Scripts/pilfile.py | 1 - Tests/check_jpeg_leaks.py | 1 - Tests/test_image.py | 1 - 4 files changed, 8 deletions(-) diff --git a/PIL/FontFile.py b/PIL/FontFile.py index 26ddff0ac..fa984fa72 100644 --- a/PIL/FontFile.py +++ b/PIL/FontFile.py @@ -17,11 +17,6 @@ import os from PIL import Image, _binary -try: - import zlib -except ImportError: - zlib = None - WIDTH = 800 diff --git a/Scripts/pilfile.py b/Scripts/pilfile.py index b4fe4942c..b954114ac 100644 --- a/Scripts/pilfile.py +++ b/Scripts/pilfile.py @@ -19,7 +19,6 @@ from __future__ import print_function -import site import getopt import glob import sys diff --git a/Tests/check_jpeg_leaks.py b/Tests/check_jpeg_leaks.py index 290bac4d8..b46643a55 100644 --- a/Tests/check_jpeg_leaks.py +++ b/Tests/check_jpeg_leaks.py @@ -1,5 +1,4 @@ from helper import unittest, PillowTestCase, hopper -from PIL import Image from io import BytesIO import sys diff --git a/Tests/test_image.py b/Tests/test_image.py index de43b010e..0b84de630 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -1,7 +1,6 @@ from helper import unittest, PillowTestCase, hopper from PIL import Image -import sys class TestImage(PillowTestCase): From 9794aafc3783277e2cf213de6e6014db01782a00 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 8 Apr 2015 21:12:37 +1000 Subject: [PATCH 0223/1037] Removed or commented unused variables --- PIL/FontFile.py | 3 ++- PIL/IptcImagePlugin.py | 2 +- PIL/PcfFontFile.py | 2 +- PIL/TiffImagePlugin.py | 4 ++-- Tests/bench_get.py | 2 +- Tests/check_j2k_leaks.py | 6 +++--- Tests/check_jpeg_leaks.py | 6 +++--- Tests/check_webp_leaks.py | 2 +- 8 files changed, 14 insertions(+), 13 deletions(-) diff --git a/PIL/FontFile.py b/PIL/FontFile.py index fa984fa72..5cf688256 100644 --- a/PIL/FontFile.py +++ b/PIL/FontFile.py @@ -78,7 +78,8 @@ class FontFile: glyph = self[i] if glyph: d, dst, src, im = glyph - xx, yy = src[2] - src[0], src[3] - src[1] + xx = src[2] - src[0] + # yy = src[3] - src[1] x0, y0 = x, y x = x + xx if x > WIDTH: diff --git a/PIL/IptcImagePlugin.py b/PIL/IptcImagePlugin.py index dc8607591..aa0193894 100644 --- a/PIL/IptcImagePlugin.py +++ b/PIL/IptcImagePlugin.py @@ -222,7 +222,7 @@ def getiptcinfo(im): offset += 2 # resource name (usually empty) name_len = i8(app[offset]) - name = app[offset+1:offset+1+name_len] + # name = app[offset+1:offset+1+name_len] offset = 1 + offset + name_len if offset & 1: offset += 1 diff --git a/PIL/PcfFontFile.py b/PIL/PcfFontFile.py index c19a1c532..c2006905e 100644 --- a/PIL/PcfFontFile.py +++ b/PIL/PcfFontFile.py @@ -204,7 +204,7 @@ class PcfFontFile(FontFile.FontFile): for i in range(4): bitmapSizes.append(i32(fp.read(4))) - byteorder = format & 4 # non-zero => MSB + # byteorder = format & 4 # non-zero => MSB bitorder = format & 8 # non-zero => MSB padindex = format & 3 diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index ec1c84220..ad085451b 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -731,8 +731,8 @@ class TiffImageFile(ImageFile.ImageFile): # (self._compression, (extents tuple), # 0, (rawmode, self._compression, fp)) - ignored, extents, ignored_2, args = self.tile[0] - args = args + (self.ifd.offset,) + extents = self.tile[0][1] + args = self.tile[0][3] + (self.ifd.offset,) decoder = Image._getdecoder(self.mode, 'libtiff', args, self.decoderconfig) try: diff --git a/Tests/bench_get.py b/Tests/bench_get.py index 8f786acf0..9c51c0258 100644 --- a/Tests/bench_get.py +++ b/Tests/bench_get.py @@ -10,7 +10,7 @@ def bench(mode): get = im.im.getpixel xy = 50, 50 # position shouldn't really matter t0 = timeit.default_timer() - for i in range(1000000): + for _ in range(1000000): get(xy) print(mode, timeit.default_timer() - t0, "us") diff --git a/Tests/check_j2k_leaks.py b/Tests/check_j2k_leaks.py index 9dbb8c1f4..8e9c4ca20 100755 --- a/Tests/check_j2k_leaks.py +++ b/Tests/check_j2k_leaks.py @@ -21,7 +21,7 @@ class TestJpegLeaks(PillowTestCase): from resource import setrlimit, RLIMIT_AS, RLIMIT_STACK setrlimit(RLIMIT_STACK, (stack_size, stack_size)) setrlimit(RLIMIT_AS, (mem_limit, mem_limit)) - for count in range(iterations): + for _ in range(iterations): with Image.open(test_file) as im: im.load() @@ -29,13 +29,13 @@ class TestJpegLeaks(PillowTestCase): from resource import setrlimit, RLIMIT_AS, RLIMIT_STACK setrlimit(RLIMIT_STACK, (stack_size, stack_size)) setrlimit(RLIMIT_AS, (mem_limit, mem_limit)) - for count in range(iterations): + for _ in range(iterations): with Image.open(test_file) as im: im.load() test_output = BytesIO() im.save(test_output, "JPEG2000") test_output.seek(0) - output = test_output.read() + test_output.read() if __name__ == '__main__': diff --git a/Tests/check_jpeg_leaks.py b/Tests/check_jpeg_leaks.py index b46643a55..b8a82a5ec 100644 --- a/Tests/check_jpeg_leaks.py +++ b/Tests/check_jpeg_leaks.py @@ -101,7 +101,7 @@ post-patch: qtables = [standard_l_qtable, standard_chrominance_qtable] - for count in range(iterations): + for _ in range(iterations): test_output = BytesIO() im.save(test_output, "JPEG", qtables=qtables) @@ -165,7 +165,7 @@ post patch: im = hopper('RGB') exif = b'12345678'*4096 - for count in range(iterations): + for _ in range(iterations): test_output = BytesIO() im.save(test_output, "JPEG", exif=exif) @@ -199,7 +199,7 @@ base case: def test_base_save(self): im = hopper('RGB') - for count in range(iterations): + for _ in range(iterations): test_output = BytesIO() im.save(test_output, "JPEG") diff --git a/Tests/check_webp_leaks.py b/Tests/check_webp_leaks.py index 79e22328b..0f54f382d 100644 --- a/Tests/check_webp_leaks.py +++ b/Tests/check_webp_leaks.py @@ -28,7 +28,7 @@ class TestWebPLeaks(PillowTestCase): with open(test_file, 'rb') as f: im_data = f.read() start_mem = self._get_mem_usage() - for count in range(iterations): + for _ in range(iterations): with Image.open(BytesIO(im_data)) as im: im.load() mem = (self._get_mem_usage() - start_mem) From 8193a07305942ba4b7f5e522fc532c4dcc77ad48 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 1 Apr 2015 23:52:59 +1100 Subject: [PATCH 0224/1037] Removed unnecessary line from ImageOps --- PIL/ImageOps.py | 1 - 1 file changed, 1 deletion(-) diff --git a/PIL/ImageOps.py b/PIL/ImageOps.py index a1706875d..931ad3c75 100644 --- a/PIL/ImageOps.py +++ b/PIL/ImageOps.py @@ -233,7 +233,6 @@ def expand(image, border=0, fill=0): :param fill: Pixel fill value (a color value). Default is 0 (black). :return: An image. """ - "Add border to image" left, top, right, bottom = _border(border) width = left + image.size[0] + right height = top + image.size[1] + bottom From 540a225ea059468d0ee3d48272cd2a821f34f778 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 3 Apr 2015 11:49:04 +1100 Subject: [PATCH 0225/1037] Removed unnecessary pass lines and commented debugging lines --- PIL/ImagePalette.py | 4 ++-- Tests/test_imageshow.py | 1 - Tests/test_imagewin.py | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/PIL/ImagePalette.py b/PIL/ImagePalette.py index 62f8814f1..c6c05d162 100644 --- a/PIL/ImagePalette.py +++ b/PIL/ImagePalette.py @@ -225,8 +225,8 @@ def load(filename): p = PaletteFile.PaletteFile(fp) lut = p.getpalette() except (SyntaxError, ValueError): - import traceback - traceback.print_exc() + #import traceback + #traceback.print_exc() pass if not lut: diff --git a/Tests/test_imageshow.py b/Tests/test_imageshow.py index e94ae2d0a..236d6e224 100644 --- a/Tests/test_imageshow.py +++ b/Tests/test_imageshow.py @@ -9,7 +9,6 @@ class TestImageShow(PillowTestCase): def test_sanity(self): dir(Image) dir(ImageShow) - pass if __name__ == '__main__': diff --git a/Tests/test_imagewin.py b/Tests/test_imagewin.py index 6fb58a0dc..7cfd7fe5c 100644 --- a/Tests/test_imagewin.py +++ b/Tests/test_imagewin.py @@ -8,7 +8,6 @@ class TestImageWin(PillowTestCase): def test_sanity(self): dir(ImageWin) - pass def test_hdc(self): # Arrange From e8679dff66e5039982e087b5c6077cf10f744582 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 3 Apr 2015 17:42:34 +1100 Subject: [PATCH 0226/1037] Fixed indentation in OleFileIO --- PIL/OleFileIO.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index a94b760c8..88b68fa4a 100755 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -360,12 +360,12 @@ WORD_CLSID = "00020900-0000-0000-C000-000000000046" #TODO: check Excel, PPT, ... #[PL]: Defect levels to classify parsing errors - see OleFileIO._raise_defect() -DEFECT_UNSURE = 10 # a case which looks weird, but not sure it's a defect -DEFECT_POTENTIAL = 20 # a potential defect -DEFECT_INCORRECT = 30 # an error according to specifications, but parsing - # can go on -DEFECT_FATAL = 40 # an error which cannot be ignored, parsing is - # impossible +DEFECT_UNSURE = 10 # a case which looks weird, but not sure it's a defect +DEFECT_POTENTIAL = 20 # a potential defect +DEFECT_INCORRECT = 30 # an error according to specifications, but parsing + # can go on +DEFECT_FATAL = 40 # an error which cannot be ignored, parsing is + # impossible #[PL] add useful constants to __all__: for key in list(vars().keys()): @@ -468,14 +468,14 @@ def _unicode(s, errors='replace'): def filetime2datetime(filetime): - """ - convert FILETIME (64 bits int) to Python datetime.datetime - """ - # TODO: manage exception when microseconds is too large - # inspired from http://code.activestate.com/recipes/511425-filetime-to-datetime/ - _FILETIME_null_date = datetime.datetime(1601, 1, 1, 0, 0, 0) - #debug('timedelta days=%d' % (filetime//(10*1000000*3600*24))) - return _FILETIME_null_date + datetime.timedelta(microseconds=filetime//10) + """ + convert FILETIME (64 bits int) to Python datetime.datetime + """ + # TODO: manage exception when microseconds is too large + # inspired from http://code.activestate.com/recipes/511425-filetime-to-datetime/ + _FILETIME_null_date = datetime.datetime(1601, 1, 1, 0, 0, 0) + #debug('timedelta days=%d' % (filetime//(10*1000000*3600*24))) + return _FILETIME_null_date + datetime.timedelta(microseconds=filetime//10) #=== CLASSES ================================================================== From f2145baedb16b1dd76b17bb09b260f7ec4cce10c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 2 Apr 2015 00:01:07 +1100 Subject: [PATCH 0227/1037] Fixed unused imports in MicImagePlugin --- PIL/MicImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/MicImagePlugin.py b/PIL/MicImagePlugin.py index cdfaf3eda..d1f8b082a 100644 --- a/PIL/MicImagePlugin.py +++ b/PIL/MicImagePlugin.py @@ -21,7 +21,7 @@ __version__ = "0.1" from PIL import Image, TiffImagePlugin -from PIL.OleFileIO import * +from PIL.OleFileIO import MAGIC, OleFileIO # From 566153f59fade763754ff2c72b71463d1dfeb6ae Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 2 Apr 2015 19:57:24 +1100 Subject: [PATCH 0228/1037] Fixed statements with no effect --- Tests/test_cffi.py | 12 ++++++------ Tests/test_file_webp_metadata.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Tests/test_cffi.py b/Tests/test_cffi.py index 5599dbb48..5d5427685 100644 --- a/Tests/test_cffi.py +++ b/Tests/test_cffi.py @@ -49,10 +49,10 @@ class TestCffi(PillowTestCase): self.skipTest("No cffi") def _test_get_access(self, im): - """ Do we get the same thing as the old pixel access """ + """Do we get the same thing as the old pixel access - """ Using private interfaces, forcing a capi access and - a pyaccess for the same image """ + Using private interfaces, forcing a capi access and + a pyaccess for the same image""" caccess = im.im.pixel_access(False) access = PyAccess.new(im, False) @@ -90,10 +90,10 @@ class TestCffi(PillowTestCase): # self._test_get_access(im) def _test_set_access(self, im, color): - """ Are we writing the correct bits into the image? """ + """Are we writing the correct bits into the image? - """ Using private interfaces, forcing a capi access and - a pyaccess for the same image """ + Using private interfaces, forcing a capi access and + a pyaccess for the same image""" caccess = im.im.pixel_access(False) access = PyAccess.new(im, False) diff --git a/Tests/test_file_webp_metadata.py b/Tests/test_file_webp_metadata.py index 08d42d7c6..9093f31ba 100644 --- a/Tests/test_file_webp_metadata.py +++ b/Tests/test_file_webp_metadata.py @@ -96,7 +96,7 @@ class TestFileWebpMetadata(PillowTestCase): file_path = "Tests/images/flower.jpg" image = Image.open(file_path) - image.info['exif'] + self.assertTrue('exif' in image.info) buffer = BytesIO() From 7f414057c97b526a074c38ba191073bff0536d50 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 2 Apr 2015 19:45:24 +1100 Subject: [PATCH 0229/1037] Fixed redefinition of built-in --- PIL/ImImagePlugin.py | 4 ++-- PIL/ImageFilter.py | 4 ++-- PIL/ImageOps.py | 4 ++-- PIL/ImageStat.py | 10 +++++----- PIL/MspImagePlugin.py | 12 ++++++------ Scripts/pilconvert.py | 8 ++++---- Scripts/pilprint.py | 10 +++++----- Tests/test_file_libtiff.py | 24 ++++++++++++------------ 8 files changed, 38 insertions(+), 38 deletions(-) diff --git a/PIL/ImImagePlugin.py b/PIL/ImImagePlugin.py index 4266f8315..c68a3cea4 100644 --- a/PIL/ImImagePlugin.py +++ b/PIL/ImImagePlugin.py @@ -313,7 +313,7 @@ SAVE = { def _save(im, fp, filename, check=0): try: - type, rawmode = SAVE[im.mode] + image_type, rawmode = SAVE[im.mode] except KeyError: raise ValueError("Cannot save %s images as IM" % im.mode) @@ -325,7 +325,7 @@ def _save(im, fp, filename, check=0): if check: return check - fp.write(("Image type: %s image\r\n" % type).encode('ascii')) + fp.write(("Image type: %s image\r\n" % image_type).encode('ascii')) if filename: fp.write(("Name: %s\r\n" % filename).encode('ascii')) fp.write(("Image size (x*y): %d*%d\r\n" % im.size).encode('ascii')) diff --git a/PIL/ImageFilter.py b/PIL/ImageFilter.py index b46845807..baa168aa7 100644 --- a/PIL/ImageFilter.py +++ b/PIL/ImageFilter.py @@ -15,7 +15,7 @@ # See the README file for information on usage and redistribution. # -from functools import reduce +import functools class Filter(object): @@ -43,7 +43,7 @@ class Kernel(Filter): def __init__(self, size, kernel, scale=None, offset=0): if scale is None: # default scale is sum of kernel - scale = reduce(lambda a, b: a+b, kernel) + scale = functools.reduce(lambda a, b: a+b, kernel) if size[0] * size[1] != len(kernel): raise ValueError("not enough coefficients in kernel") self.filterargs = size, scale, offset, kernel diff --git a/PIL/ImageOps.py b/PIL/ImageOps.py index 931ad3c75..f317645b2 100644 --- a/PIL/ImageOps.py +++ b/PIL/ImageOps.py @@ -20,7 +20,7 @@ from PIL import Image from PIL._util import isStringType import operator -from functools import reduce +import functools # @@ -213,7 +213,7 @@ def equalize(image, mask=None): if len(histo) <= 1: lut.extend(list(range(256))) else: - step = (reduce(operator.add, histo) - histo[-1]) // 255 + step = (functools.reduce(operator.add, histo) - histo[-1]) // 255 if not step: lut.extend(list(range(256))) else: diff --git a/PIL/ImageStat.py b/PIL/ImageStat.py index 7e023c673..37e7515d4 100644 --- a/PIL/ImageStat.py +++ b/PIL/ImageStat.py @@ -23,7 +23,7 @@ import math import operator -from functools import reduce +import functools class Stat: @@ -71,7 +71,7 @@ class Stat: v = [] for i in range(0, len(self.h), 256): - v.append(reduce(operator.add, self.h[i:i+256])) + v.append(functools.reduce(operator.add, self.h[i:i+256])) return v def _getsum(self): @@ -79,10 +79,10 @@ class Stat: v = [] for i in range(0, len(self.h), 256): - sum = 0.0 + layerSum = 0.0 for j in range(256): - sum += j * self.h[i + j] - v.append(sum) + layerSum += j * self.h[i + j] + v.append(layerSum) return v def _getsum2(self): diff --git a/PIL/MspImagePlugin.py b/PIL/MspImagePlugin.py index 4753be7cd..1e974d53f 100644 --- a/PIL/MspImagePlugin.py +++ b/PIL/MspImagePlugin.py @@ -49,10 +49,10 @@ class MspImageFile(ImageFile.ImageFile): raise SyntaxError("not an MSP file") # Header checksum - sum = 0 + checksum = 0 for i in range(0, 32, 2): - sum = sum ^ i16(s[i:i+2]) - if sum != 0: + checksum = checksum ^ i16(s[i:i+2]) + if checksum != 0: raise SyntaxError("bad MSP checksum") self.mode = "1" @@ -83,10 +83,10 @@ def _save(im, fp, filename): header[6], header[7] = 1, 1 header[8], header[9] = im.size - sum = 0 + checksum = 0 for h in header: - sum = sum ^ h - header[12] = sum # FIXME: is this the right field? + checksum = checksum ^ h + header[12] = checksum # FIXME: is this the right field? # header for h in header: diff --git a/Scripts/pilconvert.py b/Scripts/pilconvert.py index 66b2e5ac2..b9ebd52ae 100644 --- a/Scripts/pilconvert.py +++ b/Scripts/pilconvert.py @@ -49,7 +49,7 @@ except getopt.error as v: print(v) sys.exit(1) -format = None +output_format = None convert = None options = {} @@ -68,7 +68,7 @@ for o, a in opt: sys.exit(1) elif o == "-c": - format = a + output_format = a if o == "-g": convert = "L" @@ -90,8 +90,8 @@ try: if convert and im.mode != convert: im.draft(convert, im.size) im = im.convert(convert) - if format: - im.save(argv[1], format, **options) + if output_format: + im.save(argv[1], output_format, **options) else: im.save(argv[1], **options) except: diff --git a/Scripts/pilprint.py b/Scripts/pilprint.py index d13a2a526..2a5e23061 100644 --- a/Scripts/pilprint.py +++ b/Scripts/pilprint.py @@ -24,8 +24,8 @@ from PIL import PSDraw letter = (1.0*72, 1.0*72, 7.5*72, 10.0*72) -def description(file, image): - title = os.path.splitext(os.path.split(file)[1])[0] +def description(filepath, image): + title = os.path.splitext(os.path.split(filepath)[1])[0] format = " (%dx%d " if image.format: format = " (" + image.format + " %dx%d " @@ -65,12 +65,12 @@ for o, a in opt: # printer channel printer = "lpr -P%s" % a -for file in argv: +for filepath in argv: try: - im = Image.open(file) + im = Image.open(filepath) - title = description(file, im) + title = description(filepath, im) if monochrome and im.mode not in ["1", "L"]: im.draft("L", im.size) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 07850f8cf..26a30ca30 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -39,8 +39,8 @@ class TestFileLibTiff(LibTiffTestCase): def test_g4_tiff(self): """Test the ordinary file path load path""" - file = "Tests/images/hopper_g4_500.tif" - im = Image.open(file) + test_file = "Tests/images/hopper_g4_500.tif" + im = Image.open(test_file) self.assertEqual(im.size, (500, 500)) self._assert_noerr(im) @@ -53,8 +53,8 @@ class TestFileLibTiff(LibTiffTestCase): def test_g4_tiff_file(self): """Testing the string load path""" - file = "Tests/images/hopper_g4_500.tif" - with open(file, 'rb') as f: + test_file = "Tests/images/hopper_g4_500.tif" + with open(test_file, 'rb') as f: im = Image.open(f) self.assertEqual(im.size, (500, 500)) @@ -62,9 +62,9 @@ class TestFileLibTiff(LibTiffTestCase): def test_g4_tiff_bytesio(self): """Testing the stringio loading code path""" - file = "Tests/images/hopper_g4_500.tif" + test_file = "Tests/images/hopper_g4_500.tif" s = io.BytesIO() - with open(file, 'rb') as f: + with open(test_file, 'rb') as f: s.write(f.read()) s.seek(0) im = Image.open(s) @@ -89,8 +89,8 @@ class TestFileLibTiff(LibTiffTestCase): def test_g4_write(self): """Checking to see that the saved image is the same as what we wrote""" - file = "Tests/images/hopper_g4_500.tif" - orig = Image.open(file) + test_file = "Tests/images/hopper_g4_500.tif" + orig = Image.open(test_file) out = self.tempfile("temp.tif") rot = orig.transpose(Image.ROTATE_90) @@ -108,8 +108,8 @@ class TestFileLibTiff(LibTiffTestCase): self.assertNotEqual(orig.tobytes(), reread.tobytes()) def test_adobe_deflate_tiff(self): - file = "Tests/images/tiff_adobe_deflate.tif" - im = Image.open(file) + test_file = "Tests/images/tiff_adobe_deflate.tif" + im = Image.open(test_file) self.assertEqual(im.mode, "RGB") self.assertEqual(im.size, (278, 374)) @@ -215,8 +215,8 @@ class TestFileLibTiff(LibTiffTestCase): def test_g4_string_info(self): """Tests String data in info directory""" - file = "Tests/images/hopper_g4_500.tif" - orig = Image.open(file) + test_file = "Tests/images/hopper_g4_500.tif" + orig = Image.open(test_file) out = self.tempfile("temp.tif") From 76fec69edad4fa61c6c5280fc73632bfad7982ba Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 2 Apr 2015 18:08:14 +1100 Subject: [PATCH 0230/1037] Removed unnecessary lambdas --- Tests/test_file_png.py | 2 +- Tests/test_image_load.py | 2 +- Tests/test_imagepalette.py | 2 +- Tests/test_imagewin.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index dbbe72afa..505d48c81 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -245,7 +245,7 @@ class TestFilePng(PillowTestCase): im = Image.open(TEST_PNG_FILE) im.load() - self.assertRaises(RuntimeError, lambda: im.verify()) + self.assertRaises(RuntimeError, im.verify) def test_roundtrip_dpi(self): # Check dpi roundtripping diff --git a/Tests/test_image_load.py b/Tests/test_image_load.py index b1fc73182..f9d92e13c 100644 --- a/Tests/test_image_load.py +++ b/Tests/test_image_load.py @@ -18,7 +18,7 @@ class TestImageLoad(PillowTestCase): def test_close(self): im = Image.open("Tests/images/hopper.gif") im.close() - self.assertRaises(ValueError, lambda: im.load()) + self.assertRaises(ValueError, im.load) self.assertRaises(ValueError, lambda: im.getpixel((0, 0))) def test_contextmanager(self): diff --git a/Tests/test_imagepalette.py b/Tests/test_imagepalette.py index e56c61390..a16b590ba 100644 --- a/Tests/test_imagepalette.py +++ b/Tests/test_imagepalette.py @@ -117,7 +117,7 @@ class TestImagePalette(PillowTestCase): palette = raw("RGB", list(range(256))*3) # Act / Assert - self.assertRaises(ValueError, lambda: palette.tobytes()) + self.assertRaises(ValueError, palette.tobytes) self.assertRaises(ValueError, lambda: palette.getcolor((1, 2, 3))) f = self.tempfile("temp.lut") self.assertRaises(ValueError, lambda: palette.save(f)) diff --git a/Tests/test_imagewin.py b/Tests/test_imagewin.py index 7cfd7fe5c..c32290e43 100644 --- a/Tests/test_imagewin.py +++ b/Tests/test_imagewin.py @@ -114,7 +114,7 @@ class TestImageWinDib(PillowTestCase): buffer = dib.tobytes() # Act/Assert - self.assert_warning(DeprecationWarning, lambda: dib.tostring()) + self.assert_warning(DeprecationWarning, dib.tostring) self.assert_warning(DeprecationWarning, lambda: dib.fromstring(buffer)) From b33642c36163888dde547b7f754f3b3169890faa Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 2 Apr 2015 19:46:35 +1100 Subject: [PATCH 0231/1037] Fixed unintentional method overriding --- Scripts/thresholder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Scripts/thresholder.py b/Scripts/thresholder.py index 1e55c5a32..bfeebfa9f 100644 --- a/Scripts/thresholder.py +++ b/Scripts/thresholder.py @@ -32,7 +32,7 @@ class UI(Frame): self.canvas.pack() scale = Scale(self, orient=HORIZONTAL, from_=0, to=255, - resolution=1, command=self.update, length=256) + resolution=1, command=self.update_scale, length=256) scale.set(value) scale.bind("", self.redraw) scale.pack() @@ -41,7 +41,7 @@ class UI(Frame): # be too slow on some platforms) # self.redraw() - def update(self, value): + def update_scale(self, value): self.value = eval(value) self.redraw() From ee34d6843b634dc4d0fd130892663f486cc7d227 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 24 Apr 2015 18:24:52 +1000 Subject: [PATCH 0232/1037] Further health fixes --- PIL/EpsImagePlugin.py | 2 +- PIL/GbrImagePlugin.py | 4 +- PIL/IcnsImagePlugin.py | 2 +- PIL/ImageFile.py | 1 + PIL/JpegImagePlugin.py | 6 +-- PIL/MicImagePlugin.py | 6 +-- PIL/PyAccess.py | 2 +- PIL/SpiderImagePlugin.py | 12 +++--- Scripts/enhancer.py | 4 +- Scripts/painter.py | 4 +- Scripts/viewer.py | 4 +- Tests/check_jpeg_leaks.py | 5 +-- Tests/test_bmp_reference.py | 4 +- Tests/test_file_fli.py | 6 +-- Tests/test_file_gif.py | 10 ++--- Tests/test_file_icns.py | 10 ++--- Tests/test_file_jpeg.py | 4 +- Tests/test_file_jpeg2k.py | 4 +- Tests/test_file_libtiff.py | 4 +- Tests/test_file_libtiff_small.py | 12 +++--- Tests/test_file_mpo.py | 4 +- Tests/test_file_pcx.py | 4 +- Tests/test_file_png.py | 72 ++++++++++++++++---------------- Tests/test_file_ppm.py | 6 +-- Tests/test_file_psd.py | 6 +-- Tests/test_file_webp_metadata.py | 24 +++++------ Tests/test_font_bdf.py | 4 +- Tests/test_font_pcf.py | 4 +- Tests/test_image.py | 4 +- Tests/test_image_filter.py | 6 +-- Tests/test_image_split.py | 8 ++-- Tests/test_image_transpose.py | 7 ++-- Tests/test_imagefile.py | 6 +-- Tests/test_imagefont.py | 3 +- Tests/test_imagepalette.py | 6 +-- Tests/test_imagesequence.py | 4 +- Tests/test_imagewin.py | 8 ++-- Tests/test_mode_i16.py | 8 ++-- Tests/test_pickle.py | 4 +- Tests/threaded_save.py | 7 +--- 40 files changed, 150 insertions(+), 151 deletions(-) diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py index 62e438f0d..e2e7fa5e9 100644 --- a/PIL/EpsImagePlugin.py +++ b/PIL/EpsImagePlugin.py @@ -288,7 +288,7 @@ class EpsImageFile(ImageFile.ImageFile): if s[:11] == "%ImageData:": # Encoded bitmapped image. - [x, y, bi, mo, z3, z4, en, id] = s[11:].split(None, 7) + x, y, bi, mo = s[11:].split(None, 7)[:4] if int(bi) != 8: break diff --git a/PIL/GbrImagePlugin.py b/PIL/GbrImagePlugin.py index 0e686326c..e1580e718 100644 --- a/PIL/GbrImagePlugin.py +++ b/PIL/GbrImagePlugin.py @@ -39,8 +39,8 @@ class GbrImageFile(ImageFile.ImageFile): width = i32(self.fp.read(4)) height = i32(self.fp.read(4)) - bytes = i32(self.fp.read(4)) - if width <= 0 or height <= 0 or bytes != 1: + color_depth = i32(self.fp.read(4)) + if width <= 0 or height <= 0 or color_depth != 1: raise SyntaxError("not a GIMP brush") comment = self.fp.read(header_size - 20)[:-1] diff --git a/PIL/IcnsImagePlugin.py b/PIL/IcnsImagePlugin.py index a1ebee704..1bb1066d8 100644 --- a/PIL/IcnsImagePlugin.py +++ b/PIL/IcnsImagePlugin.py @@ -94,7 +94,7 @@ def read_32(fobj, start_length, size): def read_mk(fobj, start_length, size): # Alpha masks seem to be uncompressed - (start, length) = start_length + start = start_length[0] fobj.seek(start) pixel_size = (size[0] * size[2], size[1] * size[2]) sizesq = pixel_size[0] * pixel_size[1] diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index 79faff797..01c3e0303 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -322,6 +322,7 @@ class Parser: image = None data = None decoder = None + offset = 0 finished = 0 def reset(self): diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 0ecc902e4..01a173b5b 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -454,13 +454,13 @@ def _getmp(self): data = self.info["mp"] except KeyError: return None - file = io.BytesIO(data) - head = file.read(8) + file_contents = io.BytesIO(data) + head = file_contents.read(8) endianness = '>' if head[:4] == b'\x4d\x4d\x00\x2a' else '<' mp = {} # process dictionary info = TiffImagePlugin.ImageFileDirectory(head) - info.load(file) + info.load(file_contents) for key, value in info.items(): mp[key] = _fixup(value) # it's an error not to have a number of images diff --git a/PIL/MicImagePlugin.py b/PIL/MicImagePlugin.py index d1f8b082a..5aed618ac 100644 --- a/PIL/MicImagePlugin.py +++ b/PIL/MicImagePlugin.py @@ -54,9 +54,9 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): # best way to identify MIC files, but what the... ;-) self.images = [] - for file in self.ole.listdir(): - if file[1:] and file[0][-4:] == ".ACI" and file[1] == "Image": - self.images.append(file) + for path in self.ole.listdir(): + if path[1:] and path[0][-4:] == ".ACI" and path[1] == "Image": + self.images.append(path) # if we didn't find any images, this is probably not # an MIC file. diff --git a/PIL/PyAccess.py b/PIL/PyAccess.py index 87a6d4915..4924facd5 100644 --- a/PIL/PyAccess.py +++ b/PIL/PyAccess.py @@ -194,7 +194,7 @@ class _PyAccessI16_L(PyAccess): pixel = self.pixels[y][x] try: color = min(color, 65535) - except: + except TypeError: color = min(color[0], 65535) pixel.l = color & 0xFF diff --git a/PIL/SpiderImagePlugin.py b/PIL/SpiderImagePlugin.py index 306b348bc..f1ccb67f6 100644 --- a/PIL/SpiderImagePlugin.py +++ b/PIL/SpiderImagePlugin.py @@ -48,7 +48,7 @@ def isInt(f): return 1 else: return 0 - except: + except ValueError: return 0 iforms = [1, 3, -11, -12, -21, -22] @@ -173,11 +173,11 @@ class SpiderImageFile(ImageFile.ImageFile): # returns a byte image after rescaling to 0..255 def convert2byte(self, depth=255): - (min, max) = self.getextrema() + (minimum, maximum) = self.getextrema() m = 1 - if max != min: - m = depth / (max-min) - b = -m * min + if maximum != minimum: + m = depth / (maximum-minimum) + b = -m * minimum return self.point(lambda i, m=m, b=b: i * m + b).convert("L") # returns a ImageTk.PhotoImage object, after rescaling to 0..255 @@ -271,7 +271,7 @@ def _save(im, fp, filename): def _save_spider(im, fp, filename): # get the filename extension and register it with Image - fn, ext = os.path.splitext(filename) + ext = os.path.splitext(filename)[1] Image.register_extension("SPIDER", ext) _save(im, fp, filename) diff --git a/Scripts/enhancer.py b/Scripts/enhancer.py index 34becf873..6393c983f 100644 --- a/Scripts/enhancer.py +++ b/Scripts/enhancer.py @@ -8,9 +8,9 @@ # try: - from tkinter import * + from tkinter import Tk, Toplevel, Frame, Label, Scale, HORIZONTAL except ImportError: - from Tkinter import * + from Tkinter import Tk, Toplevel, Frame, Label, Scale, HORIZONTAL from PIL import Image, ImageTk, ImageEnhance import sys diff --git a/Scripts/painter.py b/Scripts/painter.py index 6f1c19484..234f06171 100644 --- a/Scripts/painter.py +++ b/Scripts/painter.py @@ -10,9 +10,9 @@ # try: - from tkinter import * + from tkinter import Tk, Canvas, NW except ImportError: - from Tkinter import * + from Tkinter import Tk, Canvas, NW from PIL import Image, ImageTk import sys diff --git a/Scripts/viewer.py b/Scripts/viewer.py index c0fc59d96..f9bccec4f 100644 --- a/Scripts/viewer.py +++ b/Scripts/viewer.py @@ -7,9 +7,9 @@ from __future__ import print_function try: - from tkinter import * + from tkinter import Tk, Label except ImportError: - from Tkinter import * + from Tkinter import Tk, Label from PIL import Image, ImageTk diff --git a/Tests/check_jpeg_leaks.py b/Tests/check_jpeg_leaks.py index b8a82a5ec..4d13978f8 100644 --- a/Tests/check_jpeg_leaks.py +++ b/Tests/check_jpeg_leaks.py @@ -169,7 +169,8 @@ post patch: test_output = BytesIO() im.save(test_output, "JPEG", exif=exif) - """ + def test_base_save(self): + """ base case: MB 20.99^ ::::: :::::::::::::::::::::::::::::::::::::::::::@::: @@ -195,8 +196,6 @@ base case: 0 +----------------------------------------------------------------------->Gi 0 7.882 """ - - def test_base_save(self): im = hopper('RGB') for _ in range(iterations): diff --git a/Tests/test_bmp_reference.py b/Tests/test_bmp_reference.py index b45ea76f6..79ad439ba 100644 --- a/Tests/test_bmp_reference.py +++ b/Tests/test_bmp_reference.py @@ -58,10 +58,10 @@ class TestBmpReference(PillowTestCase): } def get_compare(f): - (head, name) = os.path.split(f) + name = os.path.split(f)[1] if name in file_map: return os.path.join(base, 'html', file_map[name]) - (name, ext) = os.path.splitext(name) + name = os.path.splitext(name)[0] return os.path.join(base, 'html', "%s.png" % name) for f in self.get_files('g'): diff --git a/Tests/test_file_fli.py b/Tests/test_file_fli.py index 3580edc18..e6634c799 100644 --- a/Tests/test_file_fli.py +++ b/Tests/test_file_fli.py @@ -5,14 +5,14 @@ from PIL import Image # sample ppm stream # created as an export of a palette image from Gimp2.6 # save as...-> hopper.fli, default options. -file = "Tests/images/hopper.fli" -data = open(file, "rb").read() +test_file = "Tests/images/hopper.fli" +data = open(test_file, "rb").read() class TestFileFli(PillowTestCase): def test_sanity(self): - im = Image.open(file) + im = Image.open(test_file) im.load() self.assertEqual(im.mode, "P") self.assertEqual(im.size, (128, 128)) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 2ce728801..1aa8a36bc 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -36,9 +36,9 @@ class TestFileGif(PillowTestCase): def test_bilevel(optimize): im = Image.new("1", (1, 1), 0) - file = BytesIO() - im.save(file, "GIF", optimize=optimize) - return len(file.getvalue()) + test_file = BytesIO() + im.save(test_file, "GIF", optimize=optimize) + return len(test_file.getvalue()) self.assertEqual(test_grayscale(0), 800) self.assertEqual(test_grayscale(1), 38) @@ -49,8 +49,8 @@ class TestFileGif(PillowTestCase): from io import BytesIO im = Image.frombytes("L", (16, 16), bytes(bytearray(range(256)))) - file = BytesIO() - im.save(file, "GIF", optimize=True) + test_file = BytesIO() + im.save(test_file, "GIF", optimize=True) self.assertEqual(im.mode, "L") def test_roundtrip(self): diff --git a/Tests/test_file_icns.py b/Tests/test_file_icns.py index 23d22df1b..2edf9dd20 100644 --- a/Tests/test_file_icns.py +++ b/Tests/test_file_icns.py @@ -5,8 +5,8 @@ from PIL import Image import sys # sample icon file -file = "Tests/images/pillow.icns" -data = open(file, "rb").read() +test_file = "Tests/images/pillow.icns" +data = open(test_file, "rb").read() enable_jpeg2k = hasattr(Image.core, 'jp2klib_version') @@ -16,7 +16,7 @@ class TestFileIcns(PillowTestCase): def test_sanity(self): # Loading this icon by default should result in the largest size # (512x512@2x) being loaded - im = Image.open(file) + im = Image.open(test_file) im.load() self.assertEqual(im.mode, "RGBA") self.assertEqual(im.size, (1024, 1024)) @@ -39,11 +39,11 @@ class TestFileIcns(PillowTestCase): def test_sizes(self): # Check that we can load all of the sizes, and that the final pixel # dimensions are as expected - im = Image.open(file) + im = Image.open(test_file) for w, h, r in im.info['sizes']: wr = w * r hr = h * r - im2 = Image.open(file) + im2 = Image.open(test_file) im2.size = (w, h, r) im2.load() self.assertEqual(im2.mode, 'RGBA') diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index aa24582cc..d4929dd58 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -23,10 +23,10 @@ class TestFileJpeg(PillowTestCase): def roundtrip(self, im, **options): out = BytesIO() im.save(out, "JPEG", **options) - bytes = out.tell() + test_bytes = out.tell() out.seek(0) im = Image.open(out) - im.bytes = bytes # for testing only + im.bytes = test_bytes # for testing only return im def test_sanity(self): diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index db67e9551..9768a881d 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -22,10 +22,10 @@ class TestFileJpeg2k(PillowTestCase): def roundtrip(self, im, **options): out = BytesIO() im.save(out, "JPEG2000", **options) - bytes = out.tell() + test_bytes = out.tell() out.seek(0) im = Image.open(out) - im.bytes = bytes # for testing only + im.bytes = test_bytes # for testing only im.load() return im diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 26a30ca30..4f798675b 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -46,8 +46,8 @@ class TestFileLibTiff(LibTiffTestCase): self._assert_noerr(im) def test_g4_large(self): - file = "Tests/images/pport_g4.tif" - im = Image.open(file) + test_file = "Tests/images/pport_g4.tif" + im = Image.open(test_file) self._assert_noerr(im) def test_g4_tiff_file(self): diff --git a/Tests/test_file_libtiff_small.py b/Tests/test_file_libtiff_small.py index cd16292a0..c6a639ae9 100644 --- a/Tests/test_file_libtiff_small.py +++ b/Tests/test_file_libtiff_small.py @@ -18,8 +18,8 @@ class TestFileLibTiffSmall(LibTiffTestCase): def test_g4_hopper_file(self): """Testing the open file load path""" - file = "Tests/images/hopper_g4.tif" - with open(file, 'rb') as f: + test_file = "Tests/images/hopper_g4.tif" + with open(test_file, 'rb') as f: im = Image.open(f) self.assertEqual(im.size, (128, 128)) @@ -28,9 +28,9 @@ class TestFileLibTiffSmall(LibTiffTestCase): def test_g4_hopper_bytesio(self): """Testing the bytesio loading code path""" from io import BytesIO - file = "Tests/images/hopper_g4.tif" + test_file = "Tests/images/hopper_g4.tif" s = BytesIO() - with open(file, 'rb') as f: + with open(test_file, 'rb') as f: s.write(f.read()) s.seek(0) im = Image.open(s) @@ -41,8 +41,8 @@ class TestFileLibTiffSmall(LibTiffTestCase): def test_g4_hopper(self): """The 128x128 lena image failed for some reason.""" - file = "Tests/images/hopper_g4.tif" - im = Image.open(file) + test_file = "Tests/images/hopper_g4.tif" + im = Image.open(test_file) self.assertEqual(im.size, (128, 128)) self._assert_noerr(im) diff --git a/Tests/test_file_mpo.py b/Tests/test_file_mpo.py index 066f39dc4..7850744af 100644 --- a/Tests/test_file_mpo.py +++ b/Tests/test_file_mpo.py @@ -17,10 +17,10 @@ class TestFileMpo(PillowTestCase): # Note that for now, there is no MPO saving functionality out = BytesIO() im.save(out, "MPO", **options) - bytes = out.tell() + test_bytes = out.tell() out.seek(0) im = Image.open(out) - im.bytes = bytes # for testing only + im.bytes = test_bytes # for testing only return im def test_sanity(self): diff --git a/Tests/test_file_pcx.py b/Tests/test_file_pcx.py index 36d6e0315..10d17d349 100644 --- a/Tests/test_file_pcx.py +++ b/Tests/test_file_pcx.py @@ -31,8 +31,8 @@ class TestFilePcx(PillowTestCase): def test_pil184(self): # Check reading of files where xmin/xmax is not zero. - file = "Tests/images/pil184.pcx" - im = Image.open(file) + test_file = "Tests/images/pil184.pcx" + im = Image.open(test_file) self.assertEqual(im.size, (447, 144)) self.assertEqual(im.tile[0][1], (0, 0, 447, 144)) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 505d48c81..b3169ed25 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -19,9 +19,9 @@ MAGIC = PngImagePlugin._MAGIC def chunk(cid, *data): - file = BytesIO() - PngImagePlugin.putchunk(*(file, cid) + data) - return file.getvalue() + test_file = BytesIO() + PngImagePlugin.putchunk(*(test_file, cid) + data) + return test_file.getvalue() o32 = PngImagePlugin.o32 @@ -56,37 +56,37 @@ class TestFilePng(PillowTestCase): self.assertRegexpMatches( Image.core.zlib_version, "\d+\.\d+\.\d+(\.\d+)?$") - file = self.tempfile("temp.png") + test_file = self.tempfile("temp.png") - hopper("RGB").save(file) + hopper("RGB").save(test_file) - im = Image.open(file) + im = Image.open(test_file) im.load() self.assertEqual(im.mode, "RGB") self.assertEqual(im.size, (128, 128)) self.assertEqual(im.format, "PNG") - hopper("1").save(file) - im = Image.open(file) + hopper("1").save(test_file) + im = Image.open(test_file) - hopper("L").save(file) - im = Image.open(file) + hopper("L").save(test_file) + im = Image.open(test_file) - hopper("P").save(file) - im = Image.open(file) + hopper("P").save(test_file) + im = Image.open(test_file) - hopper("RGB").save(file) - im = Image.open(file) + hopper("RGB").save(test_file) + im = Image.open(test_file) - hopper("I").save(file) - im = Image.open(file) + hopper("I").save(test_file) + im = Image.open(test_file) def test_broken(self): # Check reading of totally broken files. In this case, the test # file was checked into Subversion as a text file. - file = "Tests/images/broken.png" - self.assertRaises(IOError, lambda: Image.open(file)) + test_file = "Tests/images/broken.png" + self.assertRaises(IOError, lambda: Image.open(test_file)) def test_bad_text(self): # Make sure PIL can read malformed tEXt chunks (@PIL152) @@ -167,16 +167,16 @@ class TestFilePng(PillowTestCase): def test_interlace(self): - file = "Tests/images/pil123p.png" - im = Image.open(file) + test_file = "Tests/images/pil123p.png" + im = Image.open(test_file) self.assert_image(im, "P", (162, 150)) self.assertTrue(im.info.get("interlace")) im.load() - file = "Tests/images/pil123rgba.png" - im = Image.open(file) + test_file = "Tests/images/pil123rgba.png" + im = Image.open(test_file) self.assert_image(im, "RGBA", (162, 150)) self.assertTrue(im.info.get("interlace")) @@ -184,8 +184,8 @@ class TestFilePng(PillowTestCase): im.load() def test_load_transparent_p(self): - file = "Tests/images/pil123p.png" - im = Image.open(file) + test_file = "Tests/images/pil123p.png" + im = Image.open(test_file) self.assert_image(im, "P", (162, 150)) im = im.convert("RGBA") @@ -195,8 +195,8 @@ class TestFilePng(PillowTestCase): self.assertEqual(len(im.split()[3].getcolors()), 124) def test_load_transparent_rgb(self): - file = "Tests/images/rgb_trns.png" - im = Image.open(file) + test_file = "Tests/images/rgb_trns.png" + im = Image.open(test_file) self.assert_image(im, "RGB", (64, 64)) im = im.convert("RGBA") @@ -209,22 +209,22 @@ class TestFilePng(PillowTestCase): in_file = "Tests/images/pil123p.png" im = Image.open(in_file) - file = self.tempfile("temp.png") - im.save(file) + test_file = self.tempfile("temp.png") + im.save(test_file) def test_save_p_single_transparency(self): in_file = "Tests/images/p_trns_single.png" im = Image.open(in_file) - file = self.tempfile("temp.png") - im.save(file) + test_file = self.tempfile("temp.png") + im.save(test_file) def test_save_l_transparency(self): in_file = "Tests/images/l_trns.png" im = Image.open(in_file) - file = self.tempfile("temp.png") - im.save(file) + test_file = self.tempfile("temp.png") + im.save(test_file) # There are 559 transparent pixels. im = im.convert('RGBA') @@ -234,8 +234,8 @@ class TestFilePng(PillowTestCase): in_file = "Tests/images/caption_6_33_22.png" im = Image.open(in_file) - file = self.tempfile("temp.png") - im.save(file) + test_file = self.tempfile("temp.png") + im.save(test_file) def test_load_verify(self): # Check open/load/verify exception (@PIL150) @@ -329,8 +329,8 @@ class TestFilePng(PillowTestCase): # Check writing and reading of tRNS chunks for RGB images. # Independent file sample provided by Sebastian Spaeth. - file = "Tests/images/caption_6_33_22.png" - im = Image.open(file) + test_file = "Tests/images/caption_6_33_22.png" + im = Image.open(test_file) self.assertEqual(im.info["transparency"], (248, 248, 248)) # check saving transparency by default diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 3731fd9b1..80c2e60da 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -3,14 +3,14 @@ from helper import unittest, PillowTestCase from PIL import Image # sample ppm stream -file = "Tests/images/hopper.ppm" -data = open(file, "rb").read() +test_file = "Tests/images/hopper.ppm" +data = open(test_file, "rb").read() class TestFilePpm(PillowTestCase): def test_sanity(self): - im = Image.open(file) + im = Image.open(test_file) im.load() self.assertEqual(im.mode, "RGB") self.assertEqual(im.size, (128, 128)) diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py index 46cef4c78..51b8cf3f4 100644 --- a/Tests/test_file_psd.py +++ b/Tests/test_file_psd.py @@ -3,14 +3,14 @@ from helper import unittest, PillowTestCase from PIL import Image # sample ppm stream -file = "Tests/images/hopper.psd" -data = open(file, "rb").read() +test_file = "Tests/images/hopper.psd" +data = open(test_file, "rb").read() class TestImagePsd(PillowTestCase): def test_sanity(self): - im = Image.open(file) + im = Image.open(test_file) im.load() self.assertEqual(im.mode, "RGB") self.assertEqual(im.size, (128, 128)) diff --git a/Tests/test_file_webp_metadata.py b/Tests/test_file_webp_metadata.py index 9093f31ba..8b1254d61 100644 --- a/Tests/test_file_webp_metadata.py +++ b/Tests/test_file_webp_metadata.py @@ -41,12 +41,12 @@ class TestFileWebpMetadata(PillowTestCase): image = Image.open(file_path) expected_exif = image.info['exif'] - buffer = BytesIO() + test_buffer = BytesIO() - image.save(buffer, "webp", exif=expected_exif) + image.save(test_buffer, "webp", exif=expected_exif) - buffer.seek(0) - webp_image = Image.open(buffer) + test_buffer.seek(0) + webp_image = Image.open(test_buffer) webp_exif = webp_image.info.get('exif', None) self.assertTrue(webp_exif) @@ -76,12 +76,12 @@ class TestFileWebpMetadata(PillowTestCase): image = Image.open(file_path) expected_icc_profile = image.info['icc_profile'] - buffer = BytesIO() + test_buffer = BytesIO() - image.save(buffer, "webp", icc_profile=expected_icc_profile) + image.save(test_buffer, "webp", icc_profile=expected_icc_profile) - buffer.seek(0) - webp_image = Image.open(buffer) + test_buffer.seek(0) + webp_image = Image.open(test_buffer) webp_icc_profile = webp_image.info.get('icc_profile', None) @@ -98,12 +98,12 @@ class TestFileWebpMetadata(PillowTestCase): image = Image.open(file_path) self.assertTrue('exif' in image.info) - buffer = BytesIO() + test_buffer = BytesIO() - image.save(buffer, "webp") + image.save(test_buffer, "webp") - buffer.seek(0) - webp_image = Image.open(buffer) + test_buffer.seek(0) + webp_image = Image.open(test_buffer) self.assertFalse(webp_image._getexif()) diff --git a/Tests/test_font_bdf.py b/Tests/test_font_bdf.py index 0df8e866b..b844f1228 100644 --- a/Tests/test_font_bdf.py +++ b/Tests/test_font_bdf.py @@ -9,8 +9,8 @@ class TestFontBdf(PillowTestCase): def test_sanity(self): - file = open(filename, "rb") - font = BdfFontFile.BdfFontFile(file) + test_file = open(filename, "rb") + font = BdfFontFile.BdfFontFile(test_file) self.assertIsInstance(font, FontFile.FontFile) self.assertEqual(len([_f for _f in font.glyph if _f]), 190) diff --git a/Tests/test_font_pcf.py b/Tests/test_font_pcf.py index 5e9e02c8c..3cc6afa64 100644 --- a/Tests/test_font_pcf.py +++ b/Tests/test_font_pcf.py @@ -17,8 +17,8 @@ class TestFontPcf(PillowTestCase): self.skipTest("zlib support not available") def save_font(self): - file = open(fontname, "rb") - font = PcfFontFile.PcfFontFile(file) + test_file = open(fontname, "rb") + font = PcfFontFile.PcfFontFile(test_file) self.assertIsInstance(font, FontFile.FontFile) self.assertEqual(len([_f for _f in font.glyph if _f]), 192) diff --git a/Tests/test_image.py b/Tests/test_image.py index 0b84de630..caee70fec 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -41,8 +41,8 @@ class TestImage(PillowTestCase): im.paste(0, (0, 0, 100, 100)) self.assertFalse(im.readonly) - file = self.tempfile("temp.ppm") - im._dump(file) + test_file = self.tempfile("temp.ppm") + im._dump(test_file) def test_comparison_with_other_type(self): # Arrange diff --git a/Tests/test_image_filter.py b/Tests/test_image_filter.py index d29fd3dfd..6a694b3ca 100644 --- a/Tests/test_image_filter.py +++ b/Tests/test_image_filter.py @@ -76,10 +76,10 @@ class TestImageFilter(PillowTestCase): # 0 1 2 # 3 4 5 # 6 7 8 - min = im.filter(ImageFilter.MinFilter).getpixel((1, 1)) + minimum = im.filter(ImageFilter.MinFilter).getpixel((1, 1)) med = im.filter(ImageFilter.MedianFilter).getpixel((1, 1)) - max = im.filter(ImageFilter.MaxFilter).getpixel((1, 1)) - return min, med, max + maximum = im.filter(ImageFilter.MaxFilter).getpixel((1, 1)) + return minimum, med, maximum self.assertEqual(rankfilter("1"), (0, 4, 8)) self.assertEqual(rankfilter("L"), (0, 4, 8)) diff --git a/Tests/test_image_split.py b/Tests/test_image_split.py index 0e057523b..beab7b546 100644 --- a/Tests/test_image_split.py +++ b/Tests/test_image_split.py @@ -45,13 +45,13 @@ class TestImageSplit(PillowTestCase): codecs = dir(Image.core) if 'zip_encoder' in codecs: - file = self.tempfile("temp.png") + test_file = self.tempfile("temp.png") else: - file = self.tempfile("temp.pcx") + test_file = self.tempfile("temp.pcx") def split_open(mode): - hopper(mode).save(file) - im = Image.open(file) + hopper(mode).save(test_file) + im = Image.open(test_file) return len(im.split()) self.assertEqual(split_open("1"), 1) self.assertEqual(split_open("L"), 1) diff --git a/Tests/test_image_transpose.py b/Tests/test_image_transpose.py index 3069df61c..e13fc8605 100644 --- a/Tests/test_image_transpose.py +++ b/Tests/test_image_transpose.py @@ -1,4 +1,5 @@ -from helper import unittest, PillowTestCase, hopper +import helper +from helper import unittest, PillowTestCase from PIL.Image import (FLIP_LEFT_RIGHT, FLIP_TOP_BOTTOM, ROTATE_90, ROTATE_180, ROTATE_270, TRANSPOSE) @@ -7,8 +8,8 @@ from PIL.Image import (FLIP_LEFT_RIGHT, FLIP_TOP_BOTTOM, ROTATE_90, ROTATE_180, class TestImageTranspose(PillowTestCase): hopper = { - 'L': hopper('L').crop((0, 0, 121, 127)).copy(), - 'RGB': hopper('RGB').crop((0, 0, 121, 127)).copy(), + 'L': helper.hopper('L').crop((0, 0, 121, 127)).copy(), + 'RGB': helper.hopper('RGB').crop((0, 0, 121, 127)).copy(), } def test_flip_left_right(self): diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index 01503dff7..5311b899f 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -24,11 +24,11 @@ class TestImageFile(PillowTestCase): if format in ("MSP", "XBM"): im = im.convert("1") - file = BytesIO() + test_file = BytesIO() - im.save(file, format) + im.save(test_file, format) - data = file.getvalue() + data = test_file.getvalue() parser = ImageFile.Parser() parser.feed(data) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 627b28c67..f1d59ae43 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -94,7 +94,8 @@ try: def _render(self, font): txt = "Hello World!" ttf = ImageFont.truetype(font, FONT_SIZE) - w, h = ttf.getsize(txt) + ttf.getsize(txt) + img = Image.new("RGB", (256, 64), "white") d = ImageDraw.Draw(img) d.text((10, 10), txt, font=ttf, fill='black') diff --git a/Tests/test_imagepalette.py b/Tests/test_imagepalette.py index a16b590ba..707ab4080 100644 --- a/Tests/test_imagepalette.py +++ b/Tests/test_imagepalette.py @@ -17,11 +17,11 @@ class TestImagePalette(PillowTestCase): palette = ImagePalette() - map = {} + test_map = {} for i in range(256): - map[palette.getcolor((i, i, i))] = i + test_map[palette.getcolor((i, i, i))] = i - self.assertEqual(len(map), 256) + self.assertEqual(len(test_map), 256) self.assertRaises(ValueError, lambda: palette.getcolor((1, 2, 3))) def test_file(self): diff --git a/Tests/test_imagesequence.py b/Tests/test_imagesequence.py index 459a053d8..1b4bb3c02 100644 --- a/Tests/test_imagesequence.py +++ b/Tests/test_imagesequence.py @@ -7,10 +7,10 @@ class TestImageSequence(PillowTestCase): def test_sanity(self): - file = self.tempfile("temp.im") + test_file = self.tempfile("temp.im") im = hopper("RGB") - im.save(file) + im.save(test_file) seq = ImageSequence.Iterator(im) diff --git a/Tests/test_imagewin.py b/Tests/test_imagewin.py index c32290e43..7ceea86ee 100644 --- a/Tests/test_imagewin.py +++ b/Tests/test_imagewin.py @@ -100,8 +100,8 @@ class TestImageWinDib(PillowTestCase): # Act # Make one the same as the using tobytes()/frombytes() - buffer = dib1.tobytes() - dib2.frombytes(buffer) + test_buffer = dib1.tobytes() + dib2.frombytes(test_buffer) # Assert # Confirm they're the same @@ -111,11 +111,11 @@ class TestImageWinDib(PillowTestCase): # Arrange im = hopper() dib = ImageWin.Dib(im) - buffer = dib.tobytes() + test_buffer = dib.tobytes() # Act/Assert self.assert_warning(DeprecationWarning, dib.tostring) - self.assert_warning(DeprecationWarning, lambda: dib.fromstring(buffer)) + self.assert_warning(DeprecationWarning, lambda: dib.fromstring(test_buffer)) if __name__ == '__main__': diff --git a/Tests/test_mode_i16.py b/Tests/test_mode_i16.py index 20e39b235..0827e218b 100644 --- a/Tests/test_mode_i16.py +++ b/Tests/test_mode_i16.py @@ -64,15 +64,15 @@ class TestModeI16(PillowTestCase): self.assertEqual(imIn.getpixel((0, 0)), 2) if mode == "L": - max = 255 + maximum = 255 else: - max = 32767 + maximum = 32767 imIn = Image.new(mode, (1, 1), 256) - self.assertEqual(imIn.getpixel((0, 0)), min(256, max)) + self.assertEqual(imIn.getpixel((0, 0)), min(256, maximum)) imIn.putpixel((0, 0), 512) - self.assertEqual(imIn.getpixel((0, 0)), min(512, max)) + self.assertEqual(imIn.getpixel((0, 0)), min(512, maximum)) basic("L") diff --git a/Tests/test_pickle.py b/Tests/test_pickle.py index f1c594be9..52d64acee 100644 --- a/Tests/test_pickle.py +++ b/Tests/test_pickle.py @@ -60,7 +60,7 @@ class TestPickle(PillowTestCase): import pickle # Act / Assert - for file in [ + for test_file in [ "Tests/images/test-card.png", "Tests/images/zero_bb.png", "Tests/images/zero_bb_scale2.png", @@ -69,7 +69,7 @@ class TestPickle(PillowTestCase): "Tests/images/p_trns_single.png", "Tests/images/pil123p.png" ]: - self.helper_pickle_string(pickle, file=file) + self.helper_pickle_string(pickle, file=test_file) def test_pickle_l_mode(self): # Arrange diff --git a/Tests/threaded_save.py b/Tests/threaded_save.py index 376abe7bd..3bcbdd0b0 100644 --- a/Tests/threaded_save.py +++ b/Tests/threaded_save.py @@ -6,10 +6,7 @@ import sys import threading import time -try: - format = sys.argv[1] -except: - format = "PNG" +test_format = sys.argv[1] if len(sys.argv) > 1 else "PNG" im = Image.open("Tests/images/hopper.ppm") im.load() @@ -28,7 +25,7 @@ class Worker(threading.Thread): sys.stdout.write("x") break f = io.BytesIO() - im.save(f, format, optimize=1) + im.save(f, test_format, optimize=1) data = f.getvalue() result.append(len(data)) im = Image.open(io.BytesIO(data)) From 80a8463225f4261e1376dc9d9179d08250c63bba Mon Sep 17 00:00:00 2001 From: Thomas Bechtold Date: Wed, 29 Apr 2015 13:02:21 +0200 Subject: [PATCH 0233/1037] Fix test TestImageFont.test_textsize_equal The current test failed on SLE11SP3 so use assert_image_similar() for the test. This fixes issue 1202. --- Tests/test_imagefont.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 627b28c67..c07d99258 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -126,7 +126,7 @@ try: target = 'Tests/images/rectangle_surrounding_text.png' target_img = Image.open(target) - self.assert_image_equal(im, target_img) + self.assert_image_similar(im, target_img, .5) def test_render_multiline(self): im = Image.new(mode='RGB', size=(300, 100)) From cb6fa8a20f47c57e4f68eeefa976676e1e0e2b7a Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 5 May 2015 00:52:56 +0300 Subject: [PATCH 0234/1037] Add CPython/PyPy classifiers --- setup.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.py b/setup.py index 216322ec2..cf64ee657 100644 --- a/setup.py +++ b/setup.py @@ -748,6 +748,8 @@ setup( "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", + 'Programming Language :: Python :: Implementation :: CPython', + 'Programming Language :: Python :: Implementation :: PyPy', ], cmdclass={"build_ext": pil_build_ext}, ext_modules=[Extension("PIL._imaging", ["_imaging.c"])], From e7d85432e4fa8a0cc847f260c91048c675fb9653 Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 5 May 2015 11:15:55 +0300 Subject: [PATCH 0235/1037] Fix OS X build PR https://github.com/python-pillow/Pillow/pull/1209 missed a `file` -> `test_file` change on line 28, which broke the Mac build. Let's also make the constant uppercase whilst we're at it. --- Tests/test_file_icns.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Tests/test_file_icns.py b/Tests/test_file_icns.py index 2edf9dd20..230a3e9b9 100644 --- a/Tests/test_file_icns.py +++ b/Tests/test_file_icns.py @@ -5,8 +5,8 @@ from PIL import Image import sys # sample icon file -test_file = "Tests/images/pillow.icns" -data = open(test_file, "rb").read() +TEST_FILE = "Tests/images/pillow.icns" +data = open(TEST_FILE, "rb").read() enable_jpeg2k = hasattr(Image.core, 'jp2klib_version') @@ -16,7 +16,7 @@ class TestFileIcns(PillowTestCase): def test_sanity(self): # Loading this icon by default should result in the largest size # (512x512@2x) being loaded - im = Image.open(test_file) + im = Image.open(TEST_FILE) im.load() self.assertEqual(im.mode, "RGBA") self.assertEqual(im.size, (1024, 1024)) @@ -25,12 +25,12 @@ class TestFileIcns(PillowTestCase): @unittest.skipIf(sys.platform != 'darwin', "requires MacOS") def test_save(self): - im = Image.open(file) + im = Image.open(TEST_FILE) - test_file = self.tempfile("temp.icns") - im.save(test_file) + TEST_FILE = self.tempfile("temp.icns") + im.save(TEST_FILE) - reread = Image.open(test_file) + reread = Image.open(TEST_FILE) self.assertEqual(reread.mode, "RGBA") self.assertEqual(reread.size, (1024, 1024)) @@ -39,11 +39,11 @@ class TestFileIcns(PillowTestCase): def test_sizes(self): # Check that we can load all of the sizes, and that the final pixel # dimensions are as expected - im = Image.open(test_file) + im = Image.open(TEST_FILE) for w, h, r in im.info['sizes']: wr = w * r hr = h * r - im2 = Image.open(test_file) + im2 = Image.open(TEST_FILE) im2.size = (w, h, r) im2.load() self.assertEqual(im2.mode, 'RGBA') From 30bb31ef596e0547ed5fa83ac3c269749ab84801 Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 5 May 2015 19:38:10 +0300 Subject: [PATCH 0236/1037] Fix test according to original: https://github.com/python-pillow/Pillow/commit/689f28aae7c9cc3fddb73e7ec2a07f169b91d4ba#diff-d48d995756d16b9d9b8151e27377a37dR25 --- Tests/test_file_icns.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/test_file_icns.py b/Tests/test_file_icns.py index 230a3e9b9..67eb1335f 100644 --- a/Tests/test_file_icns.py +++ b/Tests/test_file_icns.py @@ -27,10 +27,10 @@ class TestFileIcns(PillowTestCase): def test_save(self): im = Image.open(TEST_FILE) - TEST_FILE = self.tempfile("temp.icns") - im.save(TEST_FILE) + temp_file = self.tempfile("temp.icns") + im.save(temp_file) - reread = Image.open(TEST_FILE) + reread = Image.open(temp_file) self.assertEqual(reread.mode, "RGBA") self.assertEqual(reread.size, (1024, 1024)) From 3d52e797f87d36f011079c00670985a609aa2fe6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 7 May 2015 21:47:08 +1000 Subject: [PATCH 0237/1037] Adjusted ImageQt use of unicode() for 2/3 compatibility --- PIL/ImageQt.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/PIL/ImageQt.py b/PIL/ImageQt.py index 1723e3226..6b7d4d66e 100644 --- a/PIL/ImageQt.py +++ b/PIL/ImageQt.py @@ -59,7 +59,10 @@ class ImageQt(QImage): # handle filename, if given instead of image name if hasattr(im, "toUtf8"): # FIXME - is this really the best way to do this? - im = unicode(im.toUtf8(), "utf-8") + if str is bytes: + im = unicode(im.toUtf8(), "utf-8") + else: + im = str(im.toUtf8(), "utf-8") if isPath(im): im = Image.open(im) From fa27200fe32c8bc976c59a372d56035f47fa139c Mon Sep 17 00:00:00 2001 From: hugovk Date: Fri, 8 May 2015 00:05:59 +0300 Subject: [PATCH 0238/1037] Pyroma (really docutils)' installation no longer slow on Py3 --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 382f205d0..a75c80873 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,9 +20,7 @@ install: - "travis_retry sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-qt4 ghostscript libffi-dev libjpeg-turbo-progs cmake imagemagick" - "travis_retry pip install cffi" - "travis_retry pip install coverage nose" - - # Pyroma installation is slow on Py3, so just do it for Py2. - - if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then travis_retry pip install pyroma; fi + - "travis_retry pip install pyroma" - if [ "$TRAVIS_PYTHON_VERSION" == "2.6" ]; then travis_retry pip install unittest2; fi From 9e00c7788499c03331d1de388311cdff41eb6e39 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 8 May 2015 14:28:49 +1000 Subject: [PATCH 0239/1037] Fixed variable name in JpegImagePlugin --- PIL/JpegImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 01a173b5b..e15042504 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -704,7 +704,7 @@ def _save_cjpeg(im, fp, filename): tempfile = im._dump() subprocess.check_call(["cjpeg", "-outfile", filename, tempfile]) try: - os.unlink(file) + os.unlink(tempfile) except: pass From 3ac92445aece4b8bce8032049a5ea13a68a8ca3d Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 8 May 2015 07:28:50 -0400 Subject: [PATCH 0240/1037] Add headers [ci skip] --- docs/installation.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/installation.rst b/docs/installation.rst index 7634a77fc..e94afa892 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -1,12 +1,18 @@ Installation ============ +Warnings +-------- + .. warning:: Pillow and PIL cannot co-exist in the same environment. Before installing Pillow, please uninstall PIL. .. warning:: Pillow >= 1.0 no longer supports "import Image". Please use "from PIL import Image" instead. .. warning:: Pillow >= 2.1.0 no longer supports "import _imaging". Please use "from PIL.Image import core as _imaging" instead. +Notes +----- + .. note:: Pillow < 2.0.0 supports Python versions 2.4, 2.5, 2.6, 2.7. .. note:: Pillow >= 2.0.0 supports Python versions 2.6, 2.7, 3.2, 3.3, 3.4 From 61bc6e5be2649481a0e072d425a7d6836f9a6f7e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 8 May 2015 14:24:40 +1000 Subject: [PATCH 0241/1037] Removed unused import from pildriver script --- Scripts/pildriver.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Scripts/pildriver.py b/Scripts/pildriver.py index ba2a8c66f..bb01004f5 100644 --- a/Scripts/pildriver.py +++ b/Scripts/pildriver.py @@ -498,10 +498,6 @@ class PILDriver: if __name__ == '__main__': import sys - try: - import readline - except ImportError: - pass # not available on all platforms # If we see command-line arguments, interpret them as a stack state # and execute. Otherwise go interactive. From dbd3415709e43b77430e1ba34e17e6de4830a905 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 8 May 2015 16:37:52 +1000 Subject: [PATCH 0242/1037] Removed pre-Python 2.3 workaround --- PIL/OleFileIO.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index 88b68fa4a..c804dd454 100755 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -267,17 +267,10 @@ else: #[PL] These workarounds were inspired from the Path module # (see http://www.jorendorff.com/articles/python/path/) -#TODO: test with old Python versions - -# Pre-2.3 workaround for basestring. try: basestring except NameError: - try: - # is Unicode supported (Python >2.0 or >1.6 ?) - basestring = (str, unicode) - except NameError: - basestring = str + basestring = str #[PL] Experimental setting: if True, OLE filenames will be kept in Unicode # if False (default PIL behaviour), all filenames are converted to Latin-1. From b503d27f45ff6d5f549e12757a7b9e4b2cf088d3 Mon Sep 17 00:00:00 2001 From: Lawrence Kesteloot Date: Sat, 9 May 2015 22:10:05 -0700 Subject: [PATCH 0243/1037] Release GIL during image load (decode). --- decode.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/decode.c b/decode.c index c56f42592..6299d9124 100644 --- a/decode.c +++ b/decode.c @@ -116,12 +116,17 @@ _decode(ImagingDecoderObject* decoder, PyObject* args) { UINT8* buffer; int bufsize, status; + ImagingSectionCookie cookie; if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH, &buffer, &bufsize)) return NULL; + ImagingSectionEnter(&cookie); + status = decoder->decode(decoder->im, &decoder->state, buffer, bufsize); + ImagingSectionLeave(&cookie); + return Py_BuildValue("ii", status, decoder->state.errcode); } From 1dd3bef61507847375772ffd207e8451fa5c897e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 10 May 2015 23:55:33 +1000 Subject: [PATCH 0244/1037] Upgraded OleFileIO to 0.42b --- PIL/OleFileIO.py | 941 +++++++++++++++++++++++++++++++---------------- 1 file changed, 616 insertions(+), 325 deletions(-) diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index c804dd454..d787e59ed 100755 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -1,47 +1,70 @@ #!/usr/bin/env python -## OleFileIO_PL: -## Module to read Microsoft OLE2 files (also called Structured Storage or -## Microsoft Compound Document File Format), such as Microsoft Office -## documents, Image Composer and FlashPix files, Outlook messages, ... -## This version is compatible with Python 2.6+ and 3.x -## version 0.30 2014-02-04 Philippe Lagadec - http://www.decalage.info - -## Project website: http://www.decalage.info/python/olefileio - -## Improved version of the OleFileIO module from PIL library v1.1.6 -## See: http://www.pythonware.com/products/pil/index.htm - -## The Python Imaging Library (PIL) is - -## Copyright (c) 1997-2005 by Secret Labs AB -## Copyright (c) 1995-2005 by Fredrik Lundh - -## OleFileIO_PL changes are Copyright (c) 2005-2014 by Philippe Lagadec - -## See source code and LICENSE.txt for information on usage and redistribution. - -## WARNING: THIS IS (STILL) WORK IN PROGRESS. +# olefile (formerly OleFileIO_PL) version 0.42 2015-01-25 +# +# Module to read/write Microsoft OLE2 files (also called Structured Storage or +# Microsoft Compound Document File Format), such as Microsoft Office 97-2003 +# documents, Image Composer and FlashPix files, Outlook messages, ... +# This version is compatible with Python 2.6+ and 3.x +# +# Project website: http://www.decalage.info/olefile +# +# olefile is copyright (c) 2005-2015 Philippe Lagadec (http://www.decalage.info) +# +# olefile is based on the OleFileIO module from the PIL library v1.1.6 +# See: http://www.pythonware.com/products/pil/index.htm +# +# The Python Imaging Library (PIL) is +# Copyright (c) 1997-2005 by Secret Labs AB +# Copyright (c) 1995-2005 by Fredrik Lundh +# +# See source code and LICENSE.txt for information on usage and redistribution. -# Starting with OleFileIO_PL v0.30, only Python 2.6+ and 3.x is supported +# Since OleFileIO_PL v0.30, only Python 2.6+ and 3.x is supported # This import enables print() as a function rather than a keyword # (main requirement to be compatible with Python 3.x) # The comment on the line below should be printed on Python 2.5 or older: -from __future__ import print_function # This version of OleFileIO_PL requires Python 2.6+ or 3.x. +from __future__ import print_function # This version of olefile requires Python 2.6+ or 3.x. -__author__ = "Philippe Lagadec, Fredrik Lundh (Secret Labs AB)" -__date__ = "2014-02-04" -__version__ = '0.30' +__author__ = "Philippe Lagadec" +__date__ = "2015-01-25" +__version__ = '0.42b' #--- LICENSE ------------------------------------------------------------------ -# OleFileIO_PL is an improved version of the OleFileIO module from the -# Python Imaging Library (PIL). - -# OleFileIO_PL changes are Copyright (c) 2005-2014 by Philippe Lagadec +# olefile (formerly OleFileIO_PL) is copyright (c) 2005-2015 Philippe Lagadec +# (http://www.decalage.info) # +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# ---------- +# PIL License: +# +# olefile is based on source code from the OleFileIO module of the Python +# Imaging Library (PIL) published by Fredrik Lundh under the following license: + # The Python Imaging Library (PIL) is # Copyright (c) 1997-2005 by Secret Labs AB # Copyright (c) 1995-2005 by Fredrik Lundh @@ -67,7 +90,7 @@ __version__ = '0.30' # PERFORMANCE OF THIS SOFTWARE. #----------------------------------------------------------------------------- -# CHANGELOG: (only OleFileIO_PL changes compared to PIL 1.1.6) +# CHANGELOG: (only olefile/OleFileIO_PL changes compared to PIL 1.1.6) # 2005-05-11 v0.10 PL: - a few fixes for Python 2.4 compatibility # (all changes flagged with [PL]) # 2006-02-22 v0.11 PL: - a few fixes for some Office 2003 documents which raise @@ -142,10 +165,29 @@ __version__ = '0.30' # 2014-02-04 v0.30 PL: - upgraded code to support Python 3.x by Martin Panter # - several fixes for Python 2.6 (xrange, MAGIC) # - reused i32 from Pillow's _binary +# 2014-07-18 v0.31 - preliminary support for 4K sectors +# 2014-07-27 v0.31 PL: - a few improvements in OleFileIO.open (header parsing) +# - Fixed loadfat for large files with 4K sectors (issue #3) +# 2014-07-30 v0.32 PL: - added write_sect to write sectors to disk +# - added write_mode option to OleFileIO.__init__ and open +# 2014-07-31 PL: - fixed padding in write_sect for Python 3, added checks +# - added write_stream to write a stream to disk +# 2014-09-26 v0.40 PL: - renamed OleFileIO_PL to olefile +# 2014-11-09 NE: - added support for Jython (Niko Ehrenfeuchter) +# 2014-11-13 v0.41 PL: - improved isOleFile and OleFileIO.open to support OLE +# data in a string buffer and file-like objects. +# 2014-11-21 PL: - updated comments according to Pillow's commits +# 2015-01-24 v0.42 PL: - changed the default path name encoding from Latin-1 +# to UTF-8 on Python 2.x (Unicode on Python 3.x) +# - added path_encoding option to override the default +# - fixed a bug in _list when a storage is empty #----------------------------------------------------------------------------- # TODO (for version 1.0): -# + isOleFile should accept file-like objects like open +# + get rid of print statements, to simplify Python 2.x and 3.x support +# + add is_stream and is_storage +# + remove leading and trailing slashes where a path is used +# + add functions path_list2str and path_str2list # + fix how all the methods handle unicode str and/or bytes as arguments # + add path attrib to _OleDirEntry, set it once and for all in init or # append_kids (then listdir/_list can be simplified) @@ -177,30 +219,16 @@ __version__ = '0.30' # - move all debug code (and maybe dump methods) to a separate module, with # a class which inherits OleFileIO ? # - fix docstrings to follow epydoc format -# - add support for 4K sectors ? # - add support for big endian byte order ? # - create a simple OLE explorer with wxPython # FUTURE EVOLUTIONS to add write support: -# 1) add ability to write a stream back on disk from BytesIO (same size, no -# change in FAT/MiniFAT). -# 2) rename a stream/storage if it doesn't change the RB tree -# 3) use rbtree module to update the red-black tree + any rename -# 4) remove a stream/storage: free sectors in FAT/MiniFAT -# 5) allocate new sectors in FAT/MiniFAT -# 6) create new storage/stream -#----------------------------------------------------------------------------- +# see issue #6 on Bitbucket: +# https://bitbucket.org/decalage/olefileio_pl/issue/6/improve-olefileio_pl-to-write-ole-files + +#----------------------------------------------------------------------------- +# NOTES from PIL 1.1.6: -# -# THIS IS WORK IN PROGRESS -# -# The Python Imaging Library -# $Id$ -# -# stuff to deal with OLE2 Structured Storage files. this module is -# used by PIL to read Image Composer and FlashPix files, but can also -# be used to read other files of this type. -# # History: # 1997-01-20 fl Created # 1997-01-22 fl Fixed 64-bit portability quirk @@ -222,25 +250,19 @@ __version__ = '0.30' # "If this document and functionality of the Software conflict, # the actual functionality of the Software represents the correct # functionality" -- Microsoft, in the OLE format specification -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1997. -# -# See the README file for information on usage and redistribution. -# #------------------------------------------------------------------------------ import io import sys -import struct -import array -import os.path -import datetime +import struct, array, os.path, datetime + +#=== COMPATIBILITY WORKAROUNDS ================================================ #[PL] Define explicitly the public API to avoid private objects in pydoc: -__all__ = ['OleFileIO', 'isOleFile', 'MAGIC'] +#TODO: add more +# __all__ = ['OleFileIO', 'isOleFile', 'MAGIC'] # For Python 3.x, need to redefine long as int: if str is not bytes: @@ -261,39 +283,66 @@ if array.array('L').itemsize == 4: elif array.array('I').itemsize == 4: # on 64 bits platforms, integers in an array are 32 bits: UINT32 = 'I' +elif array.array('i').itemsize == 4: + # On 64 bit Jython, signed integers ('i') are the only way to store our 32 + # bit values in an array in a *somewhat* reasonable way, as the otherwise + # perfectly suited 'H' (unsigned int, 32 bits) results in a completely + # unusable behaviour. This is most likely caused by the fact that Java + # doesn't have unsigned values, and thus Jython's "array" implementation, + # which is based on "jarray", doesn't have them either. + # NOTE: to trick Jython into converting the values it would normally + # interpret as "signed" into "unsigned", a binary-and operation with + # 0xFFFFFFFF can be used. This way it is possible to use the same comparing + # operations on all platforms / implementations. The corresponding code + # lines are flagged with a 'JYTHON-WORKAROUND' tag below. + UINT32 = 'i' else: raise ValueError('Need to fix a bug with 32 bit arrays, please contact author...') #[PL] These workarounds were inspired from the Path module # (see http://www.jorendorff.com/articles/python/path/) +#TODO: test with old Python versions + +# Pre-2.3 workaround for basestring. try: basestring except NameError: - basestring = str + try: + # is Unicode supported (Python >2.0 or >1.6 ?) + basestring = (str, unicode) + except NameError: + basestring = str #[PL] Experimental setting: if True, OLE filenames will be kept in Unicode # if False (default PIL behaviour), all filenames are converted to Latin-1. -KEEP_UNICODE_NAMES = False +KEEP_UNICODE_NAMES = True + +if sys.version_info[0] < 3: + # On Python 2.x, the default encoding for path names is UTF-8: + DEFAULT_PATH_ENCODING = 'utf-8' +else: + # On Python 3.x, the default encoding for path names is Unicode (None): + DEFAULT_PATH_ENCODING = None + + +#=== DEBUGGING =============================================================== + +#TODO: replace this by proper logging #[PL] DEBUG display mode: False by default, use set_debug_mode() or "-d" on # command line to change it. DEBUG_MODE = False - - def debug_print(msg): print(msg) - - def debug_pass(msg): pass debug = debug_pass - def set_debug_mode(debug_mode): """ Set debug mode on or off, to control display of debugging messages. - mode: True or False + :param mode: True or False """ global DEBUG_MODE, debug DEBUG_MODE = debug_mode @@ -302,26 +351,30 @@ def set_debug_mode(debug_mode): else: debug = debug_pass + +#=== CONSTANTS =============================================================== + +# magic bytes that should be at the beginning of every OLE file: MAGIC = b'\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1' -# [PL]: added constants for Sector IDs (from AAF specifications) -MAXREGSECT = 0xFFFFFFFA; # maximum SECT -DIFSECT = 0xFFFFFFFC; # (-4) denotes a DIFAT sector in a FAT -FATSECT = 0xFFFFFFFD; # (-3) denotes a FAT sector in a FAT -ENDOFCHAIN = 0xFFFFFFFE; # (-2) end of a virtual stream chain -FREESECT = 0xFFFFFFFF; # (-1) unallocated sector +#[PL]: added constants for Sector IDs (from AAF specifications) +MAXREGSECT = 0xFFFFFFFA # (-6) maximum SECT +DIFSECT = 0xFFFFFFFC # (-4) denotes a DIFAT sector in a FAT +FATSECT = 0xFFFFFFFD # (-3) denotes a FAT sector in a FAT +ENDOFCHAIN = 0xFFFFFFFE # (-2) end of a virtual stream chain +FREESECT = 0xFFFFFFFF # (-1) unallocated sector -# [PL]: added constants for Directory Entry IDs (from AAF specifications) -MAXREGSID = 0xFFFFFFFA; # maximum directory entry ID -NOSTREAM = 0xFFFFFFFF; # (-1) unallocated directory entry +#[PL]: added constants for Directory Entry IDs (from AAF specifications) +MAXREGSID = 0xFFFFFFFA # (-6) maximum directory entry ID +NOSTREAM = 0xFFFFFFFF # (-1) unallocated directory entry -# [PL] object types in storage (from AAF specifications) -STGTY_EMPTY = 0 # empty directory entry (according to OpenOffice.org doc) -STGTY_STORAGE = 1 # element is a storage object -STGTY_STREAM = 2 # element is a stream object -STGTY_LOCKBYTES = 3 # element is an ILockBytes object -STGTY_PROPERTY = 4 # element is an IPropertyStorage object -STGTY_ROOT = 5 # element is a root storage +#[PL] object types in storage (from AAF specifications) +STGTY_EMPTY = 0 # empty directory entry (according to OpenOffice.org doc) +STGTY_STORAGE = 1 # element is a storage object +STGTY_STREAM = 2 # element is a stream object +STGTY_LOCKBYTES = 3 # element is an ILockBytes object +STGTY_PROPERTY = 4 # element is an IPropertyStorage object +STGTY_ROOT = 5 # element is a root storage # @@ -353,30 +406,52 @@ WORD_CLSID = "00020900-0000-0000-C000-000000000046" #TODO: check Excel, PPT, ... #[PL]: Defect levels to classify parsing errors - see OleFileIO._raise_defect() -DEFECT_UNSURE = 10 # a case which looks weird, but not sure it's a defect -DEFECT_POTENTIAL = 20 # a potential defect -DEFECT_INCORRECT = 30 # an error according to specifications, but parsing - # can go on -DEFECT_FATAL = 40 # an error which cannot be ignored, parsing is - # impossible +DEFECT_UNSURE = 10 # a case which looks weird, but not sure it's a defect +DEFECT_POTENTIAL = 20 # a potential defect +DEFECT_INCORRECT = 30 # an error according to specifications, but parsing + # can go on +DEFECT_FATAL = 40 # an error which cannot be ignored, parsing is + # impossible + +# Minimal size of an empty OLE file, with 512-bytes sectors = 1536 bytes +# (this is used in isOleFile and OleFile.open) +MINIMAL_OLEFILE_SIZE = 1536 #[PL] add useful constants to __all__: -for key in list(vars().keys()): - if key.startswith('STGTY_') or key.startswith('DEFECT_'): - __all__.append(key) +# for key in list(vars().keys()): +# if key.startswith('STGTY_') or key.startswith('DEFECT_'): +# __all__.append(key) -#--- FUNCTIONS ---------------------------------------------------------------- +#=== FUNCTIONS =============================================================== def isOleFile (filename): """ - Test if file is an OLE container (according to its header). + Test if a file is an OLE container (according to the magic bytes in its header). + + :param filename: string-like or file-like object, OLE file to parse + + - if filename is a string smaller than 1536 bytes, it is the path + of the file to open. (bytes or unicode string) + - if filename is a string longer than 1535 bytes, it is parsed + as the content of an OLE file in memory. (bytes type only) + - if filename is a file-like object (with read and seek methods), + it is parsed as-is. - :param filename: file name or path (str, unicode) :returns: True if OLE, False otherwise. """ - f = open(filename, 'rb') - header = f.read(len(MAGIC)) + # check if filename is a string-like or file-like object: + if hasattr(filename, 'read'): + # file-like object: use it directly + header = filename.read(len(MAGIC)) + # just in case, seek back to start of file: + filename.seek(0) + elif isinstance(filename, bytes) and len(filename) >= MINIMAL_OLEFILE_SIZE: + # filename is a bytes string containing the OLE file to be parsed: + header = filename[:len(MAGIC)] + else: + # string-like object: filename of file on disk + header = open(filename, 'rb').read(len(MAGIC)) if header == MAGIC: return True else: @@ -434,41 +509,17 @@ def _clsid(clsid): tuple(map(i8, clsid[8:16])))) -# UNICODE support: -# (necessary to handle storages/streams names which use Unicode) - -def _unicode(s, errors='replace'): - """ - Map unicode string to Latin 1. (Python with Unicode support) - - :param s: UTF-16LE unicode string to convert to Latin-1 - :param errors: 'replace', 'ignore' or 'strict'. - """ - #TODO: test if it OleFileIO works with Unicode strings, instead of - # converting to Latin-1. - try: - # First the string is converted to plain Unicode: - # (assuming it is encoded as UTF-16 little-endian) - u = s.decode('UTF-16LE', errors) - if bytes is not str or KEEP_UNICODE_NAMES: - return u - else: - # Second the unicode string is converted to Latin-1 - return u.encode('latin_1', errors) - except: - # there was an error during Unicode to Latin-1 conversion: - raise IOError('incorrect Unicode name') - def filetime2datetime(filetime): - """ - convert FILETIME (64 bits int) to Python datetime.datetime - """ - # TODO: manage exception when microseconds is too large - # inspired from http://code.activestate.com/recipes/511425-filetime-to-datetime/ - _FILETIME_null_date = datetime.datetime(1601, 1, 1, 0, 0, 0) - #debug('timedelta days=%d' % (filetime//(10*1000000*3600*24))) - return _FILETIME_null_date + datetime.timedelta(microseconds=filetime//10) + """ + convert FILETIME (64 bits int) to Python datetime.datetime + """ + # TODO: manage exception when microseconds is too large + # inspired from http://code.activestate.com/recipes/511425-filetime-to-datetime/ + _FILETIME_null_date = datetime.datetime(1601, 1, 1, 0, 0, 0) + #debug('timedelta days=%d' % (filetime//(10*1000000*3600*24))) + return _FILETIME_null_date + datetime.timedelta(microseconds=filetime//10) + #=== CLASSES ================================================================== @@ -578,6 +629,7 @@ class OleMetadata: self.language = None self.doc_version = None + def parse_properties(self, olefile): """ Parse standard properties of an OLE file, from the streams @@ -639,6 +691,7 @@ class _OleStream(io.BytesIO): fat table arguments. Attributes: + - size: actual size of data stream, after it was opened. """ @@ -650,18 +703,18 @@ class _OleStream(io.BytesIO): """ Constructor for _OleStream class. - :param fp : file object, the OLE container or the MiniFAT stream - :param sect : sector index of first sector in the stream - :param size : total size of the stream - :param offset : offset in bytes for the first FAT or MiniFAT sector + :param fp: file object, the OLE container or the MiniFAT stream + :param sect: sector index of first sector in the stream + :param size: total size of the stream + :param offset: offset in bytes for the first FAT or MiniFAT sector :param sectorsize: size of one sector - :param fat : array/list of sector indexes (FAT or MiniFAT) - :param filesize : size of OLE file (for debugging) - :returns : a BytesIO instance containing the OLE stream + :param fat: array/list of sector indexes (FAT or MiniFAT) + :param filesize: size of OLE file (for debugging) + :returns: a BytesIO instance containing the OLE stream """ debug('_OleStream.__init__:') debug(' sect=%d (%X), size=%d, offset=%d, sectorsize=%d, len(fat)=%d, fp=%s' - %(sect, sect, size, offset, sectorsize, len(fat), repr(fp))) + %(sect,sect,size,offset,sectorsize,len(fat), repr(fp))) #[PL] To detect malformed documents with FAT loops, we compute the # expected number of sectors in the stream: unknown_size = False @@ -729,7 +782,7 @@ class _OleStream(io.BytesIO): data.append(sector_data) # jump to next sector in the FAT: try: - sect = fat[sect] + sect = fat[sect] & 0xFFFFFFFF # JYTHON-WORKAROUND except IndexError: # [PL] if pointer is out of the FAT an exception is raised raise IOError('incorrect OLE FAT, sector index out of range') @@ -787,6 +840,7 @@ class _OleDirectoryEntry: DIRENTRY_SIZE = 128 assert struct.calcsize(STRUCT_DIRENTRY) == DIRENTRY_SIZE + def __init__(self, entry, sid, olefile): """ Constructor for an _OleDirectoryEntry object. @@ -842,8 +896,11 @@ class _OleDirectoryEntry: namelength = 64 # only characters without ending null char are kept: name = name[:(namelength-2)] - # name is converted from unicode to Latin-1: - self.name = _unicode(name) + #TODO: check if the name is actually followed by a null unicode character ([MS-CFB] 2.6.1) + #TODO: check if the name does not contain forbidden characters: + # [MS-CFB] 2.6.1: "The following characters are illegal and MUST NOT be part of the name: '/', '\', ':', '!'." + # name is converted from UTF-16LE to the path encoding specified in the OleFileIO: + self.name = olefile._decode_utf16_str(name) debug('DirEntry SID=%d: %s' % (self.sid, repr(self.name))) debug(' - type: %d' % self.entry_type) @@ -879,6 +936,8 @@ class _OleDirectoryEntry: minifat = False olefile._check_duplicate_stream(self.isectStart, minifat) + + def build_storage_tree(self): """ Read and build the red-black tree attached to this _OleDirectoryEntry @@ -902,15 +961,16 @@ class _OleDirectoryEntry: # (see rich comparison methods in this class) self.kids.sort() + def append_kids(self, child_sid): """ Walk through red-black tree of children of this directory entry to add all of them to the kids list. (recursive method) - child_sid : index of child directory entry to use, or None when called - first time for the root. (only used during recursion) + :param child_sid : index of child directory entry to use, or None when called + first time for the root. (only used during recursion) """ - # [PL] this method was added to use simple recursion instead of a complex + #[PL] this method was added to use simple recursion instead of a complex # algorithm. # if this is not a storage or a leaf of the tree, nothing to do: if child_sid == NOSTREAM: @@ -945,6 +1005,7 @@ class _OleDirectoryEntry: # Afterwards build kid's own tree if it's also a storage: child.build_storage_tree() + def __eq__(self, other): "Compare entries by name" return self.name == other.name @@ -964,6 +1025,7 @@ class _OleDirectoryEntry: #TODO: replace by the same function as MS implementation ? # (order by name length first, then case-insensitive order) + def dump(self, tab = 0): "Dump this entry, and all its subentries (for debug purposes only)" TYPES = ["(invalid)", "(storage)", "(stream)", "(lockbytes)", @@ -978,12 +1040,13 @@ class _OleDirectoryEntry: for kid in self.kids: kid.dump(tab + 2) + def getmtime(self): """ Return modification time of a directory entry. :returns: None if modification time is null, a python datetime object - otherwise (UTC timezone) + otherwise (UTC timezone) new in version 0.26 """ @@ -991,12 +1054,13 @@ class _OleDirectoryEntry: return None return filetime2datetime(self.modifyTime) + def getctime(self): """ Return creation time of a directory entry. :returns: None if modification time is null, a python datetime object - otherwise (UTC timezone) + otherwise (UTC timezone) new in version 0.26 """ @@ -1012,8 +1076,7 @@ class OleFileIO: OLE container object This class encapsulates the interface to an OLE 2 structured - storage file. Use the :py:meth:`~PIL.OleFileIO.OleFileIO.listdir` and - :py:meth:`~PIL.OleFileIO.OleFileIO.openstream` methods to + storage file. Use the listdir and openstream methods to access the contents of this file. Object names are given as a list of strings, one for each subentry @@ -1037,22 +1100,47 @@ class OleFileIO: TIFF files). """ - def __init__(self, filename = None, raise_defects=DEFECT_FATAL): + def __init__(self, filename=None, raise_defects=DEFECT_FATAL, + write_mode=False, debug=False, path_encoding=DEFAULT_PATH_ENCODING): """ - Constructor for OleFileIO class. + Constructor for the OleFileIO class. :param filename: file to open. + + - if filename is a string smaller than 1536 bytes, it is the path + of the file to open. (bytes or unicode string) + - if filename is a string longer than 1535 bytes, it is parsed + as the content of an OLE file in memory. (bytes type only) + - if filename is a file-like object (with read, seek and tell methods), + it is parsed as-is. + :param raise_defects: minimal level for defects to be raised as exceptions. - (use DEFECT_FATAL for a typical application, DEFECT_INCORRECT for a - security-oriented application, see source code for details) + (use DEFECT_FATAL for a typical application, DEFECT_INCORRECT for a + security-oriented application, see source code for details) + + :param write_mode: bool, if True the file is opened in read/write mode instead + of read-only by default. + + :param debug: bool, set debug mode + + :param path_encoding: None or str, name of the codec to use for path + names (streams and storages), or None for Unicode. + Unicode by default on Python 3+, UTF-8 on Python 2.x. + (new in olefile 0.42, was hardcoded to Latin-1 until olefile v0.41) """ + set_debug_mode(debug) # minimal level for defects to be raised as exceptions: self._raise_defects_level = raise_defects # list of defects/issues not raised as exceptions: # tuples of (exception type, message) self.parsing_issues = [] + self.write_mode = write_mode + self.path_encoding = path_encoding + self._filesize = None + self.fp = None if filename: - self.open(filename) + self.open(filename, write_mode=write_mode) + def _raise_defect(self, defect_level, message, exception_type=IOError): """ @@ -1061,10 +1149,12 @@ class OleFileIO: for the OleFileIO object. :param defect_level: defect level, possible values are: - DEFECT_UNSURE : a case which looks weird, but not sure it's a defect - DEFECT_POTENTIAL : a potential defect - DEFECT_INCORRECT : an error according to specifications, but parsing can go on - DEFECT_FATAL : an error which cannot be ignored, parsing is impossible + + - DEFECT_UNSURE : a case which looks weird, but not sure it's a defect + - DEFECT_POTENTIAL : a potential defect + - DEFECT_INCORRECT : an error according to specifications, but parsing can go on + - DEFECT_FATAL : an error which cannot be ignored, parsing is impossible + :param message: string describing the defect, used with raised exception. :param exception_type: exception class to be raised, IOError by default """ @@ -1075,31 +1165,70 @@ class OleFileIO: # just record the issue, no exception raised: self.parsing_issues.append((exception_type, message)) - def open(self, filename): - """ - Open an OLE2 file. - Reads the header, FAT and directory. - :param filename: string-like or file-like object + def _decode_utf16_str(self, utf16_str, errors='replace'): """ + Decode a string encoded in UTF-16 LE format, as found in the OLE + directory or in property streams. Return a string encoded + according to the path_encoding specified for the OleFileIO object. + + :param utf16_str: bytes string encoded in UTF-16 LE format + :param errors: str, see python documentation for str.decode() + :return: str, encoded according to path_encoding + """ + unicode_str = utf16_str.decode('UTF-16LE', errors) + if self.path_encoding: + # an encoding has been specified for path names: + return unicode_str.encode(self.path_encoding, errors) + else: + # path_encoding=None, return the Unicode string as-is: + return unicode_str + + + def open(self, filename, write_mode=False): + """ + Open an OLE2 file in read-only or read/write mode. + Read and parse the header, FAT and directory. + + :param filename: string-like or file-like object, OLE file to parse + + - if filename is a string smaller than 1536 bytes, it is the path + of the file to open. (bytes or unicode string) + - if filename is a string longer than 1535 bytes, it is parsed + as the content of an OLE file in memory. (bytes type only) + - if filename is a file-like object (with read, seek and tell methods), + it is parsed as-is. + + :param write_mode: bool, if True the file is opened in read/write mode instead + of read-only by default. (ignored if filename is not a path) + """ + self.write_mode = write_mode #[PL] check if filename is a string-like or file-like object: # (it is better to check for a read() method) if hasattr(filename, 'read'): - # file-like object + #TODO: also check seek and tell methods? + # file-like object: use it directly self.fp = filename + elif isinstance(filename, bytes) and len(filename) >= MINIMAL_OLEFILE_SIZE: + # filename is a bytes string containing the OLE file to be parsed: + # convert it to BytesIO + self.fp = io.BytesIO(filename) else: # string-like object: filename of file on disk - #TODO: if larger than 1024 bytes, this could be the actual data => BytesIO - self.fp = open(filename, "rb") - # old code fails if filename is not a plain string: - #if isinstance(filename, (bytes, basestring)): - # self.fp = open(filename, "rb") - #else: - # self.fp = filename + if self.write_mode: + # open file in mode 'read with update, binary' + # According to https://docs.python.org/2/library/functions.html#open + # 'w' would truncate the file, 'a' may only append on some Unixes + mode = 'r+b' + else: + # read-only mode by default + mode = 'rb' + self.fp = open(filename, mode) # obtain the filesize by using seek and tell, which should work on most # file-like objects: #TODO: do it above, using getsize with filename when possible? #TODO: fix code to fail with clear exception when filesize cannot be obtained + filesize=0 self.fp.seek(0, os.SEEK_END) try: filesize = self.fp.tell() @@ -1177,7 +1306,7 @@ class OleFileIO: self.sectDifStart, self.csectDif ) = struct.unpack(fmt_header, header1) - debug(struct.unpack(fmt_header, header1)) + debug( struct.unpack(fmt_header, header1)) if self.Sig != MAGIC: # OLE signature should always be present @@ -1196,6 +1325,7 @@ class OleFileIO: # For now only common little-endian documents are handled correctly self._raise_defect(DEFECT_FATAL, "incorrect ByteOrder in OLE header") # TODO: add big-endian support for documents created on Mac ? + # But according to [MS-CFB] ? v20140502, ByteOrder MUST be 0xFFFE. self.SectorSize = 2**self.SectorShift debug( "SectorSize = %d" % self.SectorSize ) if self.SectorSize not in [512, 4096]: @@ -1210,28 +1340,44 @@ class OleFileIO: if self.Reserved != 0 or self.Reserved1 != 0: self._raise_defect(DEFECT_INCORRECT, "incorrect OLE header (non-null reserved bytes)") debug( "csectDir = %d" % self.csectDir ) + # Number of directory sectors (only allowed if DllVersion != 3) if self.SectorSize==512 and self.csectDir!=0: self._raise_defect(DEFECT_INCORRECT, "incorrect csectDir in OLE header") debug( "csectFat = %d" % self.csectFat ) + # csectFat = number of FAT sectors in the file debug( "sectDirStart = %X" % self.sectDirStart ) + # sectDirStart = 1st sector containing the directory debug( "signature = %d" % self.signature ) # Signature should be zero, BUT some implementations do not follow this # rule => only a potential defect: + # (according to MS-CFB, may be != 0 for applications supporting file + # transactions) if self.signature != 0: self._raise_defect(DEFECT_POTENTIAL, "incorrect OLE header (signature>0)") - debug("MiniSectorCutoff = %d" % self.MiniSectorCutoff) - debug("MiniFatStart = %X" % self.MiniFatStart) - debug("csectMiniFat = %d" % self.csectMiniFat) - debug("sectDifStart = %X" % self.sectDifStart) - debug("csectDif = %d" % self.csectDif) + debug( "MiniSectorCutoff = %d" % self.MiniSectorCutoff ) + # MS-CFB: This integer field MUST be set to 0x00001000. This field + # specifies the maximum size of a user-defined data stream allocated + # from the mini FAT and mini stream, and that cutoff is 4096 bytes. + # Any user-defined data stream larger than or equal to this cutoff size + # must be allocated as normal sectors from the FAT. + if self.MiniSectorCutoff != 0x1000: + self._raise_defect(DEFECT_INCORRECT, "incorrect MiniSectorCutoff in OLE header") + debug( "MiniFatStart = %X" % self.MiniFatStart ) + debug( "csectMiniFat = %d" % self.csectMiniFat ) + debug( "sectDifStart = %X" % self.sectDifStart ) + debug( "csectDif = %d" % self.csectDif ) # calculate the number of sectors in the file # (-1 because header doesn't count) self.nb_sect = ( (filesize + self.SectorSize-1) // self.SectorSize) - 1 debug( "Number of sectors in the file: %d" % self.nb_sect ) + #TODO: change this test, because an OLE file MAY contain other data + # after the last sector. - # file clsid (probably never used, so we don't store it) - #clsid = _clsid(header[8:24]) + # file clsid + self.clsid = _clsid(header[8:24]) + + #TODO: remove redundant attributes, and fix the code which uses them? self.sectorsize = self.SectorSize #1 << i16(header, 30) self.minisectorsize = self.MiniSectorSize #1 << i16(header, 32) self.minisectorcutoff = self.MiniSectorCutoff # i32(header, 56) @@ -1254,19 +1400,22 @@ class OleFileIO: self.ministream = None self.minifatsect = self.MiniFatStart #i32(header, 60) + def close(self): """ close the OLE file, to release the file object """ self.fp.close() + def _check_duplicate_stream(self, first_sect, minifat=False): """ Checks if a stream has not been already referenced elsewhere. This method should only be called once for each known stream, and only if stream size is not null. - :param first_sect: index of first sector of the stream in FAT - :param minifat: if True, stream is located in the MiniFAT, else in the FAT + + :param first_sect: int, index of first sector of the stream in FAT + :param minifat: bool, if True, stream is located in the MiniFAT, else in the FAT """ if minifat: debug('_check_duplicate_stream: sect=%d in MiniFAT' % first_sect) @@ -1284,13 +1433,14 @@ class OleFileIO: else: used_streams.append(first_sect) + def dumpfat(self, fat, firstindex=0): "Displays a part of FAT in human-readable form for debugging purpose" # [PL] added only for debug if not DEBUG_MODE: return # dictionary to convert special FAT values in human-readable strings - VPL=8 # valeurs par ligne (8+1 * 8+1 = 81) + VPL = 8 # values per line (8+1 * 8+1 = 81) fatnames = { FREESECT: "..free..", ENDOFCHAIN: "[ END. ]", @@ -1310,22 +1460,26 @@ class OleFileIO: if i>=nbsect: break sect = fat[i] - if sect in fatnames: - nom = fatnames[sect] + aux = sect & 0xFFFFFFFF # JYTHON-WORKAROUND + if aux in fatnames: + name = fatnames[aux] else: if sect == i+1: - nom = " --->" + name = " --->" else: - nom = "%8X" % sect - print(nom, end=" ") + name = "%8X" % sect + print(name, end=" ") print() + def dumpsect(self, sector, firstindex=0): "Displays a sector in a human-readable form, for debugging purpose." if not DEBUG_MODE: return VPL=8 # number of values per line (8+1 * 8+1 = 81) tab = array.array(UINT32, sector) + if sys.byteorder == 'big': + tab.byteswap() nbsect = len(tab) nlines = (nbsect+VPL-1)//VPL print("index", end=" ") @@ -1339,8 +1493,8 @@ class OleFileIO: if i>=nbsect: break sect = tab[i] - nom = "%8X" % sect - print(nom, end=" ") + name = "%8X" % sect + print(name, end=" ") print() def sect2array(self, sect): @@ -1354,6 +1508,7 @@ class OleFileIO: a.byteswap() return a + def loadfat_sect(self, sect): """ Adds the indexes of the given sector to the FAT @@ -1371,9 +1526,11 @@ class OleFileIO: self.dumpsect(sect) # The FAT is a sector chain starting at the first index of itself. for isect in fat1: - #print("isect = %X" % isect) + isect = isect & 0xFFFFFFFF # JYTHON-WORKAROUND + debug("isect = %X" % isect) if isect == ENDOFCHAIN or isect == FREESECT: # the end of the sector chain has been reached + debug("found end of sector chain") break # read the FAT sector s = self.getsect(isect) @@ -1383,13 +1540,15 @@ class OleFileIO: self.fat = self.fat + nextfat return isect + def loadfat(self, header): """ Load the FAT table. """ - # The header contains a sector numbers - # for the first 109 FAT sectors. Additional sectors are - # described by DIF blocks + # The 1st sector of the file contains sector numbers for the first 109 + # FAT sectors, right after the header which is 76 bytes long. + # (always 109, whatever the sector size: 512 bytes = 76+4*109) + # Additional sectors are described by DIF blocks sect = header[76:512] debug( "len(sect)=%d, so %d integers" % (len(sect), len(sect)//4) ) @@ -1418,24 +1577,27 @@ class OleFileIO: if self.sectDifStart >= self.nb_sect: # initial DIFAT block index must be valid self._raise_defect(DEFECT_FATAL, 'incorrect DIFAT, first index out of range') - debug("DIFAT analysis...") + debug( "DIFAT analysis..." ) # We compute the necessary number of DIFAT sectors : - # (each DIFAT sector = 127 pointers + 1 towards next DIFAT sector) - nb_difat = (self.csectFat-109 + 126)//127 - debug("nb_difat = %d" % nb_difat) + # Number of pointers per DIFAT sector = (sectorsize/4)-1 + # (-1 because the last pointer is the next DIFAT sector number) + nb_difat_sectors = (self.sectorsize//4)-1 + # (if 512 bytes: each DIFAT sector = 127 pointers + 1 towards next DIFAT sector) + nb_difat = (self.csectFat-109 + nb_difat_sectors-1)//nb_difat_sectors + debug( "nb_difat = %d" % nb_difat ) if self.csectDif != nb_difat: raise IOError('incorrect DIFAT') isect_difat = self.sectDifStart for i in iterrange(nb_difat): - debug("DIFAT block %d, sector %X" % (i, isect_difat)) + debug( "DIFAT block %d, sector %X" % (i, isect_difat) ) #TODO: check if corresponding FAT SID = DIFSECT sector_difat = self.getsect(isect_difat) difat = self.sect2array(sector_difat) self.dumpsect(sector_difat) - self.loadfat_sect(difat[:127]) + self.loadfat_sect(difat[:nb_difat_sectors]) # last DIFAT pointer is next DIFAT sector: - isect_difat = difat[127] - debug("next DIFAT sector: %X" % isect_difat) + isect_difat = difat[nb_difat_sectors] + debug( "next DIFAT sector: %X" % isect_difat ) # checks: if isect_difat not in [ENDOFCHAIN, FREESECT]: # last DIFAT pointer value must be ENDOFCHAIN or FREESECT @@ -1453,6 +1615,7 @@ class OleFileIO: debug('\nFAT:') self.dumpfat(self.fat) + def loadminifat(self): """ Load the MiniFAT table. @@ -1491,10 +1654,16 @@ class OleFileIO: """ Read given sector from file on disk. - :param sect: sector index + :param sect: int, sector index :returns: a string containing the sector data. """ - # [PL] this original code was wrong when sectors are 4KB instead of + # From [MS-CFB]: A sector number can be converted into a byte offset + # into the file by using the following formula: + # (sector number + 1) x Sector Size. + # This implies that sector #0 of the file begins at byte offset Sector + # Size, not at 0. + + # [PL] the original code in PIL was wrong when sectors are 4KB instead of # 512 bytes: #self.fp.seek(512 + self.sectorsize * sect) #[PL]: added safety checks: @@ -1512,6 +1681,34 @@ class OleFileIO: self._raise_defect(DEFECT_FATAL, 'incomplete OLE sector') return sector + + def write_sect(self, sect, data, padding=b'\x00'): + """ + Write given sector to file on disk. + + :param sect: int, sector index + :param data: bytes, sector data + :param padding: single byte, padding character if data < sector size + """ + if not isinstance(data, bytes): + raise TypeError("write_sect: data must be a bytes string") + if not isinstance(padding, bytes) or len(padding)!=1: + raise TypeError("write_sect: padding must be a bytes string of 1 char") + #TODO: we could allow padding=None for no padding at all + try: + self.fp.seek(self.sectorsize * (sect+1)) + except: + debug('write_sect(): sect=%X, seek=%d, filesize=%d' % + (sect, self.sectorsize*(sect+1), self._filesize)) + self._raise_defect(DEFECT_FATAL, 'OLE sector index out of range') + if len(data) < self.sectorsize: + # add padding + data += padding * (self.sectorsize - len(data)) + elif len(data) < self.sectorsize: + raise ValueError("Data is larger than sector size") + self.fp.write(data) + + def loaddirectory(self, sect): """ Load the directory. @@ -1541,12 +1738,13 @@ class OleFileIO: ## break ## self.direntries.append(_OleDirectoryEntry(entry, sid, self)) # load root entry: - self._load_direntry(0) + root_entry = self._load_direntry(0) # Root entry is the first entry: self.root = self.direntries[0] # read and build all storage trees, starting from the root: self.root.build_storage_tree() + def _load_direntry (self, sid): """ Load a directory entry from the directory. @@ -1555,6 +1753,7 @@ class OleFileIO: :param sid: index of storage/stream in the directory. :returns: a _OleDirectoryEntry object + :exception IOError: if the entry has always been referenced. """ # check if SID is OK: @@ -1571,12 +1770,14 @@ class OleFileIO: self.direntries[sid] = _OleDirectoryEntry(entry, sid, self) return self.direntries[sid] + def dumpdirectory(self): """ Dump directory (for debugging only) """ self.root.dump() + def _open(self, start, size = 0x7FFFFFFF, force_FAT=False): """ Open a stream, either in FAT or MiniFAT according to its size. @@ -1585,7 +1786,7 @@ class OleFileIO: :param start: index of first sector :param size: size of stream (or nothing if size is unknown) :param force_FAT: if False (default), stream will be opened in FAT or MiniFAT - according to size. If True, it will always be opened in FAT. + according to size. If True, it will always be opened in FAT. """ debug('OleFileIO.open(): sect=%d, size=%d, force_FAT=%s' % (start, size, str(force_FAT))) @@ -1602,51 +1803,60 @@ class OleFileIO: (self.root.isectStart, size_ministream)) self.ministream = self._open(self.root.isectStart, size_ministream, force_FAT=True) - return _OleStream(self.ministream, start, size, 0, - self.minisectorsize, self.minifat, - self.ministream.size) + return _OleStream(fp=self.ministream, sect=start, size=size, + offset=0, sectorsize=self.minisectorsize, + fat=self.minifat, filesize=self.ministream.size) else: # standard stream - return _OleStream(self.fp, start, size, 512, - self.sectorsize, self.fat, self._filesize) + return _OleStream(fp=self.fp, sect=start, size=size, + offset=self.sectorsize, + sectorsize=self.sectorsize, fat=self.fat, + filesize=self._filesize) + def _list(self, files, prefix, node, streams=True, storages=False): """ - (listdir helper) + listdir helper + :param files: list of files to fill in :param prefix: current location in storage tree (list of names) :param node: current node (_OleDirectoryEntry object) :param streams: bool, include streams if True (True by default) - new in v0.26 :param storages: bool, include storages if True (False by default) - new in v0.26 - (note: the root storage is never included) + (note: the root storage is never included) """ prefix = prefix + [node.name] for entry in node.kids: - if entry.kids: + if entry.entry_type == STGTY_STORAGE: # this is a storage if storages: # add it to the list files.append(prefix[1:] + [entry.name]) # check its kids self._list(files, prefix, entry, streams, storages) - else: + elif entry.entry_type == STGTY_STREAM: # this is a stream if streams: # add it to the list files.append(prefix[1:] + [entry.name]) + else: + self._raise_defect(DEFECT_INCORRECT, 'The directory tree contains an entry which is not a stream nor a storage.') + def listdir(self, streams=True, storages=False): """ - Return a list of streams stored in this file + Return a list of streams and/or storages stored in this file :param streams: bool, include streams if True (True by default) - new in v0.26 :param storages: bool, include storages if True (False by default) - new in v0.26 (note: the root storage is never included) + :returns: list of stream and/or storage paths """ files = [] self._list(files, [], self.root, streams, storages) return files + def _find(self, filename): """ Returns directory entry of given filename. (openstream helper) @@ -1656,10 +1866,11 @@ class OleFileIO: - a string using Unix path syntax, for example: 'storage_1/storage_1.2/stream' - - a list of storage filenames, path to the desired stream/storage. + - or a list of storage filenames, path to the desired stream/storage. Example: ['storage_1', 'storage_1.2', 'stream'] + :returns: sid of requested filename - raise IOError if file not found + :exception IOError: if file not found """ # if filename is a string instead of a list, split it on slashes to @@ -1677,15 +1888,17 @@ class OleFileIO: node = kid return node.sid + def openstream(self, filename): """ Open a stream as a read-only file object (BytesIO). + Note: filename is case-insensitive. :param filename: path of stream in storage tree (except root entry), either: - a string using Unix path syntax, for example: 'storage_1/storage_1.2/stream' - - a list of storage filenames, path to the desired stream/storage. + - or a list of storage filenames, path to the desired stream/storage. Example: ['storage_1', 'storage_1.2', 'stream'] :returns: file object (read-only) @@ -1697,6 +1910,68 @@ class OleFileIO: raise IOError("this file is not a stream") return self._open(entry.isectStart, entry.size) + + def write_stream(self, stream_name, data): + """ + Write a stream to disk. For now, it is only possible to replace an + existing stream by data of the same size. + + :param stream_name: path of stream in storage tree (except root entry), either: + + - a string using Unix path syntax, for example: + 'storage_1/storage_1.2/stream' + - or a list of storage filenames, path to the desired stream/storage. + Example: ['storage_1', 'storage_1.2', 'stream'] + + :param data: bytes, data to be written, must be the same size as the original + stream. + """ + if not isinstance(data, bytes): + raise TypeError("write_stream: data must be a bytes string") + sid = self._find(stream_name) + entry = self.direntries[sid] + if entry.entry_type != STGTY_STREAM: + raise IOError("this is not a stream") + size = entry.size + if size != len(data): + raise ValueError("write_stream: data must be the same size as the existing stream") + if size < self.minisectorcutoff: + raise NotImplementedError("Writing a stream in MiniFAT is not implemented yet") + sect = entry.isectStart + # number of sectors to write + nb_sectors = (size + (self.sectorsize-1)) // self.sectorsize + debug('nb_sectors = %d' % nb_sectors) + for i in range(nb_sectors): +## try: +## self.fp.seek(offset + self.sectorsize * sect) +## except: +## debug('sect=%d, seek=%d' % +## (sect, offset+self.sectorsize*sect)) +## raise IOError('OLE sector index out of range') + # extract one sector from data, the last one being smaller: + if i<(nb_sectors-1): + data_sector = data [i*self.sectorsize : (i+1)*self.sectorsize] + #TODO: comment this if it works + assert(len(data_sector)==self.sectorsize) + else: + data_sector = data [i*self.sectorsize:] + #TODO: comment this if it works + debug('write_stream: size=%d sectorsize=%d data_sector=%d size%%sectorsize=%d' + % (size, self.sectorsize, len(data_sector), size % self.sectorsize)) + assert(len(data_sector) % self.sectorsize==size % self.sectorsize) + self.write_sect(sect, data_sector) +## self.fp.write(data_sector) + # jump to next sector in the FAT: + try: + sect = self.fat[sect] + except IndexError: + # [PL] if pointer is out of the FAT an exception is raised + raise IOError('incorrect OLE FAT, sector index out of range') + #[PL] Last sector should be a "end of chain" marker: + if sect != ENDOFCHAIN: + raise IOError('incorrect last sector index in OLE stream') + + def get_type(self, filename): """ Test if given filename exists as a stream or a storage in the OLE @@ -1716,6 +1991,7 @@ class OleFileIO: except: return False + def getmtime(self, filename): """ Return modification time of a stream/storage. @@ -1731,6 +2007,7 @@ class OleFileIO: entry = self.direntries[sid] return entry.getmtime() + def getctime(self, filename): """ Return creation time of a stream/storage. @@ -1746,20 +2023,23 @@ class OleFileIO: entry = self.direntries[sid] return entry.getctime() + def exists(self, filename): """ Test if given filename exists as a stream or a storage in the OLE container. + Note: filename is case-insensitive. :param filename: path of stream in storage tree. (see openstream for syntax) :returns: True if object exist, else False. """ try: - self._find(filename) + sid = self._find(filename) return True except: return False + def get_size(self, filename): """ Return size of a stream in the OLE container, in bytes. @@ -1767,7 +2047,7 @@ class OleFileIO: :param filename: path of stream in storage tree (see openstream for syntax) :returns: size in bytes (long integer) :exception IOError: if file not found - :exception TypeError: if this is not a stream + :exception TypeError: if this is not a stream. """ sid = self._find(filename) entry = self.direntries[sid] @@ -1776,6 +2056,7 @@ class OleFileIO: raise TypeError('object is not an OLE stream') return entry.size + def get_rootentry_name(self): """ Return root entry name. Should usually be 'Root Entry' or 'R' in most @@ -1783,6 +2064,7 @@ class OleFileIO: """ return self.root.name + def getproperties(self, filename, convert_time=False, no_conversion=None): """ Return properties described in substream. @@ -1791,10 +2073,12 @@ class OleFileIO: :param convert_time: bool, if True timestamps will be converted to Python datetime :param no_conversion: None or list of int, timestamps not to be converted (for example total editing time is not a real timestamp) + :returns: a dictionary of values indexed by id (integer) """ + #REFERENCE: [MS-OLEPS] https://msdn.microsoft.com/en-us/library/dd942421.aspx # make sure no_conversion is a list, just to simplify code below: - if no_conversion is None: + if no_conversion == None: no_conversion = [] # stream path as a string to report exceptions: streampath = filename @@ -1808,11 +2092,11 @@ class OleFileIO: try: # header s = fp.read(28) - # clsid = _clsid(s[8:24]) + clsid = _clsid(s[8:24]) # format id s = fp.read(20) - # fmtid = _clsid(s[:16]) + fmtid = _clsid(s[:16]) fp.seek(i32(s, 16)) # get section @@ -1830,34 +2114,34 @@ class OleFileIO: for i in range(num_props): try: - id = 0 # just in case of an exception + id = 0 # just in case of an exception id = i32(s, 8+i*8) offset = i32(s, 12+i*8) type = i32(s, offset) - debug('property id=%d: type=%d offset=%X' % (id, type, offset)) + debug ('property id=%d: type=%d offset=%X' % (id, type, offset)) # test for common types first (should perhaps use # a dictionary instead?) - if type == VT_I2: # 16-bit signed integer + if type == VT_I2: # 16-bit signed integer value = i16(s, offset+4) if value >= 32768: value = value - 65536 - elif type == VT_UI2: # 2-byte unsigned integer + elif type == VT_UI2: # 2-byte unsigned integer value = i16(s, offset+4) elif type in (VT_I4, VT_INT, VT_ERROR): # VT_I4: 32-bit signed integer # VT_ERROR: HRESULT, similar to 32-bit signed integer, # see http://msdn.microsoft.com/en-us/library/cc230330.aspx value = i32(s, offset+4) - elif type in (VT_UI4, VT_UINT): # 4-byte unsigned integer - value = i32(s, offset+4) # FIXME + elif type in (VT_UI4, VT_UINT): # 4-byte unsigned integer + value = i32(s, offset+4) # FIXME elif type in (VT_BSTR, VT_LPSTR): # CodePageString, see http://msdn.microsoft.com/en-us/library/dd942354.aspx # size is a 32 bits integer, including the null terminator, and # possibly trailing or embedded null chars - # TODO: if codepage is unicode, the string should be converted as such + #TODO: if codepage is unicode, the string should be converted as such count = i32(s, offset+4) value = s[offset+8:offset+8+count-1] # remove all null chars: @@ -1873,9 +2157,9 @@ class OleFileIO: # "the string should NOT contain embedded or additional trailing # null characters." count = i32(s, offset+4) - value = _unicode(s[offset+8:offset+8+count*2]) + value = self._decode_utf16_str(s[offset+8:offset+8+count*2]) elif type == VT_FILETIME: - value = long(i32(s, offset+4)) + (long(i32(s, offset+8)) << 32) + value = long(i32(s, offset+4)) + (long(i32(s, offset+8))<<32) # FILETIME is a 64-bit int: "number of 100ns periods # since Jan 1,1601". if convert_time and id not in no_conversion: @@ -1889,8 +2173,8 @@ class OleFileIO: else: # legacy code kept for backward compatibility: returns a # number of seconds since Jan 1,1601 - value = value // 10000000 # seconds - elif type == VT_UI1: # 1-byte unsigned integer + value = value // 10000000 # seconds + elif type == VT_UI1: # 1-byte unsigned integer value = i8(s[offset+4]) elif type == VT_CLSID: value = _clsid(s[offset+4:offset+20]) @@ -1904,8 +2188,8 @@ class OleFileIO: # see http://msdn.microsoft.com/en-us/library/cc237864.aspx value = bool(i16(s, offset+4)) else: - value = None # everything else yields "None" - debug('property id=%d: type=%d not implemented in parser yet' % (id, type)) + value = None # everything else yields "None" + debug ('property id=%d: type=%d not implemented in parser yet' % (id, type)) # missing: VT_EMPTY, VT_NULL, VT_R4, VT_R8, VT_CY, VT_DATE, # VT_DECIMAL, VT_I1, VT_I8, VT_UI8, @@ -1917,8 +2201,8 @@ class OleFileIO: # type of items, e.g. VT_VECTOR|VT_BSTR # see http://msdn.microsoft.com/en-us/library/dd942011.aspx - # print("%08x" % id, repr(value), end=" ") - # print("(%s)" % VT[i32(s, offset) & 0xFFF]) + #print("%08x" % id, repr(value), end=" ") + #print("(%s)" % VT[i32(s, offset) & 0xFFF]) data[id] = value except BaseException as exc: @@ -1949,105 +2233,112 @@ class OleFileIO: if __name__ == "__main__": + import sys + # [PL] display quick usage info if launched from command-line if len(sys.argv) <= 1: - print(__doc__) - print(""" -Launched from command line, this script parses OLE files and prints info. + print('olefile version %s %s - %s' % (__version__, __date__, __author__)) + print( +""" +Launched from the command line, this script parses OLE files and prints info. -Usage: OleFileIO_PL.py [-d] [-c] [file2 ...] +Usage: olefile.py [-d] [-c] [file2 ...] Options: --d : debug mode (display a lot of debug information, for developers only) +-d : debug mode (displays a lot of debug information, for developers only) -c : check all streams (for debugging purposes) + +For more information, see http://www.decalage.info/olefile """) sys.exit() check_streams = False for filename in sys.argv[1:]: - # try: - # OPTIONS: - if filename == '-d': - # option to switch debug mode on: - set_debug_mode(True) - continue - if filename == '-c': - # option to switch check streams mode on: - check_streams = True - continue +## try: + # OPTIONS: + if filename == '-d': + # option to switch debug mode on: + set_debug_mode(True) + continue + if filename == '-c': + # option to switch check streams mode on: + check_streams = True + continue - ole = OleFileIO(filename) #, raise_defects=DEFECT_INCORRECT) - print("-" * 68) - print(filename) - print("-" * 68) - ole.dumpdirectory() - for streamname in ole.listdir(): - if streamname[-1][0] == "\005": - print(streamname, ": properties") - props = ole.getproperties(streamname, convert_time=True) - props = sorted(props.items()) - for k, v in props: - #[PL]: avoid to display too large or binary values: - if isinstance(v, (basestring, bytes)): - if len(v) > 50: - v = v[:50] - if isinstance(v, bytes): - # quick and dirty binary check: - for c in (1, 2, 3, 4, 5, 6, 7, 11, 12, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31): - if c in bytearray(v): - v = '(binary data)' - break - print(" ", k, v) - - if check_streams: - # Read all streams to check if there are errors: - print('\nChecking streams...') + ole = OleFileIO(filename)#, raise_defects=DEFECT_INCORRECT) + print("-" * 68) + print(filename) + print("-" * 68) + ole.dumpdirectory() for streamname in ole.listdir(): - # print name using repr() to convert binary chars to \xNN: - print('-', repr('/'.join(streamname)), '-', end=' ') - st_type = ole.get_type(streamname) - if st_type == STGTY_STREAM: - print('size %d' % ole.get_size(streamname)) - # just try to read stream in memory: - ole.openstream(streamname) - else: - print('NOT a stream : type=%d' % st_type) + if streamname[-1][0] == "\005": + print(streamname, ": properties") + props = ole.getproperties(streamname, convert_time=True) + props = sorted(props.items()) + for k, v in props: + #[PL]: avoid to display too large or binary values: + if isinstance(v, (basestring, bytes)): + if len(v) > 50: + v = v[:50] + if isinstance(v, bytes): + # quick and dirty binary check: + for c in (1,2,3,4,5,6,7,11,12,14,15,16,17,18,19,20, + 21,22,23,24,25,26,27,28,29,30,31): + if c in bytearray(v): + v = '(binary data)' + break + print(" ", k, v) + + if check_streams: + # Read all streams to check if there are errors: + print('\nChecking streams...') + for streamname in ole.listdir(): + # print name using repr() to convert binary chars to \xNN: + print('-', repr('/'.join(streamname)),'-', end=' ') + st_type = ole.get_type(streamname) + if st_type == STGTY_STREAM: + print('size %d' % ole.get_size(streamname)) + # just try to read stream in memory: + ole.openstream(streamname) + else: + print('NOT a stream : type=%d' % st_type) + print() + +## for streamname in ole.listdir(): +## # print name using repr() to convert binary chars to \xNN: +## print('-', repr('/'.join(streamname)),'-', end=' ') +## print(ole.getmtime(streamname)) +## print() + + print('Modification/Creation times of all directory entries:') + for entry in ole.direntries: + if entry is not None: + print('- %s: mtime=%s ctime=%s' % (entry.name, + entry.getmtime(), entry.getctime())) print() -## for streamname in ole.listdir(): -## # print name using repr() to convert binary chars to \xNN: -## print('-', repr('/'.join(streamname)),'-', end=' ') -## print(ole.getmtime(streamname)) -## print() + # parse and display metadata: + meta = ole.get_metadata() + meta.dump() + print() + #[PL] Test a few new methods: + root = ole.get_rootentry_name() + print('Root entry name: "%s"' % root) + if ole.exists('worddocument'): + print("This is a Word document.") + print("type of stream 'WordDocument':", ole.get_type('worddocument')) + print("size :", ole.get_size('worddocument')) + if ole.exists('macros/vba'): + print("This document may contain VBA macros.") - print('Modification/Creation times of all directory entries:') - for entry in ole.direntries: - if entry is not None: - print('- %s: mtime=%s ctime=%s' % (entry.name, - entry.getmtime(), entry.getctime())) - print() - - # parse and display metadata: - meta = ole.get_metadata() - meta.dump() - print() - # [PL] Test a few new methods: - root = ole.get_rootentry_name() - print('Root entry name: "%s"' % root) - if ole.exists('worddocument'): - print("This is a Word document.") - print("type of stream 'WordDocument':", ole.get_type('worddocument')) - print("size :", ole.get_size('worddocument')) - if ole.exists('macros/vba'): - print("This document may contain VBA macros.") - - # print parsing issues: - print('\nNon-fatal issues raised during parsing:') - if ole.parsing_issues: - for exctype, msg in ole.parsing_issues: - print('- %s: %s' % (exctype.__name__, msg)) - else: - print('None') + # print parsing issues: + print('\nNon-fatal issues raised during parsing:') + if ole.parsing_issues: + for exctype, msg in ole.parsing_issues: + print('- %s: %s' % (exctype.__name__, msg)) + else: + print('None') ## except IOError as v: ## print("***", "cannot read", file, "-", v) + +# this code was developed while listening to The Wedding Present "Sea Monsters" \ No newline at end of file From bf1645616fe41f759a6237fbdca77b62ebc3678a Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 12 May 2015 12:43:58 +0300 Subject: [PATCH 0245/1037] Passing test for hopper.xbm and failing test for hopper_underscore.xbm --- Tests/images/hopper.xbm | 174 +++++++++++++++++++++++++++++ Tests/images/hopper_underscore.xbm | 174 +++++++++++++++++++++++++++++ Tests/test_file_xbm.py | 24 ++++ 3 files changed, 372 insertions(+) create mode 100644 Tests/images/hopper.xbm create mode 100644 Tests/images/hopper_underscore.xbm diff --git a/Tests/images/hopper.xbm b/Tests/images/hopper.xbm new file mode 100644 index 000000000..2106fbfa2 --- /dev/null +++ b/Tests/images/hopper.xbm @@ -0,0 +1,174 @@ +#define hopper_width 128 +#define hopper_height 128 +static char hopper_bits[] = { + 0xFF, 0xFF, 0xFF, 0x1F, 0xF0, 0x3D, 0x80, 0x96, 0xDA, 0xB6, 0xD6, 0x2A, + 0xA9, 0x6D, 0x25, 0x29, 0xFF, 0xFF, 0xFF, 0xBF, 0xFE, 0x7D, 0xC0, 0xFB, + 0x69, 0x69, 0xA9, 0xD5, 0x96, 0x96, 0x5E, 0x9A, 0xFF, 0xFF, 0xFF, 0x9F, + 0xFE, 0xFD, 0x80, 0xFF, 0x97, 0xB6, 0x5A, 0x6A, 0x5D, 0x6D, 0x5A, 0x62, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFD, 0xC1, 0xCF, 0x5F, 0x89, 0xA5, 0xD5, + 0xAA, 0x92, 0xA5, 0x59, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFD, 0xF8, 0x00, + 0xB8, 0x59, 0xAA, 0x56, 0xDA, 0x5A, 0x56, 0x49, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFE, 0xFC, 0xF9, 0x00, 0xF0, 0xA6, 0x55, 0xA9, 0x65, 0xA5, 0xA9, 0xA2, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFD, 0x71, 0x00, 0x60, 0x70, 0xAA, 0x54, + 0xAA, 0x5A, 0x54, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFC, 0x38, 0x00, + 0xE0, 0x80, 0x55, 0xAB, 0x55, 0x75, 0xAB, 0x56, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFE, 0x1D, 0x70, 0x00, 0xF0, 0x00, 0xAE, 0xAA, 0xAA, 0xA6, 0x95, 0x55, + 0xFB, 0xFF, 0xFF, 0xFF, 0xFE, 0x1D, 0xF0, 0x00, 0xF0, 0x00, 0x50, 0x55, + 0x55, 0x59, 0x6A, 0xAA, 0xF3, 0xFF, 0xFF, 0xFF, 0xFE, 0x04, 0xF0, 0x00, + 0xF0, 0x00, 0xA4, 0xAA, 0xA9, 0xA5, 0x95, 0x55, 0x21, 0xFF, 0xFF, 0xFF, + 0xFE, 0x01, 0xF8, 0x00, 0xF0, 0x00, 0x50, 0x55, 0x56, 0x5A, 0x6A, 0xAA, + 0x83, 0x9F, 0xFF, 0xFF, 0xFE, 0x00, 0xFC, 0x00, 0xF0, 0x01, 0x50, 0xD5, + 0x95, 0x95, 0xA5, 0x5A, 0x83, 0x9F, 0xFF, 0xFF, 0xFE, 0x81, 0xFF, 0x00, + 0xE0, 0x0B, 0xA0, 0xAA, 0x7A, 0x6A, 0x5A, 0x65, 0x80, 0x1F, 0xF0, 0xFF, + 0xFE, 0xF0, 0xFF, 0x00, 0xF0, 0x3F, 0x50, 0x55, 0xA5, 0xB5, 0xA5, 0xAA, + 0x00, 0x1F, 0xF8, 0xFD, 0xFE, 0xF0, 0xF7, 0x00, 0xF0, 0x6D, 0xA0, 0xAA, + 0x5A, 0x6A, 0x5A, 0x55, 0x83, 0x0F, 0xF8, 0xC9, 0xFE, 0x79, 0x00, 0xF8, + 0x02, 0xC0, 0x81, 0x55, 0xAD, 0xD5, 0xA5, 0xA6, 0xE3, 0x03, 0xFC, 0xC0, + 0xFE, 0xFF, 0x04, 0xF0, 0x00, 0x80, 0x63, 0xAA, 0x52, 0x2A, 0x5A, 0x58, + 0xE7, 0x03, 0xFC, 0xE1, 0xFE, 0xFF, 0x2F, 0xFC, 0x03, 0x0C, 0xFE, 0x55, + 0xA9, 0x55, 0x66, 0x15, 0xF3, 0x0F, 0x7C, 0xF0, 0xFE, 0xFF, 0xFF, 0xFD, + 0xEF, 0x38, 0xFF, 0xAA, 0x56, 0xAA, 0x99, 0xAA, 0xF7, 0x1F, 0x78, 0xF0, + 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0xFF, 0x55, 0xAA, 0x5B, 0x66, 0xA4, + 0xFF, 0x9F, 0xFD, 0xF0, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xAB, + 0x65, 0xA4, 0x99, 0x1B, 0xFF, 0xDF, 0xFF, 0xE0, 0xFE, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x69, 0x51, 0x5A, 0x24, 0x62, 0xFF, 0xDF, 0xFF, 0xEC, + 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x95, 0xAA, 0x24, 0x59, 0x55, + 0xFF, 0xFF, 0xFF, 0xFC, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x25, + 0x49, 0xAD, 0x55, 0x2A, 0xFF, 0xFF, 0xFF, 0xFC, 0xFD, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x9B, 0xA5, 0x52, 0x2A, 0x94, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x67, 0x64, 0xC1, 0x92, 0x6D, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x95, + 0x49, 0x2C, 0x55, 0x92, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, + 0x7F, 0xFF, 0xFF, 0x15, 0xB4, 0x62, 0x44, 0x49, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFD, 0xFF, 0x51, 0x30, 0x03, 0xE0, 0xFF, 0x53, 0x03, 0x89, 0x96, 0x92, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0x7F, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0xCD, + 0x68, 0x19, 0x68, 0x99, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x1F, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x13, 0x4A, 0x64, 0x23, 0x49, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x47, 0x8A, 0xA4, 0xD8, 0x30, + 0xFE, 0xFF, 0xFF, 0xFF, 0xFC, 0x07, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x39, + 0x29, 0x49, 0x06, 0x86, 0xCC, 0xFF, 0xFF, 0xFF, 0xFC, 0x03, 0x00, 0x00, + 0x80, 0x00, 0xF2, 0x81, 0x50, 0x31, 0xA2, 0x8C, 0xE4, 0xFF, 0xFF, 0xFF, + 0xFF, 0x0A, 0x08, 0x00, 0x00, 0x00, 0xF0, 0xA7, 0x46, 0x8C, 0x28, 0x31, + 0xC0, 0xFC, 0xFF, 0xFF, 0xFC, 0x03, 0x10, 0x00, 0xE0, 0x01, 0xE0, 0x19, + 0x41, 0x61, 0x66, 0x05, 0xE0, 0xB8, 0xFF, 0xFF, 0x7E, 0x0C, 0x44, 0x00, + 0x40, 0xBF, 0xEF, 0x41, 0x5C, 0x04, 0x00, 0xA8, 0xF0, 0xC1, 0xFF, 0xFF, + 0xFE, 0x21, 0x9E, 0x00, 0xE0, 0xFF, 0xED, 0xA5, 0x12, 0x92, 0xDB, 0xA2, + 0xE0, 0xC0, 0xFF, 0xFF, 0x7D, 0xFA, 0xFF, 0x00, 0x70, 0x00, 0xFF, 0x42, + 0x84, 0x0C, 0x00, 0x08, 0xC0, 0xC0, 0xFF, 0xFC, 0x7D, 0xFF, 0x90, 0x07, + 0xC0, 0x1F, 0xDB, 0x0A, 0x24, 0xC8, 0x68, 0x09, 0xCC, 0xC0, 0x7F, 0xFE, + 0x1E, 0x7F, 0x3E, 0x00, 0xD4, 0xFF, 0x3E, 0x60, 0x81, 0x21, 0x02, 0xA0, + 0xFC, 0xE0, 0x7F, 0xFF, 0xCE, 0x9F, 0xFF, 0x05, 0xFA, 0xFF, 0xBF, 0x04, + 0x24, 0x0A, 0x61, 0x1B, 0xFC, 0xC0, 0xBF, 0xFF, 0xAC, 0x7D, 0xFF, 0x03, + 0x40, 0x1F, 0x80, 0x91, 0xC2, 0xA0, 0x05, 0x80, 0xFF, 0x8D, 0x7F, 0xFF, + 0xEF, 0xF5, 0xDF, 0x00, 0x00, 0x94, 0x42, 0x81, 0x01, 0x10, 0x92, 0x85, + 0xFF, 0xFD, 0xFF, 0xFE, 0x2C, 0x62, 0x03, 0x00, 0x20, 0x00, 0x10, 0x2B, + 0x24, 0x82, 0x00, 0x20, 0xFF, 0xFD, 0xFF, 0xFF, 0xBD, 0x55, 0x01, 0x00, + 0x40, 0x00, 0x08, 0x19, 0x81, 0x58, 0x54, 0x14, 0xFF, 0xFF, 0xFF, 0xFF, + 0x3C, 0x82, 0x05, 0x08, 0x00, 0x24, 0x20, 0x81, 0x24, 0x02, 0x02, 0x82, + 0xFF, 0xFF, 0xFF, 0xFF, 0x3D, 0x82, 0x01, 0x00, 0x00, 0x00, 0x69, 0x22, + 0x90, 0xA0, 0x49, 0x06, 0xFF, 0xFF, 0xFF, 0xFF, 0x2D, 0x26, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x20, 0x0A, 0x02, 0x20, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, + 0x04, 0x81, 0x01, 0x00, 0x10, 0x00, 0xA0, 0x04, 0x02, 0x0A, 0x09, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x19, 0x02, 0x02, 0x00, 0x00, 0x02, 0x42, + 0x50, 0xA0, 0x20, 0x95, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x60, 0x00, 0x01, + 0x80, 0x02, 0x42, 0x52, 0x40, 0x20, 0xA0, 0x20, 0xFF, 0xFF, 0xFF, 0x7F, + 0x00, 0x00, 0x8C, 0x01, 0x40, 0x00, 0x00, 0x00, 0x06, 0x06, 0x09, 0x05, + 0xFF, 0xFF, 0xFF, 0x3F, 0x00, 0x50, 0x00, 0x00, 0x40, 0x00, 0x02, 0x18, + 0xA0, 0x90, 0x84, 0x64, 0xFF, 0xFF, 0xFF, 0x3F, 0x00, 0x10, 0x81, 0x07, + 0xD0, 0x00, 0x48, 0x82, 0x02, 0x02, 0x20, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, + 0x00, 0x04, 0xC0, 0x7E, 0x9F, 0x01, 0x10, 0x04, 0x00, 0x00, 0x24, 0x25, + 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x60, 0x48, 0xC0, 0xC1, 0x13, 0x00, 0x41, + 0x9A, 0x99, 0x81, 0x00, 0xFF, 0xFF, 0xFF, 0x03, 0x40, 0x40, 0x60, 0x00, + 0x84, 0x0B, 0x00, 0x50, 0x02, 0x40, 0x24, 0x90, 0xFF, 0xFF, 0xFF, 0x01, + 0x00, 0x00, 0x32, 0x00, 0x00, 0x57, 0x00, 0x04, 0x40, 0x02, 0x01, 0x05, + 0xF8, 0xFF, 0xFF, 0x01, 0x40, 0x00, 0x38, 0x00, 0x00, 0x0E, 0x10, 0x05, + 0x99, 0x0A, 0xA4, 0x0A, 0xFC, 0xFF, 0x7F, 0x00, 0x80, 0x30, 0x10, 0x00, + 0x00, 0x84, 0x04, 0x60, 0x00, 0x50, 0x00, 0x60, 0xFC, 0xFF, 0xFF, 0x00, + 0x00, 0x3A, 0x08, 0x00, 0x00, 0x04, 0x8E, 0x06, 0x00, 0x41, 0x56, 0x00, + 0xF8, 0xFE, 0xFF, 0x01, 0x80, 0x79, 0xA0, 0x7F, 0xFF, 0x01, 0x06, 0x20, + 0x51, 0x08, 0x00, 0x2A, 0xF8, 0xFF, 0xFF, 0x00, 0xE0, 0x3F, 0xE0, 0xAF, + 0x00, 0x00, 0x06, 0x11, 0x91, 0x52, 0x02, 0x92, 0xF8, 0xFF, 0xFF, 0x00, + 0x00, 0x7F, 0x00, 0x00, 0x80, 0x02, 0x02, 0x08, 0x00, 0x00, 0x50, 0x00, + 0x7E, 0xFF, 0xFF, 0x01, 0x40, 0x7F, 0x00, 0x00, 0x10, 0x80, 0x86, 0x00, + 0x08, 0x09, 0x06, 0x08, 0xFF, 0xFF, 0xFF, 0x01, 0x80, 0xFC, 0x20, 0x51, + 0x0D, 0x20, 0x23, 0x94, 0x91, 0x50, 0x20, 0x92, 0xFF, 0xFE, 0xFF, 0x03, + 0x20, 0x70, 0x05, 0x88, 0x01, 0x80, 0x21, 0x06, 0x84, 0x44, 0x80, 0x25, + 0xFF, 0xFF, 0xFF, 0x03, 0x40, 0xEA, 0x90, 0x00, 0x20, 0xA4, 0x05, 0x00, + 0x00, 0x10, 0x26, 0x00, 0xFF, 0xFF, 0xFF, 0x07, 0x40, 0xD8, 0x08, 0x00, + 0x00, 0xA0, 0x81, 0x92, 0x01, 0x01, 0x84, 0x40, 0xFF, 0xFF, 0xFF, 0x0F, + 0x20, 0xC3, 0x42, 0x00, 0x00, 0x0A, 0x24, 0x00, 0x44, 0x48, 0x20, 0x25, + 0xFF, 0xFF, 0xFF, 0x0F, 0x20, 0xD5, 0x05, 0x02, 0x00, 0x46, 0x12, 0x40, + 0x02, 0x22, 0x44, 0x00, 0xFF, 0xFF, 0xFF, 0x1F, 0x90, 0x50, 0x90, 0x00, + 0x00, 0x9A, 0x40, 0x02, 0x40, 0x00, 0x10, 0x64, 0xFF, 0xFF, 0xFF, 0x1F, + 0x98, 0x62, 0x7C, 0x10, 0x11, 0xDF, 0x07, 0x14, 0x28, 0x08, 0x01, 0x05, + 0xFF, 0xFF, 0xFF, 0x1F, 0x20, 0x74, 0xF8, 0xE5, 0xEA, 0x87, 0x23, 0x01, + 0x81, 0x11, 0x48, 0x20, 0xFF, 0xFF, 0xFF, 0x1F, 0x50, 0x38, 0xF2, 0xFF, + 0xFF, 0xC7, 0x8F, 0x80, 0x00, 0x84, 0x40, 0x04, 0xFF, 0xFF, 0xFF, 0xBF, + 0x68, 0x3D, 0xE9, 0xFF, 0x7F, 0xC1, 0x1F, 0x28, 0x14, 0x10, 0x06, 0x50, + 0xFF, 0xFF, 0xFF, 0x3F, 0x90, 0x3F, 0x81, 0xFE, 0x5F, 0x89, 0x3F, 0x82, + 0x00, 0x02, 0xA2, 0x01, 0xFF, 0xFF, 0xFF, 0xBF, 0xB0, 0x3F, 0xB1, 0xFF, + 0xBF, 0xC1, 0xFF, 0x00, 0x82, 0x80, 0x00, 0x24, 0xFF, 0xFB, 0xFF, 0x7F, + 0xF8, 0x3F, 0xA9, 0xFC, 0x5F, 0x81, 0xFF, 0x0F, 0x08, 0x00, 0x04, 0x48, + 0xFF, 0xF0, 0xFF, 0xBF, 0xFC, 0x3F, 0x41, 0xEB, 0xE7, 0xC0, 0xFF, 0x1F, + 0x40, 0x24, 0x40, 0x01, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0x3F, 0xB3, 0x7D, + 0xAD, 0x80, 0xFF, 0xFF, 0x01, 0x00, 0xA0, 0x90, 0x7F, 0xC0, 0xFF, 0xFF, + 0xFF, 0x3F, 0x43, 0xFA, 0x27, 0xC0, 0xFF, 0xFF, 0x93, 0x51, 0x04, 0x04, + 0x7F, 0x80, 0xFF, 0xFF, 0xFF, 0x3F, 0x60, 0x6D, 0x56, 0xC0, 0xFF, 0xFF, + 0x1F, 0x20, 0x0A, 0x40, 0x7F, 0x80, 0xFF, 0xFF, 0xFF, 0x3F, 0x84, 0x6A, + 0x6D, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x02, 0x7F, 0x00, 0xFF, 0xFF, + 0xFF, 0x3F, 0x2C, 0xBA, 0x22, 0xC0, 0xFF, 0xFF, 0xFF, 0x87, 0x04, 0x98, + 0x7F, 0x80, 0xFF, 0xFF, 0xFF, 0x3F, 0x48, 0x66, 0xD5, 0xC0, 0xFF, 0xFF, + 0xFF, 0x3F, 0x90, 0x02, 0x7F, 0xC0, 0xFF, 0xFF, 0xFF, 0x7F, 0x80, 0xA6, + 0x63, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0x08, 0x24, 0x7F, 0xF8, 0xFF, 0xFF, + 0xFF, 0x7F, 0x90, 0x78, 0x3A, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0x27, 0x00, + 0x7F, 0xFC, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0xF9, 0x06, 0xF0, 0xFF, 0xFF, + 0xFF, 0xFF, 0x8F, 0x61, 0x7F, 0xFC, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0xA4, + 0x0B, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x04, 0x7F, 0xFE, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0xD0, 0x01, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x08, + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0xF8, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x61, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x88, + 0x01, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x48, + 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x07, 0xFC, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x01, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xD0, + 0x0F, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x69, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x03, 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x05, + 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0x3F, 0xFE, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x21, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xF0, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x6B, + 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x23, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xE0, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xEF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x27, + 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xF0, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, + 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xF0, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x03, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, + 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xF0, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x1F, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, + 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0xF0, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2F, + 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x1F, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x00, + 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xF0, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x3F, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, + 0xF8, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x1F, 0xF0, 0xFF, 0x03, 0x00, 0xF1, 0xFF, 0x7F, 0x80, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xF0, 0xFF, 0x03, 0x00, + 0xF0, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x7F, 0x6F, 0xFD, 0xFF, 0xFF, 0x1F, + 0xF8, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0x7F, 0x7F, + 0xFD, 0xFF, 0xFF, 0x7F, 0xF0, 0xFF, 0x03, 0x00, 0xC0, 0xFF, 0xFF, 0x90, + 0xFF, 0xFF, 0x5F, 0xA2, 0xF4, 0xFF, 0xFF, 0x3F, 0xF8, 0xFF, 0x03, 0x00, + 0x80, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0x0F, 0xAA, 0x42, 0xF1, 0xFF, 0x3F, + 0xF8, 0xFF, 0xAF, 0x00, 0x80, 0xFF, 0xFF, 0xD9, 0xFF, 0xFF, 0x0F, 0x02, + 0x64, 0xF1, 0xFF, 0x3F, 0xF8, 0xFF, 0xFF, 0x3F, 0xC2, 0xFF, 0xFF, 0xDB, + 0xFF, 0xFF, 0xCB, 0xD1, 0xDA, 0xF7, 0xFF, 0x7F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x71, 0x44, 0xFC, 0xF6, 0xFF, 0x3F, + 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB0, 0xC0, + 0xA8, 0xFF, 0xFF, 0x3F, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFC, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, + 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x7F, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, }; diff --git a/Tests/images/hopper_underscore.xbm b/Tests/images/hopper_underscore.xbm new file mode 100644 index 000000000..79d9fff9a --- /dev/null +++ b/Tests/images/hopper_underscore.xbm @@ -0,0 +1,174 @@ +#define hopper_test_width 128 +#define hopper_test_height 128 +static char hopper_test_bits[] = { + 0xFF, 0xFF, 0xFF, 0x1F, 0xF0, 0x3D, 0x80, 0x96, 0xDA, 0xB6, 0xD6, 0x2A, + 0xA9, 0x6D, 0x25, 0x29, 0xFF, 0xFF, 0xFF, 0xBF, 0xFE, 0x7D, 0xC0, 0xFB, + 0x69, 0x69, 0xA9, 0xD5, 0x96, 0x96, 0x5E, 0x9A, 0xFF, 0xFF, 0xFF, 0x9F, + 0xFE, 0xFD, 0x80, 0xFF, 0x97, 0xB6, 0x5A, 0x6A, 0x5D, 0x6D, 0x5A, 0x62, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFD, 0xC1, 0xCF, 0x5F, 0x89, 0xA5, 0xD5, + 0xAA, 0x92, 0xA5, 0x59, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFD, 0xF8, 0x00, + 0xB8, 0x59, 0xAA, 0x56, 0xDA, 0x5A, 0x56, 0x49, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFE, 0xFC, 0xF9, 0x00, 0xF0, 0xA6, 0x55, 0xA9, 0x65, 0xA5, 0xA9, 0xA2, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFD, 0x71, 0x00, 0x60, 0x70, 0xAA, 0x54, + 0xAA, 0x5A, 0x54, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFC, 0x38, 0x00, + 0xE0, 0x80, 0x55, 0xAB, 0x55, 0x75, 0xAB, 0x56, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFE, 0x1D, 0x70, 0x00, 0xF0, 0x00, 0xAE, 0xAA, 0xAA, 0xA6, 0x95, 0x55, + 0xFB, 0xFF, 0xFF, 0xFF, 0xFE, 0x1D, 0xF0, 0x00, 0xF0, 0x00, 0x50, 0x55, + 0x55, 0x59, 0x6A, 0xAA, 0xF3, 0xFF, 0xFF, 0xFF, 0xFE, 0x04, 0xF0, 0x00, + 0xF0, 0x00, 0xA4, 0xAA, 0xA9, 0xA5, 0x95, 0x55, 0x21, 0xFF, 0xFF, 0xFF, + 0xFE, 0x01, 0xF8, 0x00, 0xF0, 0x00, 0x50, 0x55, 0x56, 0x5A, 0x6A, 0xAA, + 0x83, 0x9F, 0xFF, 0xFF, 0xFE, 0x00, 0xFC, 0x00, 0xF0, 0x01, 0x50, 0xD5, + 0x95, 0x95, 0xA5, 0x5A, 0x83, 0x9F, 0xFF, 0xFF, 0xFE, 0x81, 0xFF, 0x00, + 0xE0, 0x0B, 0xA0, 0xAA, 0x7A, 0x6A, 0x5A, 0x65, 0x80, 0x1F, 0xF0, 0xFF, + 0xFE, 0xF0, 0xFF, 0x00, 0xF0, 0x3F, 0x50, 0x55, 0xA5, 0xB5, 0xA5, 0xAA, + 0x00, 0x1F, 0xF8, 0xFD, 0xFE, 0xF0, 0xF7, 0x00, 0xF0, 0x6D, 0xA0, 0xAA, + 0x5A, 0x6A, 0x5A, 0x55, 0x83, 0x0F, 0xF8, 0xC9, 0xFE, 0x79, 0x00, 0xF8, + 0x02, 0xC0, 0x81, 0x55, 0xAD, 0xD5, 0xA5, 0xA6, 0xE3, 0x03, 0xFC, 0xC0, + 0xFE, 0xFF, 0x04, 0xF0, 0x00, 0x80, 0x63, 0xAA, 0x52, 0x2A, 0x5A, 0x58, + 0xE7, 0x03, 0xFC, 0xE1, 0xFE, 0xFF, 0x2F, 0xFC, 0x03, 0x0C, 0xFE, 0x55, + 0xA9, 0x55, 0x66, 0x15, 0xF3, 0x0F, 0x7C, 0xF0, 0xFE, 0xFF, 0xFF, 0xFD, + 0xEF, 0x38, 0xFF, 0xAA, 0x56, 0xAA, 0x99, 0xAA, 0xF7, 0x1F, 0x78, 0xF0, + 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0xFF, 0x55, 0xAA, 0x5B, 0x66, 0xA4, + 0xFF, 0x9F, 0xFD, 0xF0, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xAB, + 0x65, 0xA4, 0x99, 0x1B, 0xFF, 0xDF, 0xFF, 0xE0, 0xFE, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x69, 0x51, 0x5A, 0x24, 0x62, 0xFF, 0xDF, 0xFF, 0xEC, + 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x95, 0xAA, 0x24, 0x59, 0x55, + 0xFF, 0xFF, 0xFF, 0xFC, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x25, + 0x49, 0xAD, 0x55, 0x2A, 0xFF, 0xFF, 0xFF, 0xFC, 0xFD, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x9B, 0xA5, 0x52, 0x2A, 0x94, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x67, 0x64, 0xC1, 0x92, 0x6D, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x95, + 0x49, 0x2C, 0x55, 0x92, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, + 0x7F, 0xFF, 0xFF, 0x15, 0xB4, 0x62, 0x44, 0x49, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFD, 0xFF, 0x51, 0x30, 0x03, 0xE0, 0xFF, 0x53, 0x03, 0x89, 0x96, 0x92, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0x7F, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0xCD, + 0x68, 0x19, 0x68, 0x99, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x1F, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x13, 0x4A, 0x64, 0x23, 0x49, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x47, 0x8A, 0xA4, 0xD8, 0x30, + 0xFE, 0xFF, 0xFF, 0xFF, 0xFC, 0x07, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x39, + 0x29, 0x49, 0x06, 0x86, 0xCC, 0xFF, 0xFF, 0xFF, 0xFC, 0x03, 0x00, 0x00, + 0x80, 0x00, 0xF2, 0x81, 0x50, 0x31, 0xA2, 0x8C, 0xE4, 0xFF, 0xFF, 0xFF, + 0xFF, 0x0A, 0x08, 0x00, 0x00, 0x00, 0xF0, 0xA7, 0x46, 0x8C, 0x28, 0x31, + 0xC0, 0xFC, 0xFF, 0xFF, 0xFC, 0x03, 0x10, 0x00, 0xE0, 0x01, 0xE0, 0x19, + 0x41, 0x61, 0x66, 0x05, 0xE0, 0xB8, 0xFF, 0xFF, 0x7E, 0x0C, 0x44, 0x00, + 0x40, 0xBF, 0xEF, 0x41, 0x5C, 0x04, 0x00, 0xA8, 0xF0, 0xC1, 0xFF, 0xFF, + 0xFE, 0x21, 0x9E, 0x00, 0xE0, 0xFF, 0xED, 0xA5, 0x12, 0x92, 0xDB, 0xA2, + 0xE0, 0xC0, 0xFF, 0xFF, 0x7D, 0xFA, 0xFF, 0x00, 0x70, 0x00, 0xFF, 0x42, + 0x84, 0x0C, 0x00, 0x08, 0xC0, 0xC0, 0xFF, 0xFC, 0x7D, 0xFF, 0x90, 0x07, + 0xC0, 0x1F, 0xDB, 0x0A, 0x24, 0xC8, 0x68, 0x09, 0xCC, 0xC0, 0x7F, 0xFE, + 0x1E, 0x7F, 0x3E, 0x00, 0xD4, 0xFF, 0x3E, 0x60, 0x81, 0x21, 0x02, 0xA0, + 0xFC, 0xE0, 0x7F, 0xFF, 0xCE, 0x9F, 0xFF, 0x05, 0xFA, 0xFF, 0xBF, 0x04, + 0x24, 0x0A, 0x61, 0x1B, 0xFC, 0xC0, 0xBF, 0xFF, 0xAC, 0x7D, 0xFF, 0x03, + 0x40, 0x1F, 0x80, 0x91, 0xC2, 0xA0, 0x05, 0x80, 0xFF, 0x8D, 0x7F, 0xFF, + 0xEF, 0xF5, 0xDF, 0x00, 0x00, 0x94, 0x42, 0x81, 0x01, 0x10, 0x92, 0x85, + 0xFF, 0xFD, 0xFF, 0xFE, 0x2C, 0x62, 0x03, 0x00, 0x20, 0x00, 0x10, 0x2B, + 0x24, 0x82, 0x00, 0x20, 0xFF, 0xFD, 0xFF, 0xFF, 0xBD, 0x55, 0x01, 0x00, + 0x40, 0x00, 0x08, 0x19, 0x81, 0x58, 0x54, 0x14, 0xFF, 0xFF, 0xFF, 0xFF, + 0x3C, 0x82, 0x05, 0x08, 0x00, 0x24, 0x20, 0x81, 0x24, 0x02, 0x02, 0x82, + 0xFF, 0xFF, 0xFF, 0xFF, 0x3D, 0x82, 0x01, 0x00, 0x00, 0x00, 0x69, 0x22, + 0x90, 0xA0, 0x49, 0x06, 0xFF, 0xFF, 0xFF, 0xFF, 0x2D, 0x26, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x20, 0x0A, 0x02, 0x20, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, + 0x04, 0x81, 0x01, 0x00, 0x10, 0x00, 0xA0, 0x04, 0x02, 0x0A, 0x09, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x19, 0x02, 0x02, 0x00, 0x00, 0x02, 0x42, + 0x50, 0xA0, 0x20, 0x95, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x60, 0x00, 0x01, + 0x80, 0x02, 0x42, 0x52, 0x40, 0x20, 0xA0, 0x20, 0xFF, 0xFF, 0xFF, 0x7F, + 0x00, 0x00, 0x8C, 0x01, 0x40, 0x00, 0x00, 0x00, 0x06, 0x06, 0x09, 0x05, + 0xFF, 0xFF, 0xFF, 0x3F, 0x00, 0x50, 0x00, 0x00, 0x40, 0x00, 0x02, 0x18, + 0xA0, 0x90, 0x84, 0x64, 0xFF, 0xFF, 0xFF, 0x3F, 0x00, 0x10, 0x81, 0x07, + 0xD0, 0x00, 0x48, 0x82, 0x02, 0x02, 0x20, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, + 0x00, 0x04, 0xC0, 0x7E, 0x9F, 0x01, 0x10, 0x04, 0x00, 0x00, 0x24, 0x25, + 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x60, 0x48, 0xC0, 0xC1, 0x13, 0x00, 0x41, + 0x9A, 0x99, 0x81, 0x00, 0xFF, 0xFF, 0xFF, 0x03, 0x40, 0x40, 0x60, 0x00, + 0x84, 0x0B, 0x00, 0x50, 0x02, 0x40, 0x24, 0x90, 0xFF, 0xFF, 0xFF, 0x01, + 0x00, 0x00, 0x32, 0x00, 0x00, 0x57, 0x00, 0x04, 0x40, 0x02, 0x01, 0x05, + 0xF8, 0xFF, 0xFF, 0x01, 0x40, 0x00, 0x38, 0x00, 0x00, 0x0E, 0x10, 0x05, + 0x99, 0x0A, 0xA4, 0x0A, 0xFC, 0xFF, 0x7F, 0x00, 0x80, 0x30, 0x10, 0x00, + 0x00, 0x84, 0x04, 0x60, 0x00, 0x50, 0x00, 0x60, 0xFC, 0xFF, 0xFF, 0x00, + 0x00, 0x3A, 0x08, 0x00, 0x00, 0x04, 0x8E, 0x06, 0x00, 0x41, 0x56, 0x00, + 0xF8, 0xFE, 0xFF, 0x01, 0x80, 0x79, 0xA0, 0x7F, 0xFF, 0x01, 0x06, 0x20, + 0x51, 0x08, 0x00, 0x2A, 0xF8, 0xFF, 0xFF, 0x00, 0xE0, 0x3F, 0xE0, 0xAF, + 0x00, 0x00, 0x06, 0x11, 0x91, 0x52, 0x02, 0x92, 0xF8, 0xFF, 0xFF, 0x00, + 0x00, 0x7F, 0x00, 0x00, 0x80, 0x02, 0x02, 0x08, 0x00, 0x00, 0x50, 0x00, + 0x7E, 0xFF, 0xFF, 0x01, 0x40, 0x7F, 0x00, 0x00, 0x10, 0x80, 0x86, 0x00, + 0x08, 0x09, 0x06, 0x08, 0xFF, 0xFF, 0xFF, 0x01, 0x80, 0xFC, 0x20, 0x51, + 0x0D, 0x20, 0x23, 0x94, 0x91, 0x50, 0x20, 0x92, 0xFF, 0xFE, 0xFF, 0x03, + 0x20, 0x70, 0x05, 0x88, 0x01, 0x80, 0x21, 0x06, 0x84, 0x44, 0x80, 0x25, + 0xFF, 0xFF, 0xFF, 0x03, 0x40, 0xEA, 0x90, 0x00, 0x20, 0xA4, 0x05, 0x00, + 0x00, 0x10, 0x26, 0x00, 0xFF, 0xFF, 0xFF, 0x07, 0x40, 0xD8, 0x08, 0x00, + 0x00, 0xA0, 0x81, 0x92, 0x01, 0x01, 0x84, 0x40, 0xFF, 0xFF, 0xFF, 0x0F, + 0x20, 0xC3, 0x42, 0x00, 0x00, 0x0A, 0x24, 0x00, 0x44, 0x48, 0x20, 0x25, + 0xFF, 0xFF, 0xFF, 0x0F, 0x20, 0xD5, 0x05, 0x02, 0x00, 0x46, 0x12, 0x40, + 0x02, 0x22, 0x44, 0x00, 0xFF, 0xFF, 0xFF, 0x1F, 0x90, 0x50, 0x90, 0x00, + 0x00, 0x9A, 0x40, 0x02, 0x40, 0x00, 0x10, 0x64, 0xFF, 0xFF, 0xFF, 0x1F, + 0x98, 0x62, 0x7C, 0x10, 0x11, 0xDF, 0x07, 0x14, 0x28, 0x08, 0x01, 0x05, + 0xFF, 0xFF, 0xFF, 0x1F, 0x20, 0x74, 0xF8, 0xE5, 0xEA, 0x87, 0x23, 0x01, + 0x81, 0x11, 0x48, 0x20, 0xFF, 0xFF, 0xFF, 0x1F, 0x50, 0x38, 0xF2, 0xFF, + 0xFF, 0xC7, 0x8F, 0x80, 0x00, 0x84, 0x40, 0x04, 0xFF, 0xFF, 0xFF, 0xBF, + 0x68, 0x3D, 0xE9, 0xFF, 0x7F, 0xC1, 0x1F, 0x28, 0x14, 0x10, 0x06, 0x50, + 0xFF, 0xFF, 0xFF, 0x3F, 0x90, 0x3F, 0x81, 0xFE, 0x5F, 0x89, 0x3F, 0x82, + 0x00, 0x02, 0xA2, 0x01, 0xFF, 0xFF, 0xFF, 0xBF, 0xB0, 0x3F, 0xB1, 0xFF, + 0xBF, 0xC1, 0xFF, 0x00, 0x82, 0x80, 0x00, 0x24, 0xFF, 0xFB, 0xFF, 0x7F, + 0xF8, 0x3F, 0xA9, 0xFC, 0x5F, 0x81, 0xFF, 0x0F, 0x08, 0x00, 0x04, 0x48, + 0xFF, 0xF0, 0xFF, 0xBF, 0xFC, 0x3F, 0x41, 0xEB, 0xE7, 0xC0, 0xFF, 0x1F, + 0x40, 0x24, 0x40, 0x01, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0x3F, 0xB3, 0x7D, + 0xAD, 0x80, 0xFF, 0xFF, 0x01, 0x00, 0xA0, 0x90, 0x7F, 0xC0, 0xFF, 0xFF, + 0xFF, 0x3F, 0x43, 0xFA, 0x27, 0xC0, 0xFF, 0xFF, 0x93, 0x51, 0x04, 0x04, + 0x7F, 0x80, 0xFF, 0xFF, 0xFF, 0x3F, 0x60, 0x6D, 0x56, 0xC0, 0xFF, 0xFF, + 0x1F, 0x20, 0x0A, 0x40, 0x7F, 0x80, 0xFF, 0xFF, 0xFF, 0x3F, 0x84, 0x6A, + 0x6D, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x02, 0x7F, 0x00, 0xFF, 0xFF, + 0xFF, 0x3F, 0x2C, 0xBA, 0x22, 0xC0, 0xFF, 0xFF, 0xFF, 0x87, 0x04, 0x98, + 0x7F, 0x80, 0xFF, 0xFF, 0xFF, 0x3F, 0x48, 0x66, 0xD5, 0xC0, 0xFF, 0xFF, + 0xFF, 0x3F, 0x90, 0x02, 0x7F, 0xC0, 0xFF, 0xFF, 0xFF, 0x7F, 0x80, 0xA6, + 0x63, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0x08, 0x24, 0x7F, 0xF8, 0xFF, 0xFF, + 0xFF, 0x7F, 0x90, 0x78, 0x3A, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0x27, 0x00, + 0x7F, 0xFC, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0xF9, 0x06, 0xF0, 0xFF, 0xFF, + 0xFF, 0xFF, 0x8F, 0x61, 0x7F, 0xFC, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0xA4, + 0x0B, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x04, 0x7F, 0xFE, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0xD0, 0x01, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x08, + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0xF8, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x61, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x88, + 0x01, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x48, + 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x07, 0xFC, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x01, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xD0, + 0x0F, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x69, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x03, 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x05, + 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0x3F, 0xFE, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x21, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xF0, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x6B, + 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x23, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xE0, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xEF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x27, + 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xF0, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, + 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xF0, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x03, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, + 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xF0, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x1F, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, + 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0xF0, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2F, + 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x1F, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x00, + 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xF0, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x3F, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, + 0xF8, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x1F, 0xF0, 0xFF, 0x03, 0x00, 0xF1, 0xFF, 0x7F, 0x80, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xF0, 0xFF, 0x03, 0x00, + 0xF0, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x7F, 0x6F, 0xFD, 0xFF, 0xFF, 0x1F, + 0xF8, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0x7F, 0x7F, + 0xFD, 0xFF, 0xFF, 0x7F, 0xF0, 0xFF, 0x03, 0x00, 0xC0, 0xFF, 0xFF, 0x90, + 0xFF, 0xFF, 0x5F, 0xA2, 0xF4, 0xFF, 0xFF, 0x3F, 0xF8, 0xFF, 0x03, 0x00, + 0x80, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0x0F, 0xAA, 0x42, 0xF1, 0xFF, 0x3F, + 0xF8, 0xFF, 0xAF, 0x00, 0x80, 0xFF, 0xFF, 0xD9, 0xFF, 0xFF, 0x0F, 0x02, + 0x64, 0xF1, 0xFF, 0x3F, 0xF8, 0xFF, 0xFF, 0x3F, 0xC2, 0xFF, 0xFF, 0xDB, + 0xFF, 0xFF, 0xCB, 0xD1, 0xDA, 0xF7, 0xFF, 0x7F, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x71, 0x44, 0xFC, 0xF6, 0xFF, 0x3F, + 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB0, 0xC0, + 0xA8, 0xFF, 0xFF, 0x3F, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFC, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, + 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x7F, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, }; diff --git a/Tests/test_file_xbm.py b/Tests/test_file_xbm.py index 02aec70b1..a1f287ad3 100644 --- a/Tests/test_file_xbm.py +++ b/Tests/test_file_xbm.py @@ -37,6 +37,30 @@ class TestFileXbm(PillowTestCase): self.assertEqual(im.mode, '1') self.assertEqual(im.size, (32, 32)) + def test_open(self): + # Arrange + # Created with `convert hopper.png hopper.xbm` + filename = "Tests/images/hopper.xbm" + + # Act + im = Image.open(filename) + + # Assert + self.assertEqual(im.mode, '1') + self.assertEqual(im.size, (128, 128)) + + def test_open_filename_with_underscore(self): + # Arrange + # Created with `cp hopper.xbm hopper_underscore.xbm` + filename = "Tests/images/hopper_underscore.xbm" + + # Act + im = Image.open(filename) + + # Assert + self.assertEqual(im.mode, '1') + self.assertEqual(im.size, (128, 128)) + if __name__ == '__main__': unittest.main() From 98a8085d6ccf2a7296302922a30c59f8b87e66f6 Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 12 May 2015 12:49:43 +0300 Subject: [PATCH 0246/1037] Header width/height may have multiple underscores: file_name_width --- PIL/XbmImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PIL/XbmImagePlugin.py b/PIL/XbmImagePlugin.py index 604ba15a8..454a95534 100644 --- a/PIL/XbmImagePlugin.py +++ b/PIL/XbmImagePlugin.py @@ -26,8 +26,8 @@ from PIL import Image, ImageFile # XBM header xbm_head = re.compile( - b"\s*#define[ \t]+[^_]*_width[ \t]+(?P[0-9]+)[\r\n]+" - b"#define[ \t]+[^_]*_height[ \t]+(?P[0-9]+)[\r\n]+" + b"\s*#define[ \t]+.*_width[ \t]+(?P[0-9]+)[\r\n]+" + b"#define[ \t]+.*_height[ \t]+(?P[0-9]+)[\r\n]+" b"(?P" b"#define[ \t]+[^_]*_x_hot[ \t]+(?P[0-9]+)[\r\n]+" b"#define[ \t]+[^_]*_y_hot[ \t]+(?P[0-9]+)[\r\n]+" From ca7f44e7ae9fa302f05721bebe6d17483ff9ef54 Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 12 May 2015 13:34:18 +0300 Subject: [PATCH 0247/1037] Fix comment and file --- Tests/images/hopper_underscore.xbm | 6 +++--- Tests/test_file_xbm.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/images/hopper_underscore.xbm b/Tests/images/hopper_underscore.xbm index 79d9fff9a..50d81e526 100644 --- a/Tests/images/hopper_underscore.xbm +++ b/Tests/images/hopper_underscore.xbm @@ -1,6 +1,6 @@ -#define hopper_test_width 128 -#define hopper_test_height 128 -static char hopper_test_bits[] = { +#define hopper_underscore_width 128 +#define hopper_underscore_height 128 +static char hopper_underscore_bits[] = { 0xFF, 0xFF, 0xFF, 0x1F, 0xF0, 0x3D, 0x80, 0x96, 0xDA, 0xB6, 0xD6, 0x2A, 0xA9, 0x6D, 0x25, 0x29, 0xFF, 0xFF, 0xFF, 0xBF, 0xFE, 0x7D, 0xC0, 0xFB, 0x69, 0x69, 0xA9, 0xD5, 0x96, 0x96, 0x5E, 0x9A, 0xFF, 0xFF, 0xFF, 0x9F, diff --git a/Tests/test_file_xbm.py b/Tests/test_file_xbm.py index a1f287ad3..5812ac934 100644 --- a/Tests/test_file_xbm.py +++ b/Tests/test_file_xbm.py @@ -51,7 +51,7 @@ class TestFileXbm(PillowTestCase): def test_open_filename_with_underscore(self): # Arrange - # Created with `cp hopper.xbm hopper_underscore.xbm` + # Created with `convert hopper.png hopper_underscore.xbm` filename = "Tests/images/hopper_underscore.xbm" # Act From ca89d431baf4bdd50473091c9482a9206f1b5191 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 13 May 2015 16:39:25 +1000 Subject: [PATCH 0248/1037] To avoid modifications, copy image when saving in GifImagePlugin --- PIL/GifImagePlugin.py | 2 +- Tests/test_file_gif.py | 10 +++++----- Tests/test_imagefile.py | 5 +++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 8db42e8e8..ed4915b9f 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -269,7 +269,7 @@ def _save(im, fp, filename): pass # write uncompressed file if im.mode in RAWMODE: - im_out = im + im_out = im.copy() else: # convert on the fly (EXPERIMENTAL -- I'm not sure PIL # should automatically convert images on save...) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 1aa8a36bc..be0b8417e 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -92,20 +92,20 @@ class TestFileGif(PillowTestCase): def roundtrip(im, *args, **kwargs): out = self.tempfile('temp.gif') - im.save(out, *args, **kwargs) + im.copy().save(out, *args, **kwargs) reloaded = Image.open(out) - return [im, reloaded] + return reloaded orig = "Tests/images/test.colors.gif" im = Image.open(orig) - self.assert_image_equal(*roundtrip(im)) - self.assert_image_equal(*roundtrip(im, optimize=True)) + self.assert_image_similar(im, roundtrip(im), 1) + self.assert_image_similar(im, roundtrip(im, optimize=True), 1) im = im.convert("RGB") # check automatic P conversion - reloaded = roundtrip(im)[1].convert('RGB') + reloaded = roundtrip(im).convert('RGB') self.assert_image_equal(im, reloaded) @unittest.skipUnless(netpbm_available(), "netpbm not available") diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index 5311b899f..bc81575b6 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -26,7 +26,7 @@ class TestImageFile(PillowTestCase): test_file = BytesIO() - im.save(test_file, format) + im.copy().save(test_file, format) data = test_file.getvalue() @@ -37,7 +37,8 @@ class TestImageFile(PillowTestCase): return im, imOut self.assert_image_equal(*roundtrip("BMP")) - self.assert_image_equal(*roundtrip("GIF")) + im1, im2 = roundtrip("GIF") + self.assert_image_similar(im1.convert('P'), im2, 1) self.assert_image_equal(*roundtrip("IM")) self.assert_image_equal(*roundtrip("MSP")) if "zip_encoder" in codecs: From 93461e6faa9a5a2676101394bd2fae68040f6b53 Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Wed, 13 May 2015 03:05:45 -0700 Subject: [PATCH 0249/1037] Ico files are little endian, ref #1204 --- PIL/IcoImagePlugin.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PIL/IcoImagePlugin.py b/PIL/IcoImagePlugin.py index c4e24d99c..03f3aef43 100644 --- a/PIL/IcoImagePlugin.py +++ b/PIL/IcoImagePlugin.py @@ -48,7 +48,7 @@ def _save(im, fp, filename): width, height = im.size filter(lambda x: False if (x[0] > width or x[1] > height or x[0] > 255 or x[1] > 255) else True, sizes) - fp.write(struct.pack("H", len(sizes))) # idCount(2) + fp.write(struct.pack(" Date: Wed, 13 May 2015 18:10:12 +0300 Subject: [PATCH 0250/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index f96f86fdf..66d448d2f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Release GIL during image load (decode) #1224 + [lkesteloot] + - Added icns save #1185 [radarhere] From b1e8a68df8e7c858c78b89681678396005a678dc Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 14 May 2015 09:57:56 +1000 Subject: [PATCH 0251/1037] When copying Image, copy ImagePalette as well --- PIL/Image.py | 3 ++- PIL/ImagePalette.py | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/PIL/Image.py b/PIL/Image.py index 37428ec30..8ef4969ef 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -509,7 +509,8 @@ class Image: new.im = im new.mode = im.mode new.size = im.size - new.palette = self.palette + if self.palette: + new.palette = self.palette.copy() if im.mode == "P" and not new.palette: from PIL import ImagePalette new.palette = ImagePalette.ImagePalette() diff --git a/PIL/ImagePalette.py b/PIL/ImagePalette.py index c6c05d162..b5f07ee61 100644 --- a/PIL/ImagePalette.py +++ b/PIL/ImagePalette.py @@ -34,6 +34,18 @@ class ImagePalette: (size != 0 and size != len(self.palette))): raise ValueError("wrong palette size") + def copy(self): + new = ImagePalette() + + new.mode = self.mode + new.rawmode = self.rawmode + if self.palette is not None: + new.palette = self.palette[:] + new.colors = self.colors.copy() + new.dirty = self.dirty + + return new + def getdata(self): """ Get palette contents in format suitable # for the low-level From 799e8312cb6ede52d83d96f9ae414fb8ba457154 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 14 May 2015 19:43:28 +1000 Subject: [PATCH 0252/1037] Separated out feature checking from selftest --- PIL/features.py | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ selftest.py | 48 +++++++++++---------------------- 2 files changed, 87 insertions(+), 32 deletions(-) create mode 100644 PIL/features.py diff --git a/PIL/features.py b/PIL/features.py new file mode 100644 index 000000000..b9ff99ee9 --- /dev/null +++ b/PIL/features.py @@ -0,0 +1,71 @@ +from PIL import Image + +modules = { + "PIL CORE": "PIL._imaging", + "TKINTER": "PIL._imagingtk", + "FREETYPE2": "PIL._imagingft", + "LITTLECMS2": "PIL._imagingcms", + "WEBP": "PIL._webp", + "Transparent WEBP": ("WEBP", "WebPDecoderBuggyAlpha") +} + + +def check_module(feature): + module = modules[feature] + + method_to_call = None + if type(module) is tuple: + module, method_to_call = module + + try: + imported_module = __import__(module) + except ImportError: + # If a method is being checked, None means that + # rather than the method failing, the module required for the method + # failed to be imported first + return None if method_to_call else False + + if method_to_call: + method = getattr(imported_module, method_to_call) + return method() is True + else: + return True + + +def get_supported_modules(): + supported_modules = [] + for feature in get_all_modules(): + if check_module(feature): + supported_modules.append(feature) + return supported_modules + + +def get_all_modules(): + # While the dictionary keys could be used here, + # a static list is used to maintain order + return ["PIL CORE", "TKINTER", "FREETYPE2", + "LITTLECMS2", "WEBP", "Transparent WEBP"] + +codecs = { + "JPEG": "jpeg", + "JPEG 2000": "jpeg2k", + "ZLIB (PNG/ZIP)": "zip", + "LIBTIFF": "libtiff" +} + + +def check_codec(feature): + codec = codecs[feature] + return codec + "_encoder" in dir(Image.core) + + +def get_supported_codecs(): + supported_codecs = [] + for feature in get_all_codecs(): + if check_codec(feature): + supported_codecs.append(feature) + return supported_codecs + + +def get_all_codecs(): + return ["JPEG", "JPEG 2000", "ZLIB (PNG/ZIP)", "LIBTIFF"] diff --git a/selftest.py b/selftest.py index b13dfec28..1872d3e97 100644 --- a/selftest.py +++ b/selftest.py @@ -9,6 +9,7 @@ if "--installed" in sys.argv: del sys.path[0] from PIL import Image, ImageDraw, ImageFilter, ImageMath +from PIL import features if "--installed" in sys.argv: sys.path.insert(0, sys_path_0) @@ -162,22 +163,6 @@ def testimage(): """ -def check_module(feature, module): - try: - __import__(module) - except ImportError: - print("***", feature, "support not installed") - else: - print("---", feature, "support ok") - - -def check_codec(feature, codec): - if codec + "_encoder" not in dir(Image.core): - print("***", feature, "support not installed") - else: - print("---", feature, "support ok") - - if __name__ == "__main__": # check build sanity @@ -189,23 +174,22 @@ if __name__ == "__main__": print("Python modules loaded from", os.path.dirname(Image.__file__)) print("Binary modules loaded from", os.path.dirname(Image.core.__file__)) print("-"*68) - check_module("PIL CORE", "PIL._imaging") - check_module("TKINTER", "PIL._imagingtk") - check_codec("JPEG", "jpeg") - check_codec("JPEG 2000", "jpeg2k") - check_codec("ZLIB (PNG/ZIP)", "zip") - check_codec("LIBTIFF", "libtiff") - check_module("FREETYPE2", "PIL._imagingft") - check_module("LITTLECMS2", "PIL._imagingcms") - check_module("WEBP", "PIL._webp") - try: - from PIL import _webp - if _webp.WebPDecoderBuggyAlpha(): - print("***", "Transparent WEBP", "support not installed") + for feature in features.get_all_modules(): + supported = features.check_module(feature) + + if supported is None: + # A method was being tested, but the module required + # for the method could not be correctly imported + pass + elif supported: + print("---", feature, "support ok") else: - print("---", "Transparent WEBP", "support ok") - except Exception: - pass + print("***", feature, "support not installed") + for feature in features.get_all_codecs(): + if features.check_codec(feature): + print("---", feature, "support ok") + else: + print("***", feature, "support not installed") print("-"*68) # use doctest to make sure the test program behaves as documented! From 98fa49ea378af0d5485a313386dcaefa4bb17d9d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 14 May 2015 20:57:01 +1000 Subject: [PATCH 0253/1037] Changed module and codec names --- PIL/features.py | 42 +++++++++++++++++++----------------------- selftest.py | 20 ++++++++++++++++---- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/PIL/features.py b/PIL/features.py index b9ff99ee9..fd87f094f 100644 --- a/PIL/features.py +++ b/PIL/features.py @@ -1,16 +1,19 @@ from PIL import Image modules = { - "PIL CORE": "PIL._imaging", - "TKINTER": "PIL._imagingtk", - "FREETYPE2": "PIL._imagingft", - "LITTLECMS2": "PIL._imagingcms", - "WEBP": "PIL._webp", - "Transparent WEBP": ("WEBP", "WebPDecoderBuggyAlpha") + "pil": "PIL._imaging", + "tkinter": "PIL._imagingtk", + "freetype2": "PIL._imagingft", + "littlecms2": "PIL._imagingcms", + "webp": "PIL._webp", + "transp_webp": ("WEBP", "WebPDecoderBuggyAlpha") } def check_module(feature): + if feature not in modules: + raise ValueError("Unknown module %s" % feature) + module = modules[feature] method_to_call = None @@ -34,38 +37,31 @@ def check_module(feature): def get_supported_modules(): supported_modules = [] - for feature in get_all_modules(): + for feature in modules: if check_module(feature): supported_modules.append(feature) return supported_modules - -def get_all_modules(): - # While the dictionary keys could be used here, - # a static list is used to maintain order - return ["PIL CORE", "TKINTER", "FREETYPE2", - "LITTLECMS2", "WEBP", "Transparent WEBP"] - codecs = { - "JPEG": "jpeg", - "JPEG 2000": "jpeg2k", - "ZLIB (PNG/ZIP)": "zip", - "LIBTIFF": "libtiff" + "jpg": "jpeg", + "jpg_2000": "jpeg2k", + "zlib": "zip", + "libtiff": "libtiff" } def check_codec(feature): + if feature not in codecs: + raise ValueError("Unknown codec %s" % feature) + codec = codecs[feature] + return codec + "_encoder" in dir(Image.core) def get_supported_codecs(): supported_codecs = [] - for feature in get_all_codecs(): + for feature in codecs: if check_codec(feature): supported_codecs.append(feature) return supported_codecs - - -def get_all_codecs(): - return ["JPEG", "JPEG 2000", "ZLIB (PNG/ZIP)", "LIBTIFF"] diff --git a/selftest.py b/selftest.py index 1872d3e97..779e83c90 100644 --- a/selftest.py +++ b/selftest.py @@ -174,8 +174,15 @@ if __name__ == "__main__": print("Python modules loaded from", os.path.dirname(Image.__file__)) print("Binary modules loaded from", os.path.dirname(Image.core.__file__)) print("-"*68) - for feature in features.get_all_modules(): - supported = features.check_module(feature) + for name, feature in [ + ("pil", "PIL CORE"), + ("tkinter", "TKINTER"), + ("freetype2", "FREETYPE2"), + ("littlecms2", "LITTLECMS2"), + ("webp", "WEBP"), + ("transp_webp", "Transparent WEBP") + ]: + supported = features.check_module(name) if supported is None: # A method was being tested, but the module required @@ -185,8 +192,13 @@ if __name__ == "__main__": print("---", feature, "support ok") else: print("***", feature, "support not installed") - for feature in features.get_all_codecs(): - if features.check_codec(feature): + for name, feature in [ + ("jpg", "JPEG"), + ("jpg_2000", "JPEG 2000"), + ("zlib", "ZLIB (PNG/ZIP)"), + ("libtiff", "LIBTIFF") + ]: + if features.check_codec(name): print("---", feature, "support ok") else: print("***", feature, "support not installed") From 3458e7a4c3ea336bbc6164c6b7b122f36526f80b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 15 May 2015 07:33:17 +1000 Subject: [PATCH 0254/1037] Added feature checking tests --- Tests/test_features.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 Tests/test_features.py diff --git a/Tests/test_features.py b/Tests/test_features.py new file mode 100644 index 000000000..01c5d6495 --- /dev/null +++ b/Tests/test_features.py @@ -0,0 +1,21 @@ +from helper import unittest, PillowTestCase + +from PIL import features + + +class TestFeatures(PillowTestCase): + + def test_check_features(self): + for feature in features.modules: + self.assertTrue(features.check_module(feature) in [True, False, None]) + for feature in features.codecs: + self.assertTrue(features.check_codec(feature) in [True, False]) + + def test_supported_features(self): + self.assertTrue(type(features.get_supported_modules()) is list) + self.assertTrue(type(features.get_supported_codecs()) is list) + +if __name__ == '__main__': + unittest.main() + +# End of file From aed878e7d1fc46c34f7b581fc4e96d007e23eda3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 15 May 2015 07:42:22 +1000 Subject: [PATCH 0255/1037] Adjusted selftest feature label to be in sync with setup --- selftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selftest.py b/selftest.py index 779e83c90..4ffae3a16 100644 --- a/selftest.py +++ b/selftest.py @@ -194,7 +194,7 @@ if __name__ == "__main__": print("***", feature, "support not installed") for name, feature in [ ("jpg", "JPEG"), - ("jpg_2000", "JPEG 2000"), + ("jpg_2000", "OPENJPEG (JPEG2000)"), ("zlib", "ZLIB (PNG/ZIP)"), ("libtiff", "LIBTIFF") ]: From 05987c8d60171f02fbbcea069b3fe22ad43153a5 Mon Sep 17 00:00:00 2001 From: hugovk Date: Fri, 15 May 2015 11:42:36 +0300 Subject: [PATCH 0256/1037] Test unsupported features --- Tests/test_features.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Tests/test_features.py b/Tests/test_features.py index 01c5d6495..8da22088e 100644 --- a/Tests/test_features.py +++ b/Tests/test_features.py @@ -7,7 +7,8 @@ class TestFeatures(PillowTestCase): def test_check_features(self): for feature in features.modules: - self.assertTrue(features.check_module(feature) in [True, False, None]) + self.assertTrue( + features.check_module(feature) in [True, False, None]) for feature in features.codecs: self.assertTrue(features.check_codec(feature) in [True, False]) @@ -15,6 +16,19 @@ class TestFeatures(PillowTestCase): self.assertTrue(type(features.get_supported_modules()) is list) self.assertTrue(type(features.get_supported_codecs()) is list) + def test_unsupported_codec(self): + # Arrange + codec = "unsupported_codec" + # Act / Assert + self.assertRaises(ValueError, lambda: features.check_codec(codec)) + + def test_unsupported_module(self): + # Arrange + module = "unsupported_module" + # Act / Assert + self.assertRaises(ValueError, lambda: features.check_module(module)) + + if __name__ == '__main__': unittest.main() From 9fd69fa7179d5891a5e7e31314759de46e1dedb9 Mon Sep 17 00:00:00 2001 From: Hugo Date: Mon, 18 May 2015 11:29:32 +0300 Subject: [PATCH 0257/1037] Add Python 3.4 --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 8d6ea7067..80f7edef4 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33 +envlist = py26, py27, py32, py33, py34 [testenv] commands = From 74803dfd736c8e6853da800298146f70301c9bc8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 19 May 2015 13:01:03 +1000 Subject: [PATCH 0258/1037] Update WebP from 0.4.1 to 0.4.3 --- depends/install_webp.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/depends/install_webp.sh b/depends/install_webp.sh index 7810159cc..97edf2bcf 100755 --- a/depends/install_webp.sh +++ b/depends/install_webp.sh @@ -1,14 +1,14 @@ #!/bin/bash # install webp -if [ ! -f libwebp-0.4.1.tar.gz ]; then - wget 'http://downloads.webmproject.org/releases/webp/libwebp-0.4.1.tar.gz' +if [ ! -f libwebp-0.4.3.tar.gz ]; then + wget 'http://downloads.webmproject.org/releases/webp/libwebp-0.4.3.tar.gz' fi -rm -r libwebp-0.4.1 -tar -xvzf libwebp-0.4.1.tar.gz +rm -r libwebp-0.4.3 +tar -xvzf libwebp-0.4.3.tar.gz -pushd libwebp-0.4.1 +pushd libwebp-0.4.3 ./configure --prefix=/usr --enable-libwebpmux --enable-libwebpdemux && make -j4 && sudo make -j4 install From 5a8c63249e5df180388573b4eedd826095512f37 Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 19 May 2015 08:01:00 +0300 Subject: [PATCH 0259/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 66d448d2f..7337f1d8c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Update WebP from 0.4.1 to 0.4.3 #1235 + [radarhere] + - Release GIL during image load (decode) #1224 [lkesteloot] From cb4172675ac8bc36404cd370a2ffd36f921ff393 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 20 May 2015 13:28:48 +1000 Subject: [PATCH 0260/1037] Removed description for missing scripts [CI skip] --- Scripts/README.rst | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/Scripts/README.rst b/Scripts/README.rst index 1b26ea68c..f86a9a8b6 100644 --- a/Scripts/README.rst +++ b/Scripts/README.rst @@ -63,20 +63,3 @@ explode.py -------------------------------------------------------------------- Split a sequence file into individual frames. - -image2py.py --------------------------------------------------------------------- - -Convert an image to a Python module containing an IMAGE variable. -Note that the module using the module must include JPEG and ZIP -decoders, unless the -u option is used. - -olesummary.py --------------------------------------------------------------------- - -Uses the OleFileIO module to dump the summary information from an OLE -structured storage file. This works with most OLE files, including -Word documents, FlashPix images, etc. - -Note that datetime fields currently show the number of seconds since -January 1st, 1601. From e317a729f31c83c5d0d43e517507774757a5686f Mon Sep 17 00:00:00 2001 From: Justin Wilson Date: Fri, 22 May 2015 16:37:50 -0600 Subject: [PATCH 0261/1037] Setting transparency value to 0 when the tRNS contains only null byte(s) --- PIL/PngImagePlugin.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index 398a01f33..4b677b7cb 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -71,6 +71,7 @@ _MODES = { _simple_palette = re.compile(b'^\xff+\x00\xff*$') +_null_palette = re.compile(b'^\x00*$') # Maximum decompressed size for a iTXt or zTXt chunk. # Eliminates decompression bombs where compressed chunks can expand 1000x @@ -350,6 +351,8 @@ class PngStream(ChunkStream): i = s.find(b"\0") if i >= 0: self.im_info["transparency"] = i + elif _null_palette.match(s): + self.im_info["transparency"] = 0 else: self.im_info["transparency"] = s elif self.im_mode == "L": From 4e2de9d9edb633da2607bfc59e826a2d2e726ef2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 23 May 2015 20:28:41 +1000 Subject: [PATCH 0262/1037] Added missing future print imports and removed unnecessary imports --- PIL/ImageFont.py | 2 -- PIL/PSDraw.py | 2 -- PIL/PcxImagePlugin.py | 4 +++- Scripts/createfontdatachunk.py | 1 + Tests/import_all.py | 1 + Tests/make_hash.py | 2 ++ Tests/test_file_libtiff.py | 1 + Tests/test_file_tiff.py | 1 + Tests/test_format_hsv.py | 1 + Tests/test_imagemath.py | 1 + Tests/test_numpy.py | 1 + Tests/threaded_save.py | 1 + Tests/versions.py | 1 + mp_compile.py | 1 + 14 files changed, 15 insertions(+), 5 deletions(-) diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index 1e5a27f7b..dfcbbeaca 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -25,8 +25,6 @@ # See the README file for information on usage and redistribution. # -from __future__ import print_function - from PIL import Image from PIL._util import isDirectory, isPath import os diff --git a/PIL/PSDraw.py b/PIL/PSDraw.py index 6187e40ad..0f4495fe3 100644 --- a/PIL/PSDraw.py +++ b/PIL/PSDraw.py @@ -15,8 +15,6 @@ # See the README file for information on usage and redistribution. # -from __future__ import print_function - from PIL import EpsImagePlugin diff --git a/PIL/PcxImagePlugin.py b/PIL/PcxImagePlugin.py index 8eac6a594..40fafa0dc 100644 --- a/PIL/PcxImagePlugin.py +++ b/PIL/PcxImagePlugin.py @@ -25,7 +25,7 @@ # See the README file for information on usage and redistribution. # -__version__ = "0.6" +from __future__ import print_function from PIL import Image, ImageFile, ImagePalette, _binary @@ -33,6 +33,8 @@ i8 = _binary.i8 i16 = _binary.i16le o8 = _binary.o8 +__version__ = "0.6" + def _accept(prefix): return i8(prefix[0]) == 10 and i8(prefix[1]) in [0, 2, 3, 5] diff --git a/Scripts/createfontdatachunk.py b/Scripts/createfontdatachunk.py index 0c860701a..b9514eb6e 100644 --- a/Scripts/createfontdatachunk.py +++ b/Scripts/createfontdatachunk.py @@ -1,3 +1,4 @@ +from __future__ import print_function import base64 import os import sys diff --git a/Tests/import_all.py b/Tests/import_all.py index d8299d969..88a102b64 100644 --- a/Tests/import_all.py +++ b/Tests/import_all.py @@ -1,3 +1,4 @@ +from __future__ import print_function import sys sys.path.insert(0, ".") diff --git a/Tests/make_hash.py b/Tests/make_hash.py index 88bb2994b..a92886df9 100644 --- a/Tests/make_hash.py +++ b/Tests/make_hash.py @@ -1,5 +1,7 @@ # brute-force search for access descriptor hash table +from __future__ import print_function + modes = [ "1", "L", "LA", diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 4f798675b..896038b9d 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -1,3 +1,4 @@ +from __future__ import print_function from helper import unittest, PillowTestCase, hopper, py3 import os diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index b885780d6..c51f46a04 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -1,3 +1,4 @@ +from __future__ import print_function from helper import unittest, PillowTestCase, hopper, py3 from PIL import Image, TiffImagePlugin diff --git a/Tests/test_format_hsv.py b/Tests/test_format_hsv.py index d9baf91bf..dd4413f41 100644 --- a/Tests/test_format_hsv.py +++ b/Tests/test_format_hsv.py @@ -1,3 +1,4 @@ +from __future__ import print_function from helper import unittest, PillowTestCase, hopper from PIL import Image diff --git a/Tests/test_imagemath.py b/Tests/test_imagemath.py index 5d87c0229..4ca4e2c6c 100644 --- a/Tests/test_imagemath.py +++ b/Tests/test_imagemath.py @@ -1,3 +1,4 @@ +from __future__ import print_function from helper import unittest, PillowTestCase from PIL import Image diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py index f010e0df5..19b9a2014 100644 --- a/Tests/test_numpy.py +++ b/Tests/test_numpy.py @@ -1,3 +1,4 @@ +from __future__ import print_function from helper import unittest, PillowTestCase, hopper from PIL import Image diff --git a/Tests/threaded_save.py b/Tests/threaded_save.py index 3bcbdd0b0..ba8b17dbc 100644 --- a/Tests/threaded_save.py +++ b/Tests/threaded_save.py @@ -1,3 +1,4 @@ +from __future__ import print_function from PIL import Image import io diff --git a/Tests/versions.py b/Tests/versions.py index e367ae46a..89be1d7c8 100644 --- a/Tests/versions.py +++ b/Tests/versions.py @@ -1,3 +1,4 @@ +from __future__ import print_function from PIL import Image diff --git a/mp_compile.py b/mp_compile.py index 892452558..a930f4245 100644 --- a/mp_compile.py +++ b/mp_compile.py @@ -1,6 +1,7 @@ # A monkey patch of the base distutils.ccompiler to use parallel builds # Tested on 2.7, looks to be identical to 3.3. +from __future__ import print_function from multiprocessing import Pool, cpu_count from distutils.ccompiler import CCompiler import os From 3c7e37d2d71fdf4f6ab249b5777c655a27980204 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 27 May 2015 00:07:21 +1000 Subject: [PATCH 0263/1037] Replaced old-style classes --- PIL/ContainerIO.py | 2 +- PIL/EpsImagePlugin.py | 4 ++-- PIL/FontFile.py | 2 +- PIL/GifImagePlugin.py | 2 +- PIL/GimpGradientFile.py | 2 +- PIL/GimpPaletteFile.py | 2 +- PIL/IcnsImagePlugin.py | 2 +- PIL/IcoImagePlugin.py | 2 +- PIL/Image.py | 10 +++++----- PIL/ImageCms.py | 2 +- PIL/ImageDraw.py | 2 +- PIL/ImageDraw2.py | 8 ++++---- PIL/ImageEnhance.py | 2 +- PIL/ImageFile.py | 2 +- PIL/ImageFont.py | 8 ++++---- PIL/ImageMath.py | 2 +- PIL/ImageMode.py | 2 +- PIL/ImageMorph.py | 4 ++-- PIL/ImagePalette.py | 2 +- PIL/ImagePath.py | 2 +- PIL/ImageSequence.py | 2 +- PIL/ImageShow.py | 2 +- PIL/ImageStat.py | 2 +- PIL/ImageTk.py | 4 ++-- PIL/ImageWin.py | 8 ++++---- PIL/IptcImagePlugin.py | 2 +- PIL/MpegImagePlugin.py | 2 +- PIL/OleFileIO.py | 6 +++--- PIL/PSDraw.py | 2 +- PIL/PaletteFile.py | 2 +- PIL/PdfImagePlugin.py | 2 +- PIL/PngImagePlugin.py | 8 ++++---- PIL/WmfImagePlugin.py | 2 +- Scripts/explode.py | 2 +- Scripts/gifmaker.py | 2 +- Scripts/pildriver.py | 2 +- Tests/test_file_iptc.py | 2 +- Tests/test_imagefileio.py | 2 +- Tests/test_imagefont.py | 2 +- Tests/test_imageops.py | 2 +- 40 files changed, 61 insertions(+), 61 deletions(-) diff --git a/PIL/ContainerIO.py b/PIL/ContainerIO.py index dcedcd6dd..262f2afb9 100644 --- a/PIL/ContainerIO.py +++ b/PIL/ContainerIO.py @@ -19,7 +19,7 @@ # file (for example a TAR file). -class ContainerIO: +class ContainerIO(object): ## # Create file object. diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py index e2e7fa5e9..7b1f4c1ca 100644 --- a/PIL/EpsImagePlugin.py +++ b/PIL/EpsImagePlugin.py @@ -157,7 +157,7 @@ def Ghostscript(tile, size, fp, scale=1): return im -class PSFile: +class PSFile(object): """ Wrapper for bytesio object that treats either CR or LF as end of line. """ @@ -365,7 +365,7 @@ def _save(im, fp, filename, eps=1): else: raise ValueError("image mode is not supported") - class NoCloseStream: + class NoCloseStream(object): def __init__(self, fp): self.fp = fp diff --git a/PIL/FontFile.py b/PIL/FontFile.py index 5cf688256..db8e6bec1 100644 --- a/PIL/FontFile.py +++ b/PIL/FontFile.py @@ -31,7 +31,7 @@ def puti16(fp, values): ## # Base class for raster font file handlers. -class FontFile: +class FontFile(object): bitmap = None diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 8db42e8e8..d2b2f7fee 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -496,7 +496,7 @@ def getdata(im, offset=(0, 0), **params): The first string is a local image header, the rest contains encoded image data.""" - class Collector: + class Collector(object): data = [] def write(self, data): diff --git a/PIL/GimpGradientFile.py b/PIL/GimpGradientFile.py index 696f425f1..45af573bb 100644 --- a/PIL/GimpGradientFile.py +++ b/PIL/GimpGradientFile.py @@ -58,7 +58,7 @@ def sphere_decreasing(middle, pos): SEGMENTS = [linear, curved, sine, sphere_increasing, sphere_decreasing] -class GradientFile: +class GradientFile(object): gradient = None diff --git a/PIL/GimpPaletteFile.py b/PIL/GimpPaletteFile.py index a066c80cc..4bf3ca36a 100644 --- a/PIL/GimpPaletteFile.py +++ b/PIL/GimpPaletteFile.py @@ -21,7 +21,7 @@ from PIL._binary import o8 ## # File handler for GIMP's palette format. -class GimpPaletteFile: +class GimpPaletteFile(object): rawmode = "RGB" diff --git a/PIL/IcnsImagePlugin.py b/PIL/IcnsImagePlugin.py index 1bb1066d8..e0b130e81 100644 --- a/PIL/IcnsImagePlugin.py +++ b/PIL/IcnsImagePlugin.py @@ -130,7 +130,7 @@ def read_png_or_jpeg2000(fobj, start_length, size): raise ValueError('Unsupported icon subimage format') -class IcnsFile: +class IcnsFile(object): SIZES = { (512, 512, 2): [ diff --git a/PIL/IcoImagePlugin.py b/PIL/IcoImagePlugin.py index c4e24d99c..8a2144463 100644 --- a/PIL/IcoImagePlugin.py +++ b/PIL/IcoImagePlugin.py @@ -79,7 +79,7 @@ def _accept(prefix): return prefix[:4] == _MAGIC -class IcoFile: +class IcoFile(object): def __init__(self, buf): """ Parse image from file-like object containing ico file data diff --git a/PIL/Image.py b/PIL/Image.py index 37428ec30..274e7ee0e 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -35,7 +35,7 @@ class DecompressionBombWarning(RuntimeWarning): pass -class _imaging_not_installed: +class _imaging_not_installed(object): # module placeholder def __getattr__(self, id): raise ImportError("The _imaging C module is not installed") @@ -443,7 +443,7 @@ def coerce_e(value): return value if isinstance(value, _E) else _E(value) -class _E: +class _E(object): def __init__(self, data): self.data = data @@ -478,7 +478,7 @@ def _getscaleoffset(expr): # -------------------------------------------------------------------- # Implementation wrapper -class Image: +class Image(object): """ This class represents an image object. To create :py:class:`~PIL.Image.Image` objects, use the appropriate factory @@ -1975,12 +1975,12 @@ class _ImageCrop(Image): # -------------------------------------------------------------------- # Abstract handlers. -class ImagePointHandler: +class ImagePointHandler(object): # used as a mixin by point transforms (for use with im.point) pass -class ImageTransformHandler: +class ImageTransformHandler(object): # used as a mixin by geometry transforms (for use with im.transform) pass diff --git a/PIL/ImageCms.py b/PIL/ImageCms.py index ed219f7ba..6cd2f5d2d 100644 --- a/PIL/ImageCms.py +++ b/PIL/ImageCms.py @@ -147,7 +147,7 @@ for flag in FLAGS.values(): ## # Profile. -class ImageCmsProfile: +class ImageCmsProfile(object): def __init__(self, profile): """ diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py index a2a75d1c6..1fc5b4d61 100644 --- a/PIL/ImageDraw.py +++ b/PIL/ImageDraw.py @@ -47,7 +47,7 @@ except ImportError: # Application code should use the Draw factory, instead of # directly. -class ImageDraw: +class ImageDraw(object): ## # Create a drawing instance. diff --git a/PIL/ImageDraw2.py b/PIL/ImageDraw2.py index c967a200f..62ee11630 100644 --- a/PIL/ImageDraw2.py +++ b/PIL/ImageDraw2.py @@ -19,25 +19,25 @@ from PIL import Image, ImageColor, ImageDraw, ImageFont, ImagePath -class Pen: +class Pen(object): def __init__(self, color, width=1, opacity=255): self.color = ImageColor.getrgb(color) self.width = width -class Brush: +class Brush(object): def __init__(self, color, opacity=255): self.color = ImageColor.getrgb(color) -class Font: +class Font(object): def __init__(self, color, file, size=12): # FIXME: add support for bitmap fonts self.color = ImageColor.getrgb(color) self.font = ImageFont.truetype(file, size) -class Draw: +class Draw(object): def __init__(self, image, size=None, color=None): if not hasattr(image, "im"): diff --git a/PIL/ImageEnhance.py b/PIL/ImageEnhance.py index 8c0f166f3..fbacbee8f 100644 --- a/PIL/ImageEnhance.py +++ b/PIL/ImageEnhance.py @@ -21,7 +21,7 @@ from PIL import Image, ImageFilter, ImageStat -class _Enhance: +class _Enhance(object): def enhance(self, factor): """ diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index 01c3e0303..b1d261166 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -311,7 +311,7 @@ class StubImageFile(ImageFile): ) -class Parser: +class Parser(object): """ Incremental image parser. This class implements the standard feed/close consumer interface. diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index 1e5a27f7b..58889b6e5 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -38,7 +38,7 @@ except ImportError: warnings = None -class _imagingft_not_installed: +class _imagingft_not_installed(object): # module placeholder def __getattr__(self, id): raise ImportError("The _imagingft C module is not installed") @@ -64,7 +64,7 @@ except ImportError: # -------------------------------------------------------------------- -class ImageFont: +class ImageFont(object): "PIL font wrapper" def _load_pilfont(self, filename): @@ -120,7 +120,7 @@ class ImageFont: # Wrapper for FreeType fonts. Application code should use the # truetype factory function to create font objects. -class FreeTypeFont: +class FreeTypeFont(object): "FreeType font wrapper (requires _imagingft service)" def __init__(self, font=None, size=10, index=0, encoding="", file=None): @@ -193,7 +193,7 @@ class FreeTypeFont: # Image.ROTATE_90, Image.ROTATE_180, or Image.ROTATE_270. -class TransposedFont: +class TransposedFont(object): "Wrapper for writing rotated or mirrored text" def __init__(self, font, orientation=None): diff --git a/PIL/ImageMath.py b/PIL/ImageMath.py index 4dcc5125c..f92d5001f 100644 --- a/PIL/ImageMath.py +++ b/PIL/ImageMath.py @@ -31,7 +31,7 @@ def _isconstant(v): return isinstance(v, int) or isinstance(v, float) -class _Operand: +class _Operand(object): # wraps an image operand, providing standard operators def __init__(self, im): diff --git a/PIL/ImageMode.py b/PIL/ImageMode.py index 295069108..d8960017b 100644 --- a/PIL/ImageMode.py +++ b/PIL/ImageMode.py @@ -20,7 +20,7 @@ _modes = {} ## # Wrapper for mode strings. -class ModeDescriptor: +class ModeDescriptor(object): def __init__(self, mode, bands, basemode, basetype): self.mode = mode diff --git a/PIL/ImageMorph.py b/PIL/ImageMorph.py index 996eacb7d..6f92e9e67 100644 --- a/PIL/ImageMorph.py +++ b/PIL/ImageMorph.py @@ -12,7 +12,7 @@ import re LUT_SIZE = 1 << 9 -class LutBuilder: +class LutBuilder(object): """A class for building a MorphLut from a descriptive language The input patterns is a list of a strings sequences like these:: @@ -176,7 +176,7 @@ class LutBuilder: return self.lut -class MorphOp: +class MorphOp(object): """A class for binary morphological operators""" def __init__(self, diff --git a/PIL/ImagePalette.py b/PIL/ImagePalette.py index c6c05d162..b2f51dd06 100644 --- a/PIL/ImagePalette.py +++ b/PIL/ImagePalette.py @@ -21,7 +21,7 @@ import warnings from PIL import ImageColor -class ImagePalette: +class ImagePalette(object): "Color palette for palette mapped images" def __init__(self, mode="RGB", palette=None, size=0): diff --git a/PIL/ImagePath.py b/PIL/ImagePath.py index 656d5ce61..f23d01430 100644 --- a/PIL/ImagePath.py +++ b/PIL/ImagePath.py @@ -20,7 +20,7 @@ from PIL import Image # the Python class below is overridden by the C implementation. -class Path: +class Path(object): def __init__(self, xy): pass diff --git a/PIL/ImageSequence.py b/PIL/ImageSequence.py index dd01e2902..256bcbedb 100644 --- a/PIL/ImageSequence.py +++ b/PIL/ImageSequence.py @@ -16,7 +16,7 @@ ## -class Iterator: +class Iterator(object): """ This class implements an iterator object that can be used to loop over an image sequence. diff --git a/PIL/ImageShow.py b/PIL/ImageShow.py index 9527dbf97..51417c30b 100644 --- a/PIL/ImageShow.py +++ b/PIL/ImageShow.py @@ -56,7 +56,7 @@ def show(image, title=None, **options): ## # Base class for viewers. -class Viewer: +class Viewer(object): # main api diff --git a/PIL/ImageStat.py b/PIL/ImageStat.py index 37e7515d4..f3c138b3a 100644 --- a/PIL/ImageStat.py +++ b/PIL/ImageStat.py @@ -26,7 +26,7 @@ import operator import functools -class Stat: +class Stat(object): def __init__(self, image_or_list, mask=None): try: diff --git a/PIL/ImageTk.py b/PIL/ImageTk.py index 5fb5ecff3..68d388e74 100644 --- a/PIL/ImageTk.py +++ b/PIL/ImageTk.py @@ -56,7 +56,7 @@ def _pilbitmap_check(): # -------------------------------------------------------------------- # PhotoImage -class PhotoImage: +class PhotoImage(object): """ A Tkinter-compatible photo image. This can be used everywhere Tkinter expects an image object. If the image is an RGBA @@ -190,7 +190,7 @@ class PhotoImage: # BitmapImage -class BitmapImage: +class BitmapImage(object): """ A Tkinter-compatible bitmap image. This can be used everywhere Tkinter diff --git a/PIL/ImageWin.py b/PIL/ImageWin.py index 300d118c9..bcb54bc3e 100644 --- a/PIL/ImageWin.py +++ b/PIL/ImageWin.py @@ -21,7 +21,7 @@ import warnings from PIL import Image -class HDC: +class HDC(object): """ Wraps an HDC integer. The resulting object can be passed to the :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` @@ -34,7 +34,7 @@ class HDC: return self.dc -class HWND: +class HWND(object): """ Wraps an HWND integer. The resulting object can be passed to the :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` @@ -47,7 +47,7 @@ class HWND: return self.wnd -class Dib: +class Dib(object): """ A Windows bitmap with the given mode and size. The mode can be one of "1", "L", "P", or "RGB". @@ -206,7 +206,7 @@ class Dib: ## # Create a Window with the given title size. -class Window: +class Window(object): def __init__(self, title="PIL", width=None, height=None): self.hwnd = Image.core.createwindow( diff --git a/PIL/IptcImagePlugin.py b/PIL/IptcImagePlugin.py index aa0193894..47c7e1936 100644 --- a/PIL/IptcImagePlugin.py +++ b/PIL/IptcImagePlugin.py @@ -251,7 +251,7 @@ def getiptcinfo(im): return None # no properties # create an IptcImagePlugin object without initializing it - class FakeImage: + class FakeImage(object): pass im = FakeImage() im.__class__ = IptcImageFile diff --git a/PIL/MpegImagePlugin.py b/PIL/MpegImagePlugin.py index 9aca58f16..ff7c0dce4 100644 --- a/PIL/MpegImagePlugin.py +++ b/PIL/MpegImagePlugin.py @@ -22,7 +22,7 @@ from PIL._binary import i8 # # Bitstream parser -class BitStream: +class BitStream(object): def __init__(self, fp): self.fp = fp diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index c804dd454..d3c1cd9a0 100755 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -473,7 +473,7 @@ def filetime2datetime(filetime): #=== CLASSES ================================================================== -class OleMetadata: +class OleMetadata(object): """ class to parse and store metadata from standard properties of OLE files. @@ -757,7 +757,7 @@ class _OleStream(io.BytesIO): #--- _OleDirectoryEntry ------------------------------------------------------- -class _OleDirectoryEntry: +class _OleDirectoryEntry(object): """ OLE2 Directory Entry @@ -1007,7 +1007,7 @@ class _OleDirectoryEntry: #--- OleFileIO ---------------------------------------------------------------- -class OleFileIO: +class OleFileIO(object): """ OLE container object diff --git a/PIL/PSDraw.py b/PIL/PSDraw.py index 6187e40ad..66e3d7982 100644 --- a/PIL/PSDraw.py +++ b/PIL/PSDraw.py @@ -23,7 +23,7 @@ from PIL import EpsImagePlugin ## # Simple Postscript graphics interface. -class PSDraw: +class PSDraw(object): """ Sets up printing to the given file. If **file** is omitted, :py:attr:`sys.stdout` is assumed. diff --git a/PIL/PaletteFile.py b/PIL/PaletteFile.py index 37ba4cbff..ef50feefd 100644 --- a/PIL/PaletteFile.py +++ b/PIL/PaletteFile.py @@ -19,7 +19,7 @@ from PIL._binary import o8 ## # File handler for Teragon-style palette files. -class PaletteFile: +class PaletteFile(object): rawmode = "RGB" diff --git a/PIL/PdfImagePlugin.py b/PIL/PdfImagePlugin.py index 5113f099e..1d8c2ff93 100644 --- a/PIL/PdfImagePlugin.py +++ b/PIL/PdfImagePlugin.py @@ -63,7 +63,7 @@ def _save(im, fp, filename): xref = [0]*(5+1) # placeholders - class TextWriter: + class TextWriter(object): def __init__(self, fp): self.fp = fp diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index 398a01f33..53d38eecd 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -90,7 +90,7 @@ def _safe_zlib_decompress(s): # -------------------------------------------------------------------- # Support classes. Suitable for PNG and related formats like MNG etc. -class ChunkStream: +class ChunkStream(object): def __init__(self, fp): @@ -183,7 +183,7 @@ class iTXt(str): return self -class PngInfo: +class PngInfo(object): """ PNG chunk container (for use with save(pnginfo=)) @@ -620,7 +620,7 @@ def putchunk(fp, cid, *data): fp.write(o16(hi) + o16(lo)) -class _idat: +class _idat(object): # wrap output from the encoder in IDAT chunks def __init__(self, fp, chunk): @@ -771,7 +771,7 @@ def _save(im, fp, filename, chunk=putchunk, check=0): def getchunks(im, **params): """Return a list of PNG chunks representing this image.""" - class collector: + class collector(object): data = [] def write(self, data): diff --git a/PIL/WmfImagePlugin.py b/PIL/WmfImagePlugin.py index 6146c1560..bdbbc72f0 100644 --- a/PIL/WmfImagePlugin.py +++ b/PIL/WmfImagePlugin.py @@ -37,7 +37,7 @@ def register_handler(handler): if hasattr(Image.core, "drawwmf"): # install default handler (windows only) - class WmfHandler: + class WmfHandler(object): def open(self, im): im.mode = "RGB" diff --git a/Scripts/explode.py b/Scripts/explode.py index 0460fa020..53436100b 100644 --- a/Scripts/explode.py +++ b/Scripts/explode.py @@ -13,7 +13,7 @@ import os import sys -class Interval: +class Interval(object): def __init__(self, interval="0"): diff --git a/Scripts/gifmaker.py b/Scripts/gifmaker.py index 8777f74f6..5d1781499 100644 --- a/Scripts/gifmaker.py +++ b/Scripts/gifmaker.py @@ -50,7 +50,7 @@ from PIL.GifImagePlugin import getheader, getdata # sequence iterator -class image_sequence: +class image_sequence(object): def __init__(self, im): self.im = im diff --git a/Scripts/pildriver.py b/Scripts/pildriver.py index bb01004f5..32989ccdf 100644 --- a/Scripts/pildriver.py +++ b/Scripts/pildriver.py @@ -53,7 +53,7 @@ from __future__ import print_function from PIL import Image -class PILDriver: +class PILDriver(object): verbose = 0 diff --git a/Tests/test_file_iptc.py b/Tests/test_file_iptc.py index 14eb135aa..84fa873be 100644 --- a/Tests/test_file_iptc.py +++ b/Tests/test_file_iptc.py @@ -11,7 +11,7 @@ class TestFileIptc(PillowTestCase): def dummy_IptcImagePlugin(self): # Create an IptcImagePlugin object without initializing it - class FakeImage: + class FakeImage(object): pass im = FakeImage() im.__class__ = IptcImagePlugin.IptcImageFile diff --git a/Tests/test_imagefileio.py b/Tests/test_imagefileio.py index f18474403..b06178437 100644 --- a/Tests/test_imagefileio.py +++ b/Tests/test_imagefileio.py @@ -8,7 +8,7 @@ class TestImageFileIo(PillowTestCase): def test_fileio(self): - class DumbFile: + class DumbFile(object): def __init__(self, data): self.data = data diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 49aa9c8b2..88858c717 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -15,7 +15,7 @@ try: from PIL import ImageFont ImageFont.core.getfont # check if freetype is available - class SimplePatcher(): + class SimplePatcher(object): def __init__(self, parent_obj, attr_name, value): self._parent_obj = parent_obj self._attr_name = attr_name diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 0ffb14bfe..396f0da6c 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -5,7 +5,7 @@ from PIL import ImageOps class TestImageOps(PillowTestCase): - class Deformer: + class Deformer(object): def getmesh(self, im): x, y = im.size return [((0, 0, x, y), (0, 0, x, 0, x, y, y, 0))] From 54c3224b80d393ce3a8f7966d088ff04ca470c3d Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 27 May 2015 14:41:06 +0300 Subject: [PATCH 0264/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 7337f1d8c..f4f9866f6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,12 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Separated out feature checking from selftest #1233 + [radarhere] + +- Style/health fixes + [radarhere] + - Update WebP from 0.4.1 to 0.4.3 #1235 [radarhere] From e33b5c635551f7a5434d06b4f2396e42f468f64a Mon Sep 17 00:00:00 2001 From: Justin Wilson Date: Wed, 27 May 2015 09:45:27 -0600 Subject: [PATCH 0265/1037] Added test to check that png with null tRNS value defaults to a zero transparency value. --- Tests/images/tRNS_null_1x1.png | Bin 0 -> 96 bytes Tests/test_file_png.py | 7 +++++++ 2 files changed, 7 insertions(+) create mode 100644 Tests/images/tRNS_null_1x1.png diff --git a/Tests/images/tRNS_null_1x1.png b/Tests/images/tRNS_null_1x1.png new file mode 100644 index 0000000000000000000000000000000000000000..976eae9390a4816aeee265e5014468cd9a6d7464 GIT binary patch literal 96 zcmeAS@N?(olHy`uVBq!ia0vp^j3CU&3?x-=hn)ga%mF?juK#@*VoWXSL2@NQe!&b5 n&u*jvIb5DDjv*Cul9PaJHU?%h^O_Yv7K5j&pUXO@geEQkXtESH literal 0 HcmV?d00001 diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index b3169ed25..f438e24cc 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -354,6 +354,13 @@ class TestFilePng(PillowTestCase): self.assert_image_equal(im2.convert('RGBA'), im.convert('RGBA')) + def test_trns_null(self): + # Check reading images with null tRNS value, issue #1239 + test_file = "Tests/images/tRNS_null_1x1.png" + im = Image.open(test_file) + + self.assertEqual(im.info["transparency"], 0) + def test_save_icc_profile_none(self): # check saving files with an ICC profile set to None (omit profile) in_file = "Tests/images/icc_profile_none.png" From 96b96e565429b818c5136cde31a9faefe4b83547 Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 27 May 2015 22:40:24 +0300 Subject: [PATCH 0266/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index f4f9866f6..2bd4ff0a0 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Setting transparency value to 0 when the tRNS contains only null byte(s) #1239 + [juztin] + - Separated out feature checking from selftest #1233 [radarhere] From 0999ec95a0cb400974e7650805850e946425ca20 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 28 May 2015 17:20:33 +1000 Subject: [PATCH 0267/1037] Fixed typo in ImageEnhance.py --- PIL/ImageEnhance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/ImageEnhance.py b/PIL/ImageEnhance.py index fbacbee8f..56b5c0199 100644 --- a/PIL/ImageEnhance.py +++ b/PIL/ImageEnhance.py @@ -73,7 +73,7 @@ class Contrast(_Enhance): class Brightness(_Enhance): """Adjust image brightness. - This class can be used to control the brighntess of an image. An + This class can be used to control the brightness of an image. An enhancement factor of 0.0 gives a black image. A factor of 1.0 gives the original image. """ From b213f63c591df7e6512b0b92b54c589a2a0141a9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 10 May 2015 22:36:23 +1000 Subject: [PATCH 0268/1037] Removed pre-Python 2.3 workaround --- PIL/OleFileIO.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index d787e59ed..df95e2d83 100755 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -302,17 +302,10 @@ else: #[PL] These workarounds were inspired from the Path module # (see http://www.jorendorff.com/articles/python/path/) -#TODO: test with old Python versions - -# Pre-2.3 workaround for basestring. try: basestring except NameError: - try: - # is Unicode supported (Python >2.0 or >1.6 ?) - basestring = (str, unicode) - except NameError: - basestring = str + basestring = str #[PL] Experimental setting: if True, OLE filenames will be kept in Unicode # if False (default PIL behaviour), all filenames are converted to Latin-1. From f55f2d13cb3023426b5d24dd5da506a40b476bd2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 11 May 2015 00:00:36 +1000 Subject: [PATCH 0269/1037] Various health fixes --- PIL/OleFileIO.py | 196 +++++++++++++++++++++++------------------------ 1 file changed, 97 insertions(+), 99 deletions(-) diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index df95e2d83..98c126761 100755 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -399,12 +399,12 @@ WORD_CLSID = "00020900-0000-0000-C000-000000000046" #TODO: check Excel, PPT, ... #[PL]: Defect levels to classify parsing errors - see OleFileIO._raise_defect() -DEFECT_UNSURE = 10 # a case which looks weird, but not sure it's a defect -DEFECT_POTENTIAL = 20 # a potential defect -DEFECT_INCORRECT = 30 # an error according to specifications, but parsing - # can go on -DEFECT_FATAL = 40 # an error which cannot be ignored, parsing is - # impossible +DEFECT_UNSURE = 10 # a case which looks weird, but not sure it's a defect +DEFECT_POTENTIAL = 20 # a potential defect +DEFECT_INCORRECT = 30 # an error according to specifications, but parsing + # can go on +DEFECT_FATAL = 40 # an error which cannot be ignored, parsing is + # impossible # Minimal size of an empty OLE file, with 512-bytes sectors = 1536 bytes # (this is used in isOleFile and OleFile.open) @@ -504,20 +504,20 @@ def _clsid(clsid): def filetime2datetime(filetime): - """ - convert FILETIME (64 bits int) to Python datetime.datetime - """ - # TODO: manage exception when microseconds is too large - # inspired from http://code.activestate.com/recipes/511425-filetime-to-datetime/ - _FILETIME_null_date = datetime.datetime(1601, 1, 1, 0, 0, 0) - #debug('timedelta days=%d' % (filetime//(10*1000000*3600*24))) - return _FILETIME_null_date + datetime.timedelta(microseconds=filetime//10) + """ + convert FILETIME (64 bits int) to Python datetime.datetime + """ + # TODO: manage exception when microseconds is too large + # inspired from http://code.activestate.com/recipes/511425-filetime-to-datetime/ + _FILETIME_null_date = datetime.datetime(1601, 1, 1, 0, 0, 0) + #debug('timedelta days=%d' % (filetime//(10*1000000*3600*24))) + return _FILETIME_null_date + datetime.timedelta(microseconds=filetime//10) #=== CLASSES ================================================================== -class OleMetadata: +class OleMetadata(object): """ class to parse and store metadata from standard properties of OLE files. @@ -803,7 +803,7 @@ class _OleStream(io.BytesIO): #--- _OleDirectoryEntry ------------------------------------------------------- -class _OleDirectoryEntry: +class _OleDirectoryEntry(object): """ OLE2 Directory Entry @@ -1064,7 +1064,7 @@ class _OleDirectoryEntry: #--- OleFileIO ---------------------------------------------------------------- -class OleFileIO: +class OleFileIO(object): """ OLE container object @@ -1935,12 +1935,12 @@ class OleFileIO: nb_sectors = (size + (self.sectorsize-1)) // self.sectorsize debug('nb_sectors = %d' % nb_sectors) for i in range(nb_sectors): -## try: -## self.fp.seek(offset + self.sectorsize * sect) -## except: -## debug('sect=%d, seek=%d' % -## (sect, offset+self.sectorsize*sect)) -## raise IOError('OLE sector index out of range') + # try: + # self.fp.seek(offset + self.sectorsize * sect) + # except: + # debug('sect=%d, seek=%d' % + # (sect, offset+self.sectorsize*sect)) + # raise IOError('OLE sector index out of range') # extract one sector from data, the last one being smaller: if i<(nb_sectors-1): data_sector = data [i*self.sectorsize : (i+1)*self.sectorsize] @@ -2071,7 +2071,7 @@ class OleFileIO: """ #REFERENCE: [MS-OLEPS] https://msdn.microsoft.com/en-us/library/dd942421.aspx # make sure no_conversion is a list, just to simplify code below: - if no_conversion == None: + if no_conversion is None: no_conversion = [] # stream path as a string to report exceptions: streampath = filename @@ -2226,8 +2226,6 @@ class OleFileIO: if __name__ == "__main__": - import sys - # [PL] display quick usage info if launched from command-line if len(sys.argv) <= 1: print('olefile version %s %s - %s' % (__version__, __date__, __author__)) @@ -2247,55 +2245,55 @@ For more information, see http://www.decalage.info/olefile check_streams = False for filename in sys.argv[1:]: -## try: - # OPTIONS: - if filename == '-d': - # option to switch debug mode on: - set_debug_mode(True) - continue - if filename == '-c': - # option to switch check streams mode on: - check_streams = True - continue + # try: + # OPTIONS: + if filename == '-d': + # option to switch debug mode on: + set_debug_mode(True) + continue + if filename == '-c': + # option to switch check streams mode on: + check_streams = True + continue - ole = OleFileIO(filename)#, raise_defects=DEFECT_INCORRECT) - print("-" * 68) - print(filename) - print("-" * 68) - ole.dumpdirectory() + ole = OleFileIO(filename)#, raise_defects=DEFECT_INCORRECT) + print("-" * 68) + print(filename) + print("-" * 68) + ole.dumpdirectory() + for streamname in ole.listdir(): + if streamname[-1][0] == "\005": + print(streamname, ": properties") + props = ole.getproperties(streamname, convert_time=True) + props = sorted(props.items()) + for k, v in props: + #[PL]: avoid to display too large or binary values: + if isinstance(v, (basestring, bytes)): + if len(v) > 50: + v = v[:50] + if isinstance(v, bytes): + # quick and dirty binary check: + for c in (1,2,3,4,5,6,7,11,12,14,15,16,17,18,19,20, + 21,22,23,24,25,26,27,28,29,30,31): + if c in bytearray(v): + v = '(binary data)' + break + print(" ", k, v) + + if check_streams: + # Read all streams to check if there are errors: + print('\nChecking streams...') for streamname in ole.listdir(): - if streamname[-1][0] == "\005": - print(streamname, ": properties") - props = ole.getproperties(streamname, convert_time=True) - props = sorted(props.items()) - for k, v in props: - #[PL]: avoid to display too large or binary values: - if isinstance(v, (basestring, bytes)): - if len(v) > 50: - v = v[:50] - if isinstance(v, bytes): - # quick and dirty binary check: - for c in (1,2,3,4,5,6,7,11,12,14,15,16,17,18,19,20, - 21,22,23,24,25,26,27,28,29,30,31): - if c in bytearray(v): - v = '(binary data)' - break - print(" ", k, v) - - if check_streams: - # Read all streams to check if there are errors: - print('\nChecking streams...') - for streamname in ole.listdir(): - # print name using repr() to convert binary chars to \xNN: - print('-', repr('/'.join(streamname)),'-', end=' ') - st_type = ole.get_type(streamname) - if st_type == STGTY_STREAM: - print('size %d' % ole.get_size(streamname)) - # just try to read stream in memory: - ole.openstream(streamname) - else: - print('NOT a stream : type=%d' % st_type) - print() + # print name using repr() to convert binary chars to \xNN: + print('-', repr('/'.join(streamname)),'-', end=' ') + st_type = ole.get_type(streamname) + if st_type == STGTY_STREAM: + print('size %d' % ole.get_size(streamname)) + # just try to read stream in memory: + ole.openstream(streamname) + else: + print('NOT a stream : type=%d' % st_type) + print() ## for streamname in ole.listdir(): ## # print name using repr() to convert binary chars to \xNN: @@ -2303,34 +2301,34 @@ For more information, see http://www.decalage.info/olefile ## print(ole.getmtime(streamname)) ## print() - print('Modification/Creation times of all directory entries:') - for entry in ole.direntries: - if entry is not None: - print('- %s: mtime=%s ctime=%s' % (entry.name, - entry.getmtime(), entry.getctime())) - print() + print('Modification/Creation times of all directory entries:') + for entry in ole.direntries: + if entry is not None: + print('- %s: mtime=%s ctime=%s' % (entry.name, + entry.getmtime(), entry.getctime())) + print() - # parse and display metadata: - meta = ole.get_metadata() - meta.dump() - print() - #[PL] Test a few new methods: - root = ole.get_rootentry_name() - print('Root entry name: "%s"' % root) - if ole.exists('worddocument'): - print("This is a Word document.") - print("type of stream 'WordDocument':", ole.get_type('worddocument')) - print("size :", ole.get_size('worddocument')) - if ole.exists('macros/vba'): - print("This document may contain VBA macros.") + # parse and display metadata: + meta = ole.get_metadata() + meta.dump() + print() + #[PL] Test a few new methods: + root = ole.get_rootentry_name() + print('Root entry name: "%s"' % root) + if ole.exists('worddocument'): + print("This is a Word document.") + print("type of stream 'WordDocument':", ole.get_type('worddocument')) + print("size :", ole.get_size('worddocument')) + if ole.exists('macros/vba'): + print("This document may contain VBA macros.") - # print parsing issues: - print('\nNon-fatal issues raised during parsing:') - if ole.parsing_issues: - for exctype, msg in ole.parsing_issues: - print('- %s: %s' % (exctype.__name__, msg)) - else: - print('None') + # print parsing issues: + print('\nNon-fatal issues raised during parsing:') + if ole.parsing_issues: + for exctype, msg in ole.parsing_issues: + print('- %s: %s' % (exctype.__name__, msg)) + else: + print('None') ## except IOError as v: ## print("***", "cannot read", file, "-", v) From 1208fe89cd9d04187f1a4ebef2167e577e4eebdd Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 10 May 2015 23:50:06 +1000 Subject: [PATCH 0270/1037] Renamed _raise_defect to avoid protected member warnings --- PIL/OleFileIO.py | 68 ++++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index 98c126761..7449407b9 100755 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -874,17 +874,17 @@ class _OleDirectoryEntry(object): sizeHigh ) = struct.unpack(_OleDirectoryEntry.STRUCT_DIRENTRY, entry) if self.entry_type not in [STGTY_ROOT, STGTY_STORAGE, STGTY_STREAM, STGTY_EMPTY]: - olefile._raise_defect(DEFECT_INCORRECT, 'unhandled OLE storage type') + olefile.raise_defect(DEFECT_INCORRECT, 'unhandled OLE storage type') # only first directory entry can (and should) be root: if self.entry_type == STGTY_ROOT and sid != 0: - olefile._raise_defect(DEFECT_INCORRECT, 'duplicate OLE root entry') + olefile.raise_defect(DEFECT_INCORRECT, 'duplicate OLE root entry') if sid == 0 and self.entry_type != STGTY_ROOT: - olefile._raise_defect(DEFECT_INCORRECT, 'incorrect OLE root entry') + olefile.raise_defect(DEFECT_INCORRECT, 'incorrect OLE root entry') #debug (struct.unpack(fmt_entry, entry[:len_entry])) # name should be at most 31 unicode characters + null character, # so 64 bytes in total (31*2 + 2): if namelength>64: - olefile._raise_defect(DEFECT_INCORRECT, 'incorrect DirEntry name length') + olefile.raise_defect(DEFECT_INCORRECT, 'incorrect DirEntry name length') # if exception not raised, namelength is set to the maximum value: namelength = 64 # only characters without ending null char are kept: @@ -908,7 +908,7 @@ class _OleDirectoryEntry(object): if sizeHigh != 0 and sizeHigh != 0xFFFFFFFF: debug('sectorsize=%d, sizeLow=%d, sizeHigh=%d (%X)' % (olefile.sectorsize, sizeLow, sizeHigh, sizeHigh)) - olefile._raise_defect(DEFECT_UNSURE, 'incorrect OLE stream size') + olefile.raise_defect(DEFECT_UNSURE, 'incorrect OLE stream size') self.size = sizeLow else: self.size = sizeLow + (long(sizeHigh)<<32) @@ -918,7 +918,7 @@ class _OleDirectoryEntry(object): # a storage should have a null size, BUT some implementations such as # Word 8 for Mac seem to allow non-null values => Potential defect: if self.entry_type == STGTY_STORAGE and self.size != 0: - olefile._raise_defect(DEFECT_POTENTIAL, 'OLE storage with size>0') + olefile.raise_defect(DEFECT_POTENTIAL, 'OLE storage with size>0') # check if stream is not already referenced elsewhere: if self.entry_type in (STGTY_ROOT, STGTY_STREAM) and self.size>0: if self.size < olefile.minisectorcutoff \ @@ -970,7 +970,7 @@ class _OleDirectoryEntry(object): return # check if child SID is in the proper range: if child_sid<0 or child_sid>=len(self.olefile.direntries): - self.olefile._raise_defect(DEFECT_FATAL, 'OLE DirEntry index out of range') + self.olefile.raise_defect(DEFECT_FATAL, 'OLE DirEntry index out of range') # get child direntry: child = self.olefile._load_direntry(child_sid) #direntries[child_sid] debug('append_kids: child_sid=%d - %s - sid_left=%d, sid_right=%d, sid_child=%d' @@ -982,7 +982,7 @@ class _OleDirectoryEntry(object): # Check if its name is not already used (case-insensitive): name_lower = child.name.lower() if name_lower in self.kids_dict: - self.olefile._raise_defect(DEFECT_INCORRECT, + self.olefile.raise_defect(DEFECT_INCORRECT, "Duplicate filename in OLE storage") # Then the child_sid _OleDirectoryEntry object is appended to the # kids list and dictionary: @@ -990,7 +990,7 @@ class _OleDirectoryEntry(object): self.kids_dict[name_lower] = child # Check if kid was not already referenced in a storage: if child.used: - self.olefile._raise_defect(DEFECT_INCORRECT, + self.olefile.raise_defect(DEFECT_INCORRECT, 'OLE Entry referenced more than once') child.used = True # Finally walk through right side of the tree: @@ -1135,7 +1135,7 @@ class OleFileIO(object): self.open(filename, write_mode=write_mode) - def _raise_defect(self, defect_level, message, exception_type=IOError): + def raise_defect(self, defect_level, message, exception_type=IOError): """ This method should be called for any defect found during file parsing. It may raise an IOError exception according to the minimal level chosen @@ -1237,7 +1237,7 @@ class OleFileIO(object): header = self.fp.read(512) if len(header) != 512 or header[:8] != MAGIC: - self._raise_defect(DEFECT_FATAL, "not an OLE2 structured storage file") + self.raise_defect(DEFECT_FATAL, "not an OLE2 structured storage file") # [PL] header structure according to AAF specifications: ##Header @@ -1303,39 +1303,39 @@ class OleFileIO(object): if self.Sig != MAGIC: # OLE signature should always be present - self._raise_defect(DEFECT_FATAL, "incorrect OLE signature") + self.raise_defect(DEFECT_FATAL, "incorrect OLE signature") if self.clsid != bytearray(16): # according to AAF specs, CLSID should always be zero - self._raise_defect(DEFECT_INCORRECT, "incorrect CLSID in OLE header") + self.raise_defect(DEFECT_INCORRECT, "incorrect CLSID in OLE header") debug( "MinorVersion = %d" % self.MinorVersion ) debug( "DllVersion = %d" % self.DllVersion ) if self.DllVersion not in [3, 4]: # version 3: usual format, 512 bytes per sector # version 4: large format, 4K per sector - self._raise_defect(DEFECT_INCORRECT, "incorrect DllVersion in OLE header") + self.raise_defect(DEFECT_INCORRECT, "incorrect DllVersion in OLE header") debug( "ByteOrder = %X" % self.ByteOrder ) if self.ByteOrder != 0xFFFE: # For now only common little-endian documents are handled correctly - self._raise_defect(DEFECT_FATAL, "incorrect ByteOrder in OLE header") + self.raise_defect(DEFECT_FATAL, "incorrect ByteOrder in OLE header") # TODO: add big-endian support for documents created on Mac ? # But according to [MS-CFB] ? v20140502, ByteOrder MUST be 0xFFFE. self.SectorSize = 2**self.SectorShift debug( "SectorSize = %d" % self.SectorSize ) if self.SectorSize not in [512, 4096]: - self._raise_defect(DEFECT_INCORRECT, "incorrect SectorSize in OLE header") + self.raise_defect(DEFECT_INCORRECT, "incorrect SectorSize in OLE header") if (self.DllVersion==3 and self.SectorSize!=512) \ or (self.DllVersion==4 and self.SectorSize!=4096): - self._raise_defect(DEFECT_INCORRECT, "SectorSize does not match DllVersion in OLE header") + self.raise_defect(DEFECT_INCORRECT, "SectorSize does not match DllVersion in OLE header") self.MiniSectorSize = 2**self.MiniSectorShift debug( "MiniSectorSize = %d" % self.MiniSectorSize ) if self.MiniSectorSize not in [64]: - self._raise_defect(DEFECT_INCORRECT, "incorrect MiniSectorSize in OLE header") + self.raise_defect(DEFECT_INCORRECT, "incorrect MiniSectorSize in OLE header") if self.Reserved != 0 or self.Reserved1 != 0: - self._raise_defect(DEFECT_INCORRECT, "incorrect OLE header (non-null reserved bytes)") + self.raise_defect(DEFECT_INCORRECT, "incorrect OLE header (non-null reserved bytes)") debug( "csectDir = %d" % self.csectDir ) # Number of directory sectors (only allowed if DllVersion != 3) if self.SectorSize==512 and self.csectDir!=0: - self._raise_defect(DEFECT_INCORRECT, "incorrect csectDir in OLE header") + self.raise_defect(DEFECT_INCORRECT, "incorrect csectDir in OLE header") debug( "csectFat = %d" % self.csectFat ) # csectFat = number of FAT sectors in the file debug( "sectDirStart = %X" % self.sectDirStart ) @@ -1346,7 +1346,7 @@ class OleFileIO(object): # (according to MS-CFB, may be != 0 for applications supporting file # transactions) if self.signature != 0: - self._raise_defect(DEFECT_POTENTIAL, "incorrect OLE header (signature>0)") + self.raise_defect(DEFECT_POTENTIAL, "incorrect OLE header (signature>0)") debug( "MiniSectorCutoff = %d" % self.MiniSectorCutoff ) # MS-CFB: This integer field MUST be set to 0x00001000. This field # specifies the maximum size of a user-defined data stream allocated @@ -1354,7 +1354,7 @@ class OleFileIO(object): # Any user-defined data stream larger than or equal to this cutoff size # must be allocated as normal sectors from the FAT. if self.MiniSectorCutoff != 0x1000: - self._raise_defect(DEFECT_INCORRECT, "incorrect MiniSectorCutoff in OLE header") + self.raise_defect(DEFECT_INCORRECT, "incorrect MiniSectorCutoff in OLE header") debug( "MiniFatStart = %X" % self.MiniFatStart ) debug( "csectMiniFat = %d" % self.csectMiniFat ) debug( "sectDifStart = %X" % self.sectDifStart ) @@ -1422,7 +1422,7 @@ class OleFileIO(object): #TODO: would it be more efficient using a dict or hash values, instead # of a list of long ? if first_sect in used_streams: - self._raise_defect(DEFECT_INCORRECT, 'Stream referenced twice') + self.raise_defect(DEFECT_INCORRECT, 'Stream referenced twice') else: used_streams.append(first_sect) @@ -1566,10 +1566,10 @@ class OleFileIO(object): if self.csectFat <= 109: # there must be at least 109 blocks in header and the rest in # DIFAT, so number of sectors must be >109. - self._raise_defect(DEFECT_INCORRECT, 'incorrect DIFAT, not enough sectors') + self.raise_defect(DEFECT_INCORRECT, 'incorrect DIFAT, not enough sectors') if self.sectDifStart >= self.nb_sect: # initial DIFAT block index must be valid - self._raise_defect(DEFECT_FATAL, 'incorrect DIFAT, first index out of range') + self.raise_defect(DEFECT_FATAL, 'incorrect DIFAT, first index out of range') debug( "DIFAT analysis..." ) # We compute the necessary number of DIFAT sectors : # Number of pointers per DIFAT sector = (sectorsize/4)-1 @@ -1630,7 +1630,7 @@ class OleFileIO(object): (self.minifatsect, self.csectMiniFat, used_size, stream_size, nb_minisectors)) if used_size > stream_size: # This is not really a problem, but may indicate a wrong implementation: - self._raise_defect(DEFECT_INCORRECT, 'OLE MiniStream is larger than MiniFAT') + self.raise_defect(DEFECT_INCORRECT, 'OLE MiniStream is larger than MiniFAT') # In any case, first read stream_size: s = self._open(self.minifatsect, stream_size, force_FAT=True).read() #[PL] Old code replaced by an array: @@ -1666,12 +1666,12 @@ class OleFileIO(object): except: debug('getsect(): sect=%X, seek=%d, filesize=%d' % (sect, self.sectorsize*(sect+1), self._filesize)) - self._raise_defect(DEFECT_FATAL, 'OLE sector index out of range') + self.raise_defect(DEFECT_FATAL, 'OLE sector index out of range') sector = self.fp.read(self.sectorsize) if len(sector) != self.sectorsize: debug('getsect(): sect=%X, read=%d, sectorsize=%d' % (sect, len(sector), self.sectorsize)) - self._raise_defect(DEFECT_FATAL, 'incomplete OLE sector') + self.raise_defect(DEFECT_FATAL, 'incomplete OLE sector') return sector @@ -1693,7 +1693,7 @@ class OleFileIO(object): except: debug('write_sect(): sect=%X, seek=%d, filesize=%d' % (sect, self.sectorsize*(sect+1), self._filesize)) - self._raise_defect(DEFECT_FATAL, 'OLE sector index out of range') + self.raise_defect(DEFECT_FATAL, 'OLE sector index out of range') if len(data) < self.sectorsize: # add padding data += padding * (self.sectorsize - len(data)) @@ -1751,10 +1751,10 @@ class OleFileIO(object): """ # check if SID is OK: if sid<0 or sid>=len(self.direntries): - self._raise_defect(DEFECT_FATAL, "OLE directory index out of range") + self.raise_defect(DEFECT_FATAL, "OLE directory index out of range") # check if entry was already referenced: if self.direntries[sid] is not None: - self._raise_defect(DEFECT_INCORRECT, + self.raise_defect(DEFECT_INCORRECT, "double reference for OLE stream/storage") # if exception not raised, return the object return self.direntries[sid] @@ -1833,7 +1833,7 @@ class OleFileIO(object): # add it to the list files.append(prefix[1:] + [entry.name]) else: - self._raise_defect(DEFECT_INCORRECT, 'The directory tree contains an entry which is not a stream nor a storage.') + self.raise_defect(DEFECT_INCORRECT, 'The directory tree contains an entry which is not a stream nor a storage.') def listdir(self, streams=True, storages=False): @@ -2102,7 +2102,7 @@ class OleFileIO(object): # a fatal error when parsing the whole file msg = 'Error while parsing properties header in stream %s: %s' % ( repr(streampath), exc) - self._raise_defect(DEFECT_INCORRECT, msg, type(exc)) + self.raise_defect(DEFECT_INCORRECT, msg, type(exc)) return data for i in range(num_props): @@ -2203,7 +2203,7 @@ class OleFileIO(object): # a DEFECT_INCORRECT, because parsing can go on msg = 'Error while parsing property id %d in stream %s: %s' % ( id, repr(streampath), exc) - self._raise_defect(DEFECT_INCORRECT, msg, type(exc)) + self.raise_defect(DEFECT_INCORRECT, msg, type(exc)) return data From 1b98b76bcfe5dd0068ae6504d15760c99943dc41 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 28 May 2015 22:34:23 +1000 Subject: [PATCH 0271/1037] Flake8 fixes --- PIL/OleFileIO.py | 288 +++++++++++++++++++++-------------------------- 1 file changed, 130 insertions(+), 158 deletions(-) diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index 7449407b9..877599add 100755 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -256,11 +256,14 @@ __version__ = '0.42b' import io import sys -import struct, array, os.path, datetime +import struct +import array +import os.path +import datetime #=== COMPATIBILITY WORKAROUNDS ================================================ -#[PL] Define explicitly the public API to avoid private objects in pydoc: +# [PL] Define explicitly the public API to avoid private objects in pydoc: #TODO: add more # __all__ = ['OleFileIO', 'isOleFile', 'MAGIC'] @@ -276,7 +279,7 @@ except: # no xrange, for Python 3 it was renamed as range: iterrange = range -#[PL] workaround to fix an issue with array item size on 64 bits systems: +# [PL] workaround to fix an issue with array item size on 64 bits systems: if array.array('L').itemsize == 4: # on 32 bits platforms, long integers in an array are 32 bits: UINT32 = 'L' @@ -300,14 +303,14 @@ else: raise ValueError('Need to fix a bug with 32 bit arrays, please contact author...') -#[PL] These workarounds were inspired from the Path module +# [PL] These workarounds were inspired from the Path module # (see http://www.jorendorff.com/articles/python/path/) try: basestring except NameError: basestring = str -#[PL] Experimental setting: if True, OLE filenames will be kept in Unicode +# [PL] Experimental setting: if True, OLE filenames will be kept in Unicode # if False (default PIL behaviour), all filenames are converted to Latin-1. KEEP_UNICODE_NAMES = True @@ -323,15 +326,22 @@ else: #TODO: replace this by proper logging -#[PL] DEBUG display mode: False by default, use set_debug_mode() or "-d" on +# [PL] DEBUG display mode: False by default, use set_debug_mode() or "-d" on # command line to change it. DEBUG_MODE = False + + def debug_print(msg): print(msg) + + def debug_pass(msg): pass + + debug = debug_pass + def set_debug_mode(debug_mode): """ Set debug mode on or off, to control display of debugging messages. @@ -350,39 +360,39 @@ def set_debug_mode(debug_mode): # magic bytes that should be at the beginning of every OLE file: MAGIC = b'\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1' -#[PL]: added constants for Sector IDs (from AAF specifications) -MAXREGSECT = 0xFFFFFFFA # (-6) maximum SECT -DIFSECT = 0xFFFFFFFC # (-4) denotes a DIFAT sector in a FAT -FATSECT = 0xFFFFFFFD # (-3) denotes a FAT sector in a FAT -ENDOFCHAIN = 0xFFFFFFFE # (-2) end of a virtual stream chain -FREESECT = 0xFFFFFFFF # (-1) unallocated sector +# [PL]: added constants for Sector IDs (from AAF specifications) +MAXREGSECT = 0xFFFFFFFA # (-6) maximum SECT +DIFSECT = 0xFFFFFFFC # (-4) denotes a DIFAT sector in a FAT +FATSECT = 0xFFFFFFFD # (-3) denotes a FAT sector in a FAT +ENDOFCHAIN = 0xFFFFFFFE # (-2) end of a virtual stream chain +FREESECT = 0xFFFFFFFF # (-1) unallocated sector -#[PL]: added constants for Directory Entry IDs (from AAF specifications) -MAXREGSID = 0xFFFFFFFA # (-6) maximum directory entry ID -NOSTREAM = 0xFFFFFFFF # (-1) unallocated directory entry +# [PL]: added constants for Directory Entry IDs (from AAF specifications) +MAXREGSID = 0xFFFFFFFA # (-6) maximum directory entry ID +NOSTREAM = 0xFFFFFFFF # (-1) unallocated directory entry -#[PL] object types in storage (from AAF specifications) -STGTY_EMPTY = 0 # empty directory entry (according to OpenOffice.org doc) -STGTY_STORAGE = 1 # element is a storage object -STGTY_STREAM = 2 # element is a stream object -STGTY_LOCKBYTES = 3 # element is an ILockBytes object -STGTY_PROPERTY = 4 # element is an IPropertyStorage object -STGTY_ROOT = 5 # element is a root storage +# [PL] object types in storage (from AAF specifications) +STGTY_EMPTY = 0 # empty directory entry (according to OpenOffice.org doc) +STGTY_STORAGE = 1 # element is a storage object +STGTY_STREAM = 2 # element is a stream object +STGTY_LOCKBYTES = 3 # element is an ILockBytes object +STGTY_PROPERTY = 4 # element is an IPropertyStorage object +STGTY_ROOT = 5 # element is a root storage # # -------------------------------------------------------------------- # property types -VT_EMPTY=0; VT_NULL=1; VT_I2=2; VT_I4=3; VT_R4=4; VT_R8=5; VT_CY=6; -VT_DATE=7; VT_BSTR=8; VT_DISPATCH=9; VT_ERROR=10; VT_BOOL=11; -VT_VARIANT=12; VT_UNKNOWN=13; VT_DECIMAL=14; VT_I1=16; VT_UI1=17; -VT_UI2=18; VT_UI4=19; VT_I8=20; VT_UI8=21; VT_INT=22; VT_UINT=23; -VT_VOID=24; VT_HRESULT=25; VT_PTR=26; VT_SAFEARRAY=27; VT_CARRAY=28; -VT_USERDEFINED=29; VT_LPSTR=30; VT_LPWSTR=31; VT_FILETIME=64; -VT_BLOB=65; VT_STREAM=66; VT_STORAGE=67; VT_STREAMED_OBJECT=68; -VT_STORED_OBJECT=69; VT_BLOB_OBJECT=70; VT_CF=71; VT_CLSID=72; -VT_VECTOR=0x1000; +VT_EMPTY = 0; VT_NULL = 1; VT_I2 = 2; VT_I4 = 3; VT_R4 = 4; VT_R8 = 5; VT_CY = 6; +VT_DATE = 7; VT_BSTR = 8; VT_DISPATCH = 9; VT_ERROR = 10; VT_BOOL = 11; +VT_VARIANT = 12; VT_UNKNOWN = 13; VT_DECIMAL = 14; VT_I1 = 16; VT_UI1 = 17; +VT_UI2 = 18; VT_UI4 = 19; VT_I8 = 20; VT_UI8 = 21; VT_INT = 22; VT_UINT = 23; +VT_VOID = 24; VT_HRESULT = 25; VT_PTR = 26; VT_SAFEARRAY = 27; VT_CARRAY = 28; +VT_USERDEFINED = 29; VT_LPSTR = 30; VT_LPWSTR = 31; VT_FILETIME = 64; +VT_BLOB = 65; VT_STREAM = 66; VT_STORAGE = 67; VT_STREAMED_OBJECT = 68; +VT_STORED_OBJECT = 69; VT_BLOB_OBJECT = 70; VT_CF = 71; VT_CLSID = 72; +VT_VECTOR = 0x1000; # map property id to name (for debugging purposes) @@ -398,7 +408,7 @@ for keyword, var in list(vars().items()): WORD_CLSID = "00020900-0000-0000-C000-000000000046" #TODO: check Excel, PPT, ... -#[PL]: Defect levels to classify parsing errors - see OleFileIO._raise_defect() +# [PL]: Defect levels to classify parsing errors - see OleFileIO._raise_defect() DEFECT_UNSURE = 10 # a case which looks weird, but not sure it's a defect DEFECT_POTENTIAL = 20 # a potential defect DEFECT_INCORRECT = 30 # an error according to specifications, but parsing @@ -410,7 +420,7 @@ DEFECT_FATAL = 40 # an error which cannot be ignored, parsing is # (this is used in isOleFile and OleFile.open) MINIMAL_OLEFILE_SIZE = 1536 -#[PL] add useful constants to __all__: +# [PL] add useful constants to __all__: # for key in list(vars().keys()): # if key.startswith('STGTY_') or key.startswith('DEFECT_'): # __all__.append(key) @@ -418,7 +428,7 @@ MINIMAL_OLEFILE_SIZE = 1536 #=== FUNCTIONS =============================================================== -def isOleFile (filename): +def isOleFile(filename): """ Test if a file is an OLE container (according to the magic bytes in its header). @@ -502,7 +512,6 @@ def _clsid(clsid): tuple(map(i8, clsid[8:16])))) - def filetime2datetime(filetime): """ convert FILETIME (64 bits int) to Python datetime.datetime @@ -514,7 +523,6 @@ def filetime2datetime(filetime): return _FILETIME_null_date + datetime.timedelta(microseconds=filetime//10) - #=== CLASSES ================================================================== class OleMetadata(object): @@ -622,7 +630,6 @@ class OleMetadata(object): self.language = None self.doc_version = None - def parse_properties(self, olefile): """ Parse standard properties of an OLE file, from the streams @@ -707,11 +714,11 @@ class _OleStream(io.BytesIO): """ debug('_OleStream.__init__:') debug(' sect=%d (%X), size=%d, offset=%d, sectorsize=%d, len(fat)=%d, fp=%s' - %(sect,sect,size,offset,sectorsize,len(fat), repr(fp))) - #[PL] To detect malformed documents with FAT loops, we compute the + % (sect, sect, size, offset, sectorsize, len(fat), repr(fp))) + # [PL] To detect malformed documents with FAT loops, we compute the # expected number of sectors in the stream: unknown_size = False - if size==0x7FFFFFFF: + if size == 0x7FFFFFFF: # this is the case when called from OleFileIO._open(), and stream # size is not known in advance (for example when reading the # Directory stream). Then we can only guess maximum size: @@ -733,7 +740,7 @@ class _OleStream(io.BytesIO): if size == 0 and sect != ENDOFCHAIN: debug('size == 0 and sect != ENDOFCHAIN:') raise IOError('incorrect OLE sector index for empty stream') - #[PL] A fixed-length for loop is used instead of an undefined while + # [PL] A fixed-length for loop is used instead of an undefined while # loop to avoid DoS attacks: for i in range(nb_sectors): # Sector index may be ENDOFCHAIN, but only if size was unknown @@ -745,9 +752,9 @@ class _OleStream(io.BytesIO): debug('sect=ENDOFCHAIN before expected size') raise IOError('incomplete OLE stream') # sector index should be within FAT: - if sect<0 or sect>=len(fat): + if sect < 0 or sect >= len(fat): debug('sect=%d (%X) / len(fat)=%d' % (sect, sect, len(fat))) - debug('i=%d / nb_sectors=%d' %(i, nb_sectors)) + debug('i=%d / nb_sectors=%d' % (i, nb_sectors)) ## tmp_data = b"".join(data) ## f = open('test_debug.bin', 'wb') ## f.write(tmp_data) @@ -767,7 +774,7 @@ class _OleStream(io.BytesIO): # Note: if sector is the last of the file, sometimes it is not a # complete sector (of 512 or 4K), so we may read less than # sectorsize. - if len(sector_data)!=sectorsize and sect!=(len(fat)-1): + if len(sector_data) != sectorsize and sect != (len(fat)-1): debug('sect=%d / len(fat)=%d, seek=%d / filesize=%d, len read=%d' % (sect, len(fat), offset+sectorsize*sect, filesize, len(sector_data))) debug('seek+len(read)=%d' % (offset+sectorsize*sect+len(sector_data))) @@ -779,7 +786,7 @@ class _OleStream(io.BytesIO): except IndexError: # [PL] if pointer is out of the FAT an exception is raised raise IOError('incorrect OLE FAT, sector index out of range') - #[PL] Last sector should be a "end of chain" marker: + # [PL] Last sector should be a "end of chain" marker: if sect != ENDOFCHAIN: raise IOError('incorrect last sector index in OLE stream') data = b"".join(data) @@ -808,7 +815,7 @@ class _OleDirectoryEntry(object): """ OLE2 Directory Entry """ - #[PL] parsing code moved from OleFileIO.loaddirectory + # [PL] parsing code moved from OleFileIO.loaddirectory # struct to parse directory entries: # <: little-endian byte order, standard sizes @@ -833,7 +840,6 @@ class _OleDirectoryEntry(object): DIRENTRY_SIZE = 128 assert struct.calcsize(STRUCT_DIRENTRY) == DIRENTRY_SIZE - def __init__(self, entry, sid, olefile): """ Constructor for an _OleDirectoryEntry object. @@ -883,7 +889,7 @@ class _OleDirectoryEntry(object): #debug (struct.unpack(fmt_entry, entry[:len_entry])) # name should be at most 31 unicode characters + null character, # so 64 bytes in total (31*2 + 2): - if namelength>64: + if namelength > 64: olefile.raise_defect(DEFECT_INCORRECT, 'incorrect DirEntry name length') # if exception not raised, namelength is set to the maximum value: namelength = 64 @@ -911,7 +917,7 @@ class _OleDirectoryEntry(object): olefile.raise_defect(DEFECT_UNSURE, 'incorrect OLE stream size') self.size = sizeLow else: - self.size = sizeLow + (long(sizeHigh)<<32) + self.size = sizeLow + (long(sizeHigh) << 32) debug(' - size: %d (sizeLow=%d, sizeHigh=%d)' % (self.size, sizeLow, sizeHigh)) self.clsid = _clsid(clsid) @@ -920,17 +926,15 @@ class _OleDirectoryEntry(object): if self.entry_type == STGTY_STORAGE and self.size != 0: olefile.raise_defect(DEFECT_POTENTIAL, 'OLE storage with size>0') # check if stream is not already referenced elsewhere: - if self.entry_type in (STGTY_ROOT, STGTY_STREAM) and self.size>0: + if self.entry_type in (STGTY_ROOT, STGTY_STREAM) and self.size > 0: if self.size < olefile.minisectorcutoff \ - and self.entry_type==STGTY_STREAM: # only streams can be in MiniFAT + and self.entry_type == STGTY_STREAM: # only streams can be in MiniFAT # ministream object minifat = True else: minifat = False olefile._check_duplicate_stream(self.isectStart, minifat) - - def build_storage_tree(self): """ Read and build the red-black tree attached to this _OleDirectoryEntry @@ -954,7 +958,6 @@ class _OleDirectoryEntry(object): # (see rich comparison methods in this class) self.kids.sort() - def append_kids(self, child_sid): """ Walk through red-black tree of children of this directory entry to add @@ -963,13 +966,13 @@ class _OleDirectoryEntry(object): :param child_sid : index of child directory entry to use, or None when called first time for the root. (only used during recursion) """ - #[PL] this method was added to use simple recursion instead of a complex + # [PL] this method was added to use simple recursion instead of a complex # algorithm. # if this is not a storage or a leaf of the tree, nothing to do: if child_sid == NOSTREAM: return # check if child SID is in the proper range: - if child_sid<0 or child_sid>=len(self.olefile.direntries): + if child_sid < 0 or child_sid >= len(self.olefile.direntries): self.olefile.raise_defect(DEFECT_FATAL, 'OLE DirEntry index out of range') # get child direntry: child = self.olefile._load_direntry(child_sid) #direntries[child_sid] @@ -998,7 +1001,6 @@ class _OleDirectoryEntry(object): # Afterwards build kid's own tree if it's also a storage: child.build_storage_tree() - def __eq__(self, other): "Compare entries by name" return self.name == other.name @@ -1018,7 +1020,6 @@ class _OleDirectoryEntry(object): #TODO: replace by the same function as MS implementation ? # (order by name length first, then case-insensitive order) - def dump(self, tab = 0): "Dump this entry, and all its subentries (for debug purposes only)" TYPES = ["(invalid)", "(storage)", "(stream)", "(lockbytes)", @@ -1033,7 +1034,6 @@ class _OleDirectoryEntry(object): for kid in self.kids: kid.dump(tab + 2) - def getmtime(self): """ Return modification time of a directory entry. @@ -1047,7 +1047,6 @@ class _OleDirectoryEntry(object): return None return filetime2datetime(self.modifyTime) - def getctime(self): """ Return creation time of a directory entry. @@ -1134,7 +1133,6 @@ class OleFileIO(object): if filename: self.open(filename, write_mode=write_mode) - def raise_defect(self, defect_level, message, exception_type=IOError): """ This method should be called for any defect found during file parsing. @@ -1158,7 +1156,6 @@ class OleFileIO(object): # just record the issue, no exception raised: self.parsing_issues.append((exception_type, message)) - def _decode_utf16_str(self, utf16_str, errors='replace'): """ Decode a string encoded in UTF-16 LE format, as found in the OLE @@ -1177,7 +1174,6 @@ class OleFileIO(object): # path_encoding=None, return the Unicode string as-is: return unicode_str - def open(self, filename, write_mode=False): """ Open an OLE2 file in read-only or read/write mode. @@ -1196,7 +1192,7 @@ class OleFileIO(object): of read-only by default. (ignored if filename is not a path) """ self.write_mode = write_mode - #[PL] check if filename is a string-like or file-like object: + # [PL] check if filename is a string-like or file-like object: # (it is better to check for a read() method) if hasattr(filename, 'read'): #TODO: also check seek and tell methods? @@ -1221,7 +1217,7 @@ class OleFileIO(object): # file-like objects: #TODO: do it above, using getsize with filename when possible? #TODO: fix code to fail with clear exception when filesize cannot be obtained - filesize=0 + filesize = 0 self.fp.seek(0, os.SEEK_END) try: filesize = self.fp.tell() @@ -1278,7 +1274,7 @@ class OleFileIO(object): # '<' indicates little-endian byte ordering for Intel (cf. struct module help) fmt_header = '<8s16sHHHHHHLLLLLLLLLL' header_size = struct.calcsize(fmt_header) - debug( "fmt_header size = %d, +FAT = %d" % (header_size, header_size + 109*4) ) + debug("fmt_header size = %d, +FAT = %d" % (header_size, header_size + 109*4)) header1 = header[:header_size] ( self.Sig, @@ -1299,7 +1295,7 @@ class OleFileIO(object): self.sectDifStart, self.csectDif ) = struct.unpack(fmt_header, header1) - debug( struct.unpack(fmt_header, header1)) + debug(struct.unpack(fmt_header, header1)) if self.Sig != MAGIC: # OLE signature should always be present @@ -1307,47 +1303,47 @@ class OleFileIO(object): if self.clsid != bytearray(16): # according to AAF specs, CLSID should always be zero self.raise_defect(DEFECT_INCORRECT, "incorrect CLSID in OLE header") - debug( "MinorVersion = %d" % self.MinorVersion ) - debug( "DllVersion = %d" % self.DllVersion ) + debug("MinorVersion = %d" % self.MinorVersion) + debug("DllVersion = %d" % self.DllVersion) if self.DllVersion not in [3, 4]: # version 3: usual format, 512 bytes per sector # version 4: large format, 4K per sector self.raise_defect(DEFECT_INCORRECT, "incorrect DllVersion in OLE header") - debug( "ByteOrder = %X" % self.ByteOrder ) + debug("ByteOrder = %X" % self.ByteOrder) if self.ByteOrder != 0xFFFE: # For now only common little-endian documents are handled correctly self.raise_defect(DEFECT_FATAL, "incorrect ByteOrder in OLE header") # TODO: add big-endian support for documents created on Mac ? # But according to [MS-CFB] ? v20140502, ByteOrder MUST be 0xFFFE. self.SectorSize = 2**self.SectorShift - debug( "SectorSize = %d" % self.SectorSize ) + debug("SectorSize = %d" % self.SectorSize) if self.SectorSize not in [512, 4096]: self.raise_defect(DEFECT_INCORRECT, "incorrect SectorSize in OLE header") - if (self.DllVersion==3 and self.SectorSize!=512) \ - or (self.DllVersion==4 and self.SectorSize!=4096): + if (self.DllVersion == 3 and self.SectorSize != 512) \ + or (self.DllVersion == 4 and self.SectorSize != 4096): self.raise_defect(DEFECT_INCORRECT, "SectorSize does not match DllVersion in OLE header") self.MiniSectorSize = 2**self.MiniSectorShift - debug( "MiniSectorSize = %d" % self.MiniSectorSize ) + debug("MiniSectorSize = %d" % self.MiniSectorSize) if self.MiniSectorSize not in [64]: self.raise_defect(DEFECT_INCORRECT, "incorrect MiniSectorSize in OLE header") if self.Reserved != 0 or self.Reserved1 != 0: self.raise_defect(DEFECT_INCORRECT, "incorrect OLE header (non-null reserved bytes)") - debug( "csectDir = %d" % self.csectDir ) + debug("csectDir = %d" % self.csectDir) # Number of directory sectors (only allowed if DllVersion != 3) - if self.SectorSize==512 and self.csectDir!=0: + if self.SectorSize == 512 and self.csectDir != 0: self.raise_defect(DEFECT_INCORRECT, "incorrect csectDir in OLE header") - debug( "csectFat = %d" % self.csectFat ) + debug("csectFat = %d" % self.csectFat) # csectFat = number of FAT sectors in the file - debug( "sectDirStart = %X" % self.sectDirStart ) + debug("sectDirStart = %X" % self.sectDirStart) # sectDirStart = 1st sector containing the directory - debug( "signature = %d" % self.signature ) + debug("signature = %d" % self.signature) # Signature should be zero, BUT some implementations do not follow this # rule => only a potential defect: # (according to MS-CFB, may be != 0 for applications supporting file # transactions) if self.signature != 0: self.raise_defect(DEFECT_POTENTIAL, "incorrect OLE header (signature>0)") - debug( "MiniSectorCutoff = %d" % self.MiniSectorCutoff ) + debug("MiniSectorCutoff = %d" % self.MiniSectorCutoff) # MS-CFB: This integer field MUST be set to 0x00001000. This field # specifies the maximum size of a user-defined data stream allocated # from the mini FAT and mini stream, and that cutoff is 4096 bytes. @@ -1355,15 +1351,15 @@ class OleFileIO(object): # must be allocated as normal sectors from the FAT. if self.MiniSectorCutoff != 0x1000: self.raise_defect(DEFECT_INCORRECT, "incorrect MiniSectorCutoff in OLE header") - debug( "MiniFatStart = %X" % self.MiniFatStart ) - debug( "csectMiniFat = %d" % self.csectMiniFat ) - debug( "sectDifStart = %X" % self.sectDifStart ) - debug( "csectDif = %d" % self.csectDif ) + debug("MiniFatStart = %X" % self.MiniFatStart) + debug("csectMiniFat = %d" % self.csectMiniFat) + debug("sectDifStart = %X" % self.sectDifStart) + debug("csectDif = %d" % self.csectDif) # calculate the number of sectors in the file # (-1 because header doesn't count) - self.nb_sect = ( (filesize + self.SectorSize-1) // self.SectorSize) - 1 - debug( "Number of sectors in the file: %d" % self.nb_sect ) + self.nb_sect = ((filesize + self.SectorSize-1) // self.SectorSize) - 1 + debug("Number of sectors in the file: %d" % self.nb_sect) #TODO: change this test, because an OLE file MAY contain other data # after the last sector. @@ -1393,14 +1389,12 @@ class OleFileIO(object): self.ministream = None self.minifatsect = self.MiniFatStart #i32(header, 60) - def close(self): """ close the OLE file, to release the file object """ self.fp.close() - def _check_duplicate_stream(self, first_sect, minifat=False): """ Checks if a stream has not been already referenced elsewhere. @@ -1416,7 +1410,7 @@ class OleFileIO(object): else: debug('_check_duplicate_stream: sect=%d in FAT' % first_sect) # some values can be safely ignored (not a real stream): - if first_sect in (DIFSECT,FATSECT,ENDOFCHAIN,FREESECT): + if first_sect in (DIFSECT, FATSECT, ENDOFCHAIN, FREESECT): return used_streams = self._used_streams_fat #TODO: would it be more efficient using a dict or hash values, instead @@ -1426,7 +1420,6 @@ class OleFileIO(object): else: used_streams.append(first_sect) - def dumpfat(self, fat, firstindex=0): "Displays a part of FAT in human-readable form for debugging purpose" # [PL] added only for debug @@ -1450,7 +1443,7 @@ class OleFileIO(object): index = l*VPL print("%8X:" % (firstindex+index), end=" ") for i in range(index, index+VPL): - if i>=nbsect: + if i >= nbsect: break sect = fat[i] aux = sect & 0xFFFFFFFF # JYTHON-WORKAROUND @@ -1464,12 +1457,11 @@ class OleFileIO(object): print(name, end=" ") print() - def dumpsect(self, sector, firstindex=0): "Displays a sector in a human-readable form, for debugging purpose." if not DEBUG_MODE: return - VPL=8 # number of values per line (8+1 * 8+1 = 81) + VPL = 8 # number of values per line (8+1 * 8+1 = 81) tab = array.array(UINT32, sector) if sys.byteorder == 'big': tab.byteswap() @@ -1483,7 +1475,7 @@ class OleFileIO(object): index = l*VPL print("%8X:" % (firstindex+index), end=" ") for i in range(index, index+VPL): - if i>=nbsect: + if i >= nbsect: break sect = tab[i] name = "%8X" % sect @@ -1501,7 +1493,6 @@ class OleFileIO(object): a.byteswap() return a - def loadfat_sect(self, sect): """ Adds the indexes of the given sector to the FAT @@ -1533,7 +1524,6 @@ class OleFileIO(object): self.fat = self.fat + nextfat return isect - def loadfat(self, header): """ Load the FAT table. @@ -1544,7 +1534,7 @@ class OleFileIO(object): # Additional sectors are described by DIF blocks sect = header[76:512] - debug( "len(sect)=%d, so %d integers" % (len(sect), len(sect)//4) ) + debug("len(sect)=%d, so %d integers" % (len(sect), len(sect)//4)) #fat = [] # [PL] FAT is an array of 32 bits unsigned ints, it's more effective # to use an array than a list in Python. @@ -1554,7 +1544,7 @@ class OleFileIO(object): #self.dumpfat(self.fat) ## for i in range(0, len(sect), 4): ## ix = i32(sect, i) -## #[PL] if ix == -2 or ix == -1: # ix == 0xFFFFFFFE or ix == 0xFFFFFFFF: +## # [PL] if ix == -2 or ix == -1: # ix == 0xFFFFFFFE or ix == 0xFFFFFFFF: ## if ix == 0xFFFFFFFE or ix == 0xFFFFFFFF: ## break ## s = self.getsect(ix) @@ -1570,19 +1560,19 @@ class OleFileIO(object): if self.sectDifStart >= self.nb_sect: # initial DIFAT block index must be valid self.raise_defect(DEFECT_FATAL, 'incorrect DIFAT, first index out of range') - debug( "DIFAT analysis..." ) + debug("DIFAT analysis...") # We compute the necessary number of DIFAT sectors : # Number of pointers per DIFAT sector = (sectorsize/4)-1 # (-1 because the last pointer is the next DIFAT sector number) nb_difat_sectors = (self.sectorsize//4)-1 # (if 512 bytes: each DIFAT sector = 127 pointers + 1 towards next DIFAT sector) nb_difat = (self.csectFat-109 + nb_difat_sectors-1)//nb_difat_sectors - debug( "nb_difat = %d" % nb_difat ) + debug("nb_difat = %d" % nb_difat) if self.csectDif != nb_difat: raise IOError('incorrect DIFAT') isect_difat = self.sectDifStart for i in iterrange(nb_difat): - debug( "DIFAT block %d, sector %X" % (i, isect_difat) ) + debug("DIFAT block %d, sector %X" % (i, isect_difat)) #TODO: check if corresponding FAT SID = DIFSECT sector_difat = self.getsect(isect_difat) difat = self.sect2array(sector_difat) @@ -1590,7 +1580,7 @@ class OleFileIO(object): self.loadfat_sect(difat[:nb_difat_sectors]) # last DIFAT pointer is next DIFAT sector: isect_difat = difat[nb_difat_sectors] - debug( "next DIFAT sector: %X" % isect_difat ) + debug("next DIFAT sector: %X" % isect_difat) # checks: if isect_difat not in [ENDOFCHAIN, FREESECT]: # last DIFAT pointer value must be ENDOFCHAIN or FREESECT @@ -1608,7 +1598,6 @@ class OleFileIO(object): debug('\nFAT:') self.dumpfat(self.fat) - def loadminifat(self): """ Load the MiniFAT table. @@ -1633,8 +1622,8 @@ class OleFileIO(object): self.raise_defect(DEFECT_INCORRECT, 'OLE MiniStream is larger than MiniFAT') # In any case, first read stream_size: s = self._open(self.minifatsect, stream_size, force_FAT=True).read() - #[PL] Old code replaced by an array: - #self.minifat = [i32(s, i) for i in range(0, len(s), 4)] + # [PL] Old code replaced by an array: + # self.minifat = [i32(s, i) for i in range(0, len(s), 4)] self.minifat = self.sect2array(s) # Then shrink the array to used size, to avoid indexes out of MiniStream: debug('MiniFAT shrunk from %d to %d sectors' % (len(self.minifat), nb_minisectors)) @@ -1658,9 +1647,9 @@ class OleFileIO(object): # [PL] the original code in PIL was wrong when sectors are 4KB instead of # 512 bytes: - #self.fp.seek(512 + self.sectorsize * sect) - #[PL]: added safety checks: - #print("getsect(%X)" % sect) + # self.fp.seek(512 + self.sectorsize * sect) + # [PL]: added safety checks: + # print("getsect(%X)" % sect) try: self.fp.seek(self.sectorsize * (sect+1)) except: @@ -1674,7 +1663,6 @@ class OleFileIO(object): self.raise_defect(DEFECT_FATAL, 'incomplete OLE sector') return sector - def write_sect(self, sect, data, padding=b'\x00'): """ Write given sector to file on disk. @@ -1685,7 +1673,7 @@ class OleFileIO(object): """ if not isinstance(data, bytes): raise TypeError("write_sect: data must be a bytes string") - if not isinstance(padding, bytes) or len(padding)!=1: + if not isinstance(padding, bytes) or len(padding) != 1: raise TypeError("write_sect: padding must be a bytes string of 1 char") #TODO: we could allow padding=None for no padding at all try: @@ -1701,7 +1689,6 @@ class OleFileIO(object): raise ValueError("Data is larger than sector size") self.fp.write(data) - def loaddirectory(self, sect): """ Load the directory. @@ -1715,14 +1702,14 @@ class OleFileIO(object): # (stream size is not known in advance) self.directory_fp = self._open(sect) - #[PL] to detect malformed documents and avoid DoS attacks, the maximum + # [PL] to detect malformed documents and avoid DoS attacks, the maximum # number of directory entries can be calculated: max_entries = self.directory_fp.size // 128 debug('loaddirectory: size=%d, max_entries=%d' % (self.directory_fp.size, max_entries)) # Create list of directory entries - #self.direntries = [] + # self.direntries = [] # We start with a list of "None" object self.direntries = [None] * max_entries ## for sid in iterrange(max_entries): @@ -1737,8 +1724,7 @@ class OleFileIO(object): # read and build all storage trees, starting from the root: self.root.build_storage_tree() - - def _load_direntry (self, sid): + def _load_direntry(self, sid): """ Load a directory entry from the directory. This method should only be called once for each storage/stream when @@ -1750,7 +1736,7 @@ class OleFileIO(object): :exception IOError: if the entry has always been referenced. """ # check if SID is OK: - if sid<0 or sid>=len(self.direntries): + if sid < 0 or sid >= len(self.direntries): self.raise_defect(DEFECT_FATAL, "OLE directory index out of range") # check if entry was already referenced: if self.direntries[sid] is not None: @@ -1763,14 +1749,12 @@ class OleFileIO(object): self.direntries[sid] = _OleDirectoryEntry(entry, sid, self) return self.direntries[sid] - def dumpdirectory(self): """ Dump directory (for debugging only) """ self.root.dump() - def _open(self, start, size = 0x7FFFFFFF, force_FAT=False): """ Open a stream, either in FAT or MiniFAT according to its size. @@ -1806,7 +1790,6 @@ class OleFileIO(object): sectorsize=self.sectorsize, fat=self.fat, filesize=self._filesize) - def _list(self, files, prefix, node, streams=True, storages=False): """ listdir helper @@ -1835,7 +1818,6 @@ class OleFileIO(object): else: self.raise_defect(DEFECT_INCORRECT, 'The directory tree contains an entry which is not a stream nor a storage.') - def listdir(self, streams=True, storages=False): """ Return a list of streams and/or storages stored in this file @@ -1849,7 +1831,6 @@ class OleFileIO(object): self._list(files, [], self.root, streams, storages) return files - def _find(self, filename): """ Returns directory entry of given filename. (openstream helper) @@ -1881,7 +1862,6 @@ class OleFileIO(object): node = kid return node.sid - def openstream(self, filename): """ Open a stream as a read-only file object (BytesIO). @@ -1903,7 +1883,6 @@ class OleFileIO(object): raise IOError("this file is not a stream") return self._open(entry.isectStart, entry.size) - def write_stream(self, stream_name, data): """ Write a stream to disk. For now, it is only possible to replace an @@ -1942,29 +1921,28 @@ class OleFileIO(object): # (sect, offset+self.sectorsize*sect)) # raise IOError('OLE sector index out of range') # extract one sector from data, the last one being smaller: - if i<(nb_sectors-1): - data_sector = data [i*self.sectorsize : (i+1)*self.sectorsize] + if i < (nb_sectors-1): + data_sector = data[i*self.sectorsize:(i+1)*self.sectorsize] #TODO: comment this if it works - assert(len(data_sector)==self.sectorsize) + assert(len(data_sector) == self.sectorsize) else: - data_sector = data [i*self.sectorsize:] - #TODO: comment this if it works + data_sector = data[i*self.sectorsize:] + # TODO: comment this if it works debug('write_stream: size=%d sectorsize=%d data_sector=%d size%%sectorsize=%d' % (size, self.sectorsize, len(data_sector), size % self.sectorsize)) - assert(len(data_sector) % self.sectorsize==size % self.sectorsize) + assert(len(data_sector) % self.sectorsize == size % self.sectorsize) self.write_sect(sect, data_sector) -## self.fp.write(data_sector) +# self.fp.write(data_sector) # jump to next sector in the FAT: try: sect = self.fat[sect] except IndexError: # [PL] if pointer is out of the FAT an exception is raised raise IOError('incorrect OLE FAT, sector index out of range') - #[PL] Last sector should be a "end of chain" marker: + # [PL] Last sector should be a "end of chain" marker: if sect != ENDOFCHAIN: raise IOError('incorrect last sector index in OLE stream') - def get_type(self, filename): """ Test if given filename exists as a stream or a storage in the OLE @@ -1984,7 +1962,6 @@ class OleFileIO(object): except: return False - def getmtime(self, filename): """ Return modification time of a stream/storage. @@ -2000,7 +1977,6 @@ class OleFileIO(object): entry = self.direntries[sid] return entry.getmtime() - def getctime(self, filename): """ Return creation time of a stream/storage. @@ -2016,7 +1992,6 @@ class OleFileIO(object): entry = self.direntries[sid] return entry.getctime() - def exists(self, filename): """ Test if given filename exists as a stream or a storage in the OLE @@ -2032,7 +2007,6 @@ class OleFileIO(object): except: return False - def get_size(self, filename): """ Return size of a stream in the OLE container, in bytes. @@ -2049,7 +2023,6 @@ class OleFileIO(object): raise TypeError('object is not an OLE stream') return entry.size - def get_rootentry_name(self): """ Return root entry name. Should usually be 'Root Entry' or 'R' in most @@ -2057,7 +2030,6 @@ class OleFileIO(object): """ return self.root.name - def getproperties(self, filename, convert_time=False, no_conversion=None): """ Return properties described in substream. @@ -2069,7 +2041,7 @@ class OleFileIO(object): :returns: a dictionary of values indexed by id (integer) """ - #REFERENCE: [MS-OLEPS] https://msdn.microsoft.com/en-us/library/dd942421.aspx + # REFERENCE: [MS-OLEPS] https://msdn.microsoft.com/en-us/library/dd942421.aspx # make sure no_conversion is a list, just to simplify code below: if no_conversion is None: no_conversion = [] @@ -2112,7 +2084,7 @@ class OleFileIO(object): offset = i32(s, 12+i*8) type = i32(s, offset) - debug ('property id=%d: type=%d offset=%X' % (id, type, offset)) + debug('property id=%d: type=%d offset=%X' % (id, type, offset)) # test for common types first (should perhaps use # a dictionary instead?) @@ -2121,14 +2093,14 @@ class OleFileIO(object): value = i16(s, offset+4) if value >= 32768: value = value - 65536 - elif type == VT_UI2: # 2-byte unsigned integer + elif type == VT_UI2: # 2-byte unsigned integer value = i16(s, offset+4) elif type in (VT_I4, VT_INT, VT_ERROR): # VT_I4: 32-bit signed integer # VT_ERROR: HRESULT, similar to 32-bit signed integer, # see http://msdn.microsoft.com/en-us/library/cc230330.aspx value = i32(s, offset+4) - elif type in (VT_UI4, VT_UINT): # 4-byte unsigned integer + elif type in (VT_UI4, VT_UINT): # 4-byte unsigned integer value = i32(s, offset+4) # FIXME elif type in (VT_BSTR, VT_LPSTR): # CodePageString, see http://msdn.microsoft.com/en-us/library/dd942354.aspx @@ -2152,12 +2124,12 @@ class OleFileIO(object): count = i32(s, offset+4) value = self._decode_utf16_str(s[offset+8:offset+8+count*2]) elif type == VT_FILETIME: - value = long(i32(s, offset+4)) + (long(i32(s, offset+8))<<32) + value = long(i32(s, offset+4)) + (long(i32(s, offset+8)) << 32) # FILETIME is a 64-bit int: "number of 100ns periods # since Jan 1,1601". if convert_time and id not in no_conversion: debug('Converting property #%d to python datetime, value=%d=%fs' - %(id, value, float(value)/10000000)) + % (id, value, float(value) / 10000000)) # convert FILETIME to Python datetime.datetime # inspired from http://code.activestate.com/recipes/511425-filetime-to-datetime/ _FILETIME_null_date = datetime.datetime(1601, 1, 1, 0, 0, 0) @@ -2182,7 +2154,7 @@ class OleFileIO(object): value = bool(i16(s, offset+4)) else: value = None # everything else yields "None" - debug ('property id=%d: type=%d not implemented in parser yet' % (id, type)) + debug('property id=%d: type=%d not implemented in parser yet' % (id, type)) # missing: VT_EMPTY, VT_NULL, VT_R4, VT_R8, VT_CY, VT_DATE, # VT_DECIMAL, VT_I1, VT_I8, VT_UI8, @@ -2194,8 +2166,8 @@ class OleFileIO(object): # type of items, e.g. VT_VECTOR|VT_BSTR # see http://msdn.microsoft.com/en-us/library/dd942011.aspx - #print("%08x" % id, repr(value), end=" ") - #print("(%s)" % VT[i32(s, offset) & 0xFFF]) + # print("%08x" % id, repr(value), end=" ") + # print("(%s)" % VT[i32(s, offset) & 0xFFF]) data[id] = value except BaseException as exc: @@ -2267,14 +2239,14 @@ For more information, see http://www.decalage.info/olefile props = ole.getproperties(streamname, convert_time=True) props = sorted(props.items()) for k, v in props: - #[PL]: avoid to display too large or binary values: + # [PL]: avoid to display too large or binary values: if isinstance(v, (basestring, bytes)): if len(v) > 50: v = v[:50] if isinstance(v, bytes): # quick and dirty binary check: - for c in (1,2,3,4,5,6,7,11,12,14,15,16,17,18,19,20, - 21,22,23,24,25,26,27,28,29,30,31): + for c in (1, 2, 3, 4, 5, 6, 7, 11, 12, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31): if c in bytearray(v): v = '(binary data)' break @@ -2285,7 +2257,7 @@ For more information, see http://www.decalage.info/olefile print('\nChecking streams...') for streamname in ole.listdir(): # print name using repr() to convert binary chars to \xNN: - print('-', repr('/'.join(streamname)),'-', end=' ') + print('-', repr('/'.join(streamname)), '-', end=' ') st_type = ole.get_type(streamname) if st_type == STGTY_STREAM: print('size %d' % ole.get_size(streamname)) @@ -2295,11 +2267,11 @@ For more information, see http://www.decalage.info/olefile print('NOT a stream : type=%d' % st_type) print() -## for streamname in ole.listdir(): -## # print name using repr() to convert binary chars to \xNN: -## print('-', repr('/'.join(streamname)),'-', end=' ') -## print(ole.getmtime(streamname)) -## print() +# for streamname in ole.listdir(): +# # print name using repr() to convert binary chars to \xNN: +# print('-', repr('/'.join(streamname)),'-', end=' ') +# print(ole.getmtime(streamname)) +# print() print('Modification/Creation times of all directory entries:') for entry in ole.direntries: @@ -2312,7 +2284,7 @@ For more information, see http://www.decalage.info/olefile meta = ole.get_metadata() meta.dump() print() - #[PL] Test a few new methods: + # [PL] Test a few new methods: root = ole.get_rootentry_name() print('Root entry name: "%s"' % root) if ole.exists('worddocument'): @@ -2332,4 +2304,4 @@ For more information, see http://www.decalage.info/olefile ## except IOError as v: ## print("***", "cannot read", file, "-", v) -# this code was developed while listening to The Wedding Present "Sea Monsters" \ No newline at end of file +# this code was developed while listening to The Wedding Present "Sea Monsters" From 29a3de6ccc36eba8678dbc45176db7262cde8825 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 11 May 2015 00:16:35 +1000 Subject: [PATCH 0272/1037] Doc cleanup from wiredfool --- PIL/OleFileIO.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index 877599add..f51ac4bc0 100755 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -1068,7 +1068,8 @@ class OleFileIO(object): OLE container object This class encapsulates the interface to an OLE 2 structured - storage file. Use the listdir and openstream methods to + storage file. Use the :py:meth:`~PIL.OleFileIO.OleFileIO.listdir` and + :py:meth:`~PIL.OleFileIO.OleFileIO.openstream` methods to access the contents of this file. Object names are given as a list of strings, one for each subentry @@ -2037,7 +2038,7 @@ class OleFileIO(object): :param filename: path of stream in storage tree (see openstream for syntax) :param convert_time: bool, if True timestamps will be converted to Python datetime :param no_conversion: None or list of int, timestamps not to be converted - (for example total editing time is not a real timestamp) + (for example total editing time is not a real timestamp) :returns: a dictionary of values indexed by id (integer) """ From cfedc8093c19b9b09c82b7b21c4396cbe442a8d4 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 28 May 2015 21:04:10 +1000 Subject: [PATCH 0273/1037] Fixed typo --- PIL/OleFileIO.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index f51ac4bc0..f4cbc8fc6 100755 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -1384,7 +1384,7 @@ class OleFileIO(object): # Load file allocation tables self.loadfat(header) - # Load direcory. This sets both the direntries list (ordered by sid) + # Load directory. This sets both the direntries list (ordered by sid) # and the root (ordered by hierarchy) members. self.loaddirectory(self.sectDirStart)#i32(header, 48)) self.ministream = None From a662a94316d31f719c7d8786febbf94bc3620dcf Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 28 May 2015 23:11:51 +1000 Subject: [PATCH 0274/1037] Updated i16 and i32 to be in sync with _binary --- PIL/OleFileIO.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index f4cbc8fc6..4cf106d97 100755 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -477,23 +477,20 @@ def i16(c, o = 0): """ Converts a 2-bytes (16 bits) string to an integer. - :param c: string containing bytes to convert - :param o: offset of bytes to convert in string + c: string containing bytes to convert + o: offset of bytes to convert in string """ - return i8(c[o]) | (i8(c[o+1])<<8) + return struct.unpack(" Date: Fri, 29 May 2015 14:59:54 +1000 Subject: [PATCH 0275/1037] Fixed various typos --- PIL/Image.py | 2 +- PIL/ImageCms.py | 12 ++++++------ PIL/JpegImagePlugin.py | 2 +- PIL/TiffImagePlugin.py | 6 +++--- Tests/test_image_resize.py | 2 +- Tests/test_imagedraw.py | 20 ++++++++++---------- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 274e7ee0e..1c95bfca0 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1810,7 +1810,7 @@ class Image(object): self.readonly = 0 self.pyaccess = None - # FIXME: the different tranform methods need further explanation + # FIXME: the different transform methods need further explanation # instead of bloating the method docs, add a separate chapter. def transform(self, size, method, data=None, resample=NEAREST, fill=1): """ diff --git a/PIL/ImageCms.py b/PIL/ImageCms.py index 6cd2f5d2d..ebf127df3 100644 --- a/PIL/ImageCms.py +++ b/PIL/ImageCms.py @@ -64,7 +64,7 @@ pyCMS 0.0.2 alpha Jan 6, 2002 - Added try/except statements arount type() checks of + Added try/except statements around type() checks of potential CObjects... Python won't let you use type() on them, and raises a TypeError (stupid, if you ask me!) @@ -123,8 +123,8 @@ FLAGS = { "NOTCACHE": 64, # Inhibit 1-pixel cache "NOTPRECALC": 256, "NULLTRANSFORM": 512, # Don't transform anyway - "HIGHRESPRECALC": 1024, # Use more memory to give better accurancy - "LOWRESPRECALC": 2048, # Use less memory to minimize resouces + "HIGHRESPRECALC": 1024, # Use more memory to give better accuracy + "LOWRESPRECALC": 2048, # Use less memory to minimize resources "WHITEBLACKCOMPENSATION": 8192, "BLACKPOINTCOMPENSATION": 8192, "GAMUTCHECK": 4096, # Out of Gamut alarm @@ -573,7 +573,7 @@ def applyTransform(im, transform, inPlace=0): This function applies a pre-calculated transform (from ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles()) to an image. The transform can be used for multiple images, saving - considerable calcuation time if doing the same conversion multiple times. + considerable calculation time if doing the same conversion multiple times. If you want to modify im in-place instead of receiving a new image as the return value, set inPlace to TRUE. This can only be done if @@ -858,7 +858,7 @@ def getDefaultIntent(profile): If an error occurs while trying to obtain the default intent, a PyCMSError is raised. - Use this function to determine the default (and usually best optomized) + Use this function to determine the default (and usually best optimized) rendering intent for this profile. Most profiles support multiple rendering intents, but are intended mostly for one type of conversion. If you wish to use a different intent than returned, use @@ -914,7 +914,7 @@ def isIntentSupported(profile, intent, direction): see the pyCMS documentation for details on rendering intents and what they do. - :param direction: Integer specifing if the profile is to be used for input, + :param direction: Integer specifying if the profile is to be used for input, output, or proof INPUT = 0 (or use ImageCms.DIRECTION_INPUT) diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index e15042504..5cae90073 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -4,7 +4,7 @@ # # JPEG (JFIF) file handling # -# See "Digital Compression and Coding of Continous-Tone Still Images, +# See "Digital Compression and Coding of Continuous-Tone Still Images, # Part 1, Requirements and Guidelines" (CCITT T.81 / ISO 10918-1) # # History: diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index ad085451b..f89c5b784 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -294,7 +294,7 @@ class ImageFileDirectory(collections.MutableMapping): def named(self): """ - Returns the complete tag dictionary, with named tags where posible. + Returns the complete tag dictionary, with named tags where possible. """ from PIL import TiffTags result = {} @@ -749,7 +749,7 @@ class TiffImageFile(ImageFile.ImageFile): # # Rearranging for supporting byteio items, since they have a fileno # that returns an IOError if there's no underlying fp. Easier to - # dea. with here by reordering. + # deal with here by reordering. if Image.DEBUG: print("have getvalue. just sending in a string from getvalue") n, err = decoder.decode(self.fp.getvalue()) @@ -985,7 +985,7 @@ class TiffImageFile(ImageFile.ImageFile): # Write TIFF files # little endian is default except for image modes with -# explict big endian byte-order +# explicit big endian byte-order SAVE_INFO = { # mode => rawmode, byteorder, photometrics, diff --git a/Tests/test_image_resize.py b/Tests/test_image_resize.py index 414758529..d59f8ba14 100644 --- a/Tests/test_image_resize.py +++ b/Tests/test_image_resize.py @@ -54,7 +54,7 @@ class TestImagingCoreResize(PillowTestCase): # Make an image with one colored pixel, in one channel. # When resized, that channel should be the same as a GS image. # Other channels should be unaffected. - # The R and A channels should not swap, which is indicitive of + # The R and A channels should not swap, which is indicative of # an endianness issues. samples = { diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 3b9919834..a1ed20a3a 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -300,32 +300,32 @@ class TestImageDraw(PillowTestCase): img, draw = self.create_base_image_draw((20, 20)) draw.line((5, 5, 14, 5), BLACK, 2) self.assert_image_equal( - img, expected, 'line straigth horizontal normal 2px wide failed') + img, expected, 'line straight horizontal normal 2px wide failed') expected = Image.open(os.path.join(IMAGES_PATH, 'line_horizontal_w2px_inverted.png')) expected.load() img, draw = self.create_base_image_draw((20, 20)) draw.line((14, 5, 5, 5), BLACK, 2) self.assert_image_equal( - img, expected, 'line straigth horizontal inverted 2px wide failed') + img, expected, 'line straight horizontal inverted 2px wide failed') expected = Image.open(os.path.join(IMAGES_PATH, 'line_horizontal_w3px.png')) expected.load() img, draw = self.create_base_image_draw((20, 20)) draw.line((5, 5, 14, 5), BLACK, 3) self.assert_image_equal( - img, expected, 'line straigth horizontal normal 3px wide failed') + img, expected, 'line straight horizontal normal 3px wide failed') img, draw = self.create_base_image_draw((20, 20)) draw.line((14, 5, 5, 5), BLACK, 3) self.assert_image_equal( - img, expected, 'line straigth horizontal inverted 3px wide failed') + img, expected, 'line straight horizontal inverted 3px wide failed') expected = Image.open(os.path.join(IMAGES_PATH, 'line_horizontal_w101px.png')) expected.load() img, draw = self.create_base_image_draw((200, 110)) draw.line((5, 55, 195, 55), BLACK, 101) self.assert_image_equal( - img, expected, 'line straigth horizontal 101px wide failed') + img, expected, 'line straight horizontal 101px wide failed') def test_line_h_s1_w2(self): self.skipTest('failing') @@ -344,32 +344,32 @@ class TestImageDraw(PillowTestCase): img, draw = self.create_base_image_draw((20, 20)) draw.line((5, 5, 5, 14), BLACK, 2) self.assert_image_equal( - img, expected, 'line straigth vertical normal 2px wide failed') + img, expected, 'line straight vertical normal 2px wide failed') expected = Image.open(os.path.join(IMAGES_PATH, 'line_vertical_w2px_inverted.png')) expected.load() img, draw = self.create_base_image_draw((20, 20)) draw.line((5, 14, 5, 5), BLACK, 2) self.assert_image_equal( - img, expected, 'line straigth vertical inverted 2px wide failed') + img, expected, 'line straight vertical inverted 2px wide failed') expected = Image.open(os.path.join(IMAGES_PATH, 'line_vertical_w3px.png')) expected.load() img, draw = self.create_base_image_draw((20, 20)) draw.line((5, 5, 5, 14), BLACK, 3) self.assert_image_equal( - img, expected, 'line straigth vertical normal 3px wide failed') + img, expected, 'line straight vertical normal 3px wide failed') img, draw = self.create_base_image_draw((20, 20)) draw.line((5, 14, 5, 5), BLACK, 3) self.assert_image_equal( - img, expected, 'line straigth vertical inverted 3px wide failed') + img, expected, 'line straight vertical inverted 3px wide failed') expected = Image.open(os.path.join(IMAGES_PATH, 'line_vertical_w101px.png')) expected.load() img, draw = self.create_base_image_draw((110, 200)) draw.line((55, 5, 55, 195), BLACK, 101) self.assert_image_equal(img, expected, - 'line straigth vertical 101px wide failed') + 'line straight vertical 101px wide failed') expected = Image.open(os.path.join(IMAGES_PATH, 'line_vertical_slope1px_w2px.png')) expected.load() From 6c12205aea963aa9f656048ab2959489713baf0d Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 29 May 2015 08:12:39 +0300 Subject: [PATCH 0276/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 2bd4ff0a0..15ab14fb3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Upgrade olefile from 0.30 to 0.42b #1226 + [radarhere, decalage2] + - Setting transparency value to 0 when the tRNS contains only null byte(s) #1239 [juztin] From e58a773c29838e3a59a9245cb7113992dcce195e Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 29 May 2015 18:30:57 +0300 Subject: [PATCH 0277/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 15ab14fb3..ae485811d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Ico files are little endian #1232 + [wiredfool] + - Upgrade olefile from 0.30 to 0.42b #1226 [radarhere, decalage2] From 26bcc443d1c289ce9702fa6c853958f68e7b8142 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 27 May 2015 09:15:45 +1000 Subject: [PATCH 0278/1037] Specified exception types --- PIL/Image.py | 2 +- PIL/ImageDraw.py | 2 +- Tests/test_cffi.py | 8 ++++---- Tests/test_file_eps.py | 2 +- Tests/test_file_webp_lossless.py | 2 +- Tests/test_file_webp_metadata.py | 2 +- Tests/test_imageqt.py | 6 +++--- Tests/test_scipy.py | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 1c95bfca0..d246db53c 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -121,7 +121,7 @@ USE_CFFI_ACCESS = hasattr(sys, 'pypy_version_info') try: import cffi HAS_CFFI = True -except: +except ImportError: HAS_CFFI = False diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py index 1fc5b4d61..18f9c0c00 100644 --- a/PIL/ImageDraw.py +++ b/PIL/ImageDraw.py @@ -301,7 +301,7 @@ def Draw(im, mode=None): # experimental access to the outline API try: Outline = Image.core.outline -except: +except AttributeError: Outline = None diff --git a/Tests/test_cffi.py b/Tests/test_cffi.py index 5d5427685..cea0db093 100644 --- a/Tests/test_cffi.py +++ b/Tests/test_cffi.py @@ -3,7 +3,7 @@ from helper import unittest, PillowTestCase, hopper try: import cffi from PIL import PyAccess -except: +except ImportError: # Skip in setUp() pass @@ -20,7 +20,7 @@ class TestCffiPutPixel(TestImagePutPixel): def setUp(self): try: import cffi - except: + except ImportError: self.skipTest("No cffi") def test_put(self): @@ -32,7 +32,7 @@ class TestCffiGetPixel(TestImageGetPixel): def setUp(self): try: import cffi - except: + except ImportError: self.skipTest("No cffi") def test_get(self): @@ -45,7 +45,7 @@ class TestCffi(PillowTestCase): def setUp(self): try: import cffi - except: + except ImportError: self.skipTest("No cffi") def _test_get_access(self, im): diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index f1fbac922..08d2d875a 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -159,7 +159,7 @@ class TestFileEps(PillowTestCase): # check all the freaking line endings possible try: import StringIO - except: + except ImportError: # don't skip, it skips everything in the parent test return t = StringIO.StringIO(test_string) diff --git a/Tests/test_file_webp_lossless.py b/Tests/test_file_webp_lossless.py index 52a461a74..1322e03c5 100644 --- a/Tests/test_file_webp_lossless.py +++ b/Tests/test_file_webp_lossless.py @@ -4,7 +4,7 @@ from PIL import Image try: from PIL import _webp -except: +except ImportError: pass # Skip in setUp() diff --git a/Tests/test_file_webp_metadata.py b/Tests/test_file_webp_metadata.py index 8b1254d61..06df0f82e 100644 --- a/Tests/test_file_webp_metadata.py +++ b/Tests/test_file_webp_metadata.py @@ -8,7 +8,7 @@ class TestFileWebpMetadata(PillowTestCase): def setUp(self): try: from PIL import _webp - except: + except ImportError: self.skipTest('WebP support not installed') return diff --git a/Tests/test_imageqt.py b/Tests/test_imageqt.py index 7d57ed1d2..ecab9a956 100644 --- a/Tests/test_imageqt.py +++ b/Tests/test_imageqt.py @@ -19,13 +19,13 @@ class TestImageQt(PillowTestCase): def setUp(self): try: from PyQt5.QtGui import QImage, qRgb, qRgba - except: + except ImportError: try: from PyQt4.QtGui import QImage, qRgb, qRgba - except: + except ImportError: try: from PySide.QtGui import QImage, qRgb, qRgba - except: + except ImportError: self.skipTest('PyQt4 or 5 or PySide not installed') def test_rgb(self): diff --git a/Tests/test_scipy.py b/Tests/test_scipy.py index 60f1a1b1e..1632d9475 100644 --- a/Tests/test_scipy.py +++ b/Tests/test_scipy.py @@ -6,7 +6,7 @@ try: from scipy import misc HAS_SCIPY = True -except: +except ImportError: HAS_SCIPY = False From 40c05380c54c8c2dd0e0c2dcb51f2a778c23b642 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 27 May 2015 09:18:11 +1000 Subject: [PATCH 0279/1037] Changed string statement to docstring --- Tests/check_jpeg_leaks.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/check_jpeg_leaks.py b/Tests/check_jpeg_leaks.py index 4d13978f8..7204b1edb 100644 --- a/Tests/check_jpeg_leaks.py +++ b/Tests/check_jpeg_leaks.py @@ -105,7 +105,9 @@ post-patch: test_output = BytesIO() im.save(test_output, "JPEG", qtables=qtables) - """ + + def test_exif_leak(self): + """ pre patch: MB @@ -160,8 +162,6 @@ post patch: 0 11.33 """ - - def test_exif_leak(self): im = hopper('RGB') exif = b'12345678'*4096 From 045190d3dddfae5e3abd7f91f857b4829e1cfe3a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 1 Jun 2015 18:50:37 +1000 Subject: [PATCH 0280/1037] Removed support for FreeType 2.0 --- _imagingft.c | 16 ---------------- setup.py | 9 +-------- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/_imagingft.c b/_imagingft.c index ad40ee425..0bbaad865 100644 --- a/_imagingft.c +++ b/_imagingft.c @@ -21,20 +21,8 @@ #include "Python.h" #include "Imaging.h" -#if !defined(USE_FREETYPE_2_0) -/* undef/comment out to use freetype 2.0 */ -#define USE_FREETYPE_2_1 -#endif - -#if defined(USE_FREETYPE_2_1) -/* freetype 2.1 and newer */ #include #include FT_FREETYPE_H -#else -/* freetype 2.0 */ -#include -#endif - #include FT_GLYPH_H #define KEEP_PY_UNICODE @@ -59,11 +47,7 @@ struct { const char* message; } ft_errors[] = -#if defined(USE_FREETYPE_2_1) #include FT_ERRORS_H -#else -#include -#endif /* -------------------------------------------------------------------- */ /* font objects */ diff --git a/setup.py b/setup.py index 216322ec2..35308fce9 100644 --- a/setup.py +++ b/setup.py @@ -453,9 +453,6 @@ class pil_build_ext(build_ext): if os.path.isfile(os.path.join(dir, "ft2build.h")): freetype_version = 21 break - if os.path.isdir(os.path.join(dir, "freetype")): - freetype_version = 20 - break if freetype_version: feature.freetype = "freetype" feature.freetype_version = freetype_version @@ -543,12 +540,8 @@ class pil_build_ext(build_ext): # additional libraries if feature.freetype: - defs = [] - if feature.freetype_version == 20: - defs.append(("USE_FREETYPE_2_0", None)) exts.append(Extension( - "PIL._imagingft", ["_imagingft.c"], libraries=["freetype"], - define_macros=defs)) + "PIL._imagingft", ["_imagingft.c"], libraries=["freetype"])) if os.path.isfile("_imagingtiff.c") and feature.tiff: exts.append(Extension( From 77e28787343c80249cecb395e1333c48e15f3457 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 2 Jun 2015 00:09:18 +1000 Subject: [PATCH 0281/1037] Fixed redefining built-ins --- PIL/ImageFile.py | 4 ++-- PIL/ImageFont.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index b1d261166..cc1d73f09 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -173,10 +173,10 @@ class ImageFile(Image.Image): else: # use mmap, if possible import mmap - file = open(self.filename, "r+") + fp = open(self.filename, "r+") size = os.path.getsize(self.filename) # FIXME: on Unix, use PROT_READ etc - self.map = mmap.mmap(file.fileno(), size) + self.map = mmap.mmap(fp.fileno(), size) self.im = Image.core.map_buffer( self.map, self.size, d, e, o, a ) diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index c6e7cb4d9..e3fb6f503 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -67,7 +67,7 @@ class ImageFont(object): def _load_pilfont(self, filename): - file = open(filename, "rb") + fp = open(filename, "rb") for ext in (".png", ".gif", ".pbm"): try: @@ -83,7 +83,7 @@ class ImageFont(object): self.file = fullname - return self._load_pilfont_data(file, image) + return self._load_pilfont_data(fp, image) def _load_pilfont_data(self, file, image): From 9f79e4a32048863f7f0e2a10c13d2177d6a3754e Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 4 Jun 2015 13:08:59 +0300 Subject: [PATCH 0282/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index ae485811d..9c8c04d1c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Add duration and loop set to GifImagePlugin #1172 + [radarhere] + - Ico files are little endian #1232 [wiredfool] From 997e0fcc5a701ed6f70436d1e76fa3ab9dd32fda Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 4 Jun 2015 13:19:08 +0300 Subject: [PATCH 0283/1037] Pyroma tests sometimes hang on PyPy; skip for PyPy --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a75c80873..f94f8376a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,8 @@ install: - "travis_retry sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-qt4 ghostscript libffi-dev libjpeg-turbo-progs cmake imagemagick" - "travis_retry pip install cffi" - "travis_retry pip install coverage nose" - - "travis_retry pip install pyroma" + # Pyroma tests sometimes hang on PyPy; skip for PyPy + - if [ $TRAVIS_PYTHON_VERSION != "pypy" ]; then travis_retry pip install pyroma; fi - if [ "$TRAVIS_PYTHON_VERSION" == "2.6" ]; then travis_retry pip install unittest2; fi From 16261beeba0b63492aa9a860762b509a02482825 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 5 Jun 2015 11:16:33 +1000 Subject: [PATCH 0284/1037] Fixed polygon edge drawing --- libImaging/Draw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libImaging/Draw.c b/libImaging/Draw.c index c21f7cd83..d99008197 100644 --- a/libImaging/Draw.c +++ b/libImaging/Draw.c @@ -457,8 +457,8 @@ polygon_generic(Imaging im, int n, Edge *e, int ink, int eofill, if (ymin < 0) { ymin = 0; } - if (ymax >= im->ysize) { - ymax = im->ysize - 1; + if (ymax > im->ysize) { + ymax = im->ysize; } /* Process the edge table with a scan line searching for intersections */ From a1b71d5ee0dfb7435b575c5abef56aa706dd4fe7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 6 Jun 2015 00:46:50 +1000 Subject: [PATCH 0285/1037] Added test for polygon edge drawing --- Tests/images/imagedraw_ellipse_edge.png | Bin 0 -> 602 bytes Tests/test_imagedraw.py | 13 +++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 Tests/images/imagedraw_ellipse_edge.png diff --git a/Tests/images/imagedraw_ellipse_edge.png b/Tests/images/imagedraw_ellipse_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..25a95a6018a1b08145e9e84ce6dce47b4f09d403 GIT binary patch literal 602 zcmeAS@N?(olHy`uVBq!ia0vp^DImEPWhv9{%UQ=JS9}lsR;&Mis;;ayHTEa{<~_e_TVn6*?k>0a z+IO>9S=pDlYp=iJ&+dA;{7l_l{j9E+3(piCbu>A8L+kb$F{ukvHm^J7@Mp#>As?k{ zv({+(ST44DU3-3!=0eq^sV8S$nUcMFkK469%b2cy&7L_YHn_=ZQrdbx?SNI^QfGE) zo)Idy^$vsMD z&YM^!sBW-K`_R`fKS{MghcSgSCnrLc#aqRjA(H8Yki#?vkP5Jb0#In_o8NtFg7$pJ zlmRNfvG1Sh98HdC9uQ3%b~4&MGPRJm2b;Bj(~Kk9NycDyJ&>*3Ia#+jx3JGl!nrX- z>><~jDuqZU^-Ubtr!r1b1$yRbL28wHB-8Xw*LcGBy^h%w_PO--`?C`397 zIq&X+^v)TcbJN!HNgEzMp84&SLE4d=)sI}%Q++>+GS3lJJFHn^TCgvXH+cS>pUkH! Wmu~IUIp+#YGYp=telF{r5}E)4=LQ)7 literal 0 HcmV?d00001 diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index a1ed20a3a..ffefa6504 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -124,6 +124,19 @@ class TestImageDraw(PillowTestCase): def test_ellipse2(self): self.helper_ellipse(BBOX2) + def test_ellipse_edge(self): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + + # Act + draw.ellipse(((0, 0), (W-1, H)), fill="white") + del draw + + # Assert + self.assert_image_similar( + im, Image.open("Tests/images/imagedraw_ellipse_edge.png"), 1) + def helper_line(self, points): # Arrange im = Image.new("RGB", (W, H)) From ed2cca1e7118b9cfc700c843ea8e05c8efd95b4b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 7 Jun 2015 00:09:54 +1000 Subject: [PATCH 0286/1037] Fixed Tiff handling of bad EXIF data --- PIL/TiffImagePlugin.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index f89c5b784..41bb26d42 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -426,6 +426,11 @@ class ImageFileDirectory(collections.MutableMapping): for i in range(i16(fp.read(2))): ifd = fp.read(12) + if len(ifd) != 12: + warnings.warn("Possibly corrupt EXIF data. " + "Expecting to read 12 bytes but only got %d." + % (len(ifd))) + continue tag, typ = i16(ifd), i16(ifd, 2) @@ -476,7 +481,14 @@ class ImageFileDirectory(collections.MutableMapping): else: print("- value:", self[tag]) - self.next = i32(fp.read(4)) + ifd = fp.read(4) + if len(ifd) != 4: + warnings.warn("Possibly corrupt EXIF data. " + "Expecting to read 4 bytes but only got %d." + % (len(ifd))) + return + + self.next = i32(ifd) # save primitives From 109ec638d9d251d29d0a05c3885dec083e6a923f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 7 Jun 2015 00:04:39 +1000 Subject: [PATCH 0287/1037] Added test for bad EXIF data --- Tests/images/hopper_bad_exif.jpg | Bin 0 -> 128826 bytes Tests/test_file_tiff.py | 8 ++++++++ 2 files changed, 8 insertions(+) create mode 100644 Tests/images/hopper_bad_exif.jpg diff --git a/Tests/images/hopper_bad_exif.jpg b/Tests/images/hopper_bad_exif.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4cfeea1dc473e941cae1d28dee5c7936c87a210b GIT binary patch literal 128826 zcmbTdd0bN4`!;N+vcf)DR$7|cWSx$pr*bIAQ)XsFnw0}MWafZ~WQt;nd{5<^TAEOr zlA5z|zwfueH}+_jOK-?XCKHtkW_=u**Fp6*s_zT zS_ckl8=N@#`>8)n&CD&%owu~VInQd(ACQCZ(WB2%c1P0byhUEMt&KK6cM43CU5$HpgE zQ~c?#-)6qg3g$#Vey*&F*CfBB8*CMOmEZoyHvgt<&F0O@7Hv`5)@@t=wQbwC{f}+` zuL1tY)c&>qRs46q!BO_P-3Ci#`@dadr=q~l7N!3FzaF)7(TW}5*bSBK77G;-`l&Sc zGVUzJ2bnU{gm4^aSf11>jly3q_;Sz)NP7wNA1O=rB6b1d`HRf)1>E-~xz4n2LY%$m zthWSQ#G2E}T}w3EQqQ~;-gW2QBg7fDYoa*DnwQ0$7KXF`dW$KFee$|n?L8vg+V{1c zrrnnspDhw!k#mnPU_)g}T`}CWj2D*>#CY-RWe-k>6fu$_K74C!rFI7VjfPr4;e3-X$>PzGE18<#s?j$(m?4);m}*dwMQ?e z7%@Qx=_7D}K{tm(6n^{Av40SKU7sYWx=fYn5z1ql6Dl#8ZY?s^{P5=6dbc@aETHW` z*oI1!XZ+AM+!Jb^o<-)Yl>(3>*0mQul!Q-tU;M+rp-*>R6a_WX@&4bGJit;V0iz+Cl{v!>^~&>Nr=muM#8qG*eKEIZ z)f91~#xh|=CScT@6^<#B&4w|=Q-8h@_m z^|-BdYolHa32^RISUJ}b1`n#ax)w3_FkmvJqSt1z*ocaQ*tS;qcm~52*2}dib`5uO zf4-OQPf!rHC7aTtZ|;G3!+fyU}DgcH4B_}sVBm*C5RsX!CA7EqPs&;(0zSAUy# z2>%SeIf3(T&{v)7Jqy97#5olSq^sPjvM%KDQIu8sWp8_z28@MEJp$CN#T+wdC1n?T zTG#4!2@88oCQw+3<5;JdSnU-YLTP@YIKdz_OwD5gr40Ug>kH9H|Ek{oxWZbtFWcMe zy>+h5o2Co}I{R{g&7C=IIbDWw9RiLQ-)yaazA|ux7|D)?#w5Nou2D@3+9N6`07f#8a=#8cd~MFisYcs(weK^@ zgK(l5$cW%Tl2eP#1?Uf!SXKH<$W#jGc7+ATxXM;l~Ow93engJFm` zouDtWa>V+LD(7k#yKNY>m(s_5wjI4q~ z&129Tc#k$3J1D}xFrrN0uI=`Csy+GQv?n;8-_J3#+3b7+Sv_wR&kK@QSyRTOn4XGC zYyiu5j`9`GnCkR%F&6QOD1HeH(CFL=m(~R z)hN1EU)3DR0$&%EH4K^6Vf0gwB_kvq4g8s4G+cImML=vjgzeC4KF&%kk*659h`U=e zA*nxLqz#pY7}MG_JytQ=R>?%{J4B7i+c96?q)z>S`YiCp&#_QDwP1H4q9}d_OZA1$ zK70Tj?+#?;_1+H+L#(D8t78BcSeS>p7spUST=u?*zp5&sZ1iYY8r)eq^LE@W=jtFX z_G~H}H~fVYdA=_Xt_AWYYM?^RW1Up7ND^h$XjDT)YW5gD84+^NukXwhpONys+iCc# zXR#eNs0euhKH5COptY?5*xhMNF6|474w0sd>#9Od>)geQLh9#OHZR`IS%wwzy>~$` z5TY%fz8&>d7-H4tEH6=aOm8Q*`ghsiFo$jSyxiE-IP&&UY=^^4KD>&^q2(ezO$h#! zFkyb?s|BpEV!f8qSR8h^dtt#tu}MeSuU*9gBQbEv(UF z#gNax$+&`yn?(XyP=xsmhung>ZPy^|lrTl1rUy};Gz>rLF{m*cEK1C*+%{@`yW%W+ zZcXn-oOfG>C=2384s=tihTKMI9Go?KVNxlx-r-6Y$i&w8Gj zF+w&~PgC(S*S_0h0ckG7k({tG4}uri4m3DyhA_NE!S0^5zzIqwCcOk=b;KX6LK0&p z=1r@uQzHHCl?CPwGhHjU2F5ylPth=9MW(LKMiZ{A0?dE(e}X#i5fr-&A!xvI80CJN zJct2W zB>oLUOt78H(8-Y_%jCpXgRH({Y*2{i+P$2xP-}ZWDE-8k$Do-Z;I55%*Z5n@Y~zYr zZwlVO%$%rK8yl4c+DCoo<6|0iSW*NIGgO{x9vc^C*M(KzP@ydCU$O?2hggopV|u9O z#c!0;lD%PwCAo{$@NwU|o#=Hgl69;t;qdj*!zBN50nRN-dT(vb1QD)RQPzF9<*A@HXtO*eRCuzz zC6O@-#5!)Me97tYO(`%DQIpBOny@u#O>tRkH<>rS(t^|!NWYqEYDVXNerDFi3*J!4 z6!?+bn;P0IG;}5cV^1N&zKmdv?81P7>@HujW2RY)cZ8$p4<9d&Pbkdhn|W@GrPt$7 zWG)N#tr1-u)T?sp6Hpp!GDKXjOkb z0XwI^p8qMIc!2}b&?A+8hbtj!ZKNM}dQ zcgnlQVEP!tXK1(fla?d8wmtzsPxtkP`NV8EIp)ejzB&CKOJ=`?4bD-E!&uZ>7YA zM08EDXbUhR;!bX=sXG%_Z~6ZC*}pC2e=kRf|1-2?idCDJQVPq)C+D;i#vZL6TuqO- zUO%y+vcvo}E`r_nrdvzwFY)03j|H{24nr4oR{E~+M~qgEz+!}Ah;t$e9I~O}$uL^f zy2V&cppB5CQfJgom1KgG^LGn&rvNzLsI>jlKBz_XmC!d4O>*FSjS79baG|w8*mI^qO-iAUKg1}WVjjW;ZvzWMhe2F zpdvhC%ElpRc5g@g{>H&-YhYP0D-L30=O?qxFJ*P%qHS+}Fw(oH*;np}5x+8C`HrW1 z-;`Z=Gx)ikeHm~-^0ATJugLVVSZEx1>T{8|W-J_DoR5FRm zTvX6w-i`+^Uo}I~nuhrV_vKXlj%m31mQJKuB0J65UxuXn>AR3wCr`Dpb7W+@v@mN{kaY{>4CcI0i}-fZ z)7NQhTOM+2SM!K=!OQGC5cJ9-=_C2TUA?o+BZH&GqnC4HN}yiQarK5zK?PDynMuVT zc_6yuO=@%A^}04%ZV&sWVz7AxNUSw6zWd4yl!wtX(U@)C6=!>_p-rZjU2G!8B}9~a zkN1_ow`oR)u?+1TV|Y|@Nd+NChqgHMV=}fSRcJO1yRs zqK4e|aZTHrXOuOkxrFl5d)**V-_PN#A6Y`wH~`)ag0NfY({Ml_6G`Hs;R_bhfX3WU z%BTRm5dF|XY*6i1^%ff%qJ24E+tVIE?`qB{g!!rFg|w-q$xqsPuG?EA$2mnz8QS=F zt7EGjiw@kp-a@N4hvc)py&>apb19OInztA>&~AW!Zl2VBvD2)Nu5%;Xbwo9J6Vc=; zISrbAm*==F##e(Y!T$X7B#z02M#e(I=rd{q!=Tx*zTwq+{+SEazzUh6zACsHRvUD9 z#J&XmQqt%6V-@;$84tdD&YgQO3lzXG6>5Kli~}D_ZiMQ);(l=Fpy|eoTti$+$$BJT@hL!~YhF{UZz zJ_z=`2Qx(~(Tt+EY0y+%C_r6=QMDJ-qUZzBv}1wbq4yS4u3OwMhBubBS2N z_J-l=g+zIpbHQ+z#vC_{T)~Bn z$s*JGqP6ka^1|v6J?C*F#Hvn=#?RY{EXH>*vd%p3OZ#8sJjc|Zmj>5$W6F9f1h$8- zkdoI=!^+Sev(i*ArxQR|o!I?JpU5Lik@J|n-Y?o-)z0sMz)w{|Lnzc_k2#2yk3$LC zP@m#mnfflHHP((Lwk0SSEW8lY0v*=7H_RG(t9>H_1Gp@@TrUXFPb8<` zzT2D)ebjT{(<~S!_N72WEFjoU$<6m-!mc;N)(*AU03M96oPsI1mpcY(K!gQ{nN}2A z<`-00ulbHgX=lM*eM2Ze)PQyu`?`G&G8U0z-9BfRX%AcJ9h|Gufe(2ERcmI1{azR% zv_J=nA6@9di!V``C>;2%!c@b{Vdq7z*~ZZS7v2>q3A8U6Jhrxy0rL-TU(TG>l5#gx zB#vZ&qwKQHW$E0HQweP{{S~M#(m9S z&f}srwV&j6;iGT(bPaMz)MPvW$p7*e^5mHqDllL}MdN!uXXQsIWRmdu_uAqOm3uPh zopW}&)~DXWo$j;RDyoB7$ix!IrVQqoXWM8fk+CSOHi?`U>6Vc2TD#?0iwD7zQB!%J zer}RGZ>T(?drZtxuz+>U8wWlh=mIlN)(4CSUgWNITp6P*tN-!&5Bhz)7DY_?S-!XP z-cSdF-EI2Q=PKcesr#UQeU=Zl6C7tqtA96QX16eKAh@}tyAAIjUg3ku1h=Fv&Rr2A zj^*-^C%6ZX2V|HdDz9wYFAr0U^7O24FRM>=N*=#&+cjc+lNb^oMtIw|pG18aK&ksv zt?WHkm+&e+o@1pJdt;~S`LNt`Ckh6yWvla3o7L6O&fPxe#}IMAFGL*|kt#WM29tN4 zAq;4x5k6hUIU8@k{T1`P*a~qo!PQwMSCQEsm?=5-y)#oK#9R+3k(DbUJdCJ}!7Ol6 z?-&0;Kb$eC`=`f{4HI7a`AcgCQ{3-Sb0lN0(O*u$M!AnjzoRxu&u9?eslB0+WGc%) zh1#)tA^6CiOGslb%=oLH9-W7ffjfLoec~vC5%=G&l;!9XBo~&g{vm8J6*^yEzBso{ z7UV)3S>3U!_G>AqxtT_5qQS~yJbsO-i3x`}SM(O)XvB15F7oIZ+BV^N+3)M;bKW;XUxqO}rtg73o|;h5WfS-oz%fr2t>h~;eT*P4wY$Un z504C{Q))%3HvZ0Ih_EeHM3@#E89(r~+V`$LJY#W7!zZIYz@^IUR{`MK@CShkFD#5V zVZ|pbv`@FfI}1(?o2hN60LDZ1c*3|qu=yO@aYQk^jrGQ(EXSHaim`g~?rnX{1IItM zJv$Pypx>a?Z6Y+;AKV5=yQ%2Cw7g#X70Ol)WWk3n^9Ku**0x-(RZph#qaHOYPM~yj z#q+u8$Ovt0)RYa)BQRvhM+A~_+4PdC{af@!zFRw$JZd(vNHh-JCXW2s8L6%NVAj6e z^9m|0wkeR>*oePLrqJGvspf_A(a4(OpK@FAcb8Fz+7u&WU?_4m-Ik%#1UZ;dRWc6@ z?Gb-6oCv#KaeUBwp09(xUXDu3`LS?Mv_&i4sCi_?*bW=CQ%da(FAdqv^TIT&oq11$ zG1puN%Au_TIapoV*~vLxU<++@0a=U#UI)#CE!-QJaqj*D5?7?;3Mn@Kt`6PaO?j_u z@+*OIATd!C);&lRVJk7Gq=eqo78`@Vbr$;ep`G{^CAXWVsd>Y;IOdDo_l=AovfWU5 z7QdmwTT%vh`sBq8m6TdBb3=tAuM+zBY^VqagaX?!d3L+dbwlMYwj=x=_M?^JbzAp_ zN*Fd!rmq0B(Z$GxT4|y@PiCR;)1S~6Ay-W4CvP`5#R4??`OtZt*u3P>(4u>-{v{WG z>cmC8-zdG_thTZy_4bTC(S4Lw9##f?qU5#EGt;2WiH z6`#l{{}_gMB_! zw$c;itIBQ_O`4aKvwYa30h^{oPkKgq?(3oC<9qW1ZKAl)iWfb23bINSt26&R%ds-w zjo1C>B(x@%vZfaPkhw6X;dR!&`u*{TA78nin|8>%nRT|->Pmm^C)!B?(WocOkBoh3};1A;y#8z-`dnat2LuCm5k2v{x z;PCYVs}w{@b6N+VVRJ&KNNSdta56bLlDFVo_n}k(i4#9$7Dl0Z*WF~~^rQ1WGKZO} zIG@EGZS*j6Vn&6*>P%VmmMF{Bf(j(x+sZ+8FbS++$#lhqB`|TcLTki$F_O7Fy{H&f ze3UuVWJc_y+&2ta+dIyK8mAuf)(T>q&PK*q8{@UTa>a4E?kPzr#KpqVFl~B`v6(D$ zI|345 zXn8BV1z8{55Y#BNt1lU>Ha*Y2R43Y%KxvpZ*)!Mt!@0)m;HoT^9;F-91$jxP=6;%J zn|Q0kasA=(1!UzP!m8l_ICuWO!zAmg!ISGiB^8*6N~K%2shGDl8$>LcBBC8zmgfXq z-3B-aVJe{DSn*Xc^7L?dPEb$~Om}*A%<{!gw%3Ft#6t( z;?;)n`Aj&r#2#tsksgA^cinW_XZfZeJkCFne_j~oW@1z7Cn@&%d->;-dlxp%Qka>j zIE0RH8CzNfTx5e1LZ^VcX)R9QQqKpTOVk}?JD-Ai@4x%V^T3mo32f2Q1W2*N;0y1G z?TH~e=m|;}W(mHMM`$zYI8+9@DB*2Nbf1bm zqxNf>0+7VF1E4#_1ks7sA`_{1L7_#`5_@2%%`-9(u`&g-lx0nzq60C{#n z?;5c5Q`xxPms~v?N%-;~Kc@Mo5q3VJu-39UrWQq?b+`YgGb41-bUx|F^y_YbFwrnA zt)dKoBvfq&CsAY_rmzDrxkONQ)OVaF(~=puiySx%Pb`g7$7daM{`8P`SdGbacxv6v%YsLmSp zmC6f>Pc*JEP1}wXuIB4sX>TUy@2V&-AyLE+AN?amtd^{H|uRwDl{yDf2; zl6BDX6U`s+W?*+K{mGaHr{^#_DAiLHXbgP%oby+f=cMBi^jAt_PE^vVqu7XCu&}?l z#B)vyr={(+<|YCI)7cltG;5D01_!`n0{_!{(8{CR=s9kifBjS@VUD-ecF(-4j~5qh z@0cM~aG@}i4(Q6ls)18&MtP6ne7Nq&8Nd~J)4kMN&rME0c-rvy;a{6S*wu+EO`)~> zBt}qyRG8k)XGxHb&$B^zFmQ4+Gvvu?zej)Au?C2;H9AW7nywbArV2=d2&-gjN8AK9T>bdgIBU_dbH-qiSOU{B-|G;A+y_bMbPO`Q+Rj=V+ih=LE+u{yU*Ny%nVSxYQo_aqeJ<2Cs< zJvKf&gbJz zd78)e+PbHU6~KJ8t0Z_R#W9KP+(c;z%+fyrx3N8_^H0=`+-tc+vC>gSBa!>G+(c(# zI6R;nnwByGaXbVPWP4LmB8{{n?b|qnoVTQ3$M(e*jd}g&_q;co! zB;nQGAvo=L{M0EF01DEMPOo-&G{L=QX7zF!4@OVt_VwoWU+IM1@1l&A^+d6bWXxfX zp0e7cryRvQcGW!y1NK+hbkW~>szu2VB`w|@>(dAdnlgWNc~zWOi7xu(>uBHh9Xzr?vU-1%O?e!l0R`rNQ)<1wyp}|Fb_|>kYU-fo1X@CmYW4FN< z*B$Ria$LcFG^dgf+hggM6Bf%5un505@RV>J&ZK!x*bnx#$TVGB?m3Q2HU{ zvHRH%C-6bV5CLUvE*JCm89KY8IWcKnDT3?jbCvfrY+J{8V=I2p2rGSrmArxaInl3& ztu-YmMNS1Y#y3zF@O{GC9>&+-Ex}%7ePYWDeTGl8QyJeHmFXG~!h9-_s!d|$DkJTh z^snr0e=Emx)ITRZ+0agm0fhjp$tz0-IX5t92j3W*8s__VHg9<+&w^rXS%8Qe!O9pD zGJ0@TXCB_?eZ6;)xatjcZfZ>fYbO*^>*`0BYF4Xa7QTqQyGsd(!qO0JBC%mUevA2X zh+^;1;$6{xaYMOT*Jr<13ucB&e1|akGJ1qE`V=4G98oyQ;(2?`LGbHGhkSkPYs7k* z%O5`IYt6b*?bM-N+TxUe87QRh&HT-St|z0KQo$bM=b&v&9T&w8^h}+lGrIjg1$u7>9eIl3t*SLng%#}1a|1mq*h!0;TsxWUXXu&1SoFf z_y_v#Sl88fVK4BgAJ+HU`W~ExI6lP&617}Lt=(1MG)G9{4LK%P2NxpG`?|FH2L%fP zkch97&ik%DvrO3po5_Gygc;vc+z6}4o&LF5+|+yVKee<9Uz@xn9?wI3q+DTqJJ#rV%vVVSDi6A=<{f=oGQBNv zg}B#wEYyr+>uu$kbn6ms!stZqZ>8sIzIvbVz6dR$QW@E9P9*W@3DVDFyl<8mWlneN zi#*2`5w)ZBD(k09>j+9;ea`Z6TLrAlgX>u4!_7<6i2-JD$J~<^I-LAGmiV`B|8)!) zEq@~vZCj|2?Tfo7uU^&punfkFv588V0Ez8`&RY3Hz?7y=^o7Gi|7oV1jL{#Ye%Z$@P^Zw-NE7W=`@VG`GCHQAQ>g_xzaq2B zbMoyUah<=d#Te)4L?xR1q1gbHWbrNXKGy}M`t3w+;{&kp_PU^TgByxZgq=olLGl8W zjYX%J<%ck#!!5|eeGr!EgT9=P3op znd|4vc)sIvn>`ZBo><%8zC=j{IuzP9VJ>oYuH2>4BH?-H7aw*eXUy?l+8tEvT6dJd zgNj&_PURrP8<;dKyAX}<{1llNQ+teR7m)aUL4#g35hHD-uiM=jE>5^Rv{*D8sq@jq z_yhxm)vr_ZV*!#pAA-!9M+ES0M zXdBNB&0S0WC28k71t~9~p~7IkTrll2EXKa+t~il~#D-7Z#6)O`QI*iHI*J*<(le5M zAZupHp#`pMsaC5>__1e0rHn_}P+@;(#VhmAk9|quXu!(Xk#ZDvfa-O>05VM96+~@GHK+fFlyyo-@~Flw$#&o_ z0Ipfv@|F|Sep|anp*6i5S--lrXHHwUGo<3U#aZZaNGt73Qm%iwwS}H{YbW}VpTTGG z!yMA{lHoaRpQ>%?W>#mc40A@q%;dHT{*OgyY|Nrj4esD7)NouI-bXi|Sh#E1^{{Z{ z1mn>xl~q^(h=<}DNBo#eE4mLClsc-=$SSM8mb92VWNHfZCW(r$ot*ruXz~0}I4m1P zPLFH>TMGTDqtn5flirY4YEt#O9p7fNo$QaFe+@U3N|5}jj^0=Ma`^_a~$<~%QNDd{QP#RoSqMpPYs19!S!H1VgiiJJj zT^WmZ4Py53Gr`RZ0}+RYN(u;vSYE!Y2eTi!dZ~+iQMKv8Rp;Ho!CKmyIr0+N7fGCL zq|!AMpb@L*yp`FozWnBV;)o#}HHj?<^7GCs9GGEtKn2x zQ-z$$i;WEr1TLP~la`p~I!yjOSNCYg{Pkg+hIkCu@53B(eSC>Z8%6?!)xd*c`>l5% z32$u^DknISq8=r^E4{_lw@hwRa>VHdrZ#_YzilOFIUj>KAomxC+&n>mPptxwdhay$ z?z1d=wD%$ydNt`bH5c9&IZ_K;G*%AV+gf{}ERtpQj}H&8i!-zulU$!CC8ri&&28B2 z9e?<>tjc+TZG&_jS|rIY&E>87E}S!42p0DOX)xp=Tr@DYG?*KhvVZ;0oYZ-+qALls zhZ9nu`?zZ?BG|DO>K#G_9Aep!M_XIlO5!35&FVaBa^W9QJ4!7c^e}up1z$Vl#-@}K{5E3&0)y+;;Bs4O z@aQsVH9&{OQB%SJbY`biHKDyP$Rg2rLnS7P^@L$x_CeN|eH|X_t?|3QlhwqchQN41 zFMMj%F*P{y^lzr>UqJb4h5$DOONpb*EpvLR5x}vVua;0z?PE2IGr6aV75#GQK>6i= zW(Q#cO=5JVPDSL$EOD&+@`sd_;hULXu6n&C>|HcAHeN&sYlyaNr8QwGY2ZT(>_e7}|och z2<`U~Kej5J-o6{YO}ypHy2#tZaRJ2HA*dJFpU9rOe31eOO%7Fk-SgEqW%`ly+)lw3 zF{2wue~QP|d;Yz3u~ei8jvr^{*#(%%1{`q&?v9H`zDx0koQTp_1x=|f*ng>g z#|lw0xiq*^?ssYL1gj5KMAKyXkO3j8dytW+^R>o{2C<1h6GodSUT&y3w`r~emruS& z{stoeY!3pJV0slPkG! ztrX;;wKa{&PdMD*1iR{U_qYQx?D_khcL=z1&yP8D6jlC56@$IezN{1+>aBxBzW*Sc zFNnGe-bJRX@>zDf=5lk|-iX)4;P22Eq;^_s!4Cfh^CI>FE{Zt_F$!_Qd48Rc^8OtH zy;|%0zTc9R@zq&??_F)?LuTr`B-mS%(i5*D{7|s{H1RX`gip#z+$V-IkL6;B3>&jB zf52n^y3E>MTXQ(^H@kC&QlgTFy1DL45n=y%GgADMn0UsgRE4^r2%t2 zw9GA}>6~v*pZA6s1b=zN1ABxC)9aIBiZfSjQ-81hsGrVNr7i1s{|bw{B25y9HI#d! zpA$(svUFp*k`N))N(VkY31uuz<2+5}Mj^I)zpq0p z=6#Mg0m}?}@QB#rUWCC?B9O$!9J@ZeFhzX@X1)UG95ih*g7hmnpen%%EOI=u3JA{q z1lI2!4w|%0m?B2$fI!OR&RqPtA1K3_QIi5?cR7zNY0Lp#QBR!2LgQ&i@3Y{x*3reI zBf?0|#eo}nw_2G>a?bfS@aDzk(Snqzzx~=eZ%mZ5mZdKEx-<;)v$#&i$6EG&h~7m& zmf9mjo|`ww%QSPj`pl!PmVFZVWrwrMg;$DVvE2csE_xJ*0@%xpdDrWjWKcmAMKd1J zpTAE_Y))a~q=8SHlf+N7L@;r#TS*)aZ+Sg?s!I6RTuD(dzCuH_mnE4Uxr>FRtdvqE+i6$m;UR%v z9bQ_%4o1TAx-w^|Nd=;5ZDKXG{yj=FQ=VOT{65cvtE60W@^eZ?adm#gSxqLM`N)Hd z*1!0Uyu9ukntfHa>$yo1ZBMzE8OeQ*b+m!j^(Jp)VU1~3)BdB6eu#M<4WlN0WY9S? z4n%#ww{~k+oeu;&^)c>3c(LcS%$3ohYpi2cyNl-qh0>34-lGhj;KbVM+(###fADG-{k;lr%Ov4{#!fFN+*qY-hd$ArCfbAXVo{_*fJW1f}#(% z*XKgJVuYn2VZ8I+S#80FioU++%rF*Ptz`K0sJ`^k6Go^Pjc|BN8p3K}nGKGJEk`Hacx9SUAe|V}kpB3k zAq8F)tiXv67E$>pNGYsyA>$?94v1Fj*YM;umx(Jk5fN!}2(7 zZ%>j(-PF*;Ftc)`&s6QVG}p&BA4zrCI>-Ydx$pmf_03{jZepbwH1=agUAbp#Yd71w zv)pVn5qBgQxb@Bero9Z}_2MXY-XTT?Ea3b)Oku2``h3Mx^2u`f4U#V_r> z&hVom96wPvR4yd|jp9d(edL8GDA7OsPT z0f`K&pA4Y`daBB~pN3)7zb?#he7q)TSuqjLF@=sc-1c&K)K5rW6`Qbqj-|6?Vi!~9 zJEyr8?Q5zf1+R8#r}jmEa)!kQ8ORxvS6Jo2Wsg9Ddzees$Vy73de>&f1>VXxv571~ zW--2@vV2>NY`_L!M=Tb26YW2@D?VZHjC4i z&FhLJ&YfDS!K6$L*hMZ6(wuROa&W4#nM%9D&+-Uz4Xpth<4(QF8QZEa4J&GYtuF!k z_E^z__;!yJsuLf|p&n?YbGC0AOq;J}C8J;a<@W7J*6&kHDw6YbfW9B6;O%)d4)BEZI~B3>kW&=i0mFZ{ekMBmCN$JAN1Q^B5kF z0CaN%1%b(iZ?j^QYRn;-#(KEC6qUAlimvX$paCPk4xCIuh6aKyL8r^#`=OaPKOQBw zG?F|6@`>GDp#sF2!ZW{LwhL~b*xc>gK+QL9uZ$UFBNC_k>t4iIrhj-IjwtPoxWh`! z*L)*IeQeHRK|gYA2O=8iR7(uA@WV0B+G!~BZo&( z2fh!6t{8SP&A^r&;H&1KfT-BW=F7747VgiRv+v{G!4bf310VOfk13UlTWg;14&50g z%lEpB`7t7Yg)w52z!jz~+` zKLNQTdHFHSF4p&VC1c~jTXx9Qn`Et@o^c@1&NH4Ia9MXwYjEwo zmPD)aYWBhpy5H8aS6iN|^@bT_aO@(=i&71LO({n(3*NlY_Q$eGVwie%5gRs-4KxlN zQjEof14rC_{aUoKue2uo6M4dAN-@QkG+xhoy!R7!JN(_UdzXoRoY`o=So|Rg-eb9@ z#HIxTiYf9PDD!d(TgVtPr7Y=?LPf>tKamfYe%{zCVu;a#$t*gqb6`SK3n#{gs zYL|GenU-s<&0!|lEuug=F@aee9RJX>gD7_1QImy_ z-s6>~hTSWWzA6pQg1HTpeuz-A?p|J87M-snn**iBZbFYvb(!Rh-T^TGIuAD4f72J|M1}ixrEYM-YG9ep}zN4 z!OU5q!hrJL@Txg;s`<>v5X0!&g^XbritldEdF}qRe+4>sGI-9zCM)RG{Y+!Ce6QKQ z=%}zbd$XtJLWIGvgI>#)$G0u5Lfn3INnKV4>$~$$^`upOAw~~fn6Rd+CtbS!?h&Im?o=_Ca~*pJlYL`D z<;jx}NJ4r(vrtYoPWk?;U{C9?-hk$YipX%}1;jp#i5t_54|`Q!lVg$u#5VYiEJl7E zTjf|?JhyHi@PNX)JNwfK6CPX6?e&k1HW+PB-TD78_U`da_woOK*VWNc{rx_- z@AvoL?{@PKx0~AydwcEqdK~Ug101^Q{H7Fe^{B!RPL}O$;sn zz-^bU7k&le$od-nF29a%JMt zTTPAn?x335&!gmg4Ocozu17C;vqr#N`$-CsZEc-P2rk=!!Sgym-K#?ZppAK3aJ@;! z#cu2YU4{JXAg^8nn<@dzDH~nwA$q^Y`EXTB*YGD@=y(BHY((#L`ms1SbGN|SznWB4 z?O#o%CJSr|G|D~P+gA6->vlQSHMY`8I)m3GGdJ~^Yb=(ne@#tYeE+S-W&vno_oJRS zNBW3eA%C5wKXRG?+B@7oDM~u=|9r+8qMcZZ4xRh}5S37zZ@|LT+akmFjxY=EZ9NJ& ztJV^s^0_)FuY5fM2fN0w%jD2m{Vms~Q}2w-jQXQ9*&WVcp`~F8il`pw&#*m{>r#fFu*M>+0>Mu$9-k=fNnMI7Gvkr}>w~yMhc5Ft`|Fr$d4p{< zR7%9Ww8o5TWd%zRXm-)69A?GaI|8Z3oXtI+>xqy?^bpO1Gl@ zguq5kc}|%Fr8aXPS+Ta7EF*?hk(DnqW5PFM!aM->c zFn_^O^A*y;NS*g-?rhngLf@c3HOf@;IQxXodI1Na2hzJ{w7clnuG7m~PsDONjXQY@ z(V~lD{fYLvc7XzrcjW$j7VRC~)Bas&WGqN~fh5Zv$59bpcQ7&ti-`NVZTTvbVMkE< zkW#+@z*FGtK_Ht*A)HLX!xlp#vZj`?_w5w}Yfgtn zw4UjTD<(GJJHS0?Pjx`$_Ao^6I0Lt64ALy{-#qTp^GCkU3TEt1+ErnfW-Z8=h)0!W zRCi}-9v2nEGXqjq})j#yj0k!^Tq|^@0UI{;;?_>1OoU{KUDrOlRxiC!apN z&Dg<9I+dlO&TdQZyfQkwQpwB47kUwW+aF~OFqUnFDkJ{h?Guk9UlphEPHMSJ$;ve( z{=9zUb?l-?@iuB@^B+@LayeN$kt096?O4|Fm=@qVAAHzPZ*@|oNwe~Jr*(=-Nm|k0 zlgD2@-1F`2C%FU!G!L zp*g${Jb{2~i^_MxQe0=#pv{R|+MuyD3z%Hf?MedN#oZ4a0;X;OHuLy^j;%ZbUPWh< zYwCBYKDRqq3wA5d)W5alw0omnBBdN6Tu#a*#G#iWO^YWvKL@}?YJIPHPq8MiqaaH` zl?wCIW%|M=76EPipn>^@xS8w2d4_pOr+=CXm(y>?&XfpqvAyZo00Mi84|~~U+r%=; z#b>-1$YV#wKI@F-#H`Ihy&kU#BUVymwGq_{?%Wg|3=R5Rt-_V z3Q>h7P@7YqFB&)fo!TcEGOgKM4ESP~B00(2HeVtAOGcaV^tW1jx&vHrv3F^d0_$p> zr(syHfX5&Is&5C=e;&ETDy^`f$O$>^;s=th=#wTFw*zWN+5yvLw?Mu+ze0r5-Jglm zjWj8{qW}8TrgLtdmD;Bwoe=AshYl@SumYiU{~RsmGYj-Zmn1q%T68XPvlM6Uqz8yru=o9<}|b^f1wM zktjafSL7i|ZPN}lVjZWbAMzZAo2Z7aAE_x=`SaA;P1kCnB=lKeZ(;%MPLw za@Y-5un1CfXjU0YKKp`;MwhmPOubhg8|##)y!n>re4xJq+aZJK9pK}NHCQ*gfVtM5il>(RsylL(h) z9Q6~&+DrZ^+13(a^65_QONJGugdG}h8}Ut=B)ZvR6Gjx_e~7Px>}N(|r<}nJdZ)#k7t;9`2(exDpK?^#H^drdMw0#YpOdszBupl@5@c>Y6105SFW1}I zATG7H(5T5LJ*$Gv84AED5_jTyMtJj%2DrI;&?vD9J};-D(dci4@+tIDs?;6ascoRO z3}ZFoni%Wz@ZiJjynpXg#X4Z-4?lxP%xIrx3Z=$n0aNYNE8yOeGhk-ur4T}^{!{J_ zP3Y_(wDuJS1fWadNG6kUYq^qpD_6Jb+->i)sbW;<(t>xI;vuw+*VC$E;7UZ)B^~gc z144Rw*39G7`kT5j%$>~%H7aeH2eeSch zpZx9Ykdq1a^|W}53|t^zn&)Eoe3ya>bNUQpkB_tc^yJ|8`kM3@GgQ4Tf{^dJNG^Y^ zrDS}>D8SoFsZyyjfCm+)MEFJx`BxktZ2qqdcZAF$PUeI-e54)eQ|xTEIhETDd05GB zs;X}s(bV2X-*@kS&z>B&nbwGis64qO&m2Q{sV&y}`s7(R9_Ig%s9L{I`A~-;(phEp zT)~)FmKCb-&yR_30zCCGAd#UCe=lS|P&Da`7+UyBYp~U@8a3%b6D@OOZIqHty2>;m zMsydmcea2Y{m@3eLM{QbV01-2KD!K#Y^turKH5C&XHeQwaG_wL`az*%*1aju{o^wX z`^Am(x;E8BuXsD|twZg;csV7NmQ@A*R(u>Vte#Qc!ih0EZ8*YWp-5M3$BL#z3)F;# zi`qX^e(gg2+ST)q>CF6&31i35A?I5yDt(=8kJa9C%wMqNE;c2O>?@A&^=zwcFIa&2 z)&v{$~_X5 zf}ln@11>99>I-1(JY#7mQxpx>hB_|+2S3Nx)O)0jDST3LxV3e*EgMa^iT+yGY)77| zI^5&qRW>2GK4Y6~s|QC|{YhEDzlyH@wd>Y!G&W3oJN65Chx4l7;g)UA#55yPOr8ia zN!r^!uB|>%Y7PWIxxORlZN*B#oEY%4lXzSK*lG3Gu2S<#WX>2kFl7VyVxo3MlKgKN zNLo6L$qkXS7B3?-W zS?#?tn*`-&elWhEU1IMAT6f|c_fBy@Rh}VOycgw}cDEm1)nG zT4j225Dt8W^9jY*>C#gLu9b(c?u zl7wz6>B%OiZFwn+A@^uDOS-1Ve^cx&NjYZU5r8_-+;rjLR~3t6)u%HziX(oW;RewM z06}QQKAm0P%dL~!K9eic6)nTxDx>f)RChPTJe5Wwm_0<+T{@<|kD7)`Qq2jBYlQeG zwt2fWB=vnsUkm}zAp^qtQS>2N37ikJ%ns0D1zrB2O`5jJwb`To)dKUo)sgHO2Q&(; z)_uWWyI>9OqVDn12vUsXoE}35rNR-6?JLNDxv<=_3dOI~VDXVRVJV*Iu%M8-XC7Mn zf4%~Rb-k+Q-n{K8X5SiqS}dC^MAJR?kj%hXM} z;HYaIz};hV?+=y6B%dG0aP1b6XMoZ1peTFw+qB(3sPq%thwFxgE^gAPAub~nkA?YE z70y8$v~h{8Zk6*BV^(j$jjWsXweSaZEonRpyO|HkpXopk)?@V1It27-#(spNkVw>A zSX4nSY5AY~E5qWL^~tZ9R)4*JFMZ|kF4eKk#uC1P)6-Jd@37_C8#YL-AzD17r_BsOGMbdL4!0_F(z4InYCGr+C>fH99E1wE0JOtr!BmBMD z?~paRslr#x5WUR`|Laqe#_O8~$pS;Kc4&N@(_{R1nHibwV`68K;+NLofiHz!DBC|L zS;(z`Cfe_c1`v4<8I|uWpVq#u%YO1@K_Y(y{2p@F8j!_<#N_&dQ2%;>-;#FO3Mqzi z-AdbP)q0reCT|`v&eft^h_g0YZhpam-J6A8VUYV15hzs@CKy7To1~Rfe*D_CeJot& zq=ndip2$NDq)eTG%+#!qbL1085{Gq=59l#r?JQVHj73Xblbli{KL9e6XS4zz(C92Y zU=NdjKz#q5rX5zyadUCA0a%u6?38`dj*plIPf_YXo zCT#mB%;wSoR>F4~Kvn9v9i)A1`__tSeAFQ~{#t0%{6cto(%bwup$Py#%BtEL@(mu* z3i`xhsi7bi%UYwwuRhh8gw0ZE#3{>LbTfoB=V7rITy|s6b=3$cC4;d*> z!@2UJ1iqUKI{uF${YRMcOM49Wo_Azt$7R&TFiN^s`+5*F0ra%8oV-bEnY zxZ==0VS0Tvr*i-uF|GY{U5q(4DT={aN}@rbkPStYQ%eNAVl@O;wi~;*WkJn33{vfc z;-ifwATzR`Oi!7BBAs>sKh!flDe5qTB5w8iCtNPE8lsyD?SlQ;!kL- zMZqT)N?szDk2I$J%RO*R=i>M8s*-fdvgo|_LeAT5qdXooRKO8*f1iOf7G$LsDYn3^ z&42wXTpHR<%M6oW>kWOi3w-#UE9?A8AJceKs`mD=Ko?OfuM%hVWvaKR`1=}i`FKsP z*q&CvkY%;D{i- zu6Uixn+{Yty(4~70czC7$ILDVJPVb7+PM4j)CUaZa&GDKOO(`EPLWYgAjgvRC}(qx zTH#~7Ai4Hk7je$fz$sg|C%-$yV75Et8TZDR`dp`DdwqLN=4gD;WkE`%pAP^gbq~bB2%fw*g&3#RIiOXFeQWOsjaP*WnPVQE5a*}?iKa7khiM_gVQNp4h#roz?-hK*+63q_E0|M2b8)EdR7*9UDkSY zrfIkUicLj-{XCaus9-aLzNu&eRcofdTZvDpl8?$Ng=m{Kt%{bX1VGk$E7`RAzol9@3Xu71`GQlUDMXc_TP@6W z(1Ya#*6q|*_Y=0Qv$-6Yn-voHNrupY=i(vQ6pw?}op2j;=RZKoWHSpEs(t{im1woC z!s;oY@Rkn=e0-Q;g?kE=I+DGK&X@l5)KnolFvFSK*XXq_Faxc^5v|p33eP$?La&-)Q zw#xm65~GVNqn3PPl;+9kl}FU~g&Ec(1d!tXkaunZeL4ML=YxR7s{J2VZ+f%(p5H$4=^gyfUasJKP7FT(di- zK3w3MkDvI^?G^eZQ&|upbliDICb6=n6$^WaN}XIAT605$6(j2oTwH0G;yWXE1o15S z(w>+nbH`^($$Fay&mun_!6~}|ArK=-QRuhMrlFV?2b6G1u3H|*%qeGwrOOpQj5um; z6y<#@Q(ZKb^W<{PC$s(g+LKLLAz$2^#Rl8n%(j#Bu;3y>A8OW(|Lx8NZI<+E*E3014*ZyCtu zq>QT+rQfR0<%N2+R0j_O=6{dRP(=N+7H*3%21kqj;!a=bGy99 z+9jXbsr&nsCnCg8Oyin*2W&BR&1CVuw+uHvGbAVKPYHuo_ak#ea0Jq%`eg7}W&Qc@ zf&lmF8Ew%;%wj1|jK0W}ISCixbmno!>IQmwsNLKtF0SvMmk)<)V1DRnDJjTPy|KrQV#Y4X)_qaSQ*{Qxzcw z)+cQ;ubn*c(h$|Rj72-!Q)}u+w6(N|>&?OB+GHf`28D(!ZSXB}Qnfl+fb_Z8;Gmi& zn*4Yh7bNGdx$3w$M=R%`4B~pYN&hKRLy!@Y7GciKD)iT`M^AdH^$`}Q7U#ep%j=Y1 zn<-mupWC6CBBNu3jE*mbCnhmyCCDK<+EQo4z4L<}a-p?8GhTc!7De*pdY3rrx z!Il7<0M&$f&~AzJaj_%6ZO=%H2C-{;#|xHcSHQJfy)vGHvt+FyNAUs|6;DsojNdY^ zz5~|+$?}Ufk)JcI^npMgxc$ppWwKnK9f%fhqUI;L-$599_)zb~3w|ShO+y+efW5G| z*#8a2MilA&+U4^y8j0vu6CX^!&;1;-P~j!k+qM@Wr!~-5dhx((pY?r3g!ct#-7~z! zV*+{HAmyX8bM77k$FFp_O|{2zbJO$59jXu&|H93nPPBg!3S3!pM99_O+1;jxO=R5M ze3j1GD%YI@D_U4s2sHMbP)h`l9! z9K*3lX*6%>a46KBp@+;ZNUtD=XIDi(jA0#6gpiyB!&DZGZG{e$eZ$g39gP3|w;kmb znQbt%$3E1#`-zA%WS8X#^kR!wHnR9nS~eZ3taF1ln^GxX^)~hQP1oc0!2DnPqnWOY z-)x)|q}p{%{R&y>-KJz}<@9RhPSHZ-Sxg`SndRn&$bwXOr=1h)4$l6Ju{j-cK}#N0 z$!_#_cYrK>mUcl$-FcV1Q!TRRB?8vZH?!o;Zas-g92)4}vb`H46e`6UNiF(&{_xcn zLQ}|iJt)o+Dy=*G&mp(%S;_Y52y7RbH$m*}xl+BHmy z8rCcQ3NsN~>ozW#a33DpaYEmmu?Ogn0W*qctv@q#2J)ZGuBVzUE^h3`1O}E*`qBOv zM;>_5IbWVsP07ie_J$|v_Lrn>(SdSt!kx?w{|%QGk9YGYw_chYA8XNg&*!Mr1EX@H z=VhE`X8bJ2^20Y3(He3CFW_)CPLB z5gf2&QR^9@m#Q4k}8mxryQ6y2|##pX(&Uc{EVoBYa3OD~M_K;i#C zsq^buAkN&5{j^YWTg?$w9@Q?mK*E zIpGSllui8HoCB?miMXosiui!xuT|34fRRbnitv4;GoOM=*;n6MGqOLP+%A*rlRA{R zKIsa}F9iH#;2XR3pEioeQI{EafUeC^ACYG`X0oJRuy!XY zy%mpO$R^VStO5!n&xb`PW!)QrY*gCr>sgIw|=Ik{lP#Ff% zz5^iasmje49~^F!J7RDyVJ;rzVGoR!78D={ z+r@Ntf5Ne?gm;v(VLJG;H%Ev-o)5Kiaprbyo`?3>%|<$ziO}M3essfnGv0YvP2+4H zH0QZy`~^6g4=Y?IuS}lUfG52TV|1s?Q7cRnW>$}F>iw`Kd}m1!CIsQf3QpFo^uGOp zV#U{fNxl5TwO|sC;KKqru*LXJ!BPjgkm@1`h8gtuqlr}%DAD{nHKB7;ZZYIc$-KNt zHbJ411QeKUYD|lox$N?qL{c&BBxH#tuitAT#$0xE1>ETt=5(Q=x$yNmTm z&LD3*Zw!!-V}nhuk%u>sZAW@me^&&5yYtN2L`k5uf8_p zo4JM1V-7&kAjD-j3L2=Iq5U7#Z+_h9(kcT8u11|gDNApk0WSRBeD^crM=%E?>IVGp&z zsggA{aaHvB{}{*%2ysxPx5yqq<+&djYq1~*Bw&o_1xs1(;t)6$75V71#lA|_By1^v zwu)H%s&WvCI*cl;ne3Of zwbi*|B>U$NDM#MK?>_@Av*lxRSJBpAzQf%}Q0K-D-t-%$-Rz4N!6$Vn&tjpHY zmzGZdC{DJdH9W8SZ2fPW_wBTOC!B#3bmf-Dyzv!b+VC=FcD(Bj;|t?HgDl3GyXQGH zhe3cGuBUEWLDM7Jn%~&IYv}v+mYQeV41!jbSL^}8o!_Nxmz?gVm#oN~U}c|`(NBvv zKPe)CaO|R2O&WYk2>_mP*DD@XYcFnS%mcpynLm~ro)_m0;=cLcy{P|Y!2dn~bmCr9 zUytLFr^HULzXBOx^XcZo%iknN7O(Rh%Cz1^lGd{@_-8w1C!#vaK~I;~D^r@aFP{mR z>)QnG1Eycd(PSoRryee-q1ZMG#Y^1leZwfR+6I%HO;G zsRo|qckRIR;k9`5X5l|34sPtJd0CXKpYp_G58^)t=yh47lp3edYqy(Cm|B<3LnOFp zr}6=sq*Y{~vuRL}|F9ZYh$``gNt)mJ8BB4CR4mF>zx8WZjJx#&XUdKl1j%XZ)eB!! zuHMH_Z77tJ$UE*^=WRI^M1J_Uq;*v~)`?DB+K4brD7@RyKz`tx3yr8u!~@$P&d;>U z!AjOZ4tHB<8U8`@Wj<|C_%N2Iuo&GXIrEu8p8Crd)<&t|!=g$xqaQr;JG7NTi^rA* zo@Zj!3uLJL+(?7>f~OmQM#MO@rgDUqOmO z?~H}@esocK=VmPm9B$i|Du=4VKYKoEuk{6kr1=UwrxMkvY3g`fU_q>>jQbwG7w3b@P}`>ur^%i;4;~%gV5C? z>WHSvEz}Gyw6?yM&XMq3pBa!}i2UJus0~2s60LjH!`yO`B}zP6G|Ei7k=V(-+~ z{yhaB2q?0-`a^S=sI9r+O-R>u}Go0G6hY_+ZW*e}N*d$ju!}P&`V3i9p%MMSHAxlLe+N$Dtx%O{P|G3n>B?%jv4@~LHx-&Lx)8UH>hD) zqKDOrSl}$DACP`B5y~T< zUFy_+)rV7zUmqd0p90Vxn=%v4ow?3wZF(Yj$5!ua3=znI9EGXpKe?wi)s0_E3H=*~ z>gp&76CPlQZ%-B2zJKSWB4t{`WrfTz^ToQ`3d}Z$pPn+Sa3boi*#qEmn=JJ$yCwsQ zedrS{wA0^rwX{HurVl!H7YfXLQ3oRB0iB|KJE&$gCqXAYDVQUNk?}L=%`CVurcNGx z*a}wzYHqa09mmCRcE2$6R<8o|#^0aZEoj+B1bcQh?D3Vo*}SpkUt2 zUFQ0fmj#`?mRm9mH&^SH3qCq%+#PcLH_aE5vMP;NZa*IkIqx1Tks8!{8|`PhZ^E}p zJw2ja(aWw;!f0<9T_ml~pD$fowk$l99rjyOCQmAP;FNIlvF1hkWR=q;&doMJ1=?$Y zIm{6abTpAtw1E9te*w7>$DPHzDK0FJcp3(?I~ZQYvV4tu*c_wct)SFDkl}|)10p<4a?qY=NJ~igyPGUW%JV4Mk*bHaIw?cP{q#bjl!ups zb}oMSwacJ85S1hQT?iI7*|rV}Uj&J{G}T~^a((6!JmqTDw`OvR9Yfb}pfL@#kTh+yU@#+2vjxs!MN zxey;b@ZMz-nU#Lf#nob3h_n&ICAmjxL{*97S+{NLQ_F-4*G( ztp2uAU0dN1-D$5!dp{pigu&vD`Rx;|N#%A=B84nfJZ+YHXC!y&0~0jTi5IIfif0xaIs-kQ=Nl|nj)QPD44Uo zYk-0ZFbA8oEp%v5y{}*I=`$?1*>7sH!CXOS!qZSJ%RmC+x^Gq2oFzZ z@^~C$t!XVybe=;a{oKmV^CT~|v1h8*1m>0XbkWVS`sNkq7+~Ji{;T~H4d`6?h1I(a zlp&oq_2u`r5ofz?zjetpxoF>b zzK$?#CL$#@TmhbMetYCoX~dq)2|);+$=Az{6556+5lgIy3CxYI_k4MIbXr_hN2#0d zaA;Rwrp#g8*{_?h*w^Pq9InSYD|;8rVlS8s6D%FkOY2=#p~*OKL`14IqBXvgH+W_6 zBj(GLvZKMF+A?+*FA>`gvLj54^KwoB6G>iexy+?Cu{rJn zxz$AX@i_y0@w;(5%uHpNUbq!l^56h<@ag(@mGspZ`Co zD5;RhfY_T&^l0k(po1NL>~5XB$F}ON6{)wm=g_-9rBa$h{olpI2d>T(OlxLFRQklv zu@ssyC8l3WS;n-d6pw?Vqg_|vY+t7k`LtXnIJh1@ARiil@3W$f`l~TCOGdOc$&FrO zRfe0J4LCn=m!o%%*uiDH=Kng||L3&-@0Rh`BD|X=lh9&ciPXmnz|+J9q7ENjI@UI9 zToQvytXlji|2Ff+5q>z9H3hG28daq)s8X9oXGZ{p;pd-0tyX;=69)cIfrg8}xzA1x za&KBj^g|3T!iR@3_~FE$fQ*;1t`y_zk_XLgb+YB3DNtz?&cU6D9SD_9-G_GEKsel5 z)vE{qm+JQBm2$X>x|GU!=)9$qc3_1IWTxwZQs;mz6^eS@Vp}1~vy=BW*e2YJ{|UOt zxLHr9z!eRxhnEZgF)#wA5A6i-f*?467@u?~RSjR4IdCdRm?P`EI&qPWl5hR=W@|6A z;#BTgfj*Bil&s}LrmpO?RhDttqA0~9RpeJe5EYI8WVr+Bi zAK^isOu($zXeK&I?QP`{E60_Ohh?&xb01{A7bsn3Y*b1cE4w#{P2!6C zz24qnX=PL(X)zzXtSACTT$p=DgyQQ=L!3IpBF{EcY+pa((Pd=@j~EWM zdCR{Jjoxj(GGOJ6N=cWVbfd*N{*~s~7H~T zjG%n7Gb_o%Sm)QS%NLAH-*ZH=tBGbH8s~oN;umk%5la9%HWoCX0di;2KYY9a(Oe8FsdF8y$&?l? zk?kNL_zAVx&iR=Gl21a~o=i)l-I#;HlB?(=l$7|D)ZXHN`%PcN?{*w$Ot*^p048T% zQ}ewYtDbt<_lST$y_amG^|jr%exon};D~Jn$6*2dg)$aDsiCTeMTayO1v2mpvC|`)lO?ob7;k(ni`zL9fF}=?0E%q`lp1 zfLCaTHoQ4PX@zk%XD>~;xbox9g+VO&ON!p{p^v;8@x8uS)w*mGE-?JR{hmPppcPfy z28|6K3IAa>Qi3#>7kuct=m($ZJ~bn-F0)wiMf&QxYJI^4AN)yw9rnQ0MfCk-=CsOo z!2f;9R>yV~99?yOuB~u;1)o}u3PFDLkG)O7FFA^ByY(K=syKPhO}Ux>9`FOI8SuQm zT-5|T<|9_HMG4PuD4HzatINBSq4`&HeVzDEJT%N7c7ASWw(4_lZM_cdz+caG#?6O@ z|I@Uh(t!}4F@~gN~T;?iAcpnWF0GnbUuqWxa4R*O{RhPosOAKZA!de+pN zVQ5h%5VDH>n&L}OT`GN(BZsH^)eMxSUJbpDz&N-R#R0VS9huu<%aPjUqK9{s#|>~( zr9DgerS7Qkk&1`a{9q>QevfB`ZW(PlA_BTeS+z^hK?N~_#Pf#TF;|UtjPm!=0Bo$x zVPxB-m@j81{a88Au`);aFsme@|I)kBb&ac9TFQ4Wv)z!v_(lJM%L&#^G&0?PeGlA{ z;pHN~(k>r?g!va$&C7wolCw0ruIaIP*fz^D=Px`hWiOSY$jrfEkzSq;j2wMvy=_Vi zA2DK*l}dYE4mR~v{oIc(Md@XHXLZ9c&mbGp@aTP_l-q5MwBhwB-Jn6~+bbg>P6ls~ zYI8L)tS=&eoRw4kszDbDoBxO3r8-#`w%=y3M4HMlvahx)6S?z1S{xP~mA3<%w_9~H zcuYXt6d zo03D0!=#`c!%o{Tz!mhyRt%7-uH`!v9_rD`ui$^wX`Y|QNKaJVrDnbN`_Y9dp0}+K z=-+Wiz|G{ru*iAfU;Q12!{6$AMn;?8P8Q#xHIHGmmmZE@(h&G|LF?Kwxz2D`*1$db zH$2yU6<3x~K4FH$s!e(~pbV{xM*rh7F2DWGvBl@sWgt1)}~2*h#jEAJTefA>&Ov?B-q4wLUu+MT(?w# z!*V4miGVa^-*rXK+yS2Ozca;K-N}J5Jh=@F66V&&#XJdF_CO_A7TV=(s zMijomHnCh8EGQU*B_wSJoNR8WV=hEmvPwTXCM39+g1|)h~p8|EBGR`&E zi9QtRg(SdROFj>we0)MYeVLhEVc?81TosG(YxMtnx)<)Gd9uZ3p%`cUkX;*@Zo&u& z&Q0Zxzl+}&M;sh)Oyh9`j0MQ$skS2I+$7T8T)nL31JyraK}K}ZEsv5OAKXMtD=Zad zK^Yr^`UqN#=|ur=3t~aYvG+QcK$fZ|Zk()EZGewwRB36kR$x6Rlf(R@&;O^1yam=i zQn>OcYMa8!6p6LGwB757ls!?Ld<^-ZAgt6oUOM!Q6+Ub>DEUOj*NoBQuc8XKrGYmU z2Vk{k>_b9mA-r)#lA(+D->{QGt$l=@bf5BH03x01eEb%Z$A`(aB}oP#rP)(0H7%PU z2&J7Z*$sTV&9)}PXLUE~M`+A!@dzZSn^qSAXAk)5Dr$9V5aKso7>{(=c@ap`@@(du z!byKUHjDBGKgQhDW8jnllVlG^WOXRJMDRpp*STET!An1cs{3w>Quyu^4D%}m_G2o3 zn~-xJIg7sfla8yK`L})aFK7kvOEPb6qFK6Kbj||+(sj{H!1yjZVBNNnSpd(bS_h}4 z;My}wt;Lo5mP5Xwh!^2FU}b#^qcmC2Iqgs6oDpZ!k}nO32okBn6-;jc!cTswv|OE1 z85(7J$nT*oq>QhP+9_{J`>u)hPBRmp7}Z>VrL_b+O6bK$cdfO0-tAx8!APXgU#8$U zn;1b69?~Kg#3V}IwuV7wMCr8d&2ctBtmNF23(47<&J-SN4f8LefcwzofGHRLW^GtP zs_B;(oIPzT%rc20Bf3^=c0_g5E5(+1j)^t&J(LRCt}XxRw@S&+b4B!HzsoCc%QCvgXa;X3L$oHwecCfop*drt*=EC5qnrAItJ_ok zypHucsxJf?hBSGRn?~~+PJ&cpeKC9PGh5B4hqjMi>Cn5vVuh}5n@xlKK<0O-!`SqF zuC8XaUSn9}o4wYgLh*yOzJKc(3*Tr>jlLvLvB$Xg$nu_y304F!wE~f;eC0js+Mw@a ztbMUSuolhY3rWWb>Dz|yP&F2~Sxt}@;ZP-;M|@w9#yzRv;gVH0_d5E9EZl9JKj6Q@;^$&1fbF!9 z@CDi$a*w=PpYzGCr{^(XD*BDDF~E3h0bF79(hEHYoX3T zrQu~j;NwVChpF_o13JW{@r#V~5KgTxk#rnQQj0tHyYHiQ>D>Xq7_A>1j4J98+MKNe z3@M=05hOM@aGi7w-QSxoQt$g4OFAeT`d>7ieLU0q|NlFu)M>hR|jiH&MXDVaC zTh4S96QEaxL!a=uUm|@Zu9KKc^F>y+9b$v7+Tbk;A>aYqu@+f=E9-AG7kA^|T@LgX zYHL?hb9(%sS%<~;a`04j@L9y4E{J=rd0Kw}D}?OdUGn`q*r3Qc>``Q`ap}Q2K=_2) z)-b97m*)ycq9=4hSXix40nS|V%j(IIy2XjSo0*szBzx>wYu=I~5|5Z`&$_Z|v}5BY zqE8g)HemraT(8d=u?8W{j!+7p9KvJbUoS=(#!YN}>3o7dx^=Ll&eID8Sb|>;3am^$%Fj{hA~vCM8ucByd@hN8wLx*nwfC|YdjF@HhagiyyyI1}mI`Mf zDz2gF*q4!HVFbqhR;#YJu`?2-td5?!u`&+a+L}E(-gU_PNhvVw0hFn2(k$nUsJIA_ z?@glrOJfp|acavxs-m67bh`OqKv-yPS>OEmZ+T|qcJQkH_SM+SOShJkmuRac#sIMP zpgK-%qz`^OT7@<=+{CYQjWE~%JXKL^x7sJbMmr`wBCao$X(h)Drjb>}`j?Os zB^>wgr9(5EMb1oRk-Q4uWK>ApH8a&o#bV2)WlQYK9O+Z6YQ-4Le_QJfro{~# zo(Ki99+$rI7CAtxLGtrFmBoPY2b?h`{Z0bsj&QhrE;z7g+vLqO><#yQ%~C;=2N`Go zK8j*${KxkXKMo~4-xJ@x3R9f6uhY<=EA5fc22%N>r`wuEH12dtQ>@Vmx^1`sY>ibu zq0KySQMv#{@2y|DtMXT!!pDgMHE_p#dF|w4@!rCImEyAi9h?9{!e7uQRA@y#$YLF4 z7dfLBgN2OMfZ`+{V+OsrZ#;cCQ|&-2QBX&U(}=Dua~-d^cqGQRcgwc2*K}_#UxsL3 zmQ990glE`;?qqLc?{LQnn^#`}{>T8nYjJa0w2WCVDxITmy)$;io~>x8nw%a)oXa{#!f*x@I2&U04(i zV_-s!?8&WSM^230Ke`hx`zw0P|M?dWCO}!52u*OFD!z(6MS<%@_g|39HNAW zbaNc7Jj5up={r+UFK1`unJ`lEE6x~fb{*iwaqE7rV4llbvq4)x04!v|tS2fii+nQL zO%DSmvhJUh#jB63oG%yAeS8Q4C8dv)t&P>vu(HKmq=^z*pza-Roh3Ynxoj^QdX|^r zcUC;bmEKs2WlSz_evds8v>YWFliufgvOZCtEpJ&QeW}c=taL1o34*fDw3WbQ2kP$G z5st<=l~fT4X{5qBWMrYz!Olu0$@m5_w>QEFe7SFPA|E?vVbinaR#z>3;?l(cM`|g1 z#Dso#Ds*anJ06N%UD2HV$_C3ns5CbIq$NL`*$hWV72K!l#XkJH6eZ!Trsw%+Kc0m; z+ILHM+DQ=|zhDmjcZy`?{?^t)OSh?s31v8*`4wDGx7T48TJSRYUveG(Tz39oG!2CD z4t4JGlrvNdd^3OvMNPupTpK&1I z;QBLJ4#Zw%lYiOUN*#DWv)b=j7R*?hxt}*vne^umXZ>RWQ=7kB z%cccAe0yU3|GXFf*)M>h!CSZTH;VvB@TKt!fgV9IpUMrxqw6V!CR2Sw?H`V+INO?U z2|qsy^Q*8wFc^uGL=`J$czk^fdu{cpDlYNGry=)ui9d}h^s3$I&!TQQ6*VH4wNe3k z&=dr?+n~O~LMEaliMKC9jAuhK{|>>}z*1bHg}`nOOt~5!&vhsMLK-aGUMWVig0)sY=xHji7ZzeAznyE3Rbc(mN*?xcd=u;1ET=TVOc*oh@_yFI>zCU7$ zwv9@;JIz4fN|xm3p?3@(#yEJ*?}dJYfkDEDQ!Eu|9W-MKYe>9Vb-baEW_uLx{=d8Z z|G8FT@qJ3Wy`)_?R84gnvbKaTI)L(el-q;BFvS;$LT6xW> z%)yqmkAD4b0^HlF@CehQB6`%OsxoKFXDB2y9DTq=UcIDLVl62+*=gCP_E%bnP;GwPO|W`p-B1W1riTK0Hd|1d zCzA(Y)L&fz1F26@HWZ>4@J724v;-h!l}l1XKOUMiR}J?*PA zI9*{Gm6ReO6qYZ?!6XTq=ikadNc}dRQ%H>B@&Oye$FfeoZ z{Z16S?xjV=MW*duSjo|Y=&@y=UzqbOavznI+wUg$jO;wu!3d(qw5zfk1>sa zVS~4#caGBGnRi=<(kE^c93KxaR&;o*xjU6sKbNNTeC>+^Di|8xV=JUtOlKxAUzM0> zYP3&;_=}y|%cUu|-?A}~R+3`yin(ogtz!g-6z#^Q^#d@UpBj**->Bh_lF2>OoEl4*t)0pP^Jq{H~n~qFI1!B^B7|PieiSh z{c^}oA zkG=!#9?O`>%AUOh{)|zmCNvd-)9YT5Sb|vh8HV*UXIqT5xV|9nt+uX`JMFeD9R((b zrc-;P*Wmkin#z4Ve1MEc_tle$2Uz+c+eR8x2_d!YiBC&wmPtof3!|&~p7>n4L3x)L z!qf3qa-p?6*7FSLNR|YTwpA`h#TgYKc)BH@u+7M2ej#+n0<->QbPaeEYO~Iah%^XJ zjWO#beyR!*NSOD=_ACd+hlOL$?w2qzCy>Z`q^Sj^FbK1gvXdf-x6ot^&pMabi{wp~ zMFG`I6k}t{!%Yle$V$Je$~6ywe3wSu&f;Cu-R}9!!3X836!d2Vl~-m`C1;Bdvmh_m z)%6fKyRSB*k;`b@B@G+clbfO!S@~M22H0sH<6x0R{UzAgqxtLyltS@i#SdLyzR~dPE0eFFL)9i}gOvm_t(>P~`-^U9k&M7HQd85@xTPe@)+rBuH zvvj_2WnN~vW-H3MzNM01#a&W~GG09SKngWB04wq=(z z0JVNM3xbkz*b^Si%zv1nx@}n&zx;*Zj2xSI9b+^#yA%oC zjpD9uwZO5?I;GO0! zfF);j0%@hxsf9FA16;#{k1zoNAwR@=J^l#2csuLLnCIv0sep8&ps3q!ew*?%VEt^9 zeoIuG-VT-~8Ff9XU+2c}*gG?gJhlgA-nxK&0kg*WS=0V--3Jpc@w3Mo(T^IH%vt5< zjCVo)|Ag;9njtuNV4&z14cA_Y{FrE6^1Y{V+7~c8M-_(Om05F^9-ve?3Gk|Xo!P13 z(y9txjnz`b^OfYI@?QCfrSD5Xv?EBOOLmg@L!CeX@84Y} z9LpYwm6gzHL1s=^Sz9+W==%BR#s^Z^Cq>|5P~t5H$|iTxs)NNbrSQ*=3Pg5(P#u@! zbNvFwW!1!%M#(x|vse}XmLlRQwj2Ydib0ETi?5l$%~gsKZUE}Zen7{5-8R}tHwv3y z*iw*jynIk}a;q{rB8W!VM}tZ`ike;s2}n zBh4&f<0@8o7n+1KQ|^mU_fg zSp>mws&`~clSapAsZ%om9*!R78G9u8+ZWmGT)XEX0rRBPBVl)yQe*0VV~{|<_#Qxh zLo5)=rM_P6Y z%w8_Q4WxrH35J3am0%MUP3^qFTOCc~Yxl*;Nflnps-4GySQXvO&$2W~aLB1N7G)T+ zJ+&Ih3TP%oGyyB7sg<%^lb3Kod|Q5)MRv5h(@x=y7{8RHkb|E!Dm#b(zt3wvU%}Wm6uEc|C1MI@wuS0v~DQ826+j}7n*WrduRsa zpi1syK^Sbm?{v}W&;+Y0Uv!?c)KfwJYINz+$eTXnx(Cng=1YUhKS=bjN4WS9-w%@O zhAVU!wi-NjU^a#lJ_K z%fA_~`?if{9PP$LEUTz8egp>IEFjqc5zR_=W+h2&Y;%J=pmM=SGfjUbXbik)B;>1x1!Vi0ddJ~c<`@r z^RDA4m95+Eakt@>6(*EUuvoXMQ=7$}^@#>hIXYF8ae-)%{A#({Df@JE84r#!u?lUH z?a4-|9!C18vKtJ}iIq~z@`kr?H%5GrkTun%f~=A^i%}+V5gFQp$EE=|L*b44$x9O} zFD2dF+Z0BmnPY&5rKVV+qi^`-!6y(Ngs~x;vBo5xC+&m)(~g0;{bxkHf+q-yi5!>4 z;8VuVWv}^XDz>f@e=!*+b2Bml51~=FVXA(DYiOs&TTv0> zCC6*hFuJ{Nitf|VnSlzz!Lt6EErics_YfW=tG9qJ+k8g7#}v1gVF%3QwPvT>!j?yf zAjM(e=?};c5rn!-3^`?@F#;){#T~2MAQx9kaMpV29+p!E3HT}a{Xc2dDUATWRYlYx z+}ze75Q){luaMAN9MFK z1Mbje?of$-7@Vj>rM6D^I*(16TZAigOP@GQM2vTe@64<{C1a1oT7t(TYYif<7dzVA ze5}}h#gHWJtL%N~1^;TzW+)C0DKB@caPaA$$D_~}l$5v&J{Opf41`%4M0iyC%N9bz z;6F}6Z=PMBKl%=d(LldWOD@%aTlxM^`6#oirHdq=0W{A5;B^O-PduQ3ce}>|F#=cl zVLSzY7x+_qr?N$Uc#RaW8E?f`8=0~SBLG3jUQWw~m4ck(bD_KN>@*>y0hJ=Bn^_8 zY%5DI3p8wi87wFPjO8PS;4{L;GRAx>wYkl{y{_b@9Mb$jL5uv&p*dH>|t@eKhi2s;0lW?XpZKs@W8WG>PB9odx z(KvLY=Ym@A<+|5d7N<8-yzjY^+JxBGdcg-qnb z0+6|Oc-<1sb-;qou`4NGFfGcJ=6qAHiy944oAVH#gnKts@?~uYqBiqPpM8g6g245P zm{|W=P7bd-|G%-=F1kkfLKwa~P;gHMOHa5{XUyeSe7fB6~Q;i z@e9mb^0DLr93iE=y_KFTa~@9^EL1cDTfsU=jPm4p!Y-kzQ~A=@B$$k+qZ-e6%No^`s3kE+1%z0_#(v;FKp7s5Oa)fsY5%rx-6p z@0dgtfhdLM`T3wABsk^efp6!&Cd~>RYmh!*92U+dXPPcyGz>z|gxAd=DJsrDaP6b! zY39100PVRUH291U(XhklVyqR_&3-1HLTBo#(8e!s?~~o^D@ChBiR?e^9E5I<)GQVN z(H6c31Tm!L%z#J>d)cnb&R6*v@o3PQ2?rXrK)o_ca2?-rkPO<`NZ$c|IQesgM}Uull8J5R@3H6fO+m|`k) z&s%miT8$f!3>9!CK4K?ojrUBWgzk3nGCg;uUbbP3HZkDQY5=v?i@3$0jTb)mBfeyxh+0n%p!Y=$k_A zX7wfZI|pM@hJa{5pvWXSf_RYo-skMtS7V;4s9=%PP^g&_8Ro|)(i(D2QX7P%`B6iF zwTi~LM@zB@wbpr=+75e+*|jlL{BtYH#8dj^HSAmZowNN>2_R}!lE#+Q?&WLUZ=+De)sn<~FkSEH zT0o7by51mo-{^)q_>x@a4z!D*=k#DypPoyLZdid?;~|a|1^tWeu}IW@!eA6-t5H(am)8te?}L3$QQX;;JvY$6jqlJP>Vq2pAW#q9*v8o6<<29t(FM%hyW3t45C(F z;;GRfc7r}bU|e25yQn&0nj7!q; zm&9xu>2s>!tDAUlT`XU=>^9oC4GI@z+RK1N)nT0Uly#R)zGBJPbU}ZJuYxaW!w^%a zJKI`Ym}jXiBMb)TtBr{AsEV_&1@3IYzLDbjtb@5)$B}vNTYI96!LOH=>z2p3K&xz3 zkC!t*G>NURRvUxpU&{13qi~&v5ZWkM* zxH5$iW3cbiDbzK*Z`YQp<~?q z>YEvO4!SjQZ{8k_v{*D#?8?ig7fg9)PE0lr+x#IC8gC%%uExYvXp<%fIq zp=*hKpW!M6`n1iddSZQ~Vw$7p=?kzB)^KNP? zq`dc0?_>os$wg`w>Q2S0-yF?s*bx;#3quF9bR2UIvA>}*82jm2E48h;t)z!~%S&+( zn(6*qo7i^s#ZTjqH8pAX;ec?1Y=PNsRKl+(w~|Y%y3hK8Y>ZzUSdq)A?ZCum>kN-K zFPT?5$a((&Yp@C-{>bf9f!{uNo0Wk6Bz4Rr(YjLChQ`DvAE49{EB9y9&qFvdR5=@H z*ypK37i|k1T{ylfK)QgtG~m$+-3 zorezfs@F+l^=oeI9U|BI*Si$@Xae*Mko?Q>ww61B&>Apn%^xPreWSyOD4I`cr9o{S zeCfMaOe)F;KSe6Kj({pK7Tu$+&-?kMKJe#D_0TYl zFWn!{S`}-FZfOBFjoz*-yrzBc2mn|oH;FYmT9!>4sx3>w-KY!+NSDK00m_~N^|zm9 zMZCOwMpqV+pMUrr@;uI_pTlmNL%$>nC>{*~6ms*m7z{&{y60in z%LmI5O@HYq!{0ybgo1^1_a3pmL_CprM4CINTaFif{72anrAo8i$eo(;^tPrbhSq}D zxa%^gY{+^(x-D<(n;eOS3o9v+-!PapL;0!yi7Z>dnx(>w)CiJfiB_{UU+lD(&jK^- zi1w-$M@{+uBIUoi?!X>jc!xPZo|*n@ux_g|Km~apA z2?iv7$nvWvoy7;pg>_SKke#=GH*XlRY<7I(${N2lYuYzfJVma+1O}ls3cchW?r#&0 ziVil3&-J${u z0DGG0W$RGvdMkY3I*S;T88TdpdGi5@uU%~UfJdHEq_%fl&RGfc7jwzelKxO1j9~h@^Ly!Q~Bz zQu<~IR{IkiB}M)d0u`XFvVd2@FEWY*q1{CNPc+-_$*-dep~o@nG9i4%ZSFR?HO1qc zHHqDzsn@>>5}_aL)BK`i)Wn5A`+Dv?Q^m<4^8+@v@bKl+lT8wdMi#OR4=TRXX0HLQ)EM> zsYO~vi-wJD$K%7LKG(9j-U}4>0&stwnWENSM54^jc+_hKD#7=_2wF;snmE86O-;3A z9t75p{hFoK8Y-I(xjh`E$1rZBh9=KUL+zdtK?!M`pBFZS39EsESdGE=s2R>GCHOJh zV_)Q##i1F|nD|dS=F_PBeAPopwLjv#3jV-WSAJ$6n%<8k6~?E97;2MP0@@?b*Utw4 zhl#Oz%slPMw}H9nUSH7kg3IKjJ>cc|fhu!r+gppnsY=Gz((cCBX++;ixye}5Nyk}! zw{B1IY)c0Jh%>;M-DzbTU{>*;)obL}6K(;RNUc4K*5D|;WbsTxQ)Knz%#y?e9fqx3 zctfE~zz=-)SlHB5Z(*8WRamD57XO*8(T}jO)5+?;vUX)t9G$ft8;i&CJI)FA=9ewg za1vr=47z8(Lg&jGlxb0Ku_~z@;eF1D8FGpme9qArU>5J!a;9Y=yxw66Q@Hnu8ru#G zA~D4LbmUy-1<&9`zsLZ7ph)rv@MD%b)(yR;lwvASKiUEn8vUc??c@$qk- zl5Bk#-7P7i2a)R)@`P`B>0Y%!+imS9v+nKqP-5IP<)U z@Wx^`W91B)3Goi{9gk!t*79ZQi+e5;XBH_ftb+6PnH?+9oT#)fa@C#Y1e-Z-k#@I_ zZt`63W1Oy15QlR~S;6*cH;IPZ-2afYH!y?7r`Idtsl?&zV-u_-;!xiN6L~fE%Wm+f+|E%)ykZiXgRbI;xr?8+m<+V}I70 z>wo)D$6Xj(%9yX%>IB+pn})!Wat->$h5o_i9TzqreJXdpig2`X6rW3r+zTZrFE~_A zFPi3OLM(zjpV6g=tm;E=$N7PWXvbr8 z>I8Kww3Ltdg9TnE>UGDRrB#-sRf>jlE0;`P(L!?pTQmsiD1qW^DA-lWxmLC>=r!plcj|6(y*xmZKX0-AHLp6xmQ}32>gl+CI$s6>^5z zkTutygtfhS&cs;riTRkLnHBrP|;&RgFd>~L6CvnZC{GwMC1(A znLO=Tl%L9QrLm06Ko$W>0jg*EKd^@d$3H__UpL=ltw)RZ<}gs8bAZB^adWiLhP558 z`YZxV(rpKnuK8Tug2;@;f*x4QxKOtfeTq6PdXKA~{-;`UbgNe_=n-jWpOy$1*K}Vh zK8;uJY3P{)|f2$p(He7C8 z28VVXv^k!BzrFcLKBYqC%sV3reOU&eV9^u%17eJ#WtSIus*Wg_ za#d2J2mKNPg?TU%_=c$1IlzJvH6b*>QbvpDj(7%Lwj108ItQWVcGI^v8! z6!UaxsG74>a6kkLtC}=u2_T{_gWH#kF&GMab<>%8ecga4q$A#N;4vIU{Gp|9!COqU zKl4W6?RMrW6?MVE+~ICQfbsw`s6Hki>aFKVYbCXf>nl{f#GJHq)2#&f4uY>vj!`)O zk={#OfICji%Y3u6wZc0ZevpcdHqJ0kH>x0wgO9++Nvzhc;PTGS(VRgTv&`lI<=5}X z3s-A+-pJ|^28nP8?l=c_n{^v4>6TbtVuk=ECNnp-xHioU-uD%?qNjinEu1(~I$cY- zu5V%+w}tCZEDbCxQCxQG{JCpv_8Mty+YBkzv0ifH8RcGk(hbyA(E$@FOEg7rF-p#2GtbJLsh<5MuZ!25Slf3RXP2p_ ziPd8JZg^r_jnuicmChm|H0)wS4T<%{pscM;UwSKI#|-|qJ>v%TZTz5yZc^ z)|Duw_*@dJK3b23;4Z%_J|$kIclo9CP57KFu}^0M1dqg`pz3K3srWQ&eJFG}!tMFY z)#w_<+6IilsTZLYk4kU<%lPly4zc=FgKxo%4B#K31?|Asa08!gy!AD$;Tikw@TQ6z z?aoKx9>dQ&rS}R1Jl>_cu%ns?^X38b=iD+NoaXyOA}FR#k|u2`O*kpK!!8LV#yk<0 z+%LJE<5ACCacl8$m^Z%&JN5Cw1@9(`$L|j=*-NHADJdX*51Ol2NR@ue6F6#LrK*^s zyqep&9@aNFoRr(vV$S>KpLmYnAz^inXPfi0Wgw}IWMikEfH`{w*Ja|{tYUkuI5Z@YcM}YaOK{!)WaVe7`>^_zV$aQXzh_7kk-xNv(^cF zVjePYS#eFD!p-B7h)<~$3N7=qs`69@=7)JLQi5t z;&WhD{X&|<1^o3+L_Y2YZy9)tsqqUuHUkaR+#!zpIf5_`1xUkc7TI=ewY1nQW5~9F z!H{Sh6jjlOv3PyGjV_6{RId^qqagn!rtubajKpReK!#ksn!4`1eLU;|QOt;k9T^I* zLj;?2s<&aT8&sChYxsLUzk2vN_+wOOmpFaZY8Mh-`8)n7pz+xDy<^lzRy5`IN^Gn> zu{_QXL4ma%itP-SOjy<_-4e8C8((HRCn(X5p8w=?ye z#R}}Vm`F49Mf4ev2FcrU4>{S8WK%uKo%R7!2$rSzssSV<%B3&-D6o76Etil>`u2z< z`LBWIz^%TfbOE}^8=!}gGU2=>M&A)=y9i)X8qANtVnQ*eFAtoVMiQ~XA{#VDq2A|* zXtCnJ#Fp!VJ5LAr)KeJ7*vgl#A|Z!o)O;fOYf! zLRauwx?Dq4(u*iL32Z%zJ1G>6OJ-BWIG7!c#8z{dYliB*v>cT9UYd`}@&p?Q#$C{! z#8QB%2WXa7pI&NOiZuVQR2vXOyMZ6va8qQBG`h!Si8-0a3?0YZZ8N?EA38>!Cr2xg zb4DzN$giR=a5=a)tLU0*np!3+Hh`hsN6}Y#=Sg>-{1j<4G}2dF_#7GS3I#H3wqZsS zlAG1C-PdSP;>~He$4q6f`*nRc_TTbv%tK313VY=& zlSuxRZdMAgtvf5&@BqeEN=3S^D-TDPPR^?kk!|TUYO}@7cmi+$F`O;Z@!lX@>h!3?RXuv($Gi?@#8d zss+x-%Z)l4s|PJ;lTP3W5PIeDV&`3HwrqETwYb6!u-^xWziw-h1s!}59nCi@Jk~N1dJ(h{Of>!S)HpR>y8C5|?=X~woS9v>v< z%VvUr118X6xP4)#Gf@?|YE!#msl|sx9fZf3eXO|3sz+EI>XUov6YfmIN*0(j^DtHz z&{E>q)EZW8K-ekUeB<-CN28JFQ?0uR`r^jl6JqCt>V2DHAT3VKB+fOP;xkkfJB-7K zuyQUi6Gg@0m>e73wpyfV<@l@o><%KOBnX`KGAIxih$|~{UvHl?d52Fb(cIRH=3~f1KC5ZiGhY)s#PRk==7WPtCODcpXrg?roL&zTqNZ zLFZfLGgK;o6&9W*qnE)jM@H2lXn5t43Mf+HbJ%gC{Xr#%rv?$ z{=SWFePrzlrjN9hSQ!M)@P&KZ1j#H!^8T;yct3PeSnGrM!@=(Ied4_>K{aT}b7@6q ze%(8R&zwjKTNY!;t{}#h(B@9Bw8m@FVpT`!O}l*AlDw6dpe|1QF;mq?{EKP zs2tqwQQ1e4O`eNKq(QLGv3hOOOUpJMbZWYSV$D>~-bxHgGJ-6rUQ`7r zf!K&YfFpMLoHcWG_Qtru&_HTK{rcc>QpZiN&Wy(C>=CQ1xoh;e9ha z)TbjUNW>j156C!<=d(+^im^SJgR3DKa5PjnFSOWws9cky?$d-7;U+2KGTfJHC# zjZHmL@(_s^Jx3NF%R+$6XN|bi*yNQy#mS(I9bFC0o&n4KY$G7^niKQHdsK*B*}xt` z(I!$O%)n+oEZs2@k~9PhZG1qXbYl!}v5X4eBXQA*O}5q3?=@c)sJ)8gce1N!tzv8G z`-u1#;+?J(;)aBMkBU+4wPV2cI_ciY8n%dy%&r3fbG#3;*C64rt5sy%w^8V2U)CWh3V4H588o8# z#!#KvJ+|RBm(fZ=N8w5TQz9wfQoosu#>MttIRD5$-4W#0FY<}WUjMU;uOrsU z2h3j#%hIqKFZe>IdTaaJKeB5ZpSdA2O=VDKV_?4RV{f}<57XfB2(F9b4KFy7U9e(m zl1c>b$^z=M1faaOiArx`1ky4MNW`0qz$WO|%>H**SnYCwr19%wP9cS=7m2T*ob-85 zC5Ri}YaL9uG44MRskLia^B3_`m1X2y{0JzRx0{907Mot~SaCG$J$`Q@&&X37tq>q_ zt|VuZH5~xU+`r77X%VFhPzarp(2Wg+HArV(ECEER-8Y3b|9zzis?!#5Rx`mb(4*?` zv|WR8Oxd624%^6>0m-0rQGm@3Jyb5GIga#pB zp~i=Ljy|3t4(k8x9&qN^md$nGMBI=#HY(L!dmjfZ_#v4ayNxPmiUv78ww1VaAQ-F6 zyG~X8#!cgJ0)TIM>5Ko1M4s+T0|0``FB$OIJ|eT;wv_gsY8_ii0M{d_v}f>>D#q@v z+`DIq~ z)d$M6pg@b#?t#2DJ~3#;UGdn8o9ZQd2ypGx2eRir*Y3JgNb%6xs#j;G>ucr6oOiY>Ceirh#trSW?Z)y%pJ*Co-Qex*i0s z;p@i={$pp;Fbd@MHQAMLani93{0gED2{!~>x?8K?Js9M()S`8ayX^~1Ak$F5HRP-| z>RjSZA;SieSEl#2JiPXH#}9aJ=AypOrmOjLS0V{QoSvt6Z(Hq@SBJ1~ z#XT>M1NWKq_TWK-s^KUVASQnM_nl<$RnkH!g+yb`r!KN3vs&^&U~=y-{UE5Kc4kCY zm?pod>@gZ|s5KxQB-TMsF}26PFfrbx%nijF)tga>cC*ya9a(}Szi@|N$nZ5lx~g$o zFR+Rf7pJpY5}aVAQ&}QO%n$^xl#N3O`#L&1Djkd@)Uf-VcGcJlFn9UZWTvsVDVpf& zM8mm?d*;3Z;sStow#*v_`^@&nCAO6>HVQg4=XzBWekn)A}S+Z!h;a;S6q0!Q^O92-|s;l zMQ^%5Zr?ig+lh^>F4?(lur&VDENE8R(-(KYl;|h`TTab&jx|7Dl7(irF{H&cEM{f( zb35bi*bs9D8CN8!>hDKfS;Jrh84ag%9a-`bX^G5bNmGF-42i@Zl9t~b1FxEE@UsO7 zqP;t7rIayyJ)U5RgI?P^;4)zt5;+;EE}^vBgeeev-5$bcrjVvXn*u^VmSZKb>i70v zUA{9Ospx&_t58fSrY4(>{zZrUU8NsU+Pp4n%;FSP` zGk|Nu{)ce>AFeI!e+cLQ;oAKFLpXZ^xHkWfAEB3(9nMaS14Tl4eg;_) zY`F;=4<%-I@lTS*?vIDA&b6{a<=lr8v-k!4QHXHw@D}#ar1TN_M1o&+jMF#CniCyJ z*Vwoh^ZKXOBK{&ntFuPzk^8gG=pLT-)*e6sBWXVqTw>E)*P+ERX)E9c5v;_-@wU)r zNvilDT0B%;uckEH;MpRD_R0@4t*Yv~@MKOYRqe%>l1HljCe*}J?1&HK zpBmP9pc`|&ouF|D&MDpz>q2VHS?y+gAZ;?p7=$CUu}JkAH25{fNy3?DkE~eYOB1Xz zfK!^Lskf3`85Gc~d5TVAU^faaTm*W5HRzKSaX$`t<=!0A#bE7Qn&clJ&w1f)Ea}xW zkgxA0f}b{XQNC9I_=#phpon{G0*f^o#NFU1DSikDFO?4Vl{+)mstNA#8HI{Fd%GpW zbGOC~47782tD6pp%)KlUPb(hV1o!6k{PRQo^tPYaJ~eVA)kkME0)H}(ZDa;J{2?Hy zde$Z~3>$5j+Q<{kUE`WNPyf_#&(V$-3`ZOf^&_{*z@%E$;s5HB56I8POrlJkGAp=? zTbb3#+g3!VW_|MSx4J|=1FxFgmglcWiUR<^lGKuye3+ep6R8fY1WQ|PaY+?#@XgKGd^h6TyTRB{lW zJ;qV;9bJgx-6;Zo+ zCt3Qd5impNykLb6Ni{ZJukXG;iLRW07w?utN*8OS1>NlL)~&hj8QpR%8cV++U_4?u zQwx?nxwvexCCvm^rw@;M_ z9V}n=`I)tC#lL<}{z0ve`ZBUC=JE1PMnuPDXZm;Dp55_3o_EYXcA|aABimo z3p11dkbDO=!D&rR@!C^MFi;J#9VC#j%+CiGYkA}E#~?rd?dl>;Id* z#hY;xTwg7HQ8o!CAHPvIHLX>$CQQ(%@j3chS0%L+(;r zSsODhpUE4qHjVQS2dn0DY{%34G`cZ#W358g4o^vaZ+9_) z^agop*`Tx$7J%~$*&^-P-!1h^Ps$+ zvC>5eP0;>#*RdPim)22A7UGUE1vk8rap^qc)kQqA8puBOvXKw&RQkc8^Q;Bwir|0oX`Hh-XDj= ztV^0nfdf-UWa>|CPkK$QlJ+*07b(vRG_?ES_e&wBMH8v7y8x_=VNyd=Y~;b0jrP^N zK0iLUkG0;@XpcNuKBO8=DM9N)guk%o8JK_`THn`Rtpf|+W_YN5c8DE=ITxniXvEHS z_~I6wQVg6?i%a7DDwh^(`V9+KXp9WDTIs}Z>qw|^(NEo-#v?O^&d8k6gJ0PZMb_VM%c5qb4rj*oSNJWhIY0Nj z_2{Bly`60BknKzJV`0s45}esh)@eoqXft!W$no_UkggAKWpB7S+LAKjFUHV5*j)r3%ETh z&M__53AZvE4NEWDpwC{q>M7g;PobR<_Px8rcnE6oam}y#*aJLmdUAx`&}>(D5BN^xBuX;7e!bf#FMnxw#b|IPn}s!gmOd z=5Y$-I}CHntsFQfreM~VX&BK6>I1q9!NU7(oup=RQ#v$eURSe>wSZw`pfVb|0`df++kogJo3($H5KS~W`_dR z^o?!b`MC2!54UF-jGcJA>^_kY~)s;krVNKuH;LAtsaS7lr| z{yHEnEtJD%RD_vXVvJ$eRUw26;c6}wq?;0>J$})H-}A^FMT+SzK;0L{*mQv^k#Zf6W;<>$Q1~e=CZ(poo{yrM$|la% z0V&kxsA#l*%=v7sa{YAIkK(1J2MIT?BB+^N>3r{b6jpkW%Xa4&1-)hxmbKt8Y~`qE z*;h8{(?VlKH>Q)@2vb)M>6xak~%gm56n&|A0A^E0|EPhq#9evPR zLlRz{tBr=iK;x(c?;$4)b>VMpDYJ&b67NoV%NqlCXT!ymN~db)Wmrw>?0E5!JlbSXZ~6ONzT zeyyj5F-hl-(*Aj#agk~*km?E({#{P~v13=O-rR(iv?pQ9p~O+J*>c*wVGvRle2V>s zQ<%-V1n4aULv8h9g@2^+8G436Kv72Ik*))OD@>Xrr+5WB@Fb#=S0K~6-ip@plEn`& ze`Zek0|>iR!H+aC{GND#KegWMK8x=hWEL_1=t=u80xzW!Q>8F+3^VCXK{x~WoQiGz zNf?#%W5=CMO!HqgG3Ff3%)}D9EBVv;?Y~XtE}nvw9#L@Fz-a&XEuE2A<1MT6@`z4m z91p*|s4g>gFh@mrfjw4VN{@csa;-t2`YPr&Y&ma?AF7!jSZBDQ4IcsDuvJoaNqOu( z5%=NeuFd$nYs$^hAii|2}FH+mqu4e@XBQq-?Z%OdDFO#(-;1_6>}8Woi1X8 z7X3=GgA)gaS83fvdkT+m9v814g3a*E$8!Txs2}tSxaT`J!hl0d)(33FuL$=EWG-HJ zNm+glh-}B<9F+tmODXCJOGTRaglofL`%eM2SQC8yltby{$N4U?BUBNP7sA~OPWPMsHUhep`U2QwEb^l>8N>e?-&~S-n0i=%Q zZ&8P7XItIFkQd!s*{7ttX`I|hfV_RCstAj%$h(<5`(IP1O0@N0>^Z!@t>Qfgv*xI| zF|AO&6m|{^0pFPPNVwgY`n?*I(ptW!1SjlGF0WcWN=OV2^6+B}?rFy5-Zvaicy9Ax zJEAl8*9*~?s1ijSaAdY1GuO|Id7QKE`sh0|>uV=#wByZWpj+(Cb3hqHHj*0+JHt=r zCll`0{7FB8JMbx5TyrhBm|C(FY=2>QzwYSc2LZ z;DE{uiT!AESaEQHQ9kVpgpcl1DcP8cqIN$e7>oPX+5T*p>rC( z$!6LGZU4_h+Q2qUrwu5i)T?zkkD>1G1-5kufsWJibD}a$S?)1$nAyOJb|BAfWZ0AG z1rL4Dnx27DeE!H~!0m0|5wFKmj1VGsv-k_ir;%g4?o9e$>xcN^Iy$XGVc67(e`UzDPNIje@Z=ZoMw)ziGU7t@XVg!sLNO}eD+224 zNBQrgD@MNW680~CeVl)RQw{k^KmZ1j?du$-X=dr%Z=wjgx$X0$k1Pu!2pL&jHa2Gr z$=e#RjphciyW9LF@}Ze0E6rEEwzD3{>m%PH0a&ASPr9HfO-za9b(*SeloTXm4Rxj$ zNtM_$BMB25tm!*Qs7Gt7WXn~J8S^D$wDjc*t-FoZ-cnV?e(~AoV44-kBsY0e3*&P~ zijg%wVhp0W@_HbzYWyb00|)|sF2hsez4sW0z_oSX6}^i6(oli2 zvUjgp+*)$(OOEeS)fJbwG8gKNia@Lpz*jzAr&jPx{;bA& z$=+G*61Xka`&7D%q+c2|@o^ zG0_(TNm=FI{lt-l^7-4uDep0HG)SnsU!(rC`=h(A)})HNJ5!Zz{FN6%dYVPjIm(j=?(8)+E=eP5|Fy~(CQ^&=sBt1NZSB4Cc}Jj~zYA2{s3#!GK9+otvE& z6Qfp(tww5xuXn^Ap-Hyj*(Fx|tnrOz^Q(dIXml9F0%I=Y>km0jki!;!|7V-tKdq#D z5GvRX2iw?VuB_ix3=COXTArM~ZqfpCs~ZpxI!o z?S)F0fL+XH?kLU!jMeja<6SpVLaU6`1*T_QX>}ry;^E6rv5b!d0E`1{xxD&@gq}fX zp(EZct?Dkuwf;1fDzjLy3dDOUzB$&>36ZB8H-meX>{&MU09UgUc#Rww0V{VtU*+Er za(<_N!$sh55Ashpv5k#g5RMiu)Ev9!8mcr*Mpt|4o9#*p6P#DwNck%C^E z{m@dggs*a(plz|2mt67O2*P1EU%HPi3iJw36XyA8S*d92bi1!oSElI~`5vfs&6jR4 ziv5Ml16qW>3;iAN>^#!~!M5xAZOJObEY^TD{SHl$r!Mhnzl0@QP)tXzG!0vBHda4u zqPMWT_Ot-jlS9pPGtEl_37e0>ZnrtX*k~soe*s2ECahC0ECp__48z zLAjk9aizIJB~0L=!*Q2Bo!9C5d48uxs_r#1%}L|q<>k2;So?f=Aju|f%eb_EIvL~b z#@-4&0E8=za^A|3#+J9jR)@u2h;mKVvP-0<1@b^{ToZb(R_8UNGerJI1WoR>m3AKX z+&asUo{5_%CkT_h4Uym z|9Oi4f^;DrujN4>TCzMSL2dLTQp@hii?G$(&p~EaN5GWQ_;22?@>ecVsWl2#HpiwO zx^uxz$*RNi;z@sh464Kd7emBOgI(GqH1o;jwn6py9M2}$&s9ljrTAMQzjM9;%f==P z$JWfua_T%pGf<~c@FsYzz~8yf} zIj$RTo=y>fp#H7eTh{0%YpNVG1#kM;u#xhEMr_ojXZ zaw9;!mS@pPJZO2hY>wOCktNv*#DQ+`+>;&uf#KB1{{AHh4e^t{$2Ia;3wH|Jk)Apu z;&2uNZ+m!-kM>|J3l2GXdn?+x!FiRy)&Y=>>}%la7I{JmWm~3ggN4$ac=#-=F~55mnE-82u$!Ce zSydAv?Z9@?3_C?y`34egwRt90+w5|r?3CUnAe>X_k@{_qkQdy`>Cip_gyHlSPoH6_1#8f~*@ z)K>6)dGg^Gb7DMADH8Xd>V42lWJ*tmg$f=H%nTcuB)&G^o~vtQ4gp}xB3iN28z>ID z7F$>>s=fYTIA}>}!ZT1UF8CCxudj=;_Bn^BrV&^WkPcBw&b5dGZk9}kP@JI=!~=5H zTX!>Iv4;`>Cl*YBgEwKV`OBT^lLu%q#Z7~eBcb@YhHWnGfQ)B zcRIGr+Z`2E9KO+7Uvd=-g!Z~3@6~sdbw`_EDDW6lfF=gHWPL(+PxRfl#CD6Re$_FN zS|&aRMrW^X8(gW$=?G``B$+HT0XvTHG8!n5Vy2532X z+ju*t%@+um;~!g@U6<;lReFvtMC|v$lKW#&lzB6UY3MN;jPBlBW|bCCd%sEmn_UMo z+ti_A^n%5_UJYp<;3o9ui7)=E17aC@i8>q@_U2um+x9(r2z7LpsXv*S8Pqe(&hXT$ zKbF$)0(z#aGgT9sm=Cj&HWQWB7O)^Wf5{|rPh%w>L+uNpd}(FVemcPNM5XsbiU%(n z1_z%pzmwzKLN)(K#=eq))Ht2F>tAkcMGTqC!yBRx@#d|6sc&s)k>1MZ?qD+7SSOl? z0R1%IoOe^5<=r1+##K@Lp3QS5i3CM{bx#Jib-fRvfKNK@x$bcsQO!}9a%Dc+3V5+U z{i^z1lQD6r7=LsLEG`L8%>8i3*Kf|CKje+%0z-qgvA zkb&%`!){tuK&z?nn0$d^(RurgGWb2#)Do-vdw}`+)3Ac%za|_^YfC54rn9$ct>x$ zgt|G}T?=f5JGKVUZQ0H-*29Zyj%LCCc!^d-`%3c-3hLq8=}VIxihXj; zZ{8z79!#gTPzLh3J%YPr!jDx5fv^N}5Tg=v8d}#juxv8gT|ur+pgEcL?G1o*p zC)l?i$WTA){wF$C5|_JWDD|TW`jzH4T4`$Ha42?>5aS$k#U11NQ4*7S-Pdeg9e$;k zy#0@*3ZK_D%3At#-RR`jAKQ7B+A@Ab=KzK@W zUTiV$f*&#Ot_Jd=sBn0+Ij6w?aX+MI!)jRET^bDy30NljgI}-$E|ftRE8;e2qmH7-iL~H8piieV=!^^l0@^_7666@m z054wMwoJY$8E&mnXt39m<=2*0^K5R)LmnA5I~3tVof_FDE%pxk!6o#kSo3P07#9Z( zIXfC$=_Q;1TZ14pGbROes~1L-Uh$j&v|QyLqw+Vr>%-o6Mog0BL2Dpb*ZxY>pA;-E zlMHCcb00FCN?Yq?+V+mAAKjNurmNZ@5Pd%OEHGfWYPOlKXj7e69QA*CH)*v~*&~$c79GNq zRxT9+e_O6oyPYQ8>%g(Riz;+f%K?N~!v0$WI+3e=&K|}PrQfHSPhq5q!P7&4#N#vg zu6{h?Rqruee0{Rwa54LfbPr>@a9Zh(#FSjNpn3emTSQekIWoJvB0vJ9LIVO_mFr~Q z#*V|blpi}@oq4e_RC6$P+Fhbn5~-Z9=6Lbl3_wj6NVT8IHX`EO9BdBUYhVsc$BUFw zVrS-8<|+cSHRZY|ouOuZQMrguG6PRbZDoZ!$Ba`?Xij#-7&x)8 zIP~NZkA?7jpx6=mNcHZM5m=j~kNQMJ2z^ws3li5H`VRbpX|wc3uW2|4*BfKeWh_j= zSwf}=X-AN#lUiFA$c+k*>-zOB87 zBsRAxdj{y77+D2UnK6bzCDN*dGGMUAbMpe`SI^R}4?P%uf|xi(Y` z#HimIGB(z7e;K&vtH~y!7tyrIp=N1gsc#Kw80G5{$aZ&1laL1fOn`e-orY zRD_~$w;L3n=`{?3#;w2R z-2JV6QsQD_v7Cg}MJ{k5c}=K|nC5e3lcMtoXlVy_wkIMK6{`I{O?+6S8I$+5Stgx8 ziDVcY`0E$VJLbu0LjWYGUJ#yU(`BO`7(PgBH-26H9OMxBcHTV9`yNLJIP!MH8?I@r zA~f2l;I)P@i@u9jIfg-}pNe$?16E+W^8^5&Jf`C;gPD`WtQcNv`UY^#}ZrGQ~`$qvkomou{~rIGp1?&eDjYTL_iR; zF&mFtJ~UT9w;r`OftK8_HeW9yRiy(HSxOTZNbv=qnJ5dp{}Utg&eOYQ*)E?Tzl$m2 zMI7O!X6jt&^jg|3`PojuADW`x4M4ZoET9F-3+Is?RcJMMYHC>77Lo{T+AoT9bP0&$ zwEbl-;M{R&B&Ni{cyuO}@3)DYZP!(awkpw0L{%WqbiI~P+GKaQp3IM;sB0r?n(>Ut z7|80|uu%y9s~L&*az$jMIqL|Omh~SU%0JmA=^0TRq&lK+Fr^2Rb<)(IU{{GMnkX11#m|O)U*L6Ecc=KqUe!uv@D#l(a{3{G1IEOObgL( z=B1xDV#$IsqeaMt_3Z4ZwRb7B1IZYp@39~0QMgY79wOO`g);+oB;m(*v$|8-dHk>2 z!vNJ%i75ZuI}m=7C1liK^jB7d(4m%;pRvKXeMUyh)9B}dM4XaA5$0`InfO9m+lcP8 zu}Wa;aXKqL;mtUj!7Z&N&sSg%&at#2XIvs9OoV2s8jFh@!k;rQJ-(!X)0@8wU1t^Q zTaYxt%**>tEsSBvIY1ZD*G13rDdvrhdKf4njoOudcM`G|^F31jQCCGef53nfYEFE;SaGo({~uZrB~F%8GQ*T zT$xA!7^(i=Ai43TX2<0b%gd!O|n+Px~^$0fk z-B@kud*LhRr3q*vrMdBJHivW9Ygeb{*H5yEUpQft`xeyQG8fc!{RV-f1LZhD<{j#* zAQO!LZ8Njqh^P-bg|EA1z*XS9ndn~njP9Z{Qj7&#|H^BnKeF}U@m|yKHexd? zp{C^ofwuL=EOd|tL|XuF-<5N#-*aPgd;|og-0KA}(&umHt~GTiF7g|90%cRav9}X; zIQqWaqZ|bj6lk~u?mp{?oKEv2OzYg)NhG{F+r%J_gDu>&?Yww~pYqAg%=FNb``1?W zLw!IzXNs7xQP58D>DuUNPf_mNDaP#wCTwy$B^#A_b8z>cWr3!onS!7Z$2X{&cV(s} z7VcH~MnHVZ5Nmvf880XrPFZgNDRm6Ui`*Tdo*d1Y<@Rs*T`;MBIt~>|K%>j#Zx$SE zbqt#aEBO$|^AlsPAJ%d!=y?x8s0e}ze6AHu-XLz@ycCSDm@r+JW^H-#f z4hDBvMyV7Heh9-D`l5m$`&UOzDgylK8`+rFfk6+1r~7=~PVYJ9t-`XZ0wH$SmhN97S_{TV~LSK2LTI>)ew$MHf(WEgSp<)Hjy45{a%mVc~I{~0?HyV2b5mw~jG z`43}^veuHJhhvV9xP=@(p;rTnzzQLf&#Bzz*xwr@y*${|^5H(ezdi^XaA9p!{@xt) z>9HatQdAxk6ukC~MuG~C7T?P;+u6?RGKC*k{KCr6284zUOsS$mXk`OsuZlI%AeHNa z;Rkyr_N{5foFdHO&SAd}Ev9@Ja-P61kzc4=`;`mw_Il-Zs%^ODVgv*mtpLWdbkF9^ z2GV3ljt!bY9h!E)4hiYmiEu59k#5B${?REfLlub zc@}c{h+F=zgKHQY`%GX){;a6;{8eF|%M})f{bAU|(aoa{k#+!Ja+A4}9U%|j@*N4r zS%j8qF%9o^K=UOWNL9a`Q~X9-0T657Ia?2b18y&!#@fL(U@NC>n|PI;PN6E6(KnF zX{1j>QdM}g`D;@BmeN{9i>?DBy6?R=6=D4kL5N z9H$Nw%n~`;dlf1MxxPaunZw5F&e@l^J)7{)8yub!9q}9pl?mBfER8R)=iEOBfSCo& zNthhhmDjQgueXbK5R5@k5SLj8;qLSxy;=q}bsW%nVx}5facrYXFnE7K=Wl_`*fxO@ z#pi~O-k^9n3`lxon*bh?7DAegPm)>mMciPUq&T;;EYPnqhK;b`*w3u(s#zDpW`)+T zfP!3TT#HA$4`jMZ6eT);mU#p`0K%&UTqjiKJlyn^f5iRA=~q$y{vo@+xct~rY z+Ib^}PL}0e8C$|>Yq=IIX2v8<*igq&wLf+|bPN&$7TlID_V}R`w05hJ;xCEWzZT00 z*49I7HV5WpG87JbA%K-&RYGH7sB;HykB*HiT|d+K zqAwKljVR775}7ZNeFU%rc*5=4K@2?*&`oYd(8I%{UPO@-V|!+sv+Xv%!4zZl1@O4f zua3hxHQawpr99CdrA>sT@>B1p>bktXO7DxV*aql_d{v&>yTf7sI}<|+u55cocSeaYZKbEKzvjuJrY%r zP`$c|bhDRgchQ(4FCFPH00e~F6$pukxwc0s#p;?Vz$G>YR~!2d-{ff2k~m<~jR_W{ z;dXh5G$F;rTMHl21wWtE)4jin*xAuZqEK48#IgiJ1uElXrhXh5InT9=D?@|SvdYne z*G%q*n6`wHC*RGY0OF8$;pEP0TzT|GtNfDt*q`@@SPpu^>7*}wtG-V73-cEw-#1*x zN8TM)pWkA{_ozuTbG(1o!9UlxH8|NuZufVlfY?1z5q}s_*7g~#*)Y1WgpdFkH!LUn z5FDxXd)3($&WsR^Am<-ts8pQETk?Q&?t1?R7}NK!0soV0EN~Tb1&9{o+xd7(bXhXR>my5vKL|6b z04-+c1P$~&)Xyv2wB(_@)aVkwCNg^DfaO(e5qBZq7|}HpCcYtD@sC#u03Mi(dF4o4 z*&GA(mOFlo1cG(C>36uJD_$FKznd`r)d~ZIGMtFQG>w3bs;mf9k%&Z32rU$GOa7tf z?fz&TzH5z}TBn{$s}2r8TAf&YuC#$*qqn5+o-!9bM)zzF^+f-)Vtg zioPg_dHiu_G@@d3otm0)B1INv;gJq56ip{#;$P1xFCS{hpZUR6Pi z14;`*?=MCWLQ3q!bcgv+D}+@j#WX+4)d=gmel1_;!Wi#^Q3DqTIME@)c`uSnC&z6M zK#z>10=w9%rjo#HmwEH9rCf9+p6f6r&k}S?4Ii}gaew=_4pOK?=)@vs!|?0c`dVf~ zJi1S1|7q&8J&`4+hQcadfej&PhY{l*3*4S((M0T?4D^GJPc=Hnpw-H}+^0oyb1qo-nmi`jgkD3J9zQ(h z1p_a-cDFm4@83O4e;KTX`T0Xc$f-bsBR839e<3~Ig`FI*J`vL_f9e%u^e^E3bsL1F z5UyY_)j`3|{2===61wGr#r%?fD&~f4M(aaxI>)zv&}949{;M>}=Ujj^Mob`cOo+UAeq|2NfgS*-=|n)o|;*S zf@fz5hzC8p)H@~*qT#<`lK(P zuE$P$E46eNOyL1bURb^jr#W4)ZK~z5I74_EJ~-jg_f{_hDJK!YE2Cxg*&IRenRJky zMg7_51?}rPznkU6s&lYvM3r~KdaVAdYH!5uZbs{S$Ya70KD_gwuVH91d>`(wvtIw#t=Dj=J_F!gfqb0cUXXLmNu z#OoL%_pW5Kw4l^c&yA_)Ak#W#@_v8Qa3$q+#?p?BKMD8W%Hz2m9cCL@U2{WVUb`yK zsERwBHIw_=(HJnCT|>pkJh&VQo@vr!mzL->`kknuAs;{uie?aVS3{0YKCN6c0LoS6 zw8W*N<`>Zbx& zLWSsSC7cCK{CQaq^<&`wUi(aLKW+2xi zi=O5jV}A>6bwwtcP(oN`7$}<}G7X^tU2T*}CA>wiJOn*eaQv)nJVG-%J1Q%`9JkF- zefLnxq?O9eOMQ8_Q4Eb-pzBpJu#v^3oL={}KC>R4NSBJH}ll2Nb=a^ zI4YEn=sn=ZNmlZNHz(coOAJ0m+NI~a=s`dgNYT?tj@(K^@lOrbbuwJmPBbBCB5fNFT-kkbw=E?f)9Ju1+Q@2_Vh?14j z_gPd=4O#~z$w`d{9i&V+ ztFg}KJehdJNDJPb8wxCbwwyvGPvKJi+-ZQbA{luSmYbZ@7IW1tei}NUuZfMzyu~)* zB?D)Xu))K^sgC?Ewh=DK(myVW#LdugA2oIJp;E!H00s z$!_s;9{nLIACB@%`s!b%Pk$57*oL2{{|>Arh}zc2qfP2t8#!yb^uCgqQsk{(=uX); zSUp=j)iPvkRG7lJiuLgc0e$);_w#t2bTnHi;v7@_whhqnv0#IL86y+A19}YuLBOG? z`x?br*mbKO)bcqA690tN;hgbtqT>#5x#L#*#jd;%aCW)!=u89c33qeU#*Y}M(2t*) z75b-st}VrrES?tqT}{EHFD)A04>;auC?-T2W`2<2myBB3v!3?FsKMMmXYLrp5w^Gi zxW;d97b^Clyo^}Y&Az09BrSIUK+9xRhP_1oho%_BXj88%?sm20@!LmXk8399B|Ohj zgve=pe*L#zqPNYQu4e0RY0p{s@UbOJDDV`v&Or$c48Yznbn>e(Zxy7+=MCGJk3MHQ zT!3lk7nW@&_1{FUA9tyrd?45x2HT{ z{Q<;6fZo{eiRN+T}iiGHHGF`?z42jt;d_7;_FIVaw$v3G-EQpnE13Llk>h?u9 zD)0(lI@z4s@4`^oSAFfTGtHXM)c0GOsXgj{Jn+_5??r3WiB*vF$Bq}=yqP=!qA3E1 zq|#UWykTvuU=x~z-;-$hA^5=oBO}b~hPa1pF~x_8uDD{!TkOFC=t~ryvJ`udF=VY3 zn3IWC9mB_JY#k%_Rn2kR;=z_$=p*?c38ky5h>VZ*z=2#`(lJ|&)NQy$L;Neu0XI{Z zNV=+a)+(|6?=8oNXUOr3MkbM#s)9V-P0%29fq+WsQUJbgvNYl$B(J48TP6!&Fr%X^ z5Ai&Ezg`YK)hQ*v#*cJ4<6Ukfp)Ish?45^#)nRO~kk*>dU!HaQO=)b=a5=yI6H|yD zyZ_t2q!5b27CkyTl^=$wD>-Gfi@Q zaOo4NV^muo3b5BuyGs(dK1eMW5>MPMjqNc6E){qcd4ClsPt2_=zsBpJF@BrQ^0bQ2 zq<}PMCZb6zj+WF1o@PKTdNrb*&M7$YP*?QxV7++U;~o!s67MFhdBlW}2lZ+I!h_~j zS>=5~d$w-(Mb9}p3z&3oQONVP9kPY{fzjchBOthj@JzJUY!&P$^ z_DgpvDPf;$3XzDpsJt06?tqFME{p^T*ffmm1`h^gP&9G0wUS* zKE&l3x|#jro20U&OGA2&>M2gZsQ4$-u8n{rFl}@#p>=o*447!+eZo1$5h68ShQa~? zu;$yjmoQagKWyENmDy2bk9L22STYFCe=W7@c}i146L-o}yQiI+FI?T-&%9~i9;iGk@9BIrUo9A2 zynhGnh$3%7h7x1NArZo`XMt+j*uV^gT2tzjx@HxX!yWEUoHj^N>$h)^&Xj2PByBmC zR^+fs+mg^t#|(F`I?abiMAf~!h(65kDd{1@uWi7%TK!k1uMhh-=5KlqfEVK#A#dfT zRv;qCw5VjvetMr>J!Ga3bD!+_A@uZw!|wc=L3HvPq(*Tg;qR)pK_Pg{Mmz4)u8tfM zAQ^k@iShzbSz@1BNokSrm#w4wIa6xT&_dLMCFm%`3yenK)Ts0P5*Ix zf<5P9g3f97@^r3X%i5YrRF@asVl>C;LYb`Y!ct^FhJ$|Mto<(vHpkFLh}ijK$9wbr ze$7q!U?uvH$$~a~_|4#!K?oVGb6Wl+$Ia`+@Ycm*K*9jWBQ-zxJ^AAC;hq}CPb@IM z-rAC+u)sJ4`*ugy$$M@-b&&4n2CA7UwAia{%KQMXf((?!V}chP21=#J6Kuf!t=w?s0+l&H9 zyT(Ie!DDa zv$)PSx&;&p4IB=Z8|9RYPryB3v*z|2r7x^7$ZORqBHV0!(J>Q6g)3+Fs-7SnK zCbNwa|M;v$Tnlr$Fi3EWAOi6sa5|+j*_suyw#%b z?tQv-a&I0nwDz>W!_A$9L?2pxl7~;K?Nf0Mn>W^F*lmGpXn5{ucpX|TdnK`%a*L1M zi#M~Al>TYe0n(oko;Q;ms!whi^KjKE((Q-A8Ic4u@?M(Ww(PQE-`=o!lp67qK4rBj=! zjBH?XTT{czFqrdcYi`LS6Mzfm?f;_Zp}#Uu>tnvh*LaJRn&7yf`Ef@j3F2J@Oqm^b zHog+vCo3t+O~wR^OX*VGr!JkY7wsb|prPfx!3rhTG;1|ch%TGTw`=>Wu{MJMRCl-9 z+QakDV;8Zia#LQvf-T)$siRYHn;}&pwx1Pd}vgt zcQGbkYz(wx#XGb_%6stzw+m$0V?X}?oHfX7A2 zI3sci@;g1MujFM^Go!Aj5;?r9Nn9RA1{qUeiugwvbiH z2`!mpi#D>_ZG9PkuC8V**ekjQt5`AC!jr}G{iVMUJYIH2-`LJ69&EO%Uf|x!4Sss9 zp;3A?Z}OS*cie)ULNWH=JTp@e0vRWLE)R)3m}j{0Of&mcnaIou;hCHZr5JU)ZB0p9 z)2DdKT*CDRjK4cu1qj1gYX&m2bE>p;J1gEI7u@Pw+7g$e&Hrjy0&uf0Bo0xhAHhEe z|FMiPCnjmh+PZCMd;eYD9k5&#X#FdDmWr+GLWP1pou3~8wC}j?3V(l{iV!{XI}z%x zuH69~Lc>cvPT%QMd<3;+ySj6EpZ6U6Y@$gc1azgtdq@>ZM{DaVmfVexeM!2$To})v zVYl{~z>c!L^psG)nndh5yBj_5OSQ37y&IO0e*~!W`*do?b(OR}#S#c_3)$6P!uF5v zaCJ_s&nVYI8#~_SDr_*&A6Kc|1 zannf9NT))Bxfz}nGXp8%cF!OEbX}~Gzw1n7xck@D$o2I42V)yBqvwgFY?U&$?DGr$(HKXkYE#vv!hc9 zl+3=Dbd0Oi?=6{4Yu;ZxM*CbZ6kCgyZ%ejxF0=rweM?R4iM*rDzj+3l`|v%-xgF&0 zT6@Dm#Q6K{c6@0b16j!?&BjOXm&ShU7~$q+zpe{t8}mBFKN&LAUEYEdaK)92OexgYD#bbX3a8gChXpG7&Ha7DwYG8*u02Iw9#iX9Xx zYqZAIUzJdG$KYL)vhGcu@dW@G4>V~~dPcF;-?V3Ci-7rVwx3w{O=emkNg3-Za_0wX z>L&}l#mXutr1?O0cHciPXP;n3I;6Z&Za@k~o1DOf{tki8`_*SUx>r94SS&Fk1eu&w zd<2}D`Q<)BkdK2ozdJ7`1^5vl?{+bgOooTTlq{4y02$pY@bVT9u`*TnA#zU|IvnhO zr)D6OZ0I!KsxDQ}LPY>&v^&e?;}iHu=LPR!!zIhh`h}R1(Rp-Vp6s~zd8byEBFB#L67BGP}D!?i6 zxi_Bp6$KQlf)s7+;$9{95sw2#&JrR(XGV0m2ZhWD_y?l`c(tz7(VmH5>GR?*6(M+OwzV%=g9y&AWYrFBaGr>_bq$&i5Ip zt7V%pE>4Wj|2How)Jkz{SM|bgc91xA!pW|Il-MzGrO$s(4v1%4kY=}^EP<`y76DU@ zEh*GdpIjR_@ce_#;N9*dbSS)2AWD6%7?;jNa&$ipy=>eb6`!G_RUuz$zFh1OoxT4e zD#z3XQK@PBsoJD3jK4P>C#x0{7nPsu@+MdIuK@SM7Fbp-cOVMXHodp>CNY~fV>VQ4 zpgXBzLyIAA8jFc@`LOiOkNqs8X^dNkLYFp75!6eMw#7ks7pDpikB(0Sn*Btbfr%}) zou-G@%D&VM8cl$&=!41wB%(uoK}KLqK5*ZO3qSz_k*;3n6nz;sdx103&t}#d((n`l z&cv0gE>qQdMSG4Z{_il+@LhyYg(xND$a?r@^+S661ov2&(N^Du9#?9;ba!KnMZ4lO z9qEshKaCF310rQ8VFMi)$z^lhjSazj$JRjM?(j_q65?Uxn#b;UZ~i|A$*q%;giVY0 zQgxZlmcH&iK!w-Z3(&ZTJhSh*qj0wznX*`Sb-7;L!;KwQq;HqVCe3E~-alCX$Lh~u zz*;lGnNQgfK`O|ts8scfdL zY;x3129if^+Q_3ux}*Nt=&qmssnRp>(0Jf&uQ_9Kvuh^Gk24KTb^^%S#q@8>9Ibb= z_IeRFWvpiK@#+n4k9E46nVYHVJ7S1mb_B>|puOg0^VI{j`}{6+ovsXAG5oiE&|&7< zAB`_QXwA9(Vf?g>8vDBl{wGYC2e&78MMfTt8_5)&x9BUSrF{E<#-edYTiqfJ~ZWev!qh z*8n{XbjyQF!Gh)nXqIA_gS&1hp2@aAv;^@^yN?+qf`=Xx_B2k(tG1wm1(-74yF+Gu z(R-2N@A=euaLv)BKVSZmYOF>v93M)XJS3>h!`5HRLSrr&z6kJP>87M#O;LA|^VKQg-V%;Plp`~6K{ z1;_0aS2tdvl{9%f5_r%r0O7vKgM|d)x$4qamSC*U?o~}p*Kpoihlf{hIRJ119s{s^ zuKTF~J@@L0_cnf$qwAd$#*%~S)ja|paW`6Z0VM2G@3PiS0=O#l`Q876r8f;{Gk@Rz zJJUUSl+w~tw6%00bO~DPsG^#ZqP4|RRZB#qN(iw`wY3(l79)m?of2yz8o}77RFSn3 zqLxx2#1Nv@V#iG7X|7>r$D)4|HRV=gyY9Epn0n2%VQ1pV z7~~6_Z}I53mt_UNl}4?`XT5s9prDxkPMum3VIXdz+#n52lnYa*+vAbEKpi8>Oi9Oi!0%MAKil)C?V=1qei`*p31cV?&v>pV21#*tKO$-@*(K1v_f3R zU0Tda%G^UQDOm9sZvZ`21t1aF_&aMY5}$hsa}Pc>t-Nll#X@k+%VFCGtUSOo)bv~a znM!}SD#4hxc5~uPN#*kbh{XS|u|TS@w;omIIT{!qmEu`CmzlHX9@uHt5Gb$k(xrjW zHSP4`@d!WW`WZFbqw8Ru0NQ{1zPB%?PN5au1^C)@J)x?3sttv3KPH^fEzgPa5T{Kh zg=vym8%n4rn-75eo$zr(>Vxf*)R+&BO1t2qcVWB_?`>D4dHcm-%s9Wa2$vPRT!8Zo z^b(w08d)aQ%90B*LOyy0&g(Y-#_An2<8TVF`N~=+6}a#gRe2{arCj_y^2F}Mwp{F} znCq7{DOByj57O{21GX}fH?+KjmrF)SXb=JlZmK$~pBfFynldjjuW$kMdRqan+kacj z%|&pZ>a`G;6no+4p4rF-Xn$vjP+biR6@l}fjKF8} z?9QX=hfbiQ?q3GcLazT)*KzddkigJ)D0RK+zkR0=(&(CwnlTq1HqME@0KP5T{~0H1ph!jYBf--4jKQpn_o=X>$i>vj71yLhXW^0?Wld6=U&WM zHrDl>6q-)h)n)7FxMWY-l?@&Gb7WyF_gbKHywHd7lpLmDp#8bxGejCApXad$TzcOJ z^74_E?IY{!D`qK>&tzPyuQ%mxrV9$fO!x-34BpJi$v~qK!NE(4cQU_Ur;T(ROqCoW zmLaxUOJ@^9fOgrfSMsZp#O_8(;%&O#%E5V2Aw5_dBL$Fr?xQj02YWs=ek&1&@_C++ zsF)e{HIvNjIZMUF6pFQUuj zkD`4KRX(dllqt0^ypcX3$LL2c1RSF64U6pX9FCdvZ2C~gs)Xl49SJsTSkF_oc4}Z| zuQ`x7=i3yvJlg6_##Xt4;cs8&(WnYiCXI9AkI#9xDNHmtXnowwk_7lw5S0Q>n^x9_ zl!ufPV^O$Nu+Z%+6X@(j&8{ya*1Wh7(}sk;Canbrk&RxjWuFh5I2Gp|=ys|pYf9?$ zGR^yxWR34}E3C>(wk>bB>dAyCwZmJT^Q#Hc9*1jw%0iuQr5I4Ccw6ZuDAFSWHe5~m zM1q~Z_|4dRQV1vpcbc-5^5#b3NQX+%e|;{3kMDk*c5J7$;UZXJq->*>F)g_ z{2_Jc@cw`_U~g%Z2PMe;6zfh3RRAj7k?5F@x=A$i$w5D((`w;Vspz*{*PnP>A;}pA z;tfRQ@WVE3)Ehhmx3f8qXpXJp3@rqHxB!lh{j!Qg4tK6}S&d@RVPY(aw zh)UU@_NO*FALzc?*f-&_*7t0u5kf**lN41vB#|}n36LW#`jH%S0i+bCM zE0!DWqsz2wDygOJlHz+U)Dcj6RRv-efhj+c?|{4oY5u`fkI|XpJxs@XIROch-!$Hg z6fz)#7klvC6vcuJ#MI!aXp?E2ayX)PPW0nz&Y|~ydfiDAqhlQiePY>|d$Myv* zc2(QRU%U1VhkqE}3bSVG#lT%egd`MM`iO_*`Lt6ze%rF85xD<(IX02g#b0qIrZZ)w zCFaPu&?oBgT>25B1|m$C?Ye>Tu@?ahk?{$_=|HyMnJ4t`D~5t_zACdHT4ISS`(~k< zbl^Ii%TxMShv+ZbC^67ED@Oof=zMGh}iLg*`Ct=`nbLSBXRWWBd!}@nPI(uTgHooAaXf1V75&XN;({ ztZB@1(P}q=xcjrmA@?tLkv`Qqntxq8ju+AUGuS%K81>h5aUV@_}GZ|QE z33r_$N8HUPx7O8D>$B$^Sc=Gq`m5@Vkt3wd%23F##j~|@|1tLB7V0>(|5a}A?i+g} zL;ec1d3HY01{jQm=wOFiHNs%OB%6Jy1EiM5OzyK)?fB4o`!Au9?lFLM9r!sVj$4e` zmW_U}TN7zB;Cr(7fA z9!)fI7Dq&l;-{i&zMRC{d!{Q?97pmT>k7mu5gNN?V#=1JuzsJu3__-d5XR-rnKz>W z(8&qh?n!(1xHpzfxut=oS5Z$U%n8;b8_M|XN=TnogZ0DQrCOrAfPJ+1cV1Oaq*~@p z8Y?{@+f^$3q`lW^7fGnwu(5AhKvZzyRB~bcie>SbI~M+sfpNz1>_cU_*cvF&kUw9X z(`679U8^^LNt!S01kg(1}srZY8uX}n4sNMXEKsjzlMNg3@fb|H( zr~MG;l*gn6Z+&*qE!y948@ablRQ0|&k?%wwKR?vD$++{)fD z`*>RyKyx9c=(foFb?N<^Q2Gp=zbgJ|(#qN>DAt>K54Z3_&ll_$eK1vF2vsdttQyP5 zvD3HNI~u6UpHv^s7pGcFKV&bIxeOuBMjg*7ckKYTq*kCXr)Lzjr1Um=8f}NM3ESP= z!>RCY?nR)_&lq1;UyNv+2wda2y4az~*SzB*!cmy;ZOjigp=@5vwc1t-J-~6Q%f2>< z0}+6!TBY5OXx3Zs%P5ur+827Nv8yWbnf0ms zH~L#?3B{oRn`5-Y7m2QLB{>9Mjk{@x3v6#R>wAa7e8d)8+a8*RF-z0IdL)$Jcld6e z8&HZ7wrlIA6=})=mFLg)#h?yUij$o7ZceTJx6g4;YL9Wv*59-hQxWHfXqp%e4g5{g z_fYBwvT5rX>pL8EqsNN>bxJVpLWP?|JZvJTd=>Mhg}8YP(3M;T19as?uDAv82{`PS z1GPUwBRfgl#~xw;K?YO8m}A!Jkc3ihZSAbE58F?pOg_DTRKRc`)8J$}kvZY zY2J#1a>9hxofH+FVV(7DK!Nd^-?Dle?N}~}TAj0F`hQB+U@ZNWa>flgsB(*}cJrE- z4LUf}{cy{hzK{!Hig&o_glS~!uTyK?ieEwza}SVt1JIkKJn8G=ABB{ZDJ>jAvpMtD z{o8s5Dvw$}z@9z9W3o2^iQY_-kR28KI%W!o1Ko~^nt*+xuMf0>diiVaugT8wW;*|j zTWio_eY?Y#gqT$IYw8r=$I0WSkIAh$ofN$0%g91WT$kCXk{;4|1cK7Ni}G?5?RFQO z*2mvNM!hEY5PK`pI$1vZ^Ui=#i#4D>6{{x#Nz~N8NPyh>D#xBmH`Xhb#O+-q@c3G) zT({bHwQ)f)X(Xu76b!Rt>M6CvSln;PB?8(VdVD?{jVyERFt`r)7o7O^uOI66bmvyp z;AD(N=MA1fe~Xj)>YAm`^WZSvnb}-G9nhp@JfQ0))cPFL$ER&Hz{#WO&`VAWx3^H% zPATxbDgdaYpR)%@?o!|3hO8##M2BEGFls&u?;DtoAq zJFx_9J*7MQT6+l<{Iu;w!OdWZZm_m6Xx#*6mk7(*wtg`3#`Kcti$0JCzSO2gVV;A@ z%xhJTTNi&5C@A)pKYuEpLG&Es4gLyK9^zeCO`vk3i3aX0a{FnQIG>#dId<7RXc&Ec z=*q%FW+Bhbxl zjq=pE#vS_#L4k4;gXRfl^yv#HC$$X}v(|H*O&x7RAb?0_Ek=tElCrhXO=^li136KT zzJ7{?)S}A)=%Vf$OhD|p3qPHvCb|^=Wh`N^(+v%j#E0o14FflKPmeE6P{d#3wz8jZ ze!8{PC{Ppv9x4doAY#QWz$0E;Y1r%d5yc2;8}RMhbwq|c-sQ|hR2V58&d;M zBLAUa+X7!mT5R00RB1NWn93!?Gxj$aq!|0P`%4PZ0Hfe+RT=j8K8+7s>tECx-@}k6 zB2@?=#K>q_4SPua`N7n`+FIy@2Tb}8p|Jda>0DEm>eL0D__r?DQ0FGPA2l9q+E3>^ zYHVZ#{h-!U+nSs4{pP>{;D;|c^VqI~3q)wk=*HU-ia(nrDLIfR0J*9V;jFkR)=kFb z3c)q7S)(3vUD{H_uFKlR@pmEf+8}ZV9Wb&an&&z|Q=4<_19EP6BRDxZ!wY=n?z9T* z1dQB37uFgZfxp%t?AaK6F|5HtlN#mg<4OV*U*!N*T1V4Yy-0U4m%N+a*i8So?+kz? z(3MA0!3Bw;;ts%k8oXmLxocp>*3pAW|J*aB(Kk*HkLeRMYiqHMUEB|O=bKY7Ym1xx zjj0LG84W-Q>b3zK$ZkA|9aK?ep=fd6W29VCABcK-5(jYBHRpRAx0=^KD8v4u1 zJ_h)8LLsig=J^kdQAe%L#{+D(IDB}lsqp5uM{jwEwio{YKZOFkg2L_Vf$gM;WHL;8 z{_28kTW&DpR$YTXrHkj$pyk!jTG~372Qnd&7u?`pA8n5^wM$mV|JzqH5pQ-VT1%wV zaV^?d^r(!0C>o8)LgDG1T3Q2y3;k?1G~dqUULUT)-DbZHnBH^f$5RUxpX+9I!133) zSGB1xr9=el8i1WRgGtp2Yfd=2RE-%}KJIF#BJRJtK9J_t>!fImf3spO8-&>LrY;@2EJ7|9a=j|Qe4^LT9Q~+f@p@@S z)+8obN=ta<7xZMBqF#NBCta{K0UeS33Vll1>xAjg24qE>oxx0r*iLhqt-Fjd3h#RSS7eLO(pSr zM}VGT;ZB|{yjs6&x|>G(Wpep7AC%WQadJH`O@7X$nJS44EyTWYbJZ;WYu-u;3Fiq- zMWr9!4OI%{EM$gNme^Ca!5Xw2`&LA)_52RrE=#`(T{Gw(yp@&T+Edy(eWz?n30L-d zF6Gu;e*G7*h?H`~IpA%bhJxb%W+Xz%yrI*JQrDkur}EqK%jVfDZmy%;J9Tg#{O`gG zDVN?}u|6A6_~_xmNBj2etBHdT2M1%|sMyn}jnq`F)WdZR%s1EI^XZB>9BpIGt-#Y{NCKFJ^X!BA8tOg6~gcTD@DB)Xo z+p^9z-Ps$Vd|LBInceT1sf|iQAE$^v7moM?D96Z@*?~uOqQnHO23O;pZL@Y;7(Ue7323ku{St zj^`S^`v;pi<=-`wU|gI|&5l_rM|p>OBMsee2n;}|qX}HZh=+Ifb3g36OuosxtFreU zZk)f{bw)i+X;xaPBXn{?egU76tP5VRQZiAAQ?kKL?pmoHn-F9a3c_>bEFcDMsoaTP zDk?p%T%f?|{Az^t8b}E_R0wf88<$*5(=*_qX;9rH`As{3$957?8(IF74MP`x788R4 z<>}bg@`8y)=ehd{f-~i>Tm(*vL76V!5H)SVuTI#YCKvv}D8Z!WpIH|R%#Q1ZIZi4q zAi?(on($d)uDi@f$fw5Dx)K}qBZ?7gVA3;KrFI1MrMeweb^=*hQAY6ADJ-(Cj6*E& zRVl;k>%(naZdL&?;+PC~0pxvV#gduhtf1h;0el_d(A^v{uLWg@tPm^6cz6vIyTSg7 z{tepFej01XgTE%3Nf}NA8v5lN|G34jdWPb&gd+}I_R?zq;}y^OVcvYazWtm`&n{t3 z;MPlQiAyr}2}OQHY?be*o7%3o^JIFXO%N3p)dJ}$kQ$8f=Ei(@J^b56?((Y#W09!f zQgLYoKfqx;Fsim|n+KDL)@95vFxDVll^YWPJe6E`a)!d~>to zj4Za0;kiz~?x7n6+}_5sD(6;bRuYIoTO}&TxBbe?hx{jZZFz2F=aI!3g`-N=C2pSD zZsV=vL}LEOqna(vzTnwCCM~ba@RiCr8^EFSw2ej;MZ61D-@^3Nq?SuEaeu7GSU}s^ z7g&&HoyGJZ)Fgcl`iU5wcN5puF@vgLmL?IP)JSKQPT77&iz+=pvhov~J7(0#y$cx~T z3{U3a)D4$tEtRspvrPUkhMI5B=T{HA&Ld50D|#Z%MqO})m)vM7jumL6H4!TUb$!a* zk(d8Z9yBoEUnS-B%H#pQkQ$Wp-A^qIsnAr%7Pt~oXK|hr>cZ1vBWQ z=4loKXr#`IaiYzx)rBiT#sMWiHZ_h;*hUM0pf4B5Gj`0IInm%k ze#}cNwA`Rc6QR<}t1&Xd+1dBli1qGUFpJBdBq@b`sE;3d)R^kVG`0sO9y#DS!*&ZN zjgU$M+i^@5ne5S&UQ*9Cz+M~eJgVat-L;< zK1CnT{Q&U7Si6g?>Kcsl6(9l8-b|mPQnF72N}GH$Ir8MkLD?}&$f3KR$BJA^-qb0L z!5X=Aben71c`Mhyl>JNUTM()y^wa<}| z1`k`~wIOwSWK7iI&WXcuow(`N8(nq4zt+H5@;9xQB7$rQh#epv);FSqBePk zp^FxRr-1FHval>Sq8dEjlrt$I;W|Pqc#AHR8FsCi{3#Q??~MQU6)hi~@C+)I~j)o&D&k zyJRhZUD#|sIRFKD>|6`+uYxEf{I|~m_2e=eJCk7zuK?eDy%@wr)atpEGp4hVs=ca5 zkg=K~zp5$NL988j0w$UO&T=@*y+LLWxSFh`>~D>zDPdyLpymbPDMq%3e`{)P$sT!< zV^6EUj?ixi6qkFDc6CIQUaw^Sby00fUPly=3A%}q%8;`uSWe80!W{_b?=vv8F{v>!7fL@Z{>0E55 z(^y&O+RdoPW;|8 z!1#*=SxDV=;8->TROpnxNA{)P1aH47TRa?=1zEtFsSVWZ1^YU7$vcN5=Q1ZY0ZYrp zPb;xmlfd~>K70B$CoGJ0Y%u27rc7%;Zvc7n36QmkxJG+^?`eTjZyCncrpl$mOr<<%Rj1~5@l)#ENB<+9#kiMSE`qr;%r?eyP|V@NBlv zoMSgpjQ5D!F?)lG&aY~XALDm>i-Vh1`M{nWQ#>wk+xVd9&HO>DYkZU2^XCPlRCsD$ zX;VCVl|Cz#m|W=NX;uV0Z{z*mAIBuHDOIuQyUGBHf|y%V7+Pgs)?FV!JV43GDD7Vp zgws7-J^p4ZI+U`DFsE7=H4giIz&t5nSk;tRV=g8@Or)GBoMp09n$Fj~O(;2iYAUh@ zUH#aveprpE-i}>D)x0XwC_pY)H76g;Ul&J_TN4t2&2;c`eB6ok7(X-Rg+1w35uaRv z*aE)pXN!vv9BA=ERG3>@51)9biWqsF$F+!4$-$w6bkq&PBXlaJ(2Z_U2ilAoZCZH9GUGfx!Q8v8dzJ7!Vv3x)X zYKHTajPmrXv-_ol$o2NF0!0Q5?8i2I%6&D`{w|w|(HgY#TE}5gj%_J%t)d~0>WhQ7${m~IE| z{lM(FsZY;Wvj2oc=6*;10XZ`GEe9x~eHy2-W3i%|8nz5VvLjSL*0!R&mq|yk9XgDQY;SM)|1nmE&yl|I5990R?XmtLwINcj*5Ag~(%o7% zP7~0^m0wy$$VYds6}h;K3%IO5v#EYL`V{Yq3HNb#Y)ENy8Ta0hw{3(rY*&M6F`ZF?0Vf}?5H9paYnjFuwHiX1>{1S zAzvw=W_>X5Rt+RIqj4eU1Y*m#UoU~+Z8AnBcBITZfPQ&=Ua{}ZQ=oT8PfPWrZJ9}F zJD_Ksm2*YJs!>-!J&W(u4{zIv5CWi~pqqr1^TcH|c-%vO@xOgC$8&AHDiSz%8)e8~ zdj~Av_uzYT8e*KJg#d6u+IeJIgSB3jd~l_PgLFtozFgG6fVz~W1(x7 zz1#KOl1VRS26%*1qbHEs40j6+))y_%pUwvt6^Le-C%=hDK_{!X7Z+#vnRT@qtlFSK7rR2>;&l7>Lg>cFaxfvTdnT(9{B(RYp7 zWC&WOEx8OC^=9h=G(t6rIEltD+&6!w=y7|&o>|u1_$eL;f}3fR>O~9~s!;M=R|b)T zEa&Z*#sk_D1S#DF^bv4^!p3jU9*Mq6akgJV{_A}l4DQYNF?K~io+BM{xpG|ckv{~h zr+#9rFSLVzl{R&QSE0!M85@2I)Oy&)}wY`<$oqN?F`qPZOZLf5ZB)tGkj z7=nOmPh1S`Q8bU$IoOsQH)D-CQ(i*t+fi3kA$kMDf~*^s0$a&qCj#C&lk}Ww!vK{_ zt4r&h8m;L{a5EvRp|v2iq9>*-(C|YL-K)Ed|9M@VHBH}u$C!Do9ICIOx%KoVtF>nI zgkP4gjhqxGcFFtkJ48shbC55kPye?jZNFTtab^|Jalj*6I z%{Ru<-jjp3V>CFhkuaR9uw}9dXi#)%PIVGBXt{e%xPPVFmXop-lEcL$jMjd}w@fSUtkS+Rsy6zykuz>m?^?p0Y|Z|G%_Ue`Re`O?(+ARaN94A z-Ic$jmbo+=`6lFom3r2!3mq8g(Me`${xT%NN?sqYSzIZ&I`M7pUdxWTgg03k zdq%>$O%lz%s|Yl>COF(3Drl1ix~sE$J&tcWv&c$H@D}i|Ll5ehkXF;ZFMRNL=?7|9 zot%C_&@{ucJ>#b;d8lVsZu{QX)wMlYWratKx%%c2V{;q;vUomFiBfD`KN=FX3J+B5 ze(q@4ydE@t|tDgFL^pUq2${YybGK z9sd4D!&gy6`pxr>O^`=Z?SM)2d!FY-;GXK=<9yV23IJ|x=VizQxV0|5R%zXRvH$0u zYSs*ML;2Bc`k*dk+vNJHcoK}-usGjLai%hA7Fm4Nc}fSqI@X}6;nT|TEOcDVWK}ZT z1f^DwyvPokZ68@KsY_SdwZh#xX{rD7$NRw)sd}_pE?aP^^>*6>$>euwnQ`|8(~BdJ zJm7*93O9BQjZTC+to10~zrpVboift80TfVul}fvK8*Wi`dz+Q<4)Q1Yx zzJ$dcCA5Dba!=)LV4OeuQ0`=_&$flbh%V|Ps}^99>e4LCM%BnoU~S+xr*ky zxj2Z!7-A3^sVy2T>4^A-Ak-NEm(qJld0)Ld(BjPM8GK##UK^i4`CjR{Z|~d-y)(g> zJfRg>=YNmg4d|1>&~M9yEF%7if7 z&-V9sNq`n?1in3O#YUBXdMh<>SMO%dgB)9GqFsU#9OqI@)*t(CUy9jxg4dD79gQhf znZe`rzQzjD)t?Koo~tGox}0rw?>E!i218X7*DvX~nFMzvw^5o%1yDpy%{1dSaqw^I zFst1MI5cfZr*G8C@&kbLIL8=1EiHqra~HBV(&VMgWIB+@tIhPxGli)_!w=cWN?z$k zvDddgjM{ilsZ{ngcJ(5n9{_gb*Vn4;sWebI>%H!;{g+6a<5l1FEbb+J_DYNBmAv3hgZ%A$@;Wi969p148MSD6$QuFJ(y!$vr9@IY@9ntF*>KIRuM3WJ z|KvFT+OPrScAiuvf6~jY8TrQ@gB!oS5gw~Mas7@U=fq-GQu^bS72 zq^IcTu$x(mHgTC2Qpk7j5lw1iz}m>`0+>o;DHMb9Ri-1txmnelQRCw?PMQ6F*}BH0 zjHiF@wE0!lc+6ju#PZcy;2n>`aTlbQbZwq3nZhD(w^+8NK<}2NteQ9oQKhWvIRMin zWv1%{fF`0t?!C{JMwLk)OY^ZCS)W6xcCg>hH|-tEvl~$-zwCZyQ8u8#uhDksUmex3 zfsD%uZ!g<47ksZatd1XZdN~wUpxC~mxB%>DRiI|4y#Q@Qss;-;pr)0jT}&vFu?jwv z-L#1>y8IG`zm$*p_rx42K}PUOtI$L8@vF&xhm}`!=_G|%%}@B*iz^y6_iR@VHU;Kz`*FzvZBzr&hQm|ec|t~ z^Zp}u)b(yJ{K(SN*JPB6RMh|yuB6byK%J*BbyRR=h`(I^33>wDYmDFlG}SmnwIp_F z*}9dn0dlk?bM$sS0p}0z!Eo?7S7F#dDyw9b;VD1_uqoGJ7n&apk@i>=(bxAH zuXDO0^V^gpp$??fyCX{WbuG=qH4f84{Do1Efr>yy&zEzhg9!wcd^ux|Q`Ny82fJBi zAOQL!+XVwz10N!kOOW;>G|NgpauIKYKz$5!JH}ORB7W3qnRG5ZN^6L zV7yCB7})V1Ybgk=y(1Sgx$E^n)K>pJRccw@y(V|cimRzCclW|LrpiIg2P70!qT74N@koMW$7g%bH?-x5N%)V8=`#5Mc<#FBGvj zTz~jBhNv=)`6Fjh-=^aB@Rf8w7g?Yd15WmvI|z0dxi*AxU^!l0%3FyP9M z;~i&HwKU7>TRSpWA2pI#(^N8vG6M2&<=QHa=|}5ty>3q=x56w|OfyXL%w8<+n1@7E z>{zpwQDHnd4{C%sACE1fWCM^JhIIkbOBQOJ^KATcL|FEc3U;iHR8LVkgWSF7LdS%S z>?k7x2+}*ZhyibbshfvQKllQz@PdLrK^o98`;R^M(Whql^A>*cvgGSyJW{owA=-X=&8qM@_H5jEI7Y% zkX?nw_V+rzIK&5t!Cm&pz&k%CsL&)shLJbUleUvF$K1USz~ch8emPeog2 zqQcko-MrpFD#*8J1_*zXovqdtO5(OA*ow{n8i`t3{%(7`&l%XU-o$_u*)}?mU8uPg zo7Ub(XspRv8Ti|f^P;A9BX#!)eZq~>@+lE!@2!xuBX@AT=SOcrugk`+50nB}?NSf4 z*y=uPZ^lpCE3A%>4VCd-WA8>k+D+Nh-GMZ&r~nSGK*b+~gpH%bA`7&^zz6suA zX|T)0VveG3M&=)~-Q1UrS{kfR&5W|9Ho^>OYroiEl2fsBL-P4M@WmZh*J*72cx?i1 zl3!39klyG@YvR71d24Yv2Q*Xjgqj~VI_{(R%8^%iZ;ueGBq_FLUuY(iSf;8xNrbs5 zhRgR_!0YFGN0uQyWmuq|l%`x{Wp#FxS)$LUl<955f5Ez>(&h#^52ewXy`jz6_r9U! z0Ol;_$8K+>36b_-ASS$y*u&^8bVE4tFFlsMYc+eq@~n6Ax0Gi4p`F{!O(crN=)_7oU{_ncg6X8=K^Y{8Ki-K$f+o(Ek|#G2)C1kg&JC8LcQmz^-&mP z$a(3cr?$>kE2=34(|sx`07kZ4kRyXo)Z0v6)DE6R!MY7&HnK~D&M}sPgt7miP@uB6 z7@IO6>^tQY30`O)F*TOxi;Z#tIwuZyI#`!)g0%fpu&1i%L4Ko1GT4kFR{L@8r=#=* zXPN}yK-U4O(9py0L-Z5!R<^(Mi;L&owk3^#-l;ckLPlwNAo8sDMi@oFp2e?)*@_)7(7XS9N zuCahFz~O`r{rn~N5?sp0|5I}l^~ItZ?Rrr@$Uyv2q*+=#@2TG0$b^8eY(EIlAhTl`ohYXc zZ-A5*w*aok0PNG=;K({0u4|}lb`S8QVxW1}%_)or*b-bAo{j(?C@oqII&ou9EkMlP z(tTyDT0U5P7w=d9m!~!wsZYuYr+Sw99OrE>wBB$FQAZm)L$x3ZXpK(*xEi>OWv>;<^M3kA@4{LlFgS+l zcEO~u*>GNr$NDzzk`B%4t;H3@rZa%Z$cepNw{mdsmEzS#1__yed$Ojzkxmy|$p<`s zho{4OrIR@-=Ou&{ubv5M^u>|6Y+E1a5e%-E6+AgYB>J)SE^R0&nOr#^fhw20_h_e^ zj(C^%lq}>#Trv3OxCYrMPdJTgrO2@dp-?{jC?(5`(9 z08~DK+ckFey70!HY_bOPJadU1a|pU!sjI?Yn)kPekc)|%9!lXpG;|`DV#AlF6ckNK zT|M@w)UGYdy#h+eAIlg77)=DL$HTR14Q)_0-i^4JoPY)x;vi=q{|qbIYiisP?giwa zaJi%y1D9bn_AFBMxb>-;GAf&JaF!5Js_x`g!ogMZ@6@+5gS55J;Q^SvKg^gJlp+=>Ix-b{-X3g&+iqwqxD~rlNjffoCWP zy3q*|G1p*;tTeC;Y(YA|wo3SKC`i6V~u zdf4!du-vW@KGs?d3zlBI1(BAKDA}!OW=y4N3fTNkPQT~?XW)Ak^hof`wq~AQwd3#g z^K0Z+jvg9WC7fq(dlYPJ9k?7*^Kwr~j%QA{sj}pEO1SCOI!4%lE4WqxaV~u6O$(9&>ZTuv zXnP9m=*!b-4+cX%&LhkCSX^i35i$zd3G}70PW4pKzr3gAu%C|`r}^j;D?Q=&)yY^| z<}{pT`9&P{gI)g=VwC3Gvmz(}vz0nUf@>1}6GitaDDSH3cdlcemWL{hR|3lKAeBmV zeZ`fu-eJPFp%yVV#^*ku%zDSH?04UGDk?ZbvwhNkga!^dnIrEw&y7usLI2RcXjjh^ z#*u4u)B=b_)QDz#1VOX(i5jIPvA0dZADytNnAP(V``$G< zV*WV}`IpEtiwK0$sEyCDrnf^VEeq}FT~=Fe5pcYoa37r9wlP+&K*|SB$jpjZBn8E4 z)o*-_?8!r^fpnm48DU*Mw&37CplzU^a~wU((D6>^8u{Ey+>>=Nw&LhV#idpodzUYW zc!QHb|Fi8}!+XzRl84PYi@C9oP+w1J^Zk@f=fY2i!Sh1ONfW-2vP!K|AD9p$^vQ2M z6=R6IVKGrlN`%u`hBKL*0Uar;qw72&ubB>1cARNoI4mu|vwRIv{1S?OXrTT0eVd37 z6i$VBi(LcBKQG*|o({LQ=zEVSGpY#kvSzMBc@?@F$>$pBx)<@th15eN5Rgr^RPv1naP;_vfq7p?*ruHS;s z#SecDT5F2^G)a(_bPjd#&r4E8P?+T3h{(y^D^Rmj01!czbph^7u?jd|SmlL4;o5!< zg}>*{i^J1SeR*>8#H-x@ub;L>dLMSK+YA)#TOyh3kZK;{Swi%h-0n5da3>V}cUas4v{~AhQN@Nc2iI zI37Cg=+v44go*wX*;aXiyPw|^%PXAdDD(6j9aBGFq7{k08L(tIo40EUJdpE`#H5YZ zD{;wDoFo*HxMOsP5Tcv39(M*C+kuibQH-(Zig3EoSHT-9%Qzvin~H%suxJj|gQu!9 z`x|VPG_rnt7V&MbXrbKJU?boSAP`}_qSuyF8i@aOL2S`6i-cB?QsINrHdNaC&Ir02 z@*4K})be6%khG$CNg(p%rGgg+A`A-(BPZLFp&>+`#rGI1Hm;;Kv774Am#RySf!?|5 z5=fl|D*u{3X?YAiWrNTqJ-<-p!RR>OADTHUhx>*`fwHV_*iq=p%ML~7*X)0RJB<6V zQ2Ro&h@8-Z^eIDZKH$y+<;o20np&R~`O<1jpKFRVPIxHP|EvMgEVo>DG9x8c5nI)z zdSa7ib)h3LL=mB#hL9-ni=(qpE2mTv+oA|FI-%n$~O4T;?3T2Y`?1P zMjEWcRB^?hP!SkaP$&jlz|qIsiVw9{bxiWKb^}-Wom!RW67BbxOj1>V;w^b(s?aV@ z;d5Z@mraAqTVB33UIFqGPe#b883%duPFp|}qVJ9ONZ5c@NAX}aFOa%}k13Jy@URZ4 zwDqa1zWa4&1Q10=J+=^o7Y=;b_yy7>OZ+ z%~}6((VnhH<5M_GuYmHDn&Scbq8m5DH$lvd*ZA!2%@`}Y((mUrj*MHT-5j2~neU#? zFjjWutUA)O5+tXN^ko`ED5&^IMWXkVG7}zH3~ayK zCRN&nwMGP|^`6u)&O&)}&i?8vze|cEQ!fMGuNhC9|8{j3o#3#YwrNvM3+yX(BE{ob zEimQ=;6tulhY1Wz<1on;0!4;fwtKD{|8-Mc12a`1vOhPY#mpK<{_dwpqDsAcCl61v zW$sVZraM%p64Je$8lGG)>hv7`GE(g>k9-+cicbaf(E}5{V*&7w^ae@pm_$GpJH#F1 zmF3^O5b!N!av??OpbHX=Bf{KUxogD>+E-D9v4Z0ap;B?imvI7Jns%p+ku`Ov>G@r5 zHDtF~WfAVdDQ&!jjDu)Z=#u%2o!R4!XY%Q0+L%s)KgxBoz3@dbec{+ z{*HnanaaRB#lgvM6rFd5r;AphGWCqt9~GX06|MfU<)URYW1u6P@7cGvu21JLRu=g6 zAEp09S}T`#qkxLZ?kSvd&(pwDoGr)uCd-_gcZhRSsZAI#T@%%5%a*c7!xmxD;DN>Q zYW)U0I`Ylm4Z!j!>%*YGbTX}d7;IxrnYVH)C{-L$>b@V&Q6bpUGhCf1^|yTzyfrMM zj{gGC+CFv082BvKgsp75cymn?qrok+0z1Hct$34Qe?&Md(5bCB)k^Z0RyQA;_&4JJ z)=nhp!B_Qc+h2Q&BlWn%=+k>aCsCy*GGdzT5*3JFoxRE z<(B)JSBRFZp}S2d zwosAL4KnCnqE;}dFEp>k-g#s(2HJXKM#$hzyeqm_xei%fh-D zOm_QyR^x~%z`KfU6Ep*T^I0l~uF-5xG=@~ndFy*w=8Foed?%d%@vwV*GpW+Ba4LB( zQbRMcz7l%u^F>9zPk~2(;R!oorIkQ9r-J^WzSmw@!aaq)PWa0#E&*6`Xgp(w=%#Jy zAqY&Xx^S0NE{uoIP3-J3p{}(lZR)*H?gp(u2*UT5T51Uub zE=9oK0fb=IGdl1>s=nbnkS4eT;1lJacfnz*Z)~QLP3sK?$T>nM{3hXZ6}mjgUAwzG zZ=l6r-9r&C2Uz_+(y$DmM_PfaHhUVg2&Ap3=Gptl0|f(Wl6crawI{MKOdmnm&U~1O zIl9t+y3l0npBNI;6dPa+6gnPXA24>|L`DMU>Yr;}-#8Pk28gujyGpOx2OQs^Z-F{F zaDYsGWgO(S1n=_={uP!0e#*%&Y|9IMM9c@8YVqb~00ctll+)RnKc_d_b`I}3((5v! zpFe*++nHT7=z~ho`t$T-vD~_Xh=@>Axzh|@G~Zx8d1tln1?0ClG=`k$${3>et!1io z3&c)8=gS2ft_3hoQeW6xoPXpi?g?>N%te8)_K!}xJV3Cn<@AhdGP;;Mr(}`o z!Q%N)F1A(Ib9Aa`9`=0DA={lXYM7-0B|NwU_L|e`X2A>2)6Ug?Y*J<{UfmS>Wq0Kl zWrM;=IyUV&!Y)#L+pqH9C96TbAbECWkkF-atViz0WBaAD5K++~!6)wtVZXZAzfww#c9-7zGf6svCCia{sd!paM+7yVK`=cfXWPU0q(9jMD_Y)OGwchfB6 z-?&C@44h=vGd>W=z0drXT^U@;us~#;e9-lP&KX|h3=L-Le(d#7@$HfI`t4d2z^%?z z*Z3tDS8dRRdkrCH#i%7vq`b6e!XU-3If3|&UiShIbacm912=Lld<$~6=;wzq|3mU> zzt~Lt`Nm>x&+?wKp+CF_8?$=Aj-8s$g;7%kkhtGP5tVQsjb5+a`!Dzt?+Yz|(1XFs z*F%NbXryZIcH$afiHZp){fj1Qf0$sXStaHBvDTT9D?vy7@T)hPq}XwhX`FfcA;5Cv zRF;#Bh9eJ2H2w2o<7ziH3B39oa~|k%F9!WH$<{0p5?A>V-+qk=9Ht%bwj4qzP^#kb zhO_K|7fP(E)?58i>nC`Z`p_7>+6)1Hs1KMmtsl4jjp5mifxs^@G`K(yFI@F# zayb6M6`6mcBa5Te6FXn?IMT#i=t}~;$FCX04}eLye0Yiz=`>MVkk+>TKl69#o^Wwbn5j4xzNq>{t-^t(ipeI&APxiD~5rp_hsm!fIA zgn3)9!H&(O46`kLpzJhPk<{^F^J{lLp*?fVd|694f5g02H%$-*O_*@w_badT+qS3q zy`KoKBbpiih?2<81uaa#|Blr?Zc@gLQD_C`I@T+2A+{n$Xmdy0xM*|e$yxx?Ug5M>eAFMomzn;#*(pGr z2LtFg^4~yD{eCo9?W|*ONX)R{>|gLWcR2vQ2>L_wf9177hbTjfHWw+g@m{TzPO?up zKtT@+t4;o8d`_URX-9sm@Pgfd{x*eQT-aM!b3I40LE@ve$uk8g_2M7uzRngIy$oq; z9l`=GyLnJlG66K0X5&DRt71`Z~Klqw9Uefv9okK`J>N+E}c3Ky|UPSPme8MZnLM&qxTj<|q_Me;+0G8Igk?wjomjiZZiV-MqYu*7 zfasy!&$)LwTblX3DOjN}{Y=6?U`7{RI zosO{oA=8qm@%_@=RBdF@L<(KrF`=^P-qCi3P3j8N1=L2H{Is++CECjV;D9>$SYGY3 z8ck~$QtQ$$%~=?FpXQ&-W@j4dsm8wAKX?>Oo2CG52x-rL83DRoz>}Q-iD++Og=+{z zRH_xTM@vzl(N&~GGO@wNryu6t70UcEkcIB2h9rYp*bIFKN%Z+?OVJiSVc*w$ybgK1i zc&i-nnm76P^$Gn$jz=8|O!P93Sl_)ey>)xKR^e=BN`#u(0ruRixD#KLM_GQE+Z_T) zJmCc^eQ!ZjRKoe3GgQKnPGh)nxv=bI(l4^GuGzMeNUOmYF_-Nd?#rBuvE|_$1?>h; zoo9B2hE96FahGd`URYiqz(_CA_LcgxERJ z&RYlRb=sXs-Q>8903n$k`D(XSWKoWU#NPppR->mmkp>y=n%E6tXU}{%tqey)20AG3 zD7<}SM!(tUp%FEaG7u?kZBlKHa$9CvYW6H9i0&R0ZJD0y+OpFFS6%3zISdapVCH=p zh&pMRi?A}t{6c;wb?FfFyo%F!8j&r>J&#$uI(l7p(jq>#bP5%2lG0sM@K(Za4oh24 zK$JGZEPWdIAsOf6!r=MMs6zu@cN@+7U{m}PCG zg-L!uTNNUVBJsPslv(z%lj1ivx5@VQ5K;eV0CR~$-Ma_XghIsj-omMmTfS6$-WnCa zO6I3Nna+fE0obqh(2%JN;&$Kc*VxGZ>$U3{Bx55Pz1@2PEsjrTYe{2>` z%=+|G5Sww)w)Ip%@-0~rxdNZsG3Y*e+Nw7VK*?IWAwEAB z6JmKap`(!+ZjH3Ik)>-IIF`ECrl-FHMHrx0-TjpL*r(bc`xa-iv$-~H0c#8jjxg7g z&MkLu7FBBVh~z!5TXuvbk7n*n*4!R=C)(`K0#nfZ{_FA}X@f#j4@h+boDhBCx z`*jvt@2G)PZ*pPX1yId7oZihGAUsF$cMprUzApb>^dtXLCqnc7G%`LbJ+6GqF|9|C zN-^rPx9m(h$$X5mKZFlW8U?Nlex}t-VjY4-(u$bSYg8VfWk1>+9TtiDWS%uXG- z#tKps2qjVQ!dthh)%Kf`&yJdv{7bWjzbiSIM+W@9BF5gT(%INeNNZ6G#Xb3q02LpY zSpiVB5i*MwyVl@@WSyo^blBT>XoI1{?vce0TKi3E_Y!(u9V6C@g*>iJFi@b{b}C36 zMxEEu=b2Av+#IQ9XzS0#cCQ;_v#jIFM$F2dkDOgvOiBL(=r=^X{d+%}P%}9u9855M zZMMWvJuC{3>Zg?9e)k{5ybY4nhr4uVqOcmzOk)Z?FP?pAy4K>vS&q2r6;(xz-e@Mt zXI_;^nk}j-P@_qvJ3ag$Z~iDyfI4u@1~x@?bOzdl_dPXo9$CfSrv(H#fr}f*4IkH7g@D#J2Sd zTSLt$7#EFso1ry-y(>S9ijDihc)xLX72exb3fcaj^Z#cBkmXp{B1>E4mDAu&@AP{G zogTD8H`i)?&f+gX3z9PM4a-8%J_a!c{HA63Kj~**UD_0=s?Q0wYba4AgD8yH{?ME} z(5$7P&w0Bi8`Pv@LJtsM076RtM?9`W$4J)YN1aUF zF#rIoLH)E)jLP{XWH_x(8>q-`^oiuL*Pt81oo&PLLbA!scI*{(=iW{|6j_n zJYi@5DbyIdn7XL{JjC$gvM+i>4z)vuz z`G;oS#Ftg!KP1a!WX zb2RCXEgodPy3-YsjH#Wj30ab>K#0w^w<5M9BlHuOeg zLJbWHU&kGXv(=m%R9YPDgR26^C4##%C1Jr)Z{1NClVKl$xp7pCtTHs!u#tHDhn&NV zn+?6k?^!n2_#{pdWR^a%J%6>S@Y!aR`Ozdj=}z%m_mT<0oN+LQg5odUV`_ZkEStG) z-;ISCmXIas+r=g;&qC1_-!wk|PkZYV4<1s`x?bAVQ}Y@#x(`X}$m^3&mmw%ri|!%e zL>JM^vCEtPsPhduHBG!9_9e56Ns5)n9`?)7zOY=}Ot0rgVrtia7+$y4dq>=~Ypt3O z)F+@k;sAPnXB65fgUW#E$f58_^&kn8i39e&$5Q^1^>984Cjy|scw)XLL0&3GJ8yJs zZlq#WHG>pT_ZF)N%`PuoKA&or9!8#3*VNzait_(BAG?9BM)BJW8WR!<2)IHMg8#4;W!s3U6wl!qTT+26XvUAiQzvf*0su(*it6 zGq5=VY*zmg6fUXzMV6>FRvjN`=3$ArKf@JY$dRDVqF+I_>gMqC-6} z8Jy;sFK|Rb2Izz5^F?$2h$@-w)3xe~gqzttqgFq&MzBJ(uF_(<`wa9tb)kk@jbU5@ zuN%wuzKQv`rqEq{fWGmsz?n6CoImw796o-wCJCb^86waUt$tGi=-_fF4aO#LM14zQjMj91#LlQC3nH z7T!3--&c)`wjENQDL_oc>HTzST;^gnleV}=d3`~enk$R^>cUyCJ^1ePT(eDcT58g} zWW&Emk{aJRIgW$R|KQA7)ME~X6;1YCmNTp6LXgCQG+OG>&So=Z2XI8) zD|qAX75wyF(iKk3V#tmfRmBHw-KCj`vmD-X$nKyu1PDF?xq<*hky(Jrpmi*CufSF%jD%k1FQ2-PeY;tf%^0z+nsX*mC3{L3od-L*NU(Ps{I^!9V`kM&A7_ zq)??Q6X!X^4nUaLORE!&0o7pt?ZR89VfHsKbZt~cR|fePP%Ur66GmI%pX4tQD`Z~E zzx|akR1^y6<*7o(4n}_paC`@JIq&vtZj|yT4+jWg6~3k<8>?rzz6}ZYnM2d;qJbK| zSS*5Ht@C$T87J0pYF5Pr75^t*ML-fmY2EqXu7iMSw^Oscz9Psjui8fuWq#IgJ&b31 zpUrbq@>ngFXuFp-!`+zphQaS0Enb_lYGy6siB;u_&V>zmk?wQ!17Y_UL)tLJI)MOT zXl*C0y2qK%!!rV`pb3A*RGo&}} z5KU9s-CdM1VxkBYj__?!)Q%jXErHY=a-DgEoGL7Auv6tJ6L*m$a#-M(K(m2=C)TDkui3O*~{dW86Sens(CJl(lrcK|SNZ<>qDL}p`YO~I5HJ4#Vf*; zndO zS^fPb*ub(q0)+-JCqJio_>+OqPv|$)|aLm*^EH2R= z&&$`gWC4b=yp7)~7UDvUxvPw`$g#8aUsjF8&n0bFeAHXV~u4q&7Ih zG|}Y;qE~cx0=Ta|_txW|QRue?u?2xYPYD0neAKU2ABEFiWYWWU)XD23kv4G^dicq% z*z*(4F=Mr#np=k7?a`(!C{{9Of- z1w~c+7dlURv^Mh1V^R|)593j8Ji#E8P2 zN=`-JE4bCr%B92Xx@!T{Sa=a6^Y)zp-MegAKw)hmq8g!Ulp0gj8sgE=ra=+_kZqyK z1%F>jEZ;9>r8)`-Y0{Is2UOk_?_Uo(E=fL==uqwBgMTc8v@2Y;u5I8fG<7Ot^0)Kk z`S+TnTch$-Sd{|#;gbXQY{STs--q{eMmY&h$v@naC8xI|lLZ=`@wPL-{l%dQ|HhFb z*`y9IK2$(Jo3me!{lPt!5w6U`yi)5w!^QD7+VR!FzRS$^XEslP|V{l~?iBcYz;c zf~V}kZxpxyIQ!4Ga~YWg^BLMJ>RJJm_K@@lrFwYSWbu=9CPUwq^2qL(wb3<9c^!negl2*79Y;rEsTXFZZx!EZ$t>lB zBm{U+%S+#Cz0%{7EiC%W-2v?ckFUDRF6i31$k~~o`XN?BjdR-nsBr4 zZ6LgcXux7$KmvL!GDG_`f50&x?}73;qEz zy3ceO%)$~xe-)l=V^0u6&CU^Y#NWl0AT2f@1^QjlxC46B>kN#shh|Ac4OmoCUm3gk zjwbJ~l%;#_=by+sM)GZ~|86@kJJyyFJ!KtbN2}>QH)ln98o5@bZOUI+*~g2Sa9etB znZB-tG4_7Ee22~ERj>WG%T9~WrN$HjX}xy3O*|$e5mJL6?6R`i6O#ba^OWt)sQJiv zf`25u?P^kMTEnnbe$BsL_?g3ST{2}re|WWYW(51sdisO9b~(sJE|lgi4yIC<$$ax+ zajPzjbGrE5sAkwRDmu}jrV|GlBt;w0-o+Va#mqeJUex&lK{c6D!PT{q)#a(Gx?KAR z>tYZpRZOs$?69y>eGc?yd%Yj(B>yucLTy|Kye{o0MExS`k3I^JA8IE2dP5E}BhhGx zuAV394##G<*l3aQMlxzIefhsa^^VPBOj0$X!GVhR>-W6VK3bmY+ZAXGJ1D=d;TUkw zUCrN?ZPmKS;}*d_tLLYX3M@*$ot7C-+qFcS(q2!@wvSm_!Z9(ek0xR6s`Dgujm?0p z!U&c_=0Y7%Qhw&fFhbq(JVI8m`gw`=Tr{q`H^x1^_(a=jKa1bW5F6)vD|{TNuBD2L zH~C)yYE7%p+(qQ>D|8y7hPwE`vw`k$6ds%5hJ(gUv63YTa#w|~IOw}ATLQFGKzgzc zL%9u9TNLTa+-TLa-p!#sx#&2`JDb(>S)!@?L|(*lXkA+~OS%;s``gkL-@)nZtz(au zZvaq}*UVhI`nXD1H9$1q(0p$CCqSdZpf6d)HPj{i+5ZmIN_=xV?*Wr05EPYP70v=R zNIU(-Ox5(Z@8@(sHg5EuXB@LU)N@}gE{FlyeeK8lhmUpMSV!DUi903ZCbiXGbq8WslBOTIA&5UT#8TSbn6@+ z#)3SM9zr0sukLs9XH<3w7F<}iAC2!b7!XP%7;_EHgqv@{YTwG~U)SM<#?B*jYO?=O zq`A?^8h$OK_y{9^&U^FjcFv^0HCd-!ta|PaZ3u18Fv|IF*WHFgz``dRt-s>mN*x6H z16=`vc^-9KD8P4KW6^+vFm`ZpgD+MT=+d=8n4^IXwsOizmcUUoY1`lw4Q9~yZjM~P znSm|FO%1sCDNTzBQvp|l7BcM}WW4QR;)lsDtHI|c7TtRa*PDG^rV8fM9={98xy${L z{t8`|Y8_Sb5M!^PI>vZLg^HGKJS>0}-~r{Ad^=HV)@ zT#7XRw#=09YcFNd_~M@!y2o88j$iy7Dgt}O-b8IrdyW1ny|79)rW``$_0*f%Tp7(E zH)ipFj-ZtnsGNh3yO)st@O|q6-Z$%HDP4jk3g#3qX5P1Zz3sQR0gxx63v-VoG(orF ztMYL{p_Y42$3KC!1<`TZ{o~MRbnz}yXVS4Dy%8IzSoH)xS!wT>eRULqIV6dVx1+?_ zc+Hb-MfKs8ZwKWC6Lx#@My&JLx$v{WEx-O*y{12`Z$)hp`3r?}fhSpw()JYdf_ddU zG}CiOJyK>pG{26S!7g8T+f76F+it$CGF?yI|nXIL8A;(RPa z6>>%&_2Vqj49Q&a?Nf2N-*CLF=>3m05$v8%X??SFvg=3FEu(3W?zP8T0Z4Edj^cwP zB0?SxIfz{fZn27Ce0@~^xBNSK)szBH<^*a_`XIfL*^uq}7s^|c`8#pvq^!BTc)E|F zXTatER4U0g$m4Eo+T@PSZT#*C$?R4J2#}-p7>!%-_tMMe%NKfYNS7GNQ%d~8;^G?m zAllkVK}h636e{9EW!i;nOw!K|m7OOoJoK$#g>`u`nfF$pVLL;OcMr`v23Ntqt%M8g zU+8D=sHq+wB$GenPAM<1ZM&^1gEi-!?r=2C8`XT@^KtQPA`@V~jwMv}H0*_@Yb`^mq$ zjnqPbn$b+p1bEzo=RLzE@C-^O)Z}MKSKZ(Gzh}M;C0^=0+{$~@xw223viKoS9E?E+ zN8#QAamy)XYl3GuHXYIehlWZSGX>#20r4(F0-0a(LdzJ$x}G{-%_5Ej0g}7K?)3By z2<4YKDqx3_`?iZ~5JjhQoiG|1617`8ro^H zgRgLtNj?I*m#^hcx=spcl-cE0O{h2Lh<_{{aoX)r9W=xXUtjSZ_CyFY|YLI5Z*rcjMOk%!tH-xRuqh zo&RC4TgU^#JEOib7q&t&(m&)7J1}K`3{IdeYFdLCBJF(!q&+XQ`R0X}6CD82@W*0d zD9?P@j{HkAczWDdxZNPO_N9sh;?dhIPMOyqZ?~EJ;ZR=-{rfWAUiW7^XK8Q{{MIV9 ze02$8jBseA@v!iWxau-K#$-to?#$O5xup`+djMxLOzpqSzTofr6>we=@l{G|rLHo} zL%C0$EO#GE;+ehNE|d>0sy4(WFt>{rCnbSi7KGA`#>v%NZxoaO=_JYUH|%yH4k#R% zBaX_4%i=SE{pE~S9Juzb6R(Z`N4-y(6V7L%)BM)aSx9pFjs-u)*c~)jHMc)_ZxO&k zbS_OW@s{Zcj}LU+_ILepWvY&|$UAdgd>A1wZkhZsx_3^%tg4-3LK=`UI`y;hW?7P^C z06{VZpB>1S`R6D?yij*W%Z39mOW5NzB}}dv*35L`t11D3h>9v`>JW;|b(?_K*|*R- zW%hIjG=iPiwwPe36l5NGx4F&A;M}C?{vtb^DqqolWQ-_jx)9)lL?IAwCc15gk)!9y z{d2Jvf`Bg+N$g8(7(pst?jZ^$pBX5<(+1dni>1PO4H7Z-`_Dp@y1G>iF8}_hMx~MP zv=Z_Xm50|OPHrI3x~qWsc8^0N8yLN_r_cxIv>jc584%;%DE&5k%@U|Sv_w2Gj^S?E zmIqEiy0eN2k#!xO*4L}q%FF74C+ zkd}!UWRm&gkq$t2Y(n*i?{Edgk}An2xj1Ffp#8cz{|WT*{nn>_=qQZQ$DP;pis+Ls z6(buR`FZT5(@Z|#ehVoTPWp_047WSQGXYXdbNe5Rm>P^M8_}Z)R)nIMBgl;gsP+m} z%L2f`ed&E11I^y;Na=gae4S6su8<6k!|~o-B$C_{={;J8(C}`5z*#q=2fX!1?!`X; zR@%5yYoK2K)xNXYKVK)WOc8xk=G4{#AfF?A!@r;tl9nDcQ!?uQYF60&aE@c3LvMV5z`ibyqog|hzt-Eq8 z$j22;*e;%0=ar?3e7>f}_=f&&!0Y03`isR5(ZDLU{D$>`5@R__XNT! zsql^D64$)IxX{dXSe)H)SS392YrsXn{M%uaj|k0|9PATwFP8D)W}jbv9gN5

|07 z&{NBxa6xa{x-lw21zMPVi?htAe8A#hDD*7ficTsX?(8S%NZ1a+Q%yE@J>t2Q@7{zbX-mIM5NUF3GY9e`+B^gkj5Z-Q_zna| z+c-o7dH&Y*^M}H{By5R6KK}VATm}RZNtBv;jM&@JWgOv=hB_ zIq=Xi42?C0tTjjcG}HHRYp2QVdE403;>9jMw~}-7NxCC+Jfn7@DKZg8CNxhS>2kc? zHuQkOs8f1oTAU3X`MN#uwIK0cg4PNBGInG)zRuVhB^PYOAKqvV#fAfCCW!VSuHJ%jnH<*^%M61s%eSpHKg0Cjl)Q*iO*I=WkC8<6a4kbR13hxw+bz4G9{x*#{&Wp&0Nb^0gQ{=?5JSPO-ij0#TuOS|yt3e)t~!2AY=%Q}4aXTNmnrB)1aHWR?ZeHt~l}7baKd5(9Sd zK8N@o4n7NBZv(vS$|rFcd@yv%#6uz^H?BNA|I(3y(9Dqdk&Dw7?Wc5i{^f1K-UdyG zknyIZy?J3}YdXH?s?o{W1%OM8#5fG%50+yCSJ z-_hE*j=S~UH26qopSUpEwUJ{!Rs*I|Kev6)TMAqnHk-DPzW0JZ+vzhuV)Ih3_asjJ z--S0bsu#2WKaGApd1rs7%)X1+et3~>WLPT)A8{2{%wOoQn74Rabdm$>DLu<>%K9X+ zip(ybIAL}|u%~E7E{ODCdQ zY54Tc0sN@@5c^gfN?iQRYt?sfDgmvD;)pZ#I%=@cprZlTR8!fmVB$-h>aPOuZZ%+K z`^mRA`HK(^eb`_gbUx8-yKcQUt<6pb45tH@E4Go`JyN+OjA$}}_AJ5Wz$BDE=Gf$Y3h%3?NWcx&31f?C*8_$ zvd|1HRe-P;Nq|7#*|HVh%RA=Ank`Ba&r0hTOc7J$~CXV zRe&xoIC@y!9r!DD5G=|rkKebj`bn~GuZ0imJYrZ9R*ulojghYDoCZ3|EGeB>xiG=H z_23tMAmSRrHGxoA4QyTv{&Ne-A6UwONP=MA50j-k`$lYhrwUcN+H@LQI9p~@?J9Gc zJx@YTe(e#sy$b!LZLnn@roZ?WsbL}KH>cEOVjiNG(2foej#d>kPo!K4qG@?tI8s;+}m8AxrvxV6u=uJn|J)k6JzdCP?S$x3cW-(Q~GVpZsh)2ME}2 z-#NwV?2-*WOVU*IyUGSrYHCc@!Dl|fw1j~HA8npFxRzL|9}x!ho&=${Y>867qATE6OLqQcN0l3KX zmJ)B_%7lS|BJ25y8T6D_YG;q#*ngO&a$VS`1)IU zQ!Kc+$1k?3)-PZJU+O+$4s7iKrrEq&BX_-kdK>oqqHi)hGqr<)!p zlv!b^6M4*Hl)l>wJnpvC@;RMqIOqt?$UZQyqtP&e-qBJ)9|#{oXc$voEmIqc0=5e? zi^xA{m<3k^AlkfwQ!huxR)zcf&TjHP|5O1_M)!&r?M{4YXk&4xjDZA)rpD&fukO5< z0q33%*M`^5uK#HuY+ro!;Q4(-;fNwSjG`d!geO8O@R34CV5Y^kj%?XtY(Ef%2R`q%gB8@J$I(Qo?Se-TlhBP68G^X-{mdrC%5Lf{l+DDw#d9*77Hv z`p^bVcEkpgZCgv>ipY@;vkPCA!SB1nLeQa6gt&8mt}a)zZ3Nzrh20zS&6J*cakgq_ zbkx)rdRt?YtmZMziC{s|kAgc?Ij6z0cwO<=q7hUiCe{xO0PeyAsb8P9PZ{&uP9cM& zHAJxEt)kv89i8Xmo@9k{qS2q9(3m^Cn2#mdecYNz?2qWdP3`V>5T+U?X@rDSBjcB! zvVFy>hmm_nCOhTLUwTO7@i{AlnUcLlITOfem#O>pjl9tAc?5{x5YXCDRp9)wApnjX zTSYc|0hJCvO8k2AN+nlECY_|mHp8Q+sg=Oy^!W&+c_bu3OSUgHS4?eCza?1||e<+&@3y;>*Pj9`~7*&S@HT8SvJ@ z52icg-3rNm{!ZpYqHoQFQuuPc@@{L-3)wiFP}K3$a#?+QE2=Ucv|FqTSh!Mx1b&G! z{F6Jmo?N5f2c{Fi<`JddRP%|MCAf zAJWYWjqZ-F!);Zr(i*u~u(Q}0YHfWzKOv(a{iFUZC$+tC`Y%78d;9p`u3h^f+Zst- zy3(6ym3LMPx<``E1CZd!SCsxejMO)^Z=6oj^uKrA^M~aT{~*bReAOS$wtCGW`w*{V z%vnkY_5XIc?1+L?lT-un`@D&roqjfUMkD5gkXt+m+M?4V!)rJO9Sid8tNqe6eCVc$ z(2HBNvMHJ$c)rqO>vf;moWKA3|8|`V)RQt>ri{BeHm)eS%742itNEV)6i3jB0x`<> z7dGp_1A3$D>xV=B`j1isN{j0+#(}3W0IU%Hx2w*gr6hdvB-(IPHLXj4ds!>d70M&; zSrxs!;F|l^kusM_hIfY;Omy?O{02eqQA00_(Zhao>OoB+5P+3iev)L!w?Wwsbj8ZO zcFuAW?~(^m2#sfEGj+^1u30EM&537{sB2m&&%EFx=Auur+uE)b_1s~hb?-C>T~WLP z#1vx%NJ{Gz8S;RVl z>TC7G^#6AKr7O^tm&#OoTD>+!_H>IP^kPgS1UjF=Igw|c+~%88ArQlsl*J`ox5tPg zk+stc_X9-ndZ25Ob5>}fI>b5kDxk0e@B2D~dq%qP4s+P=<2Z7ES>pAE#;lQkAKro) zR^=lAivVzvm&~i;#zjc82sE`eb!>qWb2u+6Azg2C_mo9xbj{Ye;gF0@oaSG{;c~0g zXKAohf|~NWlu7$PY`u9n+v)rF-I?yuquMH32CbqCp^K%pW!jFdX=-b2u}rHiNkpo) zSTohyi(=3+s9ItRMHV7t>{V5j5F{i-QA}b^$4V zQ$WK7i|gW^ZlDyLUB!}k#Ev5i&h{ym43wGW#s=K}Fg;^d2HG=3azs?ZQ6VpED}YiP z#YPeec|%teV)PUtDVVR4pDLP}ja0D-?iqMzJyPO;+|!q?p)H8z~fP{V+fqL%S*P)dv}~ zIF8Jzk=4HDyof%y^7fG^LoqLM!<01$RqIx4l@p>{NHMm2nFyPjNL@D3=+XON6L^aq zOk4T*o1|m@s};MfSw&h&lV;J;TmWCsUKwuat3|b^6>s%bs!oyyzM>y+ZN$N&Iq(xh98raDN@hn_`s}J|NXm z;8Es1js3M^j>Rw^Cvb zrOW#&W%Z<)+V1YI@D1_;EeOzgCh%oyJ-f_=St}*}3h!{2T1SV4G#V z&IV*||B{zWTHDzN_JZtia7jsbTufe7!= zT@L35{R;a+a}mYYD;AQJtR5&lV4vQ40chY}1GP23R(5P+o_(;P96`>FiyqmXKlIij zfXbcy@V!F$Tq_!*`@6}L?V+zd>-Qh=09M3L^vSXUEeY%m$CpCY?7V)964TyGY$D*F z`LaVwwJLl{Zvan6zy0*>+-aMbhr@2#GRkSj+E;3dz>uZ&wiR~q-;iT#wa0;RtadGY zUhxyKxpp`=2xDnVtreQmys~-U+?i@8mfK$&{kbymm&9#1Z1vrj%5X(y&FKtK>y%Nl zsdiv7*Ef1MD+q9OZ{ux=!!%fxi^p%lX>y1j;QMSW_ih0_0?<8Z8?kKU)l)ghOFB7GVvfmxzt1zsMo> zTz0oT0}k~CErCqeFZ5o@4aO(d)I>tf-eafjm}_Qg4h5Un(*?v$PL$iSvQ2h3&jHTC zvaG9T;+VEk>MqrH3uMMHn6lRNhk$S7E5gl*P_qz@iS5qQs*9+a@I-D{h;uLzxYsqW z#SG-*zFobgR`{~4GX3za*#+g;r-owD-ujQyx0DNucC93ozv-Q*Y7d0D!f^1eIG8Tm zmpE}t{C;Bun&bS<%^Iz%tAkPf#Nl}!&C0!8EwAy6sh@v)oE~ccH%T!DIdNy!z?a$s z4D(CpdpZxbbPaPBb##_%RummbEt~+nGAhgXnZx*4)zM}*Rg9RGwq05@Z>}~1nK~)j zEAGnTiM?JGOY+_^=|p`K9>t}d2+!iu@4i8WjEqo}VrL7>$K5;tKiiWWyFYrO?vdB!;0W>M zdY5N8q=MD_%C(z(e_2RiLP0E7$2?LZ*Y`})>$FcW`KMIA!=-;KQY0$ z2+fR_%QgDqT-?Q^4B0Av>|~73t*c>;S8c9DwqR`W_C$_w&P6t17_3X2u`2)ybg{WJ|VF zJQ|;OA65t-H(xfN=Uj7eey+N13oqyjA}I!t)!g)Q{fVot?=U4_eZYOsGLreGVzuo9CUa}Y9@&)jkIM|^RdgS zJSYn+SQ{nnbAp~-l7qE;J3%4=qMXGm(=BPY4MQ_Wx3du^GF;r&gX_qU=YUZPGUnJu z?MVoF2jFOL@Ohoz+%0gGZu5r<4@+3nzr_hw=YdlzemuS@b*61-#Bo>&!2FbC%k*2> z=4!)D=xs{>=#dGaOO8zpf6s+s@C27TJDQy5>!0~xNN?wh%#wuqbJjN>@6d?*wcfH< z{ZvhS!WOV-AM?~L=1=(8v6h(w9P{E}@OYItFR@mu@ zsGT=-fe39i-*5u`404@F_W$k0cFG*}ndu`a?r0(!qq!)L z^^5NJd9;4Z2dasd9kuGQheuxa+G$mbnCAmzj3-P36+^y?<8_9=fyv3Rsea=TdphmU z1t3L0Pd9%C7}Jlnlv7e=%r4!86O5;q=vG_z2MvI3!!HY}hnt!P*HD6cyjw%F9!}fD zZE(;S=^c2_6F?M!beumdNw6s6*nkKV1+<{;YiE}He8O~`6KS|iE%5RDd`nqYti1$<|>FRLP5GK-bvW<+4w;hE4}JV?(=Yu zf+F*rYM6OWiRa>Dj?G^^`Ltjn#v^0>hBuNpq5|ER)t+nO~G*Mb?q{TKKRqi#be+H62-|D7c@L=wbdz(Y_7`@%pXGMI zVoa{+f8nQcO~-u|N2t#>Jku0f*U2wz%Un4N#<%cg^J-oaMGIz4@C3w?IV~M)rZ86l z%^S<}L<5bfu)x|SoXv{xIsI&POdX)|8!J?7S3E!j_}~zD&xLOW^MmZOMNQj!4DaE) zjOwWoe*loCK>^t5U@)7-Mg8#_s$pk3B74B^yS6e+Tm41pcuLPTdYYc(EF{77I$w>m zvqugfb}2y;Ro_n{#4o;tDuyah4YG>$?GOy*to2emT+vG}G1d)PMu??xzk$YMpo+b=6>BP+q=WYfkxg zCG$?2-H7JUjhezs*tEXG;8I#zFm`Q2ZK_rgi`*EnZjnSlwlt{gTc-fzgWG+@oDIX4 zDg^ts;l}Q+*?4)zLb|(cKm~jNb(xkeUgikoECK_Aga&^-URA~9O~S}*+jw9sc!w`5 z;K|KH#&BrrAPxs~L=MMqYD4qvHk}jp=J^54_POS~O-uQlb*Bb79(22TDnceP7<6mK zKnA3DV;BIi%m)BZPg&X9T+(o99a_&$`iaIA)SN^R?xMWT0YT!{8V!m>RKYKw0En)N zry+BB8NCLw)H^UAefo)D#X#k;a@}@Se#ZlD)F44lT1DM%3V|_Ad&0`mu~eUFk%nGk6+;c<|Bgh#ch~Svj3O2 zwm%aQHOT?p@_@H*sHVTh77+zMGc8--Xx%=`!RKVRSklMs>o-Y_ z89gxakZ#Qh&J3bVuAcSP!_0Hq9DI6H>029HEjG1Cew;pb2x%@`=p^k+?$|inNF7=_ zPS`Sg5Qf5R|3!9I3Z^Ea{emyQV0(IcUWBt^v*wZ?FjuzhgH`Je!Bm1bVfMuSn{N3J zLF#87yp)6mR~T3|G(lgV_>@xZ&=vY1SmU{W87aTtpL2(j!MV{iLene3#&NmrJl7GD za!;mf^In%T@2y1ZHRI z1!}^X_^p%SA^GKq4+S6*kaBi(e~H8y6lk80hxh1zjmnnJeq^7Wrp9o&d={YjogAW|4t(O zjtxiOQ!(UOVj5JA*Yab|;+xYh+JsP&kG|;9Iz@?nbGE4_|F&E7oV`1s;4r=7Gc5~R zG(R!~e^5rjPav3-bW9*wyD|5)Iqb8MLj9D>R%V9|jA1=tdr?dhyo-s;&lr{>;uXN-rgbI+gnGo%<)K?B9?4 zCNO>71r_-`(N6=Ku7^#*ghj|bk!^zh_N7gkTNw-3H$x}tjUMy-{y0x6;O{<9#W*!u zKkxp8>xBaoIXUDPw0^WOdtkGMLK)$QLo11Q}Pdfzw_sd zt9$4>qE#f$oxD1HC*TaKQKu+H@sbSA(Dun)Cy$*@P_s*d-F8ulzPx3N@Clw;RQAx! zyWw?6b&4GVr%5ihf(9=~xfT4ym2dgfT~XOq zqIS@I0Hv9Spw^TR^7;Y;{B!e(3JQFeOc+W{?di=X#+nmVl*f^nc+eX~hOf$R<(!wC zbIo@%Q9Fb!V3^DiNzEhr6NY@48?P(;K~fJ6uiR&n2R4&_NPdSh4rS|uU8>DxVJ6O- zmKC72f<$pd*eZN4Bd9!s6*xsW_Y=^HItbY6P^xqTF2OsnxIgmSd?+i^EUU0rdSP(+ zW_XfUe?U1@v$YuT*&tA$HHmOyBFWb&+%|SP`DUR53UYYD-NS7RS=Xib>_og>D$2~9 z=a2z5?|Y{)NFq3l5jiOIJ+NF4yDO`-HFK)p7DmJxs42qAk)8^JOF`u3Semv^#TVn& zl8j~#)W*G@^7r)?n&TIHFrOmzg@y4zy`#}R$unmF^^7c7Vm!?@94kWNJ< zg~Cuw0RMioC^|N(#1#m>N09C2*|hL+r#hsw{X&kLj$6uXS@)jim=VVj^R6N)M2pJyn z1(~2k((#OsKvJs@Q94EFk2ak>Z=t8(h90Gec zu#}*AXuTyeISmMWz+g@T>9I#ZO1wp&A+NMIF4CtQD>ACi_<8YMf#YUDc7(*0zI~J^T_Eq{yPryuIt~9eSv*&W0s5h7ONe!*Q z$j!O_kFhZuGysVe<$Dh;w_H;@^kt)GbPF~mdw4;Kn-y5@(WVw#B(nVLKD{+fpsLx> z$n-DH0Aiw3EGNJ-WME4`BC#z7V1!+DtT0EI`Q=1nH}rkO3p&n6fo1@fJP+K56%nW z9i{NH;%a^4iHU4N!E#>K0-P}%a^;&3|3CiJ(Qu+}C{b?1hoJdg1vH}g0EAwlUu&-2y+6;fg55#EA0 zJet$*6oJqosThBqg%%?ReracX8W>mzS%0ypYM5R&q$&I$jm%|WzoHUNl@;SGi+@JW zUt9#gaw-j zab9<_!vz~c3n{jgFc-Ugr!2$dZ0IY&kfd6Tt!qZDm*T|tUGcO4zDohZI^0x+Ni@c$WeQqi1gZU2XI7Ui{*eF zebciDk{&oEdk~x1CB3iYG$9WmtyP(r@W?2c?Z2E>27((Y?Qomf@9ML-f8mq=DHY5K zQ)eP~ie#=SX3z5@`tK<>JY>wr@X>~>eJ`ND*cS>OrXH^Wy&KsvR(vC7G_ha>(lli6 z)i1lHAS+H5)^@4MV`Zi&PXK|chxTvGWrMLbU#GLDGomb=Vvc!-{^5SzpC#@KRqinw zTa$^XLS?zC!x4F+xPQ(s|9&=q?2zs+#)aVlmF>}NYIk9YmEP!NBXq)T8d|V%7O+Sv zkfD3ZsCA! zQ|4W?syp0Q35C69Q%lT4pZfa+!t-F`N0}6?JAz@HL63&HF#3@7db~$ob`s&7mD){BUoT92FOs{zJ(As zV4WsikzYB6RioImw5UKn0=>$U8}|f-OcgMeqa1=+_l!yPF5~$Sho;unx+wc4%5VvI7kDDJWMjKKP3uLZMbWh0u{N#Py{V+r%+>sh>z#@ZxeMHpYm8crjG3hpDQg5$ zSF=;MC@d)Z(l|<{g6-Qt7>QpdDBo(L#yfxd4j?}wE%IpsH3x?2RlT^a>)RzjKX`?R z4z{%%F>jCQ$ z67J~j|F-~N-5X0sLQ=W1G}5qsp1Ljn9~>z0n(t!vvU;`P(TQ z9BY9;HBQgzLP*v5O-P%U@D3(L!2#+$ZFb2j-@rqKXy)wR5FNBq{DZ89z>M_TF3C4=C=bZl%nX9g_5(Fc0j?jL7>D+&nzm4<|!N9t!$6D%KJ<| zUBQ|qOQ7Fx)b&_1t3Q#^pVCiszNVj#)=K>-m}x!2Ez4U6)T!o|BV{C2lWIq>#n)K? zV#uYXwS|v4I#0dR`e*h=P2y2G7xq=YLUX|^>mxK@-|bHi%dhSH2PAX9kWW{`5PQoJ zD?T;b{shQ3n&2PxykyuZ^lVgyWc*w zqb7`lllC3CvM0I}JX>0gY}Fr*^xjSko$SBrUxW4@Ue;GJksi)NTLu}&RpF6_6m8iY zltL9U^US7dc{-RsAfx%5>KTqro$I(Dsr+WoTua+Ozv0m)bXW`x2k7tGrSoFLb*J2= z){{d9%9|!Bdbd)eZi#A08oOs+aEK6po0F~j9JKatsry#9!vSW=r=X|^xUpA~o?@5u z#3$09bxen0T!W6384;+JSLR6|zUi<}a%Bkh`=bIYlm`pe?U71L;Y1~Fp{!V1SUk_w z90k&G@8$m3;RH2-e$%(J%u&)y05}1I{|wo_F4|#>6`bnEW+PZ9nq$5${`%5Q@(18c zYybOGe_Z*-!PwV}N=!;Dfjhgg7EFq*3A{IQ$jhe8%@t|abAbGaqwe-$09clftYPF` zI9P*!F!jZ5jd|_jz?DH7Oyq3^bt%c6dQEJCDPJFg_0P~$-GF5Wgef0?cOa9^Ih%Un zUoIZK7&sQIR?J1}NOIn67h=XtuRaI{JV&VSTpu>!hM%;T8{OORZh&J-+I;w9hjw68 z+WyTbuT%&zeoLOyM&E0G=5$Q7D%NtkTWlOmE51!_dO5GKZW}D0|1m$fbV5KiGQ{l4 z%n%^!#CNmF$O5tkJ>W(3H9NQ2J`xC*cy$pU7_&(Z;}6(057 zFa!LX$%wR2Ojy*<)rz_mCvrc0RF6o0=+V#}XExZNlek?r=#`-+hr7=^w6%{_=q5R? zTgL}!cPaHGtO-JxZVk<%>-~mBWfM9Z4_3<};tQ|S1JSxg{y}4ymEyX~Ls~;jD^lh) zcohUVH`_1>26S0TO5_Fne6-wK;g&2$F}`d>?@cbRW5oE`;qGMF_+fQ#-GwM7 zKFyJqv4=^h)kncD@)tV<7=&v6p1cDv0&;(u2O>mlREOB`EPb=}r1r-S??q2}W~3Ex zfNZD7U$;HAsb)0@X-L;Uv&{E3y|P_|uG((CMjSHmfWm-~Y(T#eDsJ4Q1pv)G;A-$IX5usz(Pl~$xItB&8iC|Ygy*rk!L_Qp0RdAeZJ88r=Umpb zGjVwGNiaywg(>Kt7qaJb@7H|^MT-eHWSVysObeVmk0*VDPD@R(bo(JwZ9qQP|620Y zEpAcJdNRt#i>dSZ$r0|^yjxUOnNt0{QEqfaFv=HwGDbVa$hnbJA7PT07iB05Vn^{d zLD7AMhqmPSsI(MwvrWsVyRujCk{j>8a^I`T+fEsg8<)XOYs#Txm+X*6dlv~5x)+tB`f=)SC(0uKmq98ZhYs@(17X@ zN5VF#w7nPgD3{dV(o)*jX7J*((kfBL1*K=}TXO^z<>#C0TO&)3*bMOcG~bH!S(IqS z!5T>2mbWQI+^X9;xMc#4#V5=~48gUvNrvGi=dUPnX(w%n6W92LX@aB&F}fX=LaPLt zSNS5vQ$@7?-1PW%Ub$9gRKSBbU&Miu)J@w4a*W5lRisqG%aHH!X5wL8n4boP{Os?( zZ(VBtlA-?H36jg(gbjSJgqbu2Med3E`WV}~v9kFk!OcuP&XMa;hQmz>-!FA$3;lWi z%iqQmwt+(m$Y=8buES{SvTdAH^%IU1=_r!51te3`x0zCA5gXANrf7SjF@=Buj!W`> z122<)u?cE*P1P=(0sWuT&Y3sOvhl5_$wJ#Sn!&V5A2eB_54zWJJDbJWKi1O4AINXd+FL}P6!o=>xqDfe4pats%eidPt-H9=W!{MT+b67T z2e}xQZh#^nw=w9|JZ+P5%-TpRjY?{2rsg;%W^;vz-d*{763Ze@C5e;DUJ22%5ovPs zhk*Fa#)l$M|1!P$3tKC5PkND54kWFz@$U;nVczfL>4&1vwkJujq=T%ibFo1vxSZ0}>BiFdIEVc|t z_SQv3_+f>-LnC^cO{p-|h4Qm8%A4PepNuE@Xx{ptcBEmh> zf(fo&=f>d^W8@tCf|#o~)HRbDU+xN=s@y{Iw2BWfzPHc9p*q{}j@c? zNiIaDZwjNxc0uO0rtxKx%Yz&%uvK=VSB=aXz>zU4vMiSWlM)^~Tw2!Wz4Lp>yAc*H zRASODRTBEdIzmPH1LAx0)F@V>Zc^*HL6S0Gznri{O3|8~&-%8j=Cuc0YLs65TFFL%_pix@3{TV@hXvG^1gG*e#wGMEuN?cxkNNc?y9|iCjX=F{6CAX{Ic3Eh_9Mj^uR_9o`Bs?kdEQ} zWA;)ny=AUUqFL;$`s?~<#eG4D*N*42kArLc(Pc^S2q0#8WMwQ+3NjPP0)vjO0m*e! z`wDk#@YdY`%tNMm@dNOmif%xXZxFi7ZG03Ku=}&3+@AU*J+j-eQ@802VX8lNc-jt0 zP7Xa3YCQ=k9g{rm$@O^-dCQpn)@2kDE>7D{=Fba0lH|K;k^?l1a7~nu)H{@>x`s@9 z3vxF2xW5DQ8cxoN56v@El_esbr3pWp;{-MT8jos*!z;=bo}Lmo6YZXRuryqH&DU0PHG z60Tx5T{UKO)froKS)@G><^V<9J1&}-$ZJmd8SOTKQS4EWYEW2;ozdE8(63zWJnKT6 zj8*DrPnb$l$FA^WEfEL<=ODDX33j-2NnbYBTt)I^yEK@e5AQz28%4^+H;^T<)o?Ea zFm2@o7kdL?TA?hK;kp8>!hm+Tc;a5W$u(V#Ye|Lwk}v2O`eZufP;lA~tn;*0ftvp?$|19CyH3LV$?3K=ujD=`D(XI_v zaX^Vx>&Fo&v5rc~BB{x`stjtj=Ao${zJE#%%N#YRjW5_Q;>F%?jOnoz?Tw{D#qUle zw~JX$JdAMe`J0sB2f5p`6X2HNA)qtj?gB%YN^hF{_VuJ9M0V@8M5S1gs;A2PvRLju zvEU^vK!z&$#Otljc!bD+9jz5VaV%aN=2UpDJR5gGx&^WL({?Q~2wbHlE7$zBX?w@y z%CoeNfdi7?C82-MS=-7;uIMM%RnF(gAysuzqXxc_n!y3v4JtKNzic4cJo8s7rR2O? zo~_{RD>Xub*ftW`Gp-sWAY;9sjbw24NTq zoyz}}hcwR08jhV=8Ji*w3*35cQ(G8!vs>ws2%*Bat^&PbbxBhZQf?HER6{e@Oq83S zluUT~iVtCfylzs52KdTehi>|1EVHG<`DlVbz#h1rwTzh^ie6y^6j19n`kGxyV zphYY}XGx&$0u~S^?b0odTFakG)wipx{JHXv9Xp(2<-Y)|J^A3w_T{c z5xt!*HTLy1*v_RX<+!C57O{OjPY6@Y28__sPJ#-`k=E943zB{sNRufMF2cf4xT`)G)kjt~2eBmn$+SeEZLN4Ws0U*REBF z5!+xNzi*tY=rS2IW1HX42H$6Ra@^0%ea@J3sG~GENo>mjG@2)WZzKH7eTK&tig5ji zSMMpLY9u*YYj=$|!+~YJQcK`+VeXV1liY{-Dg#NX8{&4Jb>on!eINk$$@v3Q9n?8| zFpD40dKfeRdHD9i5&b=r3r_v_bbrracB*S>M;#gXgf*Hf!&l4;@K_@+QBV8rPw3Tr zt&MJug)pTONpae+cnTIh7+fLDW@v}T+olUtz^5{LU&mbd&YMGO9gpULpo5_jm{BmHHm{s(<5%`|(DWsG>24#52`*}e;?51~{7#H0`i0K~ zdbJz$Nwu!L!uY~{(u-dXFFGrQW8GpZN?yMelB+fn#6Nz%vfM( zpyvK^gZbS}mC38Z{rUg)djIna-Rj4V$(+LDv^zqMxKPN8AwpZs2X2U#iw+h7{&EDb z52zv!aXiD$pFYANb+|*7;XC)TH5;u3_Zlflv(q1>6yG;V(_2Q91nqV)8(mfi{+lvL zSY8TR^c_R%5d3r>rdksmy8FRPA42Rq||^9rr?OX%pac`WDOT4C*FzlAGfr}g&lXho%y%9){h+>=OcFtVWo3j154Yd+hxTm zr)`NWDYIhUaI}87MwRjLBZhbtHR}S4Lj7WAp_b?G&71}uBU6CgZ3=Sqi{}+#P0F;F zj7Jp3LbRt4Rf!lehj-15aXTBXp3XT}lGtkG#9pYr6!X)&nwTW*?`?qSuPu6*z1%$A zuz616x(3p-5Dtvhn%YHnS{qdLHG*sX-i-e7jwKGD_QpS|cQ?Q3N{MsYy81ugtTVNM z`Bb7Mn_?PnuH(674#4fNf%fohha%xRk8Jo=?+6(EDyOfd9;Z4+vc>D%;GQujzRYLs z;G@@AXBEQkrFwlaaUEL55Nf@YZq`3BwL*ii%?|aVU95B)pD4VSRvH3z zI!QAQb&ODI`1X1PLe4rz-I-W7(8`qZYWji$BKRPH`L_mwz)`*az$KAwXnoqbkF8_4 zk@dmNv#x?P^=H74q${pXaAb$LaV*ThEAnDGuN`HTnHxs zo#34 z_;pJ7cmo^=i0;ypfakWJtpd+oAqXpiW^jJCWvE_<9@TW<* zNit5&e~nX>wi~P1w&9c{^%|>Qv!D;HsJEZV{6LLc?!IDVeLM$Ei?|(YlAz1`6LLs; z7Q4c)X^!GUep$ewn}>pB^4<8C!IO0j#wiO|RfgGPY%5T8s3H`-zcEYQIsb)eX^MWV+8{G6u(!rD<(C^IhjtBbpmUFsuvz}ji#j_b!&_`A6e^s2g)feiw$M+NTxXp`W zpDoR&+;M4JIN;J1fQDO7Y+W5DtU_dcLuO|;Ha@>dKXLN*Zo2`E43l$KO4>F$&&!iO zzQ;?vC9y!r8{avN1{EoLBymB|F1IczB~1QJJ-jEN{NwtO>KiMks*zt073IIwy$DE* zqy9(zWN#4>zqBZ_wUb;G1zr=~R2)%o^BXxlih=?I(=RsWIRWbX8K{0Bfh_g1xVod) zCLu$^d)HJXUW$)aL@GIv&{Hr6AgSKh`~=p0LC*bpx!!1PwSj99xhW6IT$Rb!T3~z5>|_nz3+p1r30eG*O$$Jum8>dQcl$Kl#z@r)A6{q;j&#+@x< zt!3i%_CBj552#fVa2BvtSbvN(jP@+7WOEEMe+mfP{nuH&t5dZJI9aK}JX5<1$R&jU z|3*$S+J6iDdNEpOaIS%+rx4>hH(_~m5Y`zT{endsBG z#&XWktZR|Fl>gsv8vvv~B*_aCg^GU{rlqV94EAYIH}9hjivj|I#TDL7FK&sy@{sKQ zt!$C)EZlNEM_c>5ChOQ;-xpCh*9|hE)K}@wY;z44KF{{g$ABkhX2z>_w~%NcPuB-o z?@I!@lU!dV+_AGc0g~RNqj8cm1D!Z_m7sOUrxQS}2cP;g{7C@^mZn|4kbb>kNl^K| z-V{^++oe+4uNKPnE0<^&ZbB{t+pP8%*|A!2=07lxNGph(`SBEU)OX@$itaM=HpNH* z@OSUlxbW^2w#=i!;%Ma{RN4Re&o>k;rG4h0NIOQWLF6f6d{@*j@E#05UzO<)5rHag zftn;1@8N&?nDpc`sW9?LWO=*w=*6m^i@#b)WsF2UU7p8;TCqYsDC9na<}-s-dRF** zwil%hf9pO9N4`;k*_-|-yZ(E~ET8YeYc&ke@V1ja0(NrX+y2*wUd0=e|I^kW~p6juS`Frmwi{U4?W*^erLvRoR${_ zFPN{Cj^@^+6#2Rt^u(FUO(iGyE-lT`5H$wci7!i>bJ9;LhG;kT@2Q%qsT4J5hCy<~LnyrsX!_y8BF9a zjTpzh#1NY$dbGl~)+6urTKfWa|5@MNdwOL(K4@K}TUGK}$uu`>xGPKWSyh9%QXLGw z!GogB7d)aP_L3QdHLyK_|Eb7z_cNK-T>U{pXSZIRc~l8t!}FO0quMD2CqAwA$sTi^ zH%y+=hIW2hjx^id`X=ah(t8Haoq-=*uC66KTOaK8IcVB+zp0_AKB-#`C%LO@Dp!IS zL_o%za{UYOZtQ(T`jW|&Nd>26)qxus2!G-tc8tBdq51ImuY>x2rPpju{}}xD*~Iun zP3C*u90AAK#x=-kT(F;=D(wUYjy~h0D~F zoQbW@2HdE+wFfIgr$R^kmi2VjP#D=S6t*UFT=|tANLnn<%73sc!!xhjka5*uF)HJ- z6RMe_=12Wk@cv)RM9a=8=T49n@B?=+(t_a}%!^;!hwl>R{=kiDuNm1pNx=5VE-@FhIR#w_Y3tvu_ zXSxBr*z*NvYgY5dwM)pM6^c3f4m~KjJSziT3i$M)oKN~p1;#DJob z1-HWXlyd`CLa(-gqsWkre41PS--A8Rf<*Y{@)*#?Af=jo<=!&aHp~O%&deUM@5!ry zmrfY~DP!AP)temcysEtYlk~<0s7YL|?_HN0o+sm6o?SYAFP&u&rtte+-;!_Yzq)td zPx-MU9k}tcz@QkY0`+ZWJS9=9YM|}r4wkV`{PODji9zWAE+AU0#pr-CqI&^%knh=G z>qm0+&{G*m0V;`71}qZtZ9GwU9K#%v^s&} z{7u{DNLa|1F%aWq_O%y97p;?UCA^=1>}Wel_r~mxYTm@bM0-=BM`pqx9BSu_?aV%2 zbFZ`)W@7F?MeqN1p)Lgdoe~}&2bs);)M4Ui%Au1z5+IdZs~F=A65}g9CTuHjiT6J4 zmuI|Bd!R9p{g=yj?v<;f@4P`z-wx4#hm3OWFi9S|i&KzzhKe_at;9YH_JQSO@?;xq zccu3uHW%X^#i7Z;jj5xv++%NSAw!VKm-ISvkyzVSa;>$$)HNzoswrN-Xf+p=xoIaA zZ|1|c=7x>>r7w-PQ+qEb$%23E$OWt;;_x3k;_gYNfpDKx^tA;BFIW)Uzedk)K#Vxv zWWhjx@1x|Qe?tUbybb~{jQ^U@w=diF<$t|N|9knbT>!wLk`~X!_;xhx~__jqmREgxLPSx=Twp}a=i@RJOOP=Z09gChfvR`> zEE9MtbEY>SfQ@8q-hU^O$46H;YQ(a zHZ)OD8jYJ87pzn|FS?IcC+g%l+oT_5o=7z@v1I}-K6<{I1l+a$)HbbEb-t=%QPEQe zri$RfQ1$v~6DVTlc4O)=ApzBYE%<)MZR+lO^%fa&R3opB|2Pc)M=6!0z4ni`?h-$* z--@qLDjc0aDmZ2Hd`9l)*`!XbJcCvF7PUi_CWua7C)`}!c<>GkMt(T9|HJzF-Jnf< zwPyBbws~^78zGyqA97!e2wmd-KVU=G-9LMJ0f;`vlnnF$COGJxNzF9Oe1b@CgrDtC2Na!#LbGWJCPQu&S)4 zqVlTltNqP!+_QMNkzxvHDdDx|Uzx9t-}|;t3XG4DAe=o*dRYQkmzcQvw!)p!L5qbUBmMU)NO4l+47bAb{_&-CA|FzCL^8l=e(NZXI zN@1h;X7~iY2Sj>k0E9ebQw!B9{hg9dwlDhaZ4(d~C-{!A*IkP43V93jXP)xc-VT{J zen3txb^Rj71ZXHIh~PjP$t6$si`R;d_60opYODWDHrK}Eb4Z`br6ae135!=}j=)Dm zo)gDNzVUI17q|av3l0#Yj3_QHW(2asDe*IA(5vO*oylQ(U?&0kncasEgAZFD{m=XU zpSPQS9cZ)VnqOehK%$LJw;0y6gXpf1p5WY(eIAR5{B0!wT{^+*)ofHM%QrQ8Npw+9 zJ$%Ze$CTA2ptW86u>;C}SX*xRdc*QnvAb_JS50Km&++OiTG)spd|O~Qb7obH2j6@3 z7`yWbGlHS{UyK4Qs!4fcwrGO@sX4=dcL>*hr9y(ATJHkk+%h}Fr|omvJoRir2-bQ^ z3!akEu6Jy#Tla|Ny$>lN&BqUUd^jZkWB5M>Mpdn(s-1q&)Qa!58$oApuyXMIq3HW> zp`kjh))S}G5i%M%IWor{#tc4qDsXuA$2dIkfCxx+jMixdZo!@Eh43J{FR^(@7a!xb zv>A2kLz0badTZOm0hCXZHN@vh?wRP%_bZ>e|A*P~-|z50{FXY|r3W?AQPp!IaB0iB z*fV(wWG=($rIg@Mw5A+||EI7o4QDfN-=3%IphtBhErX%xLYN}V5ENBRs&uoZm{u(j zks7;*Jkt)f6s2vnQPdhr5=#~m%h;=SAxMawT0$t1TJnGI={)cIe0h)K|0SPt9JzBR z_jO&r^Ewx1IN@5Q85JL zP+ndE-`l=8)djoan{KD{b~bIa0Ds*#%-6?jO`^mOzj9c!DB&C{HX2hbV0e<7Y zX9V1iqg{I%Abx5uZrcN@PrHI;qcCH7{s#|t3%QrBOP7q|Lw2=HO5Lq&kj@qK`r1?X z@9Sit54Uz14aayln6HDTGXUGdho&Hj{6aGzP>8|tDCQOQ>bfFwMbLu3QO;tYJ-`aL z$9J181fqW2=uJC&@*z0K_bGs$?NMa+3%uwVlNpH*nD9_L@RUVlFvbv1@S?R?K&}*9 z5SLPk9Yw!@D)^Z;DFe3hkqQ@QBJ(9bdhsfI?6eB}YJ9yNZFc~G7AJI|aM`W$T-1K*AAzUZofs@U|Fgs4Pc}im z24TtGA#@59@0=6x8kMa`jU~;VHtkJ2Od6x_VJB96ckQ^?*9}v$yEnp_aZicxZts%C z;c{F4MXo{8Ydn9+wkHRBKJup}#rl?EX<;P)cVpc*$ce*dIuQ@&kxG~*h1RUOtm>M2 zD#lKl^Da)2^%^9+s5kPFg9jNQl|Hqece@s7nfZIAiun*V%lYaM+5jBZGjns0x}1R0 z)dpxvI$-jT57S$5)X!=Wlq`RePwBLb*( zu^l8XA%Ytk`(`jOoqKjQue$mk5OiGacdF0L+8BCc9U8_!g1dIYH?aVxrb~rpBTyyA zbn~Sc>xX-Civ2-RIlLJc#mUslZd`&iyptgGo@Z>{kEsu~+Ns ze=q;P#&zJoJs>9V4#jfy=59-u{am0p^|M@PQXlHCKdd&0{89l4-&yS_U-)c|R z!pti@5u9$*#&nzIEot9r_kuX{Cp)^ad+xc4$ydiFkTLp$V6xiK;jK>-ZXenUkOw>I z?+h%=`ksuxCgIPf%ej5SnO>PAO?b%ECz1=pV6)yL&xlhqyKcM|T1Fry-Eu=rcynwCd@1L-lrt7ZZrH8QX_9-9+((uHlQWcU02Ky%IPdXz>)E>6-C zSEnUQbupbHq*xj`1&ETQ-6IrjI0nggTs%+kxyTndltx=6SgjIe=^!}x*ki**O*v%t zYnAN)>^!vc)7nQjNvnCKX<>GP7pO_)C^bf9RCGPVj)8ZLW<`Q+cp7O6u$A`n`dCuY z_(j)DeKp&9&lyj#X8Q_mM(HwGhxeCA^1~Is<=p+!Yg&h!-C8KKAG`H#O3wzrSukS7 z4C`MjAG3#)z2tR&dG;JPnFb83OMYt?VyRfmg%&sBsMqN6LP*h5Vkeq-U!${)0RlOT zINMc%i1FE>LTasH&x7j9lqEj8OP0GJkz%boOghB97spPG>+X75SFkr-Ao0|TtF*Hu z;8q=1!aa-sOi|c>31ugf7uqs=PmF52#n#cYh=7v=UBmzxh7m|}EkQ1gAwSY`aNKq6 zpsVVM^FD`M3kB1SMf~8M?MB=wam<&yDN8uL=4%pe((R!m8D-7B-jR9tv3QtCDizV! zE#1Plf0Rg3C<7M*C<2`IvBHfpbHddv`2DG{-2D@s8#kgn&jss?E{9))za>UmS=C+Z7IfOn-uFn z&Yx*C%Mr#AgG+6S`Fza^1T;gwSLbaR+)8SI4_9IuM#&gfS$dl`<(jD6~kvnpyE>)pr1_P1cn3QQvCk z_ir6dH_<|tb{rn$tfJ1>NgRuT4o;~8rq%2n&C*iIh>A2hWH&R}Y~ojrYF&MO+&;DC z$;9JWFT&z))dx&Q=Mz6({Zxw4_~iOE4bt?nVH73TE?#gNiZ-2Erkq;bCYWZ`C!-^A z(V-n~1D{|RjNPacY)jif%n3U4(%k^0^@G%p8!$a)7*KKLa!L_+F6VV*KHs$Eu_vz+ zrNzAuc$<{yz4DT4Wwk@~zKd~H3rSNv_Pp$0DxW|C-tO^;&PlVnAypks-dOp-dku~h%k3%|=P9#FiW#R=OWtBom7<vxwm;GVF*$KxqHl8*lCzDUxBYL%;1_kB*@mhg9aHNKL!ou|BIJRN;LY@sSbF=g?Bj+oCvGzN|dSzx+IeT;5XBsfx>%37{KX~tyR0sqYr?A$H zy2+k4m820ri`pHOe}rdQ-GqT}D(S=4!Bp;__@K{*J2!IxTh2~L>e~yInI4Ht++iT? zOhANxoRQAKOiujJ5&S@yYBdyni{$UjH1Q+gGm88b7DU!5bq|g)m4*b*ZwyR~dALPj zsc}OF5u2U7Vk%EyIXcOfuC(&^*fZgjL9HI3-tXt%9r{gZB16l#%?_<(9s~XUN0Fxo zq5?5YC)_7^i(Vtl<0-lZrFRNiW3(^SvNb9a8k`%dpto`*?UG-_SJv|#7}bL`6U@FP zrrW`C-4fAk!Ir=B!QYtvK7dwdesf(tXLNgb8fL_#W!{NEPlAe;dusMhvKze2xyUx4K6P@9akLQ$o4p+v_?5nXKHElh*LV8thO6s(Zc*uD95kiTr zHxc$^X%k&4muo{2W)D$(-ybQ+>=|<#yjT5KlWv%8G6XP*B6;kE~KW~ z4fza^AtM2+D)v>?3~F_)%Nt&EP)HT@q}@eDdAoGJPqN*y<FMF-;S6~ZqU7wX{B3vlOb#Q3v)GT?x;W3MKo&6B1uW`MFe&|KFEk?l z8B;w6*H(^_T*b|eW{Sd_so1IY!U$i5`iv4%9ZOv52sZ&A=WyrUxvB9e)oQpMWwY+~ zVHTVxb8hpq*E5GiOo&RMM4CZo3ux}j--9PHre2Qe4}1&l3%IN$eZxHEE0;o;Y3YKP zp>P>$@c7r2YrjXnX#a-4FSJW!Jg2di?0Z7=Qyr9!a{FBdeMp;(-*rymU!rmbRauF! z3&0Xed*fFIr~3y$R7ZrD1-_I30o@IU{QfSrL?FZgc*B+GOX67E^loWGCD(??bK}bj z>h3g4yt6nYqV?})!+A^cQ7=Yipn#3!;7BB<`_B)_+A4#htlf~99wTQ$9l3N`cg1BB zWyx*AByCxpge^ZkOwLV;)Jz8}&;O#msyZ}p3;5+U&}4!a4AQJWD{Jo;nMsRryS^}X z^n;BI_w>vMI7dYDhICDco; z&&}>kJVQY(3i(FQ)`lI2fZ1$dWpn&WAjBRFaX8s|oK)l=#@W#?ob4a64@ZV12S4tI zvIrx2RL4$$U`0ez-Xk8Cknl-lUq%-xzf8*@?|+ z%k)(!Dn+UP-qx?^ihX)#h)R+l)rBN=Vq_w9z@$^~w@zn}Nge5Y{*OIZ^&FrHXOP?3 zHg_Fe|7{HPln9(aZffF~_}P>)Gg|{N!SX_&)tMn-Va`U|wDIJMM0`!Cp=Iw!PC@1&BKr9NJ2Ao;FY$o+)6)RqXm9%du6QP_TP09R^dkQ@Cz) zdtx!Z;+3|-B_jUF%TMoI$K83Zz@zBNq!Uqji_V1B>HBtvV`~|Dg@x4NjAJiK!cM|@ z1uwf*56-2iM=F&Um$e-H+KAb8kMNz&Nu{D=*AUaG$2=mcfS7{oC>>;`2@58B%7JK+ zFUGgu!Nage;1IOopvU#?V4OeTWF(D6o9BcJpY5GV67hxuqqRQ7bepLOou2^|pG%pJ z>B!}`llE@IR#mpimC~kb(}{~TnK+~43^rmX*czb+c^7`N6J25_w)$u20k(gi0Qe-- zGrr-Bfs#s#6gt;0TbohcuN{gCw?*=#$WRM8(QAKBObEv*HS$b_`ccu(pBj?`bv~LvreP4^z-g3` zU7w_XnsnSjb9&6xZA?3i0ouXmim=B|hi~Bo8p716>o4;bohlF@g-1K0icp2|MMjjd zzj2|YqQpLmYfl8SE0XK}+|JlGPX1EnEWiCCOV8e|>E~(qM*3bOc~4G=0!TdFvansb z_U8sF$Hv^oJx7-kqi+ym@yVK1US<5HFz&s$x--n#;V5jEXaWs)(S0-&dJzRi0TN|8 zKgUy?ChVMwDOq2b5|sw$j#*EN`ZwW8(wZh;p+@0e;pT615&Df@B|3OKJ<8IPvZ_sf zG-NP-d%k;RrL3ryiG`K#Q}(Xp`t5{egnF0xD` zMihL~Os;*CU>nd6aS0>|0{AGM9wke4b`hZo?mE9Wx&J;yMOIeIFs;BUU)^|_ zzmRBn!po&@&GJgfP~%0Ry+!q}Hvi;W%n%apso;J#CQm z=6f$2MW2(=MM%B0+>y^QDhv>VFNehH0cR(8-_{T9NMa{Klx5>olfreb*V(w=9;CJ0 z0My?KbbPe_YlIEEznprjyT3` zpO*H7F_>1tMOfDDKfK=994|rpMpZ1o!S|BcYVz0L9goBG-_!|F#zP$eX}+8}z~%+UW#7v&9P`Gc?9B6acpyeVZVRkLWaritiD*q>%*$0Iqcz zIvdm)g{1%~eDZ|X+(=HZ6DGXR)RaHVXO=IBY5dsx}_s=0l6FWndef&hbVw_8MBGeppG8Xh@} z=l(fN2%Wt_Z|WIWjeXgt-7r~(J2N3UX$?3GZ0`H09P?KQU2)o&7}w1ZL9Nfbc~rb~ z`a2edqia0Bv(~lil*dTGpu?qUIgD{&)foWwE?pw$2>;-TF}CnOS@EMD(w6(_LuEwx zCFSXh?>E+-gYZ>j+LpGebt!&ch=5NXey+Gi|1BO$=6+f&Xu-b<7toQ%^RUf03GaNo zgFz&OMxsT!#{A)!BK2#PDXwZMZRfAN8XJGvnj>_PR%x%7<3xi=5g5mTDNe|4sj3KU zbaU{>q+Lr*z(op0yA-H8ND}1MrEyNsasJh8RlVAh~XB?(RSp*o3)+Uu);A{f6sS@ z=08N=%Y%UuHbug{O1olz72g5ha{R^@dzymVVi;Xe>|U0UVGMN&#%534s2FL1)+Ufh+&g^vnOi6@n61Ll5A zyl8pt-9IY_PtyN)`^*Va%*}0 zW^3X_A#B>C#%l^|E$PPLDg0Zm^F3&+>r2Xz;4#TgERe>z=yoccgB6V#)uDo!p#a@%ro>xRp_ve)eS3N*A7 zVkhNgk(-M+<5pFD$|^uDH{8HTo8J|dSC_LrddAl#er2Ob8dS_h?xj84O1)vVIClT# zdofRP3Uz(0P1)&fX-A1iZy7@QJP^R}6o?A>wWc*$|QJKy(yk}Gw>BMktVMVb!q9p{1UG(gZBD7jd}hU^)mEOCWCOc6^A5iyA!nav#r?$0wI) zG%pc~QVdul1~?-}s&O4j&@Ff^DfzxS^G4m?zrVp2S|W_(`+ZVmz3#fl6ZJuJI<=d? zh&=`GE%ytLdd^XZt>dRg2G5Pz=5TM3>Xxkzor30HhvS{)zkjx4)@MF-*;8<__RfCx zE<_5zoQdqc!7tPWt`M7dE}=8N0vJ4u0FO(Qdw4DA?L7W=X{?^@T5?y* z!arpd(2}-yaz!-i(Y&y9nWj&>$#D|ZL@qrw$cl~g{h@t2+;R;(@N1ajbZmFJlafLirO|Ke2 z$zRN`s*E77^Wp0{+_ihyt74<3bqmubY+jqzFX?=D<0F-R z2EwM9-X}lD#yhbXc<{|B-|AiaS%E$TgZm4|J3npSNRf-cxen?;q{FR+4I>#hg!@H# zAtq<@82HoBbCvP7^zF2KSok5-56;8O=*VJ?#ecWzu0*Q{U4i}PPWV%_PAE?LzQ07f ziUU#ftU%an)MteLcC!F*fOFV$WET*_Z_Q=V zcY*a~j?k6z6e0Yh)^2)S#xbLvBe6~whu=)dPZWJdly^fGvu_JW7X>BfNNLf#rL+XE zH%+|J(;HS(IKl9=N-W>ZOs7idkxGJhl({WsoL({lJr+M%6kPBUm1f;>NdIJ#d>g4R zvRl)r@|&|^hBLq={tbaU4KwC@a;#h8%*iZJQ$KLvWIYFb;m0D9ZVmwk^C4L`mwG;S z8r-&sf}GfiX4a-Ln@SIlIf;HO{7l$3RLNF`?&s4yLH<>}MPjrKJET9zI~?T;Ma3w5 zx#5@Iwy|+ZdStunvO#oVIh8)3 zO0&n8=M}y8e%e`$xV{A-&7AnW@ma61 zwF>z_TSkrn3+K!Z7Sht&`X5wM3FFfQ8DvCL!yE5(2M2NPu5+{vAm*rS2Uhv<69NJ_ zE|=)P7>>D=amz?VXWSZNDF&ecW08Q`hFTUO60G8wHD?iy33*^c`41Uu9}SQIe(o-W z#|&)ugl1w*oNEA6(zrI11|xL*W-YXM?b>PAlP~IJcmeF1GI2|w z&A&r!P|7o&18R)wnqdRh*#~tEgCQ&zjp|GXT6s0C=844qS{q$AVE#bgm~DSgdclK+ zB=c0Rq+;3=pcoHETNf?vIF%lM&&6&kJW@!ml?^3c9_@U*SX$-pGd(w@Kh_Z)Gpx~) zZuj%4sy}8nzfJA`%e77uhCa z-`1$MKZvk}7M)`WsNCh>N9gmfV9<%eiDJd*DU`+P zk<4j_GYGdMB}%j|PHJ%BveBgnxBzcP#GK12>H_}U0VY0qH24W}H+mp|93r^f>rmTJ qJ#+z>KhB}a9Jy*{#Gsb_WnSyC19sn!HC@bqe#PXZv$XmDjQ$@%1XZ*E literal 0 HcmV?d00001 diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index c51f46a04..745412324 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -3,6 +3,8 @@ from helper import unittest, PillowTestCase, hopper, py3 from PIL import Image, TiffImagePlugin +import struct + class TestFileTiff(PillowTestCase): @@ -77,6 +79,12 @@ class TestFileTiff(PillowTestCase): im._setup() self.assertEqual(im.info['dpi'], (72., 72.)) + def test_bad_exif(self): + try: + Image.open('Tests/images/hopper_bad_exif.jpg')._getexif() + except struct.error: + self.fail("Bad EXIF data should not pass incorrect values to _binary unpack") + def test_little_endian(self): im = Image.open('Tests/images/16bit.cropped.tif') self.assertEqual(im.getpixel((0, 0)), 480) From b680d863790156a4a57a33c4d178f3d2de6c8a45 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 6 Jun 2015 19:53:56 -0400 Subject: [PATCH 0288/1037] Add 2.8.2 release info [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 9c8c04d1c..4f32f7e40 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -40,6 +40,12 @@ Changelog (Pillow) - Tiff: allow writing floating point tag values #1113 [bpedersen2] +2.8.2 (2015-06-06) +------------------ + +- Bug fix: Fixed Tiff handling of bad EXIF data + [radarhere] + 2.8.1 (2015-04-02) ------------------ From b4503f6cd45502a01e5c84319d68231ef8d4f79d Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 6 Jun 2015 20:27:09 -0400 Subject: [PATCH 0289/1037] Make sure we upload a tar.gz too, Part 2 [ci skip] Do a better job: - Add gztar to --format instead of calling setup.py twice - Create same sdists across all targets not just upload --- Makefile | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index a729e4088..27ec990b7 100644 --- a/Makefile +++ b/Makefile @@ -67,9 +67,8 @@ docserver: # password: # repository = http://test.pythonpackages.com sdisttest: - python setup.py sdist --format=zip upload -r test + python setup.py sdist --format=gztar,zip upload -r test sdistup: - python setup.py sdist --format=zip upload - python setup.py sdist upload + python setup.py sdist --format=gztar,zip upload sdist: - python setup.py sdist --format=zip + python setup.py sdist --format=gztar,zip From d31c6c7847f242ccd2eef1be01a74ca5abaa3384 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 7 Jun 2015 09:38:09 -0400 Subject: [PATCH 0290/1037] Clean up Makefile and requirements.txt [ci skip] - Remove relative calls to pip, python (assume virtualenv activated or otherwise "safe" global env) - Full define development and documentation requirements in requirements.txt ``make test-dep`` installs documentation dependencies too, hopefully that's not too annoying. This means development process for documentation and code can begin with: virtualenv . source bin/activate pip install -r requirements.txt --- Makefile | 14 +++++++------- requirements.txt | 7 ++++++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 27ec990b7..08a147220 100644 --- a/Makefile +++ b/Makefile @@ -14,12 +14,12 @@ help: pre: virtualenv . - bin/pip install -r requirements.txt - bin/python setup.py develop - bin/python selftest.py - bin/nosetests Tests/test_*.py - bin/python setup.py install - bin/python test-installed.py + pip install -r requirements.txt + python setup.py develop + python selftest.py + nosetests Tests/test_*.py + python setup.py install + python test-installed.py check-manifest pyroma . viewdoc @@ -52,7 +52,7 @@ coverage: coverage report test-dep: - pip install coveralls nose nose-cov pep8 pyflakes + pip install -r requirements.txt docs: $(MAKE) -C docs html diff --git a/requirements.txt b/requirements.txt index 86ddbd771..bd37d7ba9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,8 @@ -# Testing reqs +# Testing and documentation requirements -e . +-r docs/requirements.txt +coveralls nose +nose-cov +pep8 +pyflakes From 19432c3e531921a543e49567871272cc1b54d742 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 7 Jun 2015 09:45:26 -0400 Subject: [PATCH 0291/1037] Clean up Makefile [ci skip] - Clean up test.pythonpackages.com setup description --- Makefile | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 08a147220..6755e9b65 100644 --- a/Makefile +++ b/Makefile @@ -60,12 +60,13 @@ docs: docserver: cd docs/_build/html && python -mSimpleHTTPServer 2> /dev/null& -# Test sdist upload via test.pythonpackages.com, no creds required -# .pypirc: -# [test] -# username: -# password: -# repository = http://test.pythonpackages.com +# Test sdist upload via test.pythonpackages.com. Create .pypirc first: +# +# [test] +# username: +# password: +# repository = http://test.pythonpackages.com +# sdisttest: python setup.py sdist --format=gztar,zip upload -r test sdistup: From 0706f6b50459b8e6f33b1c5da94a01d2761981c7 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 7 Jun 2015 09:50:01 -0400 Subject: [PATCH 0292/1037] Clean up Makefile [ci skip] - Rename test-dep -> install-req to better describe what target does; keep target name short. --- Makefile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 6755e9b65..2134b3a43 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: pre clean install test inplace coverage test-dep help docs livedocs +.PHONY: pre clean install install-req test inplace coverage help docs livedocs help: @echo "Please use \`make ' where is one of" @@ -10,7 +10,7 @@ help: @echo " coverage run coverage test (in progress)" @echo " docs make html docs" @echo " docserver run an http server on the docs directory" - @echo " test-dep install coveraget and test dependencies" + @echo " install-req install documentation and test dependencies" pre: virtualenv . @@ -34,6 +34,9 @@ install: python setup.py install python selftest.py --installed +install-req: + pip install -r requirements.txt + test: python test-installed.py @@ -51,9 +54,6 @@ coverage: coverage combine coverage report -test-dep: - pip install -r requirements.txt - docs: $(MAKE) -C docs html From 91d5da745ffe77c1b8f1a7f26b82700d2a4c8dec Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 7 Jun 2015 09:54:02 -0400 Subject: [PATCH 0293/1037] Clean up Makefile [ci skip] - Rename sdistup, sdisttest -> upload, upload-test to better reflect what target does; keep target name short; add targets to help --- Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 2134b3a43..004b807ea 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,8 @@ help: @echo " docs make html docs" @echo " docserver run an http server on the docs directory" @echo " install-req install documentation and test dependencies" + @echo " upload upload sdists to PyPI" + @echo " upload-test upload sdists to test.pythonpackages.com" pre: virtualenv . @@ -67,9 +69,9 @@ docserver: # password: # repository = http://test.pythonpackages.com # -sdisttest: +upload-test: python setup.py sdist --format=gztar,zip upload -r test -sdistup: +upload: python setup.py sdist --format=gztar,zip upload sdist: python setup.py sdist --format=gztar,zip From 99b4da48dce1d4f0585295ca29e883b4486c4782 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 7 Jun 2015 09:59:46 -0400 Subject: [PATCH 0294/1037] Clean up Makefile [ci skip] - Rename ``pre`` -> ``release-test`` - Remove virtualenv creation and call ``install-req`` from ``release-test`` --- Makefile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 004b807ea..bcc02d13c 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: pre clean install install-req test inplace coverage help docs livedocs +.PHONY: release-test clean install install-req test inplace coverage help docs livedocs help: @echo "Please use \`make ' where is one of" @@ -11,12 +11,12 @@ help: @echo " docs make html docs" @echo " docserver run an http server on the docs directory" @echo " install-req install documentation and test dependencies" - @echo " upload upload sdists to PyPI" - @echo " upload-test upload sdists to test.pythonpackages.com" + @echo " upload build and upload sdists to PyPI" + @echo " upload-test build and upload sdists to test.pythonpackages.com" + @echo " release-test run code and package tests before release" -pre: - virtualenv . - pip install -r requirements.txt +release-test: + $(MAKE) install-req python setup.py develop python selftest.py nosetests Tests/test_*.py From e3d5306bde2f7fe214c5aaf72af8535cc061265d Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 7 Jun 2015 10:03:31 -0400 Subject: [PATCH 0295/1037] Clean up Makefile [ci skip] - Alpha sort and add missing targets (though we may only need ones that are known to cause target execution issues) --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index bcc02d13c..9c44dfbf2 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,6 @@ -.PHONY: release-test clean install install-req test inplace coverage help docs livedocs +# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html +# XXX Do we need all these phony targets? +.PHONY: clean coverage docs docserver help inplace install install-req release-test sdist test upload upload-test help: @echo "Please use \`make ' where is one of" From 7dafa9a97e2b4b357a0774c5444e2db866dfa2a1 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 7 Jun 2015 10:08:38 -0400 Subject: [PATCH 0296/1037] Clean up Makefile [ci skip] - Alpha sort targets and help targets - Add welcome message --- Makefile | 84 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/Makefile b/Makefile index 9c44dfbf2..a5878e486 100644 --- a/Makefile +++ b/Makefile @@ -2,51 +2,12 @@ # XXX Do we need all these phony targets? .PHONY: clean coverage docs docserver help inplace install install-req release-test sdist test upload upload-test -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " clean remove build products" - @echo " install make and install" - @echo " test run tests on installed pillow" - @echo " inplace make inplace extension" - @echo " coverage run coverage test (in progress)" - @echo " docs make html docs" - @echo " docserver run an http server on the docs directory" - @echo " install-req install documentation and test dependencies" - @echo " upload build and upload sdists to PyPI" - @echo " upload-test build and upload sdists to test.pythonpackages.com" - @echo " release-test run code and package tests before release" - -release-test: - $(MAKE) install-req - python setup.py develop - python selftest.py - nosetests Tests/test_*.py - python setup.py install - python test-installed.py - check-manifest - pyroma . - viewdoc - clean: python setup.py clean rm PIL/*.so || true rm -r build || true find . -name __pycache__ | xargs rm -r || true -install: - python setup.py install - python selftest.py --installed - -install-req: - pip install -r requirements.txt - -test: - python test-installed.py - -inplace: clean - python setup.py build_ext --inplace - coverage: # requires nose-cov coverage erase @@ -64,6 +25,48 @@ docs: docserver: cd docs/_build/html && python -mSimpleHTTPServer 2> /dev/null& +help: + @echo "Welcome to Pillow development. Please use \`make ' where is one of" + @echo " clean remove build products" + @echo " coverage run coverage test (in progress)" + @echo " docs make html docs" + @echo " docserver run an http server on the docs directory" + @echo " html to make standalone HTML files" + @echo " inplace make inplace extension" + @echo " install make and install" + @echo " install-req install documentation and test dependencies" + @echo " release-test run code and package tests before release" + @echo " test run tests on installed pillow" + @echo " upload build and upload sdists to PyPI" + @echo " upload-test build and upload sdists to test.pythonpackages.com" + +inplace: clean + python setup.py build_ext --inplace + +install: + python setup.py install + python selftest.py --installed + +install-req: + pip install -r requirements.txt + +release-test: + $(MAKE) install-req + python setup.py develop + python selftest.py + nosetests Tests/test_*.py + python setup.py install + python test-installed.py + check-manifest + pyroma . + viewdoc + +sdist: + python setup.py sdist --format=gztar,zip + +test: + python test-installed.py + # Test sdist upload via test.pythonpackages.com. Create .pypirc first: # # [test] @@ -73,7 +76,6 @@ docserver: # upload-test: python setup.py sdist --format=gztar,zip upload -r test + upload: python setup.py sdist --format=gztar,zip upload -sdist: - python setup.py sdist --format=gztar,zip From 5f4e407927c4d02255d2267d9677efebb826bf1d Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 7 Jun 2015 10:18:31 -0400 Subject: [PATCH 0297/1037] Clean up Makefile [ci skip] - Rename targets docs, docserver -> doc, docserve; shorten - Rename help and phony targets --- Makefile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index a5878e486..85b644a83 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html # XXX Do we need all these phony targets? -.PHONY: clean coverage docs docserver help inplace install install-req release-test sdist test upload upload-test +.PHONY: clean coverage doc docserve help inplace install install-req release-test sdist test upload upload-test clean: python setup.py clean @@ -19,18 +19,18 @@ coverage: coverage combine coverage report -docs: +doc: $(MAKE) -C docs html -docserver: +docserve: cd docs/_build/html && python -mSimpleHTTPServer 2> /dev/null& help: @echo "Welcome to Pillow development. Please use \`make ' where is one of" @echo " clean remove build products" @echo " coverage run coverage test (in progress)" - @echo " docs make html docs" - @echo " docserver run an http server on the docs directory" + @echo " doc make html docs" + @echo " docserve run an http server on the docs directory" @echo " html to make standalone HTML files" @echo " inplace make inplace extension" @echo " install make and install" From 70b2be6bd5c253478b88f164d69b28db9237327b Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 7 Jun 2015 10:22:15 -0400 Subject: [PATCH 0298/1037] Clean up Makefile [ci skip] - Remove nose-cov comment: we know from requirements.txt that nose-cov is required. - Phony targets comment: who cares? We'll just keep listing them all for now. URL still helpful. - Coverage report comment: I don't get it. Reformat and add "XXX" to draw attention from someone who does. --- Makefile | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 85b644a83..9a0deb90c 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,4 @@ # https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html -# XXX Do we need all these phony targets? .PHONY: clean coverage doc docserve help inplace install install-req release-test sdist test upload upload-test clean: @@ -9,12 +8,10 @@ clean: find . -name __pycache__ | xargs rm -r || true coverage: -# requires nose-cov coverage erase coverage run --parallel-mode --include=PIL/* selftest.py nosetests --with-cov --cov='PIL/' --cov-report=html Tests/test_*.py -# doesn't combine properly before report, -# writing report instead of displaying invalid report +# XXX Doesn't combine properly before report, writing report instead of displaying invalid report. rm -r htmlcov || true coverage combine coverage report @@ -29,8 +26,8 @@ help: @echo "Welcome to Pillow development. Please use \`make ' where is one of" @echo " clean remove build products" @echo " coverage run coverage test (in progress)" - @echo " doc make html docs" - @echo " docserve run an http server on the docs directory" + @echo " doc make html docs" + @echo " docserve run an http server on the docs directory" @echo " html to make standalone HTML files" @echo " inplace make inplace extension" @echo " install make and install" From e69d028b6ee1e92763a23a59fcb2bb8d3bf6254e Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 7 Jun 2015 10:28:40 -0400 Subject: [PATCH 0299/1037] Clean up Makefile [ci skip] --- Makefile | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 9a0deb90c..bd2d3add3 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ coverage: coverage erase coverage run --parallel-mode --include=PIL/* selftest.py nosetests --with-cov --cov='PIL/' --cov-report=html Tests/test_*.py -# XXX Doesn't combine properly before report, writing report instead of displaying invalid report. +# Doesn't combine properly before report, writing report instead of displaying invalid report. rm -r htmlcov || true coverage combine coverage report @@ -64,14 +64,12 @@ sdist: test: python test-installed.py -# Test sdist upload via test.pythonpackages.com. Create .pypirc first: -# -# [test] -# username: -# password: -# repository = http://test.pythonpackages.com -# +# https://docs.python.org/2/distutils/packageindex.html#the-pypirc-file upload-test: +# [test] +# username: +# password: +# repository = http://test.pythonpackages.com python setup.py sdist --format=gztar,zip upload -r test upload: From 9a333c89968950c1d87f9f82272df86962a57495 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 7 Jun 2015 10:29:52 -0400 Subject: [PATCH 0300/1037] Clean up --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index bd2d3add3..4d96c497d 100644 --- a/Makefile +++ b/Makefile @@ -66,10 +66,10 @@ test: # https://docs.python.org/2/distutils/packageindex.html#the-pypirc-file upload-test: -# [test] -# username: -# password: -# repository = http://test.pythonpackages.com +# [test] +# username: +# password: +# repository = http://test.pythonpackages.com python setup.py sdist --format=gztar,zip upload -r test upload: From 1b80fe5507060e316ad8bc0766c33cef856adc0a Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 14 Apr 2015 17:43:05 -0700 Subject: [PATCH 0301/1037] Provide n_frames attribute to multi-frame formats. cf #1190, #1192. Tests missing. --- PIL/DcxImagePlugin.py | 4 ++++ PIL/FliImagePlugin.py | 35 ++++++++++++++++++++++++++++++----- PIL/GifImagePlugin.py | 23 ++++++++++++++++++++++- PIL/ImImagePlugin.py | 4 ++++ PIL/MicImagePlugin.py | 4 ++++ PIL/MpoImagePlugin.py | 4 ++++ PIL/PsdImagePlugin.py | 4 ++++ PIL/SpiderImagePlugin.py | 10 +++++++--- PIL/TiffImagePlugin.py | 33 ++++++++++++++++++++++----------- 9 files changed, 101 insertions(+), 20 deletions(-) diff --git a/PIL/DcxImagePlugin.py b/PIL/DcxImagePlugin.py index 0940b3935..978c90e80 100644 --- a/PIL/DcxImagePlugin.py +++ b/PIL/DcxImagePlugin.py @@ -62,6 +62,10 @@ class DcxImageFile(PcxImageFile): self.__fp = self.fp self.seek(0) + @property + def n_frames(self): + return len(self._offset) + def seek(self, frame): if frame >= len(self._offset): raise EOFError("attempt to seek outside DCX directory") diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py index 4ecaccdc4..1acae31bf 100644 --- a/PIL/FliImagePlugin.py +++ b/PIL/FliImagePlugin.py @@ -86,9 +86,10 @@ class FliImageFile(ImageFile.ImageFile): self.palette = ImagePalette.raw("RGB", b"".join(palette)) # set things up to decode first frame - self.frame = -1 + self.__frame = -1 self.__fp = self.fp - + self.__rewind = self.fp.tell() + self._n_frames = None self.seek(0) def _palette(self, palette, shift): @@ -109,11 +110,35 @@ class FliImageFile(ImageFile.ImageFile): palette[i] = (r, g, b) i += 1 + @property + def n_frames(self): + if self._n_frames is None: + current = self.tell() + try: + while True: + self.seek(self.tell() + 1) + except EOFError: + self._n_frames = self.tell() + 1 + self.seek(current) + return self._n_frames + + def seek(self, frame): + if frame == self.__frame: + return + if frame < self.__frame: + self._seek(0) + for f in range(self.__frame + 1, frame + 1): + self._seek(f) + def seek(self, frame): - if frame != self.frame + 1: + if frame == 0: + self.__frame = -1 + self.__fp.seek(self.__rewind) + + if frame != self.__frame + 1: raise ValueError("cannot seek to frame %d" % frame) - self.frame = frame + self.__frame = frame # move to next frame self.fp = self.__fp @@ -132,7 +157,7 @@ class FliImageFile(ImageFile.ImageFile): def tell(self): - return self.frame + return self.__frame # # registry diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 283300320..150773b67 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -87,9 +87,30 @@ class GifImageFile(ImageFile.ImageFile): self.__fp = self.fp # FIXME: hack self.__rewind = self.fp.tell() - self.seek(0) # get ready to read first frame + self._n_frames = None + self._seek(0) # get ready to read first frame + + @property + def n_frames(self): + if self._n_frames is None: + current = self.tell() + try: + while True: + self.seek(self.tell() + 1) + except EOFError: + self._n_frames = self.tell() + 1 + self.seek(current) + return self._n_frames def seek(self, frame): + if frame == self.__frame: + return + if frame < self.__frame: + self._seek(0) + for f in range(self.__frame + 1, frame + 1): + self._seek(f) + + def _seek(self, frame): if frame == 0: # rewind diff --git a/PIL/ImImagePlugin.py b/PIL/ImImagePlugin.py index c68a3cea4..589928d0e 100644 --- a/PIL/ImImagePlugin.py +++ b/PIL/ImImagePlugin.py @@ -260,6 +260,10 @@ class ImImageFile(ImageFile.ImageFile): self.tile = [("raw", (0, 0)+self.size, offs, (self.rawmode, 0, -1))] + @property + def n_frames(self): + return self.info[FRAMES] + def seek(self, frame): if frame < 0 or frame >= self.info[FRAMES]: diff --git a/PIL/MicImagePlugin.py b/PIL/MicImagePlugin.py index 5aed618ac..aa41bf359 100644 --- a/PIL/MicImagePlugin.py +++ b/PIL/MicImagePlugin.py @@ -71,6 +71,10 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): self.seek(0) + @property + def n_frames(self): + return len(self.images) + def seek(self, frame): try: diff --git a/PIL/MpoImagePlugin.py b/PIL/MpoImagePlugin.py index c345fd327..9d21728b9 100644 --- a/PIL/MpoImagePlugin.py +++ b/PIL/MpoImagePlugin.py @@ -62,6 +62,10 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile): def load_seek(self, pos): self.__fp.seek(pos) + @property + def n_frames(self): + return self.__framecount + def seek(self, frame): if frame < 0 or frame >= self.__framecount: raise EOFError("no more images in MPO file") diff --git a/PIL/PsdImagePlugin.py b/PIL/PsdImagePlugin.py index 02c94a860..d30695adb 100644 --- a/PIL/PsdImagePlugin.py +++ b/PIL/PsdImagePlugin.py @@ -132,6 +132,10 @@ class PsdImageFile(ImageFile.ImageFile): self._fp = self.fp self.frame = 0 + @property + def n_frames(self): + return len(self.layers) + def seek(self, layer): # seek to given layer (1..max) if layer == self.frame: diff --git a/PIL/SpiderImagePlugin.py b/PIL/SpiderImagePlugin.py index f1ccb67f6..7de5156b1 100644 --- a/PIL/SpiderImagePlugin.py +++ b/PIL/SpiderImagePlugin.py @@ -127,12 +127,12 @@ class SpiderImageFile(ImageFile.ImageFile): if self.istack == 0 and self.imgnumber == 0: # stk=0, img=0: a regular 2D image offset = hdrlen - self.nimages = 1 + self._nimages = 1 elif self.istack > 0 and self.imgnumber == 0: # stk>0, img=0: Opening the stack for the first time self.imgbytes = int(h[12]) * int(h[2]) * 4 self.hdrlen = hdrlen - self.nimages = int(h[26]) + self._nimages = int(h[26]) # Point to the first image in the stack offset = hdrlen * 2 self.imgnumber = 1 @@ -154,6 +154,10 @@ class SpiderImageFile(ImageFile.ImageFile): (self.rawmode, 0, 1))] self.__fp = self.fp # FIXME: hack + @property + def n_frames(self): + return self._nimages + # 1st image index is zero (although SPIDER imgnumber starts at 1) def tell(self): if self.imgnumber < 1: @@ -164,7 +168,7 @@ class SpiderImageFile(ImageFile.ImageFile): def seek(self, frame): if self.istack == 0: return - if frame >= self.nimages: + if frame >= self._nimages: raise EOFError("attempt to seek past end of file") self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes) self.fp = self.__fp diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 41bb26d42..8fdca05de 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -648,6 +648,8 @@ class TiffImageFile(ImageFile.ImageFile): self.__first = self.__next = self.ifd.i32(ifh, 4) self.__frame = -1 self.__fp = self.fp + self._frame_pos = [] + self._n_frames = None if Image.DEBUG: print("*** TiffImageFile._open ***") @@ -657,10 +659,22 @@ class TiffImageFile(ImageFile.ImageFile): # and load the first frame self._seek(0) + @property + def n_frames(self): + if self._n_frames is None: + current = self.tell() + try: + while True: + self._seek(self.tell() + 1) + except EOFError: + self._n_frames = self.tell() + 1 + self.seek(current) + return self._n_frames + def seek(self, frame): "Select a given frame as current image" if frame < 0: - frame = 0 + raise ValueError("invalid seek") self._seek(frame) # Create a new core image object on second and # subsequent frames in the image. Image may be @@ -668,17 +682,9 @@ class TiffImageFile(ImageFile.ImageFile): Image._decompression_bomb_check(self.size) self.im = Image.core.new(self.mode, self.size) - def tell(self): - "Return the current frame number" - return self._tell() - def _seek(self, frame): self.fp = self.__fp - if frame < self.__frame: - # rewind file - self.__frame = -1 - self.__next = self.__first - while self.__frame < frame: + while len(self._frame_pos) <= frame: if not self.__next: raise EOFError("no more images in TIFF file") if Image.DEBUG: @@ -688,14 +694,19 @@ class TiffImageFile(ImageFile.ImageFile): # was passed to libtiff, invalidating the buffer self.fp.tell() self.fp.seek(self.__next) + self._frame_pos.append(self.__next) if Image.DEBUG: print("Loading tags, location: %s" % self.fp.tell()) self.tag.load(self.fp) self.__next = self.tag.next self.__frame += 1 + self.fp.seek(self._frame_pos[frame]) + self.tag.load(self.fp) + self.__frame = frame self._setup() - def _tell(self): + def tell(self): + "Return the current frame number" return self.__frame def _decoder(self, rawmode, layer, tile=None): From 51f3560dc45dd39d14d67108ddc5b95b9bd3aa84 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Thu, 23 Apr 2015 01:06:32 -0700 Subject: [PATCH 0302/1037] Restore negative seeks for TIFF. --- PIL/TiffImagePlugin.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 8fdca05de..59de84273 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -673,9 +673,7 @@ class TiffImageFile(ImageFile.ImageFile): def seek(self, frame): "Select a given frame as current image" - if frame < 0: - raise ValueError("invalid seek") - self._seek(frame) + self._seek(max(frame, 0)) # Questionable backwards compatibility. # Create a new core image object on second and # subsequent frames in the image. Image may be # different size/mode. From 3ad70423add94de5a61fc6ee8f526ad9d0b292d7 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Thu, 23 Apr 2015 11:03:11 -0700 Subject: [PATCH 0303/1037] Fix typo in FliImagePlugin (seek -> _seek). --- PIL/FliImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py index 1acae31bf..178623951 100644 --- a/PIL/FliImagePlugin.py +++ b/PIL/FliImagePlugin.py @@ -130,7 +130,7 @@ class FliImageFile(ImageFile.ImageFile): for f in range(self.__frame + 1, frame + 1): self._seek(f) - def seek(self, frame): + def _seek(self, frame): if frame == 0: self.__frame = -1 From 0c51b7967ed371d3e140d83449ae3910c5b1ea32 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 8 Jun 2015 01:01:50 +1000 Subject: [PATCH 0304/1037] Fixed seek bug in FliImagePlugin --- PIL/FliImagePlugin.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py index 178623951..32cd05049 100644 --- a/PIL/FliImagePlugin.py +++ b/PIL/FliImagePlugin.py @@ -135,6 +135,7 @@ class FliImageFile(ImageFile.ImageFile): if frame == 0: self.__frame = -1 self.__fp.seek(self.__rewind) + self.__offset = 128 if frame != self.__frame + 1: raise ValueError("cannot seek to frame %d" % frame) @@ -153,7 +154,7 @@ class FliImageFile(ImageFile.ImageFile): self.decodermaxblock = framesize self.tile = [("fli", (0, 0)+self.size, self.__offset, None)] - self.__offset = self.__offset + framesize + self.__offset += framesize def tell(self): From 46f439604cb0a3d8b034b327d6d99c2bd580f8fc Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 8 Jun 2015 01:01:34 +1000 Subject: [PATCH 0305/1037] Added tests --- PIL/FliImagePlugin.py | 2 -- Tests/images/hopper.im | Bin 0 -> 49664 bytes Tests/images/hopper_merged.psd | Bin 0 -> 97862 bytes Tests/test_file_dcx.py | 4 ++++ Tests/test_file_fli.py | 4 ++++ Tests/test_file_gif.py | 4 ++++ Tests/test_file_im.py | 33 +++++++++++++++++++++++++++++++++ Tests/test_file_mpo.py | 4 ++++ Tests/test_file_psd.py | 7 +++++++ Tests/test_file_spider.py | 4 ++++ Tests/test_file_tiff.py | 7 +++++++ 11 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 Tests/images/hopper.im create mode 100644 Tests/images/hopper_merged.psd create mode 100644 Tests/test_file_im.py diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py index 32cd05049..0660ddeb6 100644 --- a/PIL/FliImagePlugin.py +++ b/PIL/FliImagePlugin.py @@ -131,7 +131,6 @@ class FliImageFile(ImageFile.ImageFile): self._seek(f) def _seek(self, frame): - if frame == 0: self.__frame = -1 self.__fp.seek(self.__rewind) @@ -157,7 +156,6 @@ class FliImageFile(ImageFile.ImageFile): self.__offset += framesize def tell(self): - return self.__frame # diff --git a/Tests/images/hopper.im b/Tests/images/hopper.im new file mode 100644 index 0000000000000000000000000000000000000000..ceeb01049c98a69b31cd89b5d3f11e327a86d2c0 GIT binary patch literal 49664 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 Aga7~l literal 0 HcmV?d00001 diff --git a/Tests/images/hopper_merged.psd b/Tests/images/hopper_merged.psd new file mode 100644 index 0000000000000000000000000000000000000000..6b5c7ebe1fe8e424b26fa4689d1ba2d6f7f9078b GIT binary patch literal 97862 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 literal 0 HcmV?d00001 diff --git a/Tests/test_file_dcx.py b/Tests/test_file_dcx.py index 2acf7b459..7d2ae32d8 100644 --- a/Tests/test_file_dcx.py +++ b/Tests/test_file_dcx.py @@ -30,6 +30,10 @@ class TestFileDcx(PillowTestCase): # Assert self.assertEqual(frame, 0) + def test_n_frames(self): + im = Image.open(TEST_FILE) + self.assertEqual(im.n_frames, 1) + def test_seek_too_far(self): # Arrange im = Image.open(TEST_FILE) diff --git a/Tests/test_file_fli.py b/Tests/test_file_fli.py index e6634c799..04c2006c9 100644 --- a/Tests/test_file_fli.py +++ b/Tests/test_file_fli.py @@ -18,6 +18,10 @@ class TestFileFli(PillowTestCase): self.assertEqual(im.size, (128, 128)) self.assertEqual(im.format, "FLI") + def test_n_frames(self): + im = Image.open(test_file) + self.assertEqual(im.n_frames, 2) + if __name__ == '__main__': unittest.main() diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 08b2f10c9..0e9e65a18 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -134,6 +134,10 @@ class TestFileGif(PillowTestCase): except EOFError: self.assertEqual(framecount, 5) + def test_n_frames(self): + im = Image.open("Tests/images/iss634.gif") + self.assertEqual(im.n_frames, 43) + def test_dispose_none(self): img = Image.open("Tests/images/dispose_none.gif") try: diff --git a/Tests/test_file_im.py b/Tests/test_file_im.py new file mode 100644 index 000000000..24e00b2f0 --- /dev/null +++ b/Tests/test_file_im.py @@ -0,0 +1,33 @@ +from helper import unittest, PillowTestCase, hopper + +from PIL import Image + +# sample im +TEST_IM = "Tests/images/hopper.im" + + +class TestFileIm(PillowTestCase): + + def test_sanity(self): + im = Image.open(TEST_IM) + im.load() + self.assertEqual(im.mode, "RGB") + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.format, "IM") + + def test_n_frames(self): + im = Image.open(TEST_IM) + self.assertEqual(im.n_frames, 1) + + def test_roundtrip(self): + out = self.tempfile('temp.im') + im = hopper() + im.save(out) + reread = Image.open(out) + + self.assert_image_equal(reread, im) + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_file_mpo.py b/Tests/test_file_mpo.py index 7850744af..1a0ebc453 100644 --- a/Tests/test_file_mpo.py +++ b/Tests/test_file_mpo.py @@ -95,6 +95,10 @@ class TestFileMpo(PillowTestCase): im.seek(0) self.assertEqual(im.tell(), 0) + def test_n_frames(self): + im = Image.open("Tests/images/sugarshack.mpo") + self.assertEqual(im.n_frames, 2) + def test_image_grab(self): for test_file in test_files: im = Image.open(test_file) diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py index 51b8cf3f4..dca3601b2 100644 --- a/Tests/test_file_psd.py +++ b/Tests/test_file_psd.py @@ -16,6 +16,13 @@ class TestImagePsd(PillowTestCase): self.assertEqual(im.size, (128, 128)) self.assertEqual(im.format, "PSD") + def test_n_frames(self): + im = Image.open("Tests/images/hopper_merged.psd") + self.assertEqual(im.n_frames, 1) + + im = Image.open(test_file) + self.assertEqual(im.n_frames, 2) + if __name__ == '__main__': unittest.main() diff --git a/Tests/test_file_spider.py b/Tests/test_file_spider.py index 7bfedad1a..7d24b2fe5 100644 --- a/Tests/test_file_spider.py +++ b/Tests/test_file_spider.py @@ -42,6 +42,10 @@ class TestImageSpider(PillowTestCase): # Assert self.assertEqual(index, 0) + def test_n_frames(self): + im = Image.open(TEST_FILE) + self.assertEqual(im.n_frames, 1) + def test_loadImageSeries(self): # Arrange not_spider_file = "Tests/images/hopper.ppm" diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 745412324..02a63586c 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -150,6 +150,13 @@ class TestFileTiff(PillowTestCase): self.assertEqual( im.getextrema(), (-3.140936851501465, 3.140684127807617)) + def test_n_frames(self): + im = Image.open('Tests/images/multipage-lastframe.tif') + self.assertEqual(im.n_frames, 1) + + im = Image.open('Tests/images/multipage.tiff') + self.assertEqual(im.n_frames, 3) + def test_multipage(self): # issue #862 im = Image.open('Tests/images/multipage.tiff') From 9e24ae023d67de9822f481d252cdecaa7ded560d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 2 Jun 2015 22:50:22 +1000 Subject: [PATCH 0306/1037] Fixed various typos --- _imagingcms.c | 2 +- _imagingft.c | 2 +- encode.c | 2 +- libImaging/QuantOctree.c | 4 ++-- libImaging/Resample.c | 2 +- libImaging/TiffDecode.c | 2 +- libImaging/Unpack.c | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/_imagingcms.c b/_imagingcms.c index 3b822006a..cda7c5f1f 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -278,7 +278,7 @@ findLCMStype(char* PILmode) return TYPE_YCbCr_8; } else if (strcmp(PILmode, "LAB") == 0) { - // LabX equvalent like ALab, but not reversed -- no #define in lcms2 + // LabX equivalent like ALab, but not reversed -- no #define in lcms2 return (COLORSPACE_SH(PT_LabV2)|CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)); } diff --git a/_imagingft.c b/_imagingft.c index ad40ee425..d8f6d6338 100644 --- a/_imagingft.c +++ b/_imagingft.c @@ -572,7 +572,7 @@ setup_module(PyObject* m) { PyType_Ready(&Font_Type); if (FT_Init_FreeType(&library)) - return 0; /* leave it uninitalized */ + return 0; /* leave it uninitialized */ FT_Library_Version(library, &major, &minor, &patch); diff --git a/encode.c b/encode.c index fef102176..6bdb8c71a 100644 --- a/encode.c +++ b/encode.c @@ -737,7 +737,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) return NULL; } - // While failes on 64 bit machines, complains that pos is an int instead of a Py_ssize_t + // While fails on 64 bit machines, complains that pos is an int instead of a Py_ssize_t // while (PyDict_Next(dir, &pos, &key, &value)) { for (pos=0;pos 64). For a quantization to 256 colors all 64 coarse colors will be used @@ -421,7 +421,7 @@ int quantize_octree(Pixel *pixelData, /* add fine colors to the lookup cube */ add_lookup_buckets(lookupCube, paletteBuckets, nFineColors, nCoarseColors); - /* create result pixles and map palatte indices */ + /* create result pixels and map palette indices */ qp = malloc(sizeof(Pixel)*nPixels); if (!qp) goto error; map_image_pixels(pixelData, nPixels, lookupCube, qp); diff --git a/libImaging/Resample.c b/libImaging/Resample.c index a87f2db83..597fca3e9 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -2,7 +2,7 @@ * The Python Imaging Library * $Id$ * - * Pillow image resamling support + * Pillow image resampling support * * history: * 2002-03-09 fl Created (for PIL 1.1.3) diff --git a/libImaging/TiffDecode.c b/libImaging/TiffDecode.c index 76bd887a7..25336e7fa 100644 --- a/libImaging/TiffDecode.c +++ b/libImaging/TiffDecode.c @@ -245,7 +245,7 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int // back in. Can't use read encoded stripe. // This thing pretty much requires that I have the whole image in one shot. - // Prehaps a stub version would work better??? + // Perhaps a stub version would work better??? while(state->y < state->ysize){ if (TIFFReadScanline(tiff, (tdata_t)state->buffer, (uint32)state->y, 0) == -1) { TRACE(("Decode Error, row %d\n", state->y)); diff --git a/libImaging/Unpack.c b/libImaging/Unpack.c index 9db43147f..522e9b04c 100644 --- a/libImaging/Unpack.c +++ b/libImaging/Unpack.c @@ -808,7 +808,7 @@ unpackI12_I16(UINT8* out, const UINT8* in, int pixels){ FillOrder = 2 should be used only when BitsPerSample = 1 and the data is either uncompressed or compressed using CCITT 1D - or 2D compression, to avoid potentially ambigous situations. + or 2D compression, to avoid potentially ambiguous situations. Yeah. I thought so. We'll see how well people read the spec. We've got several fillorder=2 modes in TiffImagePlugin.py From 7ea13e8543e9ebdf72c855912356eb1e85545a0d Mon Sep 17 00:00:00 2001 From: Hugo Date: Mon, 8 Jun 2015 09:09:25 +0300 Subject: [PATCH 0307/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 4f32f7e40..518a4244f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Provide n_frames attribute to multi-frame formats #1261 + [anntzer, radarhere] + - Add duration and loop set to GifImagePlugin #1172 [radarhere] From e9a359ced2da677b14b38c7322f5da0bab089d48 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Mon, 8 Jun 2015 07:24:40 -0400 Subject: [PATCH 0308/1037] Comment badges Re: #1264 --- README.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 430810f0e..be50c5531 100644 --- a/README.rst +++ b/README.rst @@ -6,15 +6,18 @@ Python Imaging Library (Fork) Pillow is the "friendly PIL fork" by `Alex Clark and Contributors `_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. -.. image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master +.. + image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master :target: https://travis-ci.org/python-pillow/Pillow :alt: Travis CI build status (Linux) -.. image:: https://pypip.in/v/Pillow/badge.png +.. + image:: https://pypip.in/v/Pillow/badge.png :target: https://pypi.python.org/pypi/Pillow/ :alt: Latest PyPI version -.. image:: https://pypip.in/d/Pillow/badge.png +.. + image:: https://pypip.in/d/Pillow/badge.png :target: https://pypi.python.org/pypi/Pillow/ :alt: Number of PyPI downloads From 02f84fddf4244f72bc3fe22d662a36b95637880b Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 8 Jun 2015 15:02:28 +0300 Subject: [PATCH 0309/1037] Shields.io badges instead of broken pypip.in. PNG->SVG for consistency [CI skip] --- README.rst | 23 ++++++++++------------- docs/index.rst | 8 ++++---- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/README.rst b/README.rst index be50c5531..cff9b89ec 100644 --- a/README.rst +++ b/README.rst @@ -6,26 +6,23 @@ Python Imaging Library (Fork) Pillow is the "friendly PIL fork" by `Alex Clark and Contributors `_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. -.. - image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master +.. image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master :target: https://travis-ci.org/python-pillow/Pillow :alt: Travis CI build status (Linux) -.. - image:: https://pypip.in/v/Pillow/badge.png - :target: https://pypi.python.org/pypi/Pillow/ - :alt: Latest PyPI version +.. image:: https://img.shields.io/pypi/v/pillow.svg + :target: https://pypi.python.org/pypi/Pillow/ + :alt: Latest PyPI version -.. - image:: https://pypip.in/d/Pillow/badge.png - :target: https://pypi.python.org/pypi/Pillow/ - :alt: Number of PyPI downloads +.. image:: https://img.shields.io/pypi/dm/pillow.svg + :target: https://pypi.python.org/pypi/Pillow/ + :alt: Number of PyPI downloads -.. image:: https://coveralls.io/repos/python-pillow/Pillow/badge.png?branch=master - :target: https://coveralls.io/r/python-pillow/Pillow?branch=master +.. image:: https://coveralls.io/repos/python-pillow/Pillow/badge.svg?branch=master + :target: https://coveralls.io/r/python-pillow/Pillow?branch=master :alt: Code coverage -.. image:: https://landscape.io/github/python-pillow/Pillow/master/landscape.png +.. image:: https://landscape.io/github/python-pillow/Pillow/master/landscape.svg :target: https://landscape.io/github/python-pillow/Pillow/master :alt: Code health diff --git a/docs/index.rst b/docs/index.rst index 0c7365c64..979551693 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -7,19 +7,19 @@ Pillow is the "friendly PIL fork" by `Alex Clark and Contributors Date: Mon, 8 Jun 2015 16:12:28 +0300 Subject: [PATCH 0310/1037] Reinstate OS X badge [CI skip] --- README.rst | 4 ++++ docs/index.rst | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/README.rst b/README.rst index cff9b89ec..52e9843a8 100644 --- a/README.rst +++ b/README.rst @@ -10,6 +10,10 @@ Pillow is the "friendly PIL fork" by `Alex Clark and Contributors Date: Tue, 9 Jun 2015 20:21:00 +1000 Subject: [PATCH 0311/1037] Removed interlace flag from GifImagePlugin getdata --- PIL/GifImagePlugin.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 150773b67..18fcb9b21 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -313,8 +313,13 @@ def _save(im, fp, filename): for s in header: fp.write(s) + flags = 0 + + if get_interlace(im): + flags = flags | 64 + # local image header - get_local_header(fp, im) + get_local_header(fp, im, (0, 0), flags) im_out.encoderconfig = (8, get_interlace(im)) ImageFile._save(im_out, fp, [("gif", (0, 0)+im.size, 0, @@ -343,7 +348,7 @@ def get_interlace(im): return interlace -def get_local_header(fp, im, offset=(0, 0)): +def get_local_header(fp, im, offset, flags): transparent_color_exists = False try: transparency = im.encoderinfo["transparency"] @@ -394,12 +399,6 @@ def get_local_header(fp, im, offset=(0, 0)): o8(1) + o16(number_of_loops) + # number of loops o8(0)) - - flags = 0 - - if get_interlace(im): - flags = flags | 64 - fp.write(b"," + o16(offset[0]) + # offset o16(offset[1]) + @@ -571,7 +570,7 @@ def getdata(im, offset=(0, 0), **params): im.encoderinfo = params # local image header - get_local_header(fp, im, offset) + get_local_header(fp, im, offset, 0) ImageFile._save(im, fp, [("gif", (0, 0)+im.size, 0, RAWMODE[im.mode])]) From 67c75061d2b00c7f74f3fbcdf4c9543b011c2ead Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 9 Jun 2015 14:23:09 +0300 Subject: [PATCH 0312/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 518a4244f..0b92b1c24 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -7,7 +7,7 @@ Changelog (Pillow) - Provide n_frames attribute to multi-frame formats #1261 [anntzer, radarhere] -- Add duration and loop set to GifImagePlugin #1172 +- Add duration and loop set to GifImagePlugin #1172, #1269 [radarhere] - Ico files are little endian #1232 From 2334d278d4aaca77ce45a0d2b1ccf902dd3d2130 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 10 Jun 2015 13:16:17 -0700 Subject: [PATCH 0313/1037] in-progress, 27x64 build, appveyor --- winbuild/build_dep.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index 2e9118fd6..dfe48da11 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -241,17 +241,17 @@ fetch_libs() script = [header()] #, cp_tk()] -for compiler in compilers.values(): -#if True: -# compiler = compilers[(7,32)] +#for compiler in compilers.values(): +if True: + compiler = compilers[(7,64)] script.append(setup_compiler(compiler)) -# script.append(nmake_libs(compiler)) + script.append(nmake_libs(compiler)) -# script.append(extract_openjpeg(compiler)) -# -# script.append(build_freetype(compiler)) + script.append(extract_openjpeg(compiler)) + + script.append(build_freetype(compiler)) script.append(build_lcms2(compiler)) -# script.append(nmake_openjpeg(compiler)) + script.append(nmake_openjpeg(compiler)) script.append(end_compiler()) with open('build_deps.cmd', 'w') as f: From 8124851534231a11aa988b01217b4a1a573b5145 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 10 Jun 2015 14:37:08 -0700 Subject: [PATCH 0314/1037] platforms for appveyor --- winbuild/config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/winbuild/config.py b/winbuild/config.py index 467167f1f..26f81d84d 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -1,9 +1,9 @@ SF_MIRROR = 'http://hivelocity.dl.sourceforge.net' SF_MIRROR = 'http://iweb.dl.sourceforge.net' -pythons = {'26':7, +pythons = {#'26':7, '27':7, - '32':7, + #'32':7, '33':7.1, '34':7.1} From b73a4dc5ff170531100c522606ff54e674243562 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 10 Jun 2015 14:37:25 -0700 Subject: [PATCH 0315/1037] name change --- winbuild/build_dep.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index dfe48da11..40e6751b2 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -247,9 +247,9 @@ if True: script.append(setup_compiler(compiler)) script.append(nmake_libs(compiler)) - script.append(extract_openjpeg(compiler)) + #script.append(extract_openjpeg(compiler)) - script.append(build_freetype(compiler)) + script.append(msbuild_freetype(compiler)) script.append(build_lcms2(compiler)) script.append(nmake_openjpeg(compiler)) script.append(end_compiler()) From ca52d5630df3f646ffdfc37b58e34110fbdd0033 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 10 Jun 2015 15:03:32 -0700 Subject: [PATCH 0316/1037] relative path for cmake --- winbuild/build_dep.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index 40e6751b2..30f43c462 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -100,7 +100,7 @@ copy /Y /B %BUILD%\tk8.5.13\xlib\X11\* %INCLIB%\tcl85\include\X11\ def header(): return r"""setlocal set MSBUILD=C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe -set CMAKE="C:\Program Files (x86)\CMake 2.8\bin\cmake.exe" +set CMAKE="cmake.exe" set INCLIB=%~dp0\depends set BUILD=%~dp0\build """ + "\n".join('set %s=%%BUILD%%\%s' %(k.upper(), v['dir']) From 69f49a1b9179f00e2a2f25fd041afeb6b8789a81 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 10 Jun 2015 15:05:29 -0700 Subject: [PATCH 0317/1037] in-progress appveyor path --- winbuild/build_pillow_27x64.cmd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/winbuild/build_pillow_27x64.cmd b/winbuild/build_pillow_27x64.cmd index 24e8f0c9e..88467b838 100644 --- a/winbuild/build_pillow_27x64.cmd +++ b/winbuild/build_pillow_27x64.cmd @@ -13,8 +13,8 @@ set LIB=%LIB%;%INCLIB%\msvcr90-x64 set INCLUDE=%INCLUDE%;%INCLIB%\msvcr90-x64;%INCLIB%\tcl85\include setlocal -set LIB=%LIB%;C:\Python27x64\tcl -call c:\vp\27x64\Scripts\python.exe setup.py %BLDOPT% +set LIB=%LIB%;C:\Python27-x64\tcl +call c:\python27-x64\python.exe setup.py %BLDOPT% endlocal endlocal From 8f3540b9f90b78a53223abf8f129bd8329c15967 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 10 Jun 2015 15:19:21 -0700 Subject: [PATCH 0318/1037] need to detect the right compiler --- winbuild/build_dep.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index 30f43c462..3c5cb634e 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -251,7 +251,7 @@ if True: script.append(msbuild_freetype(compiler)) script.append(build_lcms2(compiler)) - script.append(nmake_openjpeg(compiler)) + #script.append(nmake_openjpeg(compiler)) script.append(end_compiler()) with open('build_deps.cmd', 'w') as f: From 2c28d5365a2f793e16d4ffc4592a78e269e24afd Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 10 Jun 2015 15:30:57 -0700 Subject: [PATCH 0319/1037] lets not do lcms either right now --- winbuild/build_dep.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index 3c5cb634e..ca732374d 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -250,7 +250,7 @@ if True: #script.append(extract_openjpeg(compiler)) script.append(msbuild_freetype(compiler)) - script.append(build_lcms2(compiler)) + #script.append(build_lcms2(compiler)) #script.append(nmake_openjpeg(compiler)) script.append(end_compiler()) From ef1c9d171c79ac8872382ed8bde4e48e2db5e2af Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 10 Jun 2015 15:51:58 -0700 Subject: [PATCH 0320/1037] removing missing file --- MANIFEST.in | 1 - 1 file changed, 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 4fb47c637..30e73c075 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -74,7 +74,6 @@ recursive-include docs *.txt recursive-include docs BUILDME recursive-include docs COPYING recursive-include docs Guardfile -recursive-include docs LICENSE recursive-include docs Makefile recursive-include libImaging *.c recursive-include libImaging *.h From 49c100515be2e9104ff99dfde38d2663dfda3b3f Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 10 Jun 2015 16:42:12 -0700 Subject: [PATCH 0321/1037] build libs based on the python environment variable for appveyor --- winbuild/build_dep.py | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index ca732374d..36876c488 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -235,15 +235,8 @@ copy /Y /B %%LCMS%%\Lib\MS\*.lib %%INCLIB%% endlocal """ % compiler -mkdirs() -fetch_libs() -#extract_binlib() -script = [header()] #, cp_tk()] - -#for compiler in compilers.values(): -if True: - compiler = compilers[(7,64)] +def add_compiler(compiler): script.append(setup_compiler(compiler)) script.append(nmake_libs(compiler)) @@ -254,7 +247,36 @@ if True: #script.append(nmake_openjpeg(compiler)) script.append(end_compiler()) + + +mkdirs() +fetch_libs() +#extract_binlib() +script = [header()] #, cp_tk()] + + +#for compiler in compilers.values(): +# add_compiler(compiler) + +if 'PYTHON' in os.environ: + bit = 32 + if '64' in os.environ['PYTHON']: + bit = 64 + break + + ver = 7 + for k,v in pythons: + if k in os.environ['PYTHON']: + ver = v + break + + add_compiler(compilers[(ver,bit)]) +else: + compiler = compilers[(7,64)] + with open('build_deps.cmd', 'w') as f: f.write("\n".join(script)) + + From ecb1cef99b5eae47db76eb94280b840af34e0fc5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 11 Jun 2015 11:10:05 +1000 Subject: [PATCH 0322/1037] Added background color index saving to GifImagePlugin --- PIL/GifImagePlugin.py | 3 ++- Tests/test_file_gif.py | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 18fcb9b21..54e92a711 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -537,7 +537,8 @@ def getheader(im, palette=None, info=None): # size of global color table + global color table flag header.append(o8(color_table_size + 128)) # background + reserved/aspect - header.append(o8(0) + o8(0)) + background = im.info["background"] if "background" in im.info else 0 + header.append(o8(background) + o8(0)) # end of Logical Screen Descriptor # add the missing amount of bytes diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 0e9e65a18..f415a1b2b 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -201,6 +201,15 @@ class TestFileGif(PillowTestCase): self.assertEqual(reread.info['loop'], number_of_loops) + def test_background(self): + out = self.tempfile('temp.gif') + im = Image.new('L', (100, 100), '#000') + im.info['background'] = 1 + im.save(out) + reread = Image.open(out) + + self.assertEqual(reread.info['background'], im.info['background']) + if __name__ == '__main__': unittest.main() From 680310c7e2cd9624e3320ba92caacdd3291c094d Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 11 Jun 2015 10:55:42 -0700 Subject: [PATCH 0323/1037] use the PYTHON environment variable to choose the python --- winbuild/build.py | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/winbuild/build.py b/winbuild/build.py index c25faaecf..f96111b9d 100644 --- a/winbuild/build.py +++ b/winbuild/build.py @@ -3,13 +3,14 @@ import subprocess, multiprocessing import shutil import sys, getopt +import os from config import * def setup_vms(): ret = [] for py in pythons.keys(): - for arch in ('', 'x64'): + for arch in ('', '-x64'): ret.append("virtualenv -p c:/Python%s%s/python.exe --clear %s%s%s" % (py, arch, VIRT_BASE, py, arch)) ret.append("%s%s%s\Scripts\pip.exe install nose" % @@ -58,6 +59,7 @@ exit """ def build_one(py_ver, compiler): + # UNDONE virtual envs if we're not running on appveyor args = {} args.update(compiler) args['py_ver'] = py_ver @@ -70,7 +72,7 @@ set INCLUDE=%%INCLUDE%%;%%INCLIB%%\%(inc_dir)s;%%INCLIB%%\tcl85\include setlocal set LIB=%%LIB%%;C:\Python%(py_ver)s\tcl -call c:\vp\%(py_ver)s\Scripts\python.exe setup.py %%BLDOPT%% +call %%PYTHON%%\python.exe setup.py %%BLDOPT%% endlocal endlocal @@ -106,6 +108,29 @@ def main(op): for (version, status, trace, err) in results: print ("Compiled %s: %s" % (version, status)) +def run_one(op): + py = os.environ['PYTHON'] + + py_version = '27' + for k,v in pythons.items(): + if k in py: + compiler_version = v + py_version = k + break + + bit = 32 + if '64' in py: + bit = 64 + py_version = '%s-x64' % py_version + + + run_script((py_version, + "\n".join([header(op), + build_one(py_version, compilers[(compiler_version, bit)]), + footer()]) + )) + + if __name__=='__main__': @@ -118,5 +143,8 @@ if __name__=='__main__': op = 'install' if '--dist' in opts: op = "bdist_wininst --user-access-control=auto" - - main(op) + + if 'PYTHON' in os.environ: + run_one(op) + else: + main(op) From a69a47c042b83ef03bdb8d5b81cdf9f303a6a984 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 11 Jun 2015 13:11:26 -0700 Subject: [PATCH 0324/1037] refactoring for PYTHON environment --- winbuild/build.py | 18 ++++-------------- winbuild/build_dep.py | 17 +++-------------- winbuild/config.py | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/winbuild/build.py b/winbuild/build.py index f96111b9d..a4c4a5e92 100644 --- a/winbuild/build.py +++ b/winbuild/build.py @@ -109,24 +109,14 @@ def main(op): print ("Compiled %s: %s" % (version, status)) def run_one(op): - py = os.environ['PYTHON'] - - py_version = '27' - for k,v in pythons.items(): - if k in py: - compiler_version = v - py_version = k - break - - bit = 32 - if '64' in py: - bit = 64 - py_version = '%s-x64' % py_version + + compiler = compiler_fromEnv() + py_version = pyversion_fromEnv() run_script((py_version, "\n".join([header(op), - build_one(py_version, compilers[(compiler_version, bit)]), + build_one(py_version, compiler), footer()]) )) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index 36876c488..8c782853e 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -250,7 +250,7 @@ def add_compiler(compiler): mkdirs() -fetch_libs() +#fetch_libs() #extract_binlib() script = [header()] #, cp_tk()] @@ -259,20 +259,9 @@ script = [header()] #, cp_tk()] # add_compiler(compiler) if 'PYTHON' in os.environ: - bit = 32 - if '64' in os.environ['PYTHON']: - bit = 64 - break - - ver = 7 - for k,v in pythons: - if k in os.environ['PYTHON']: - ver = v - break - - add_compiler(compilers[(ver,bit)]) + add_compiler(compiler_fromEnv()) else: - compiler = compilers[(7,64)] + add_compiler(compilers[(7,64)]) with open('build_deps.cmd', 'w') as f: f.write("\n".join(script)) diff --git a/winbuild/config.py b/winbuild/config.py index 26f81d84d..7ac50f209 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -1,3 +1,5 @@ +import os + SF_MIRROR = 'http://hivelocity.dl.sourceforge.net' SF_MIRROR = 'http://iweb.dl.sourceforge.net' @@ -102,3 +104,33 @@ compilers = { (7,64): { }, } + + +def pyversion_fromEnv(): + py = os.environ['PYTHON'] + + py_version = '27' + for k,v in pythons.items(): + if k in py: + py_version = k + break + + if '64' in py: + py_version = '%s-x64' % py_version + + return py_version + + +def compiler_fromEnv(): + py = os.environ['PYTHON'] + + for k,v in pythons.items(): + if k in py: + compiler_version = v + break + + bit = 32 + if '64' in py: + bit = 64 + + return compilers[(compiler_version, bit)] From ee71e8a80ef9e8631a57ebf82ff391dafa69cef0 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 11 Jun 2015 13:40:46 -0700 Subject: [PATCH 0325/1037] sigh --- winbuild/build_dep.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index 8c782853e..166dd8f85 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -250,7 +250,7 @@ def add_compiler(compiler): mkdirs() -#fetch_libs() +fetch_libs() #extract_binlib() script = [header()] #, cp_tk()] From 07eea7be525fc5737784dbf2f60030a4ff575af8 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 11 Jun 2015 20:35:21 -0700 Subject: [PATCH 0326/1037] updated freetype, working ft builds on vc2010 --- winbuild/build_dep.py | 23 +++++++++++++++++++---- winbuild/config.py | 15 ++++++++++----- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index 166dd8f85..55a09c995 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -206,8 +206,23 @@ endlocal """ % compiler - def msbuild_freetype(compiler): + if compiler['env_version'] == 'v7.1': + return msbuild_freetype_71(compiler) + return msbuild_freetype_70(compiler) + +def msbuild_freetype_71(compiler): + return r""" +rem Build freetype +setlocal +rd /S /Q %%FREETYPE%%\objs +%%MSBUILD%% %%FREETYPE%%\builds\windows\vc%(vc_version)s\freetype.sln /t:Clean;Build /p:Configuration="Release" /p:Platform=%(platform)s /m +xcopy /Y /E /Q %%FREETYPE%%\include %%INCLIB%% +copy /Y /B %%FREETYPE%%\objs\vc%(vc_version)s\%(platform)s\*.lib %%INCLIB%%\freetype.lib +endlocal +""" %compiler + +def msbuild_freetype_70(compiler): return r""" rem Build freetype setlocal @@ -255,13 +270,13 @@ fetch_libs() script = [header()] #, cp_tk()] -#for compiler in compilers.values(): -# add_compiler(compiler) if 'PYTHON' in os.environ: add_compiler(compiler_fromEnv()) else: - add_compiler(compilers[(7,64)]) + for compiler in compilers.values(): + add_compiler(compiler) + #add_compiler(compilers[(7,32)]) with open('build_deps.cmd', 'w') as f: f.write("\n".join(script)) diff --git a/winbuild/config.py b/winbuild/config.py index 7ac50f209..9c281ee51 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -26,12 +26,17 @@ libs = { 'zlib':{ 'hash': 'md5:dd70349cedb3981371686e1c9b89a7f9', # not found - generated by wiredfool 'dir': 'tiff-4.0.3', }, - 'freetype':{ - 'url':'http://download.savannah.gnu.org/releases/freetype/ft253.zip', - 'hash': 'md5:b3858f7e69740ac04ef53366aeb172bc', # not found - generated by wiredfool - 'dir': 'freetype-2.5.3', +# 'freetype':{ +# 'url':'http://download.savannah.gnu.org/releases/freetype/ft253.zip', +# 'hash': 'md5:b3858f7e69740ac04ef53366aeb172bc', # not found - generated by wiredfool +# 'dir': 'freetype-2.5.3', +# }, +'freetype':{ + 'url': 'http://download.savannah.gnu.org/releases/freetype/freetype-2.6.tar.gz', + 'hash':'md5:1d733ea6c1b7b3df38169fbdbec47d2b', + 'dir': 'freetype-2.6', }, - 'lcms':{ +'lcms':{ 'url':SF_MIRROR+'/project/lcms/lcms/2.6/lcms2-2.6.zip', 'hash': 'sha1:eea25f001246fa2e6b242ac456cecff7483cf061', 'dir': 'lcms2-2.6', From 0347bcc91d0385c90cb357dc3584c370d61f975e Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 12 Jun 2015 09:56:12 -0700 Subject: [PATCH 0327/1037] Tests that faill on appveyor CI, but pass on a real machine --- Tests/helper.py | 2 ++ Tests/test_imagegrab.py | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Tests/helper.py b/Tests/helper.py index 1255b1819..be696b0d7 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -233,6 +233,8 @@ def netpbm_available(): def imagemagick_available(): return IMCONVERT and command_succeeds([IMCONVERT, '-version']) +def onAppveyor(): + return 'APPVEYOR' in os.environ if sys.platform == 'win32': IMCONVERT = os.environ.get('MAGICK_HOME', '') diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py index ea6b499b2..03b026f0d 100644 --- a/Tests/test_imagegrab.py +++ b/Tests/test_imagegrab.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase, onAppveyor import sys @@ -7,10 +7,12 @@ try: class TestImageGrab(PillowTestCase): + @unittest.skipIf(onAppveyor()) def test_grab(self): im = ImageGrab.grab() self.assert_image(im, im.mode, im.size) + @unittest.skipIf(onAppveyor()) def test_grab2(self): im = ImageGrab.grab() self.assert_image(im, im.mode, im.size) From 87d5c8a506a1c130b6a2c373140aa3db4a24f80f Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 12 Jun 2015 10:17:04 -0700 Subject: [PATCH 0328/1037] added explanation --- Tests/test_imagegrab.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py index 03b026f0d..763d95488 100644 --- a/Tests/test_imagegrab.py +++ b/Tests/test_imagegrab.py @@ -7,12 +7,12 @@ try: class TestImageGrab(PillowTestCase): - @unittest.skipIf(onAppveyor()) + @unittest.skipIf(onAppveyor(), "Test fails on appveyor") def test_grab(self): im = ImageGrab.grab() self.assert_image(im, im.mode, im.size) - @unittest.skipIf(onAppveyor()) + @unittest.skipIf(onAppveyor(), "Test fails on appveyor") def test_grab2(self): im = ImageGrab.grab() self.assert_image(im, im.mode, im.size) From 1bdb3653d00eb4c1f265a3eb26a2d9af6490ccda Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 15 Jun 2015 17:49:25 -0700 Subject: [PATCH 0329/1037] delegate to env variable for x64 extension, is -x64 on appveyor, has been x64 elsewhere --- winbuild/build.py | 4 ++-- winbuild/config.py | 3 ++- winbuild/test.py | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/winbuild/build.py b/winbuild/build.py index a4c4a5e92..38bcb8310 100644 --- a/winbuild/build.py +++ b/winbuild/build.py @@ -10,7 +10,7 @@ from config import * def setup_vms(): ret = [] for py in pythons.keys(): - for arch in ('', '-x64'): + for arch in ('', X64_EXT): ret.append("virtualenv -p c:/Python%s%s/python.exe --clear %s%s%s" % (py, arch, VIRT_BASE, py, arch)) ret.append("%s%s%s\Scripts\pip.exe install nose" % @@ -96,7 +96,7 @@ def main(op): compilers[(compiler_version, 32)]), footer()]))) - scripts.append(("%sx64" % py_version, + scripts.append(("%s%s" % (py_version, X64_EXT), "\n".join([header(op), build_one("%sx64" %py_version, compilers[(compiler_version, 64)]), diff --git a/winbuild/config.py b/winbuild/config.py index 9c281ee51..b237703f3 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -10,6 +10,7 @@ pythons = {#'26':7, '34':7.1} VIRT_BASE = "c:/vp/" +X64_EXT = os.environ.get('X64_EXT',"x64") libs = { 'zlib':{ 'url':'http://zlib.net/zlib128.zip', @@ -121,7 +122,7 @@ def pyversion_fromEnv(): break if '64' in py: - py_version = '%s-x64' % py_version + py_version = '%s%s' % (py_version, X64_EXT) return py_version diff --git a/winbuild/test.py b/winbuild/test.py index 229b724cb..fc693d8d8 100644 --- a/winbuild/test.py +++ b/winbuild/test.py @@ -31,7 +31,7 @@ if __name__=='__main__': os.chdir('..') pool = multiprocessing.Pool() matrix = [(python, architecture) for python in pythons - for architecture in ('', 'x64')] + for architecture in ('', X64_EXT)] results = pool.map(test_one, matrix) From 13740da14ff213289be97bf9bd48526c7f52f351 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 15 Jun 2015 17:56:24 -0700 Subject: [PATCH 0330/1037] added appveyor.yml file instead of using online gui config --- appveyor.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000..e37b18a98 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,30 @@ +version: 2.9.pre.{build} +branches: + only: + - winbuild +shallow_clone: true +clone_folder: c:\pillow +init: +- ECHO %PYTHON% +environment: + X64_EXT: -x64 + matrix: + - PYTHON: C:/Python27-x64 + - PYTHON: C:/Python34 + - PYTHON: C:/Python27 + - PYTHON: C:/Python34-x64 +install: +- git clone https://github.com/wiredfool/pillow-depends.git c:\pillow-depends +- xcopy c:\pillow-depends\*.zip c:\pillow\winbuild\ +- xcopy c:\pillow-depends\*.tar.gz c:\pillow\winbuild\ +- cd c:\pillow\winbuild\ +- c:\python34\python.exe c:\pillow\winbuild\build_dep.py +- c:\pillow\winbuild\build_deps.cmd +build_script: +- '%PYTHON%\python.exe c:\pillow\winbuild\build.py' +- cd c:\pillow +- '%PYTHON%\python.exe selftest.py --installed' +test_script: +- cd c:\pillow +- '%PYTHON%\Scripts\pip.exe install nose' +- '%PYTHON%\python.exe test-installed.py' \ No newline at end of file From ccf9eb5628618d70764a1ad6d144bf01917d748e Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 15 Jun 2015 22:25:48 -0700 Subject: [PATCH 0331/1037] testing obsolescence --- winbuild/{ => .obsolete}/build.cmd | 0 winbuild/{ => .obsolete}/build_dep.cmd | 0 winbuild/{ => .obsolete}/build_deps.cmd | 0 winbuild/{ => .obsolete}/build_openjpeg.cmd | 0 winbuild/{ => .obsolete}/build_pillow_26.cmd | 0 winbuild/{ => .obsolete}/build_pillow_26x64.cmd | 0 winbuild/{ => .obsolete}/build_pillow_27.cmd | 0 winbuild/{ => .obsolete}/build_pillow_27x64.cmd | 0 winbuild/{ => .obsolete}/build_pillow_32.cmd | 0 winbuild/{ => .obsolete}/build_pillow_32x64.cmd | 0 winbuild/{ => .obsolete}/build_pillow_33.cmd | 0 winbuild/{ => .obsolete}/build_pillow_33x64.cmd | 0 winbuild/{ => .obsolete}/build_pillow_34.cmd | 0 winbuild/{ => .obsolete}/build_pillow_34x64.cmd | 0 winbuild/{ => .obsolete}/build_pillow_virtualenvs.cmd | 0 winbuild/{ => .obsolete}/jpeg.cmd | 0 winbuild/{ => .obsolete}/lcms.cmd | 0 winbuild/{ => .obsolete}/t.py | 0 winbuild/{ => .obsolete}/tiff.cmd | 0 19 files changed, 0 insertions(+), 0 deletions(-) rename winbuild/{ => .obsolete}/build.cmd (100%) rename winbuild/{ => .obsolete}/build_dep.cmd (100%) rename winbuild/{ => .obsolete}/build_deps.cmd (100%) rename winbuild/{ => .obsolete}/build_openjpeg.cmd (100%) rename winbuild/{ => .obsolete}/build_pillow_26.cmd (100%) rename winbuild/{ => .obsolete}/build_pillow_26x64.cmd (100%) rename winbuild/{ => .obsolete}/build_pillow_27.cmd (100%) rename winbuild/{ => .obsolete}/build_pillow_27x64.cmd (100%) rename winbuild/{ => .obsolete}/build_pillow_32.cmd (100%) rename winbuild/{ => .obsolete}/build_pillow_32x64.cmd (100%) rename winbuild/{ => .obsolete}/build_pillow_33.cmd (100%) rename winbuild/{ => .obsolete}/build_pillow_33x64.cmd (100%) rename winbuild/{ => .obsolete}/build_pillow_34.cmd (100%) rename winbuild/{ => .obsolete}/build_pillow_34x64.cmd (100%) rename winbuild/{ => .obsolete}/build_pillow_virtualenvs.cmd (100%) rename winbuild/{ => .obsolete}/jpeg.cmd (100%) rename winbuild/{ => .obsolete}/lcms.cmd (100%) rename winbuild/{ => .obsolete}/t.py (100%) rename winbuild/{ => .obsolete}/tiff.cmd (100%) diff --git a/winbuild/build.cmd b/winbuild/.obsolete/build.cmd similarity index 100% rename from winbuild/build.cmd rename to winbuild/.obsolete/build.cmd diff --git a/winbuild/build_dep.cmd b/winbuild/.obsolete/build_dep.cmd similarity index 100% rename from winbuild/build_dep.cmd rename to winbuild/.obsolete/build_dep.cmd diff --git a/winbuild/build_deps.cmd b/winbuild/.obsolete/build_deps.cmd similarity index 100% rename from winbuild/build_deps.cmd rename to winbuild/.obsolete/build_deps.cmd diff --git a/winbuild/build_openjpeg.cmd b/winbuild/.obsolete/build_openjpeg.cmd similarity index 100% rename from winbuild/build_openjpeg.cmd rename to winbuild/.obsolete/build_openjpeg.cmd diff --git a/winbuild/build_pillow_26.cmd b/winbuild/.obsolete/build_pillow_26.cmd similarity index 100% rename from winbuild/build_pillow_26.cmd rename to winbuild/.obsolete/build_pillow_26.cmd diff --git a/winbuild/build_pillow_26x64.cmd b/winbuild/.obsolete/build_pillow_26x64.cmd similarity index 100% rename from winbuild/build_pillow_26x64.cmd rename to winbuild/.obsolete/build_pillow_26x64.cmd diff --git a/winbuild/build_pillow_27.cmd b/winbuild/.obsolete/build_pillow_27.cmd similarity index 100% rename from winbuild/build_pillow_27.cmd rename to winbuild/.obsolete/build_pillow_27.cmd diff --git a/winbuild/build_pillow_27x64.cmd b/winbuild/.obsolete/build_pillow_27x64.cmd similarity index 100% rename from winbuild/build_pillow_27x64.cmd rename to winbuild/.obsolete/build_pillow_27x64.cmd diff --git a/winbuild/build_pillow_32.cmd b/winbuild/.obsolete/build_pillow_32.cmd similarity index 100% rename from winbuild/build_pillow_32.cmd rename to winbuild/.obsolete/build_pillow_32.cmd diff --git a/winbuild/build_pillow_32x64.cmd b/winbuild/.obsolete/build_pillow_32x64.cmd similarity index 100% rename from winbuild/build_pillow_32x64.cmd rename to winbuild/.obsolete/build_pillow_32x64.cmd diff --git a/winbuild/build_pillow_33.cmd b/winbuild/.obsolete/build_pillow_33.cmd similarity index 100% rename from winbuild/build_pillow_33.cmd rename to winbuild/.obsolete/build_pillow_33.cmd diff --git a/winbuild/build_pillow_33x64.cmd b/winbuild/.obsolete/build_pillow_33x64.cmd similarity index 100% rename from winbuild/build_pillow_33x64.cmd rename to winbuild/.obsolete/build_pillow_33x64.cmd diff --git a/winbuild/build_pillow_34.cmd b/winbuild/.obsolete/build_pillow_34.cmd similarity index 100% rename from winbuild/build_pillow_34.cmd rename to winbuild/.obsolete/build_pillow_34.cmd diff --git a/winbuild/build_pillow_34x64.cmd b/winbuild/.obsolete/build_pillow_34x64.cmd similarity index 100% rename from winbuild/build_pillow_34x64.cmd rename to winbuild/.obsolete/build_pillow_34x64.cmd diff --git a/winbuild/build_pillow_virtualenvs.cmd b/winbuild/.obsolete/build_pillow_virtualenvs.cmd similarity index 100% rename from winbuild/build_pillow_virtualenvs.cmd rename to winbuild/.obsolete/build_pillow_virtualenvs.cmd diff --git a/winbuild/jpeg.cmd b/winbuild/.obsolete/jpeg.cmd similarity index 100% rename from winbuild/jpeg.cmd rename to winbuild/.obsolete/jpeg.cmd diff --git a/winbuild/lcms.cmd b/winbuild/.obsolete/lcms.cmd similarity index 100% rename from winbuild/lcms.cmd rename to winbuild/.obsolete/lcms.cmd diff --git a/winbuild/t.py b/winbuild/.obsolete/t.py similarity index 100% rename from winbuild/t.py rename to winbuild/.obsolete/t.py diff --git a/winbuild/tiff.cmd b/winbuild/.obsolete/tiff.cmd similarity index 100% rename from winbuild/tiff.cmd rename to winbuild/.obsolete/tiff.cmd From d708fd190b568e5212c424420e8c2b782837bcb2 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 16 Jun 2015 15:03:58 +0300 Subject: [PATCH 0332/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 0b92b1c24..fbe6ab1a6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Added background saving to GifImagePlugin #1273 + [radarhere] + - Provide n_frames attribute to multi-frame formats #1261 [anntzer, radarhere] From ac50350f3dcc15c7c04e722f2bb7b789f564acd4 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 16 Jun 2015 09:24:51 -0700 Subject: [PATCH 0333/1037] fix pythonpath for virtualenvs --- winbuild/build.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/winbuild/build.py b/winbuild/build.py index 38bcb8310..59db83416 100644 --- a/winbuild/build.py +++ b/winbuild/build.py @@ -62,6 +62,10 @@ def build_one(py_ver, compiler): # UNDONE virtual envs if we're not running on appveyor args = {} args.update(compiler) + if 'PYTHON' in os.environ: + args['python_path'] = "%PYTHON%" + else: + args['python_path'] = "%s%s\\Scripts" % (VIRT_BASE, py_ver) args['py_ver'] = py_ver return r""" setlocal EnableDelayedExpansion @@ -72,7 +76,7 @@ set INCLUDE=%%INCLUDE%%;%%INCLIB%%\%(inc_dir)s;%%INCLIB%%\tcl85\include setlocal set LIB=%%LIB%%;C:\Python%(py_ver)s\tcl -call %%PYTHON%%\python.exe setup.py %%BLDOPT%% +call %(python_path)s\python.exe setup.py %%BLDOPT%% endlocal endlocal From 61312d0a52e23320f79c145a49bf492ccdf364b4 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 16 Jun 2015 09:25:36 -0700 Subject: [PATCH 0334/1037] race condition in font.png, single thread master tests. Add return code, clarify output --- winbuild/test.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/winbuild/test.py b/winbuild/test.py index fc693d8d8..69ab7e427 100644 --- a/winbuild/test.py +++ b/winbuild/test.py @@ -1,6 +1,9 @@ #!/usr/bin/env python3 -import subprocess, os, multiprocessing, glob +import subprocess +import os +import glob +import sys from config import * @@ -29,17 +32,13 @@ def test_one(params): if __name__=='__main__': os.chdir('..') - pool = multiprocessing.Pool() matrix = [(python, architecture) for python in pythons for architecture in ('', X64_EXT)] - results = pool.map(test_one, matrix) + results = map(test_one, matrix) for (python, architecture, status, trace) in results: - print ("%s%s: %s" % (python, architecture, status)) + print ("%s%s: %s" % (python, architecture, status and 'ERR' or 'PASS')) - - - - - + res = all(status for (python, architecture, status, trace) in results) + sys.exit(res) From 9e0242226a3d266e29b91d50d30d43a51c257b22 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 16 Jun 2015 09:39:08 -0700 Subject: [PATCH 0335/1037] Updated Readme -- working locally and on appveyor --- winbuild/README.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/winbuild/README.md b/winbuild/README.md index dd70e5b0c..213e9bda4 100644 --- a/winbuild/README.md +++ b/winbuild/README.md @@ -5,9 +5,14 @@ For more extensive info, see the windows build instructions `docs/build.rst`. * See https://github.com/python-imaging/Pillow/issues/553#issuecomment-37877416 and https://github.com/matplotlib/matplotlib/issues/1717#issuecomment-13343859 -* Works best with Python 3.4, due to virtualenv and pip batteries included. -* Check config.py for virtual env paths. -* `python get_pythons.py` downloads all the python releases, and their signatures. Install in `c:\PythonXX[x64]\`. -* `python build_dep.py` downloads and builds all the dependencies, in 32 and 64 bit versions, and with both compiler versions. +* Works best with Python 3.4, due to virtualenv and pip batteries included. Python3+ required for fetch command. +* Check config.py for virtual env paths, suffix for 64 bit releases. Defaults to `x64`, set `X64_EXT` to change. +* When running in CI with one python per invocation, set the `PYTHON` env variable to the python folder. (e.g. `PYTHON`=`c:\Python27\`) This overrides the matrix in config.py and will just build and test for the speciic python. +* `python get_pythons.py` downloads all the python releases, and their signatures. (Manually) Install in `c:\PythonXX[x64]\`. +* `python build_dep.py` downloads and creates a build script for all the dependencies, in 32 and 64 bit versions, and with both compiler versions. +* (in powershell) `build_deps.cmd` invokes the dependency build. * `python build.py --clean` makes pillow for the matrix of pythons. -* `python test.py` runs the tests on pillow in all the virtual envs. \ No newline at end of file +* `python test.py` runs the tests on pillow in all the virtual envs. +* Currently working with zlib, libjpeg, freetype, and libtiff on Python 2.7, 3.3, and 3.4, both 32 and 64 bit, on a local win7 pro machine and appveyor.com (3.3 untested there) +* Webp is built, not detected. +* LCMS and OpenJpeg are not building. \ No newline at end of file From fe18b039ca9e11c6deed8f21a9e5eca815badbb7 Mon Sep 17 00:00:00 2001 From: Andriy Sokolovskiy Date: Tue, 16 Jun 2015 20:33:27 +0300 Subject: [PATCH 0336/1037] Register MIME type for BMP --- PIL/BmpImagePlugin.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index 30ca10971..793bd8edd 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -279,3 +279,5 @@ Image.register_open(BmpImageFile.format, BmpImageFile, _accept) Image.register_save(BmpImageFile.format, _save) Image.register_extension(BmpImageFile.format, ".bmp") + +Image.register_mime(BmpImageFile.format, "image/bmp") From 2ff332e800908a74780dfd29467843c276682cc1 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 16 Jun 2015 11:59:25 -0700 Subject: [PATCH 0337/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index fbe6ab1a6..e661f14ea 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,17 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Adjusted ImageQt use of unicode() for 2/3 compatibility #1218 + [radarhere] + +- Identify XBM file created with filename including underscore #1230 (fixes #1229) + [hugovk] + +- Copy image when saving in GifImagePlugin #1231 (fixes #718) + [radarhere] + +- Removed support for FreeType 2.0 #1247 + [radarhere] - Added background saving to GifImagePlugin #1273 [radarhere] From cedb226ef2ae08d396238f050c4e6dcbb10a9f81 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 16 Jun 2015 13:29:30 -0700 Subject: [PATCH 0338/1037] Style nits --- docs/build.rst | 22 +++++++++++----------- setup.py | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/build.rst b/docs/build.rst index 577c218d9..5125e39b3 100644 --- a/docs/build.rst +++ b/docs/build.rst @@ -7,8 +7,8 @@ Building Pillow on Windows This page will describe a build setup to build Pillow against the supported python versions in 32 and 64 bit modes, using freely availble Microsoft compilers. This has been developed and tested -against 64bit Windows 7 Professional and a bare Windows Server 2012 -64bit RTM version on Amazon EC2. +against 64-bit Windows 7 Professional and Windows Server 2012 +64-bit version on Amazon EC2. Prerequsites ------------ @@ -17,25 +17,25 @@ Extra Build Helpers ^^^^^^^^^^^^^^^^^^^ * Powershell (available by default on Windows Server) -* Github client (provides git+bash shell) +* GitHub client (provides git+bash shell) Optional: -* GPG (for checking signatures) (UNDONE -- python signature checking) +* GPG (for checking signatures) (UNDONE -- Python signature checking) Pythons ^^^^^^^ -The build routines expect Python to be installed at C:\PythonXX for 32 -bit versions or C:\PythonXXx64 for the 64 bit versions. +The build routines expect Python to be installed at C:\PythonXX for +32-bit versions or C:\PythonXXx64 for the 64-bit versions. -Download Python 3.4.0, install it, and add it to the path. This is the +Download Python 3.4, install it, and add it to the path. This is the Python that we will use to bootstrap the build process. (The download routines are using 3.2+ features, and installing 3.4 gives us pip and virtualenv as well, reducing the number of packages that we need to install.) -Download the rest of the pythons by opening a command window, changing +Download the rest of the Pythons by opening a command window, changing to the `winbuild` directory, and running `python get_pythons.py`. @@ -43,7 +43,7 @@ UNDONE -- gpg verify the signatures (note that we can download from https) Run each installer and set the proper path to the installation. Don't -set any of them as the default python, or add them to the path. +set any of them as the default Python, or add them to the path. Compilers @@ -70,7 +70,7 @@ a command window, change directory into `winbuild` and run `python build_dep.py`. This will download libjpeg, libtiff, libz, and freetype. It will then -compile 32 and 64 bit versions of the libraries, with both versions of +compile 32 and 64-bit versions of the libraries, with both versions of the compilers. UNDONE -- lcms fails. @@ -81,7 +81,7 @@ Building Pillow Once the dependencies are built, run `python build.py --clean` to build and install Pillow in virtualenvs for each python -build. `build.py --dist` will build windows installers instead of +build. `build.py --dist` will build Windows installers instead of installing into virtualenvs. UNDONE -- suppressed output, what about failures. diff --git a/setup.py b/setup.py index d5c085552..6ee559f5b 100644 --- a/setup.py +++ b/setup.py @@ -467,7 +467,7 @@ class pil_build_ext(build_ext): if _find_library_file(self, "lcms2"): feature.lcms = "lcms2" elif _find_library_file(self, "lcms2_static"): - #alternate windows name. + #alternate Windows name. feature.lcms = "lcms2_static" if _tkinter and _find_include_file(self, "tk.h"): From 159b01f54b4bd61d05652c2caa9c8018a3408348 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 16 Jun 2015 13:29:41 -0700 Subject: [PATCH 0339/1037] fix imports --- winbuild/fixproj.py | 3 ++- winbuild/get_pythons.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/winbuild/fixproj.py b/winbuild/fixproj.py index e52de707f..9b5203fb8 100644 --- a/winbuild/fixproj.py +++ b/winbuild/fixproj.py @@ -1,4 +1,5 @@ -import sys, os +import sys + with open(sys.argv[1], 'r') as fd: content = '\n'.join(line.strip() for line in fd if line.strip()) if len(sys.argv) == 3: diff --git a/winbuild/get_pythons.py b/winbuild/get_pythons.py index fcce5fe3e..71988301d 100644 --- a/winbuild/get_pythons.py +++ b/winbuild/get_pythons.py @@ -1,4 +1,5 @@ from fetch import fetch +import os if __name__=='__main__': for version in ['2.6.5', '2.7.6', '3.2.5', '3.3.5', '3.4.0']: From f793cb74eb19e624dd2208e4df93a27060dbe485 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 16 Jun 2015 13:30:57 -0700 Subject: [PATCH 0340/1037] removed obsolete --- winbuild/.obsolete/build.cmd | 72 ------ winbuild/.obsolete/build_dep.cmd | 222 ------------------ winbuild/.obsolete/build_deps.cmd | 105 --------- winbuild/.obsolete/build_openjpeg.cmd | 36 --- winbuild/.obsolete/build_pillow_26.cmd | 23 -- winbuild/.obsolete/build_pillow_26x64.cmd | 23 -- winbuild/.obsolete/build_pillow_27.cmd | 23 -- winbuild/.obsolete/build_pillow_27x64.cmd | 23 -- winbuild/.obsolete/build_pillow_32.cmd | 23 -- winbuild/.obsolete/build_pillow_32x64.cmd | 23 -- winbuild/.obsolete/build_pillow_33.cmd | 23 -- winbuild/.obsolete/build_pillow_33x64.cmd | 23 -- winbuild/.obsolete/build_pillow_34.cmd | 23 -- winbuild/.obsolete/build_pillow_34x64.cmd | 23 -- .../.obsolete/build_pillow_virtualenvs.cmd | 10 - winbuild/.obsolete/jpeg.cmd | 37 --- winbuild/.obsolete/lcms.cmd | 41 ---- winbuild/.obsolete/t.py | 3 - winbuild/.obsolete/tiff.cmd | 39 --- 19 files changed, 795 deletions(-) delete mode 100644 winbuild/.obsolete/build.cmd delete mode 100644 winbuild/.obsolete/build_dep.cmd delete mode 100644 winbuild/.obsolete/build_deps.cmd delete mode 100644 winbuild/.obsolete/build_openjpeg.cmd delete mode 100644 winbuild/.obsolete/build_pillow_26.cmd delete mode 100644 winbuild/.obsolete/build_pillow_26x64.cmd delete mode 100644 winbuild/.obsolete/build_pillow_27.cmd delete mode 100644 winbuild/.obsolete/build_pillow_27x64.cmd delete mode 100644 winbuild/.obsolete/build_pillow_32.cmd delete mode 100644 winbuild/.obsolete/build_pillow_32x64.cmd delete mode 100644 winbuild/.obsolete/build_pillow_33.cmd delete mode 100644 winbuild/.obsolete/build_pillow_33x64.cmd delete mode 100644 winbuild/.obsolete/build_pillow_34.cmd delete mode 100644 winbuild/.obsolete/build_pillow_34x64.cmd delete mode 100644 winbuild/.obsolete/build_pillow_virtualenvs.cmd delete mode 100644 winbuild/.obsolete/jpeg.cmd delete mode 100644 winbuild/.obsolete/lcms.cmd delete mode 100644 winbuild/.obsolete/t.py delete mode 100644 winbuild/.obsolete/tiff.cmd diff --git a/winbuild/.obsolete/build.cmd b/winbuild/.obsolete/build.cmd deleted file mode 100644 index cd54fb2b4..000000000 --- a/winbuild/.obsolete/build.cmd +++ /dev/null @@ -1,72 +0,0 @@ -rem @echo off -rem Build Pillow -rem for Python 2.6, 2.7, 3.2, and 3.3, 32 and 64 bit -rem using Windows SDK 7.0 and 7.1 - -setlocal -rem Adjust the following if necessary -set MPLSRC=%~dp0\.. -set INCLIB=%~dp0\depends -rem set BLDOPT=bdist_wininst --user-access-control=auto -set BLDOPT=install -cd /D %MPLSRC% - -rem Set TkAgg as default backend -echo [rc_options] > setup.cfg -echo backend = TkAgg >> setup.cfg -echo [gui_support] >> setup.cfg -echo tkagg = True >> setup.cfg - -rem Using Windows SDK 7.0 -rem "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Setup\WindowsSdkVer.exe" -q -version:v7.0 - -rem Build for 64 bit Python 2.6, 2.7, 3.2 -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x64 /vista -set DISTUTILS_USE_SDK=1 -set LIB=%LIB%;%INCLIB%\msvcr90-x64 -set INCLUDE=%INCLUDE%;%INCLIB%\msvcr90-x64;%INCLIB%\tcl85\include - -setlocal -set LIB=%LIB%;C:\Python27x64\tcl -rd /q /s build -call z:\vpy27x64\Scripts\activate.bat -call python.exe setup.py %BLDOPT% -endlocal - -endlocal - -exit - -rem Using Windows SDK 7.1 -rem "%ProgramFiles%\Microsoft SDKs\Windows\v7.1\Setup\WindowsSdkVer.exe" -q -version:v7.1 - -rem Build for 32 bit Python 3.3 -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" /Release /x86 /xp -set DISTUTILS_USE_SDK=1 -set LIB=%LIB%;%INCLIB%\msvcr100-x32 -set INCLUDE=%INCLUDE%;%INCLIB%\msvcr100-x32;%INCLIB%\tcl85\include -setlocal -set LIB=%LIB%;C:\Python33\tcl -rd /q /s build -call C:\Python33\python.exe setup.py %BLDOPT% -endlocal -endlocal - -rem Build for 64 bit Python 3.3 -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" /Release /x64 /vista -set DISTUTILS_USE_SDK=1 -set LIB=%LIB%;%INCLIB%\msvcr100-x64 -set INCLUDE=%INCLUDE%;%INCLIB%\msvcr100-x64;%INCLIB%\tcl85\include -setlocal -set LIB=%LIB%;C:\Python33x64\tcl -rd /q /s build -call C:\Python33x64\python.exe setup.py %BLDOPT% -endlocal -endlocal - -rd /q /s build -rem copy /Y /B dist\*.* %~dp0 -endlocal \ No newline at end of file diff --git a/winbuild/.obsolete/build_dep.cmd b/winbuild/.obsolete/build_dep.cmd deleted file mode 100644 index f3f256427..000000000 --- a/winbuild/.obsolete/build_dep.cmd +++ /dev/null @@ -1,222 +0,0 @@ -@echo off -rem Build Pillow Dependencies - -setlocal -set MSBUILD=C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe -set CMAKE="C:\Program Files (x86)\CMake 2.8\bin\cmake.exe" -set INCLIB=%~dp0\depends -set BUILD=%~dp0\build - -echo "Removing Directories" -rd /S /Q %INCLIB% -rd /S /Q %BUILD% - -mkdir %INCLIB% -mkdir %BUILD% - -rem Get freetype -py -3 fetch.py http://download.savannah.gnu.org/releases/freetype/ft253.zip -py -3 unzip.py ft253.zip %BUILD% -set FREETYPE=%BUILD%\freetype-2.5.3 -copy /Y /B ft253.zip %INCLIB% - -rem Get zlib -py -3 fetch.py http://zlib.net/zlib128.zip -py -3 unzip.py zlib128.zip %BUILD% -set ZLIB=%BUILD%\zlib-1.2.8 -copy /Y /B zlib128.zip %INCLIB% - -rem Get libjpeg -py -3 fetch.py http://www.ijg.org/files/jpegsr9a.zip -py -3 unzip.py jpegsr9a.zip %BUILD% -set LIBJPEG=%BUILD%\jpeg-9a -copy /Y /B jpegsr9a.zip %INCLIB% - -rem get libtiff -py -3 fetch.py ftp://ftp.remotesensing.org/pub/libtiff/tiff-4.0.3.zip -py -3 unzip.py tiff-4.0.3.zip %BUILD% -set TIFF=%BUILD%\tiff-4.0.3 -copy /Y /B tiff-4.0.3.zip %INCLIB% - -rem Get lcms2 -py -3 fetch.py http://hivelocity.dl.sourceforge.net/project/lcms/lcms/2.6/lcms2-2.6.zip -py -3 unzip.py lcms2-2.6.zip %BUILD% -set LCMS=%BUILD%\lcms2-2.6 -copy /Y /B lcms2-2.6.zip %INCLIB% - -rem Get tcl/tk - -py -3 fetch.py http://hivelocity.dl.sourceforge.net/project/tcl/Tcl/8.5.13/tcl8513-src.zip -py -3 unzip.py tcl8513-src.zip %BUILD% -copy /Y /B tcl8513-src.zip %INCLIB% -py -3 fetch.py http://hivelocity.dl.sourceforge.net/project/tcl/Tcl/8.5.13/tk8513-src.zip -py -3 unzip.py tk8513-src.zip %BUILD% -copy /Y /B tk8513-src.zip %INCLIB% - -mkdir %INCLIB%\tcl85\include\X11 -copy /Y /B %BUILD%\tcl8.5.13\generic\*.h %INCLIB%\tcl85\include\ -copy /Y /B %BUILD%\tk8.5.13\generic\*.h %INCLIB%\tcl85\include\ -copy /Y /B %BUILD%\tk8.5.13\xlib\X11\* %INCLIB%\tcl85\include\X11\ - -rem Build for VC 2008 64 bit -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x64 /vista -set INCLIB=%INCLIB%\msvcr90-x64 -mkdir %INCLIB% - -rem Build libjpeg -setlocal -cd /D %LIBJPEG% -nmake -f makefile.vc setup-vc6 -nmake -f makefile.vc clean -nmake -f makefile.vc all -copy /Y /B *.dll %INCLIB% -copy /Y /B *.lib %INCLIB% -copy /Y /B j*.h %INCLIB% -endlocal - -rem Build zlib -setlocal -cd /D %ZLIB% -nmake -f win32\Makefile.msc clean -nmake -f win32\Makefile.msc -copy /Y /B *.dll %INCLIB% -copy /Y /B *.lib %INCLIB% -copy /Y /B zlib.lib %INCLIB%\z.lib -copy /Y /B zlib.h %INCLIB% -copy /Y /B zconf.h %INCLIB% -endlocal - -rem Build libtiff -setlocal -rem do after building jpeg and zlib -copy %~dp0\nmake.opt %TIFF% - -cd /D %TIFF% -nmake -f makefile.vc clean -nmake -f makefile.vc -copy /Y /B libtiff\*.dll %INCLIB% -copy /Y /B libtiff\*.lib %INCLIB% -copy /Y /B libtiff\tiff*.h %INCLIB% -endlocal - -rem Build freetype -setlocal -py -3 %~dp0\fixproj.py %FREETYPE%\builds\windows\vc2008\freetype.sln x64 -py -3 %~dp0\fixproj.py %FREETYPE%\builds\windows\vc2008\freetype.vcproj x64 -rd /S /Q %FREETYPE%\objs -%MSBUILD% %FREETYPE%\builds\windows\vc2008\freetype.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=x64 -xcopy /E /Q %FREETYPE%\include %INCLIB% -xcopy /E /Q %FREETYPE%\objs\win32\vc2008 %INCLIB% -copy /Y /B %FREETYPE%\objs\win32\vc2008\*.lib %INCLIB%\freetype.lib -endlocal - -rem Build lcms2 -setlocal -py -3 %~dp0\fixproj.py %LCMS%\Projects\VC2008\lcms2.sln x64 -py -3 %~dp0\fixproj.py %LCMS%\Projects\VC2008\lcms2.vcproj x64 -rd /S /Q %LCMS%\objs -%MSBUILD% %LCMS%\Projects\VC2008\lcms2.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=x64 -xcopy /E /Q %LCMS%\include %INCLIB% -xcopy /E /Q %LCMS%\objs\win32\VC2008 %INCLIB% -copy /Y /B %LCMS%\objs\win32\VC2008\*.lib %INCLIB%\lcms2.lib -endlocal - -endlocal -rem UNDONE --removeme! -exit - -rem Build for VC 2008 32 bit -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x86 /xp -set INCLIB=%INCLIB%\msvcr90-x32 -mkdir %INCLIB% - -rem Build zlib -setlocal -cd /D %ZLIB% -nmake -f win32\Makefile.msc clean -nmake -f win32\Makefile.msc -copy /Y /B *.dll %INCLIB% -copy /Y /B *.lib %INCLIB% -copy /Y /B zlib.lib %INCLIB%\z.lib -copy /Y /B zlib.h %INCLIB% -copy /Y /B zconf.h %INCLIB% -endlocal - -rem Build freetype -setlocal -%~dp0\fixproj.py %FREETYPE%\builds\windows\vc2008\freetype.sln Win32 -%~dp0\fixproj.py %FREETYPE%\builds\windows\vc2008\freetype.vcproj Win32 -rd /S /Q %FREETYPE%\objs -%MSBUILD% %FREETYPE%\builds\windows\vc2008\freetype.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=Win32 -xcopy /E /Q %FREETYPE%\include %INCLIB% -xcopy /E /Q %FREETYPE%\objs\win32\vc2008 %INCLIB% -copy /Y /B %FREETYPE%\objs\win32\vc2008\*.lib %INCLIB%\freetype.lib -endlocal - -endlocal - -rem Build for VC 2010 64 bit -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" /Release /x64 /vista -set INCLIB=%INCLIB%\msvcr100-x64 -mkdir %INCLIB% - -rem Build zlib -setlocal -cd /D %ZLIB% -nmake -f win32\Makefile.msc clean -nmake -f win32\Makefile.msc -copy /Y /B *.dll %INCLIB% -copy /Y /B *.lib %INCLIB% -copy /Y /B zlib.lib %INCLIB%\z.lib -copy /Y /B zlib.h %INCLIB% -copy /Y /B zconf.h %INCLIB% -endlocal - -rem Build freetype -setlocal -py -3 %~dp0\fixproj.py %FREETYPE%\builds\windows\vc2010\freetype.sln x64 -py -3 %~dp0\fixproj.py %FREETYPE%\builds\windows\vc2010\freetype.vcxproj x64 -rd /S /Q %FREETYPE%\objs -%MSBUILD% %FREETYPE%\builds\windows\vc2010\freetype.sln /t:Clean;Build /p:Configuration="Release";Platform=x64 -xcopy /E /Q %FREETYPE%\include %INCLIB% -xcopy /E /Q %FREETYPE%\objs\win32\vc2010 %INCLIB% -copy /Y /B %FREETYPE%\objs\win32\vc2010\*.lib %INCLIB%\freetype.lib -endlocal - -endlocal - -rem Build for VC 2010 32 bit -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" /Release /x86 /xp -set INCLIB=%INCLIB%\msvcr100-x32 -mkdir %INCLIB% - -rem Build zlib -setlocal -cd /D %ZLIB% -nmake -f win32\Makefile.msc clean -nmake -f win32\Makefile.msc -copy /Y /B *.dll %INCLIB% -copy /Y /B *.lib %INCLIB% -copy /Y /B zlib.lib %INCLIB%\z.lib -copy /Y /B zlib.h %INCLIB% -copy /Y /B zconf.h %INCLIB% -endlocal - -rem Build freetype -setlocal -%~dp0\fixproj.py %FREETYPE%\builds\windows\vc2010\freetype.sln Win32 -%~dp0\fixproj.py %FREETYPE%\builds\windows\vc2010\freetype.vcxproj Win32 -rd /S /Q %FREETYPE%\objs -%MSBUILD% %FREETYPE%\builds\windows\vc2010\freetype.sln /t:Clean;Build /p:Configuration="Release";Platform=Win32 -xcopy /E /Q %FREETYPE%\include %INCLIB% -xcopy /E /Q %FREETYPE%\objs\win32\vc2010 %INCLIB% -copy /Y /B %FREETYPE%\objs\win32\vc2010\*.lib %INCLIB%\freetype.lib -endlocal - -endlocal - -endlocal \ No newline at end of file diff --git a/winbuild/.obsolete/build_deps.cmd b/winbuild/.obsolete/build_deps.cmd deleted file mode 100644 index faac4669a..000000000 --- a/winbuild/.obsolete/build_deps.cmd +++ /dev/null @@ -1,105 +0,0 @@ -setlocal -set MSBUILD=C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe -set CMAKE="C:\Program Files (x86)\CMake 2.8\bin\cmake.exe" -set INCLIB=%~dp0\depends -set BUILD=%~dp0\build -set OPENJPEG=%BUILD%\openjpeg-2.0.0 -set TIFF=%BUILD%\tiff-4.0.3 -set LCMS=%BUILD%\lcms2-2.6 -set ZLIB=%BUILD%\zlib-1.2.8 -set WEBP=%BUILD%\libwebp-0.4.0 -set FREETYPE=%BUILD%\freetype-2.5.3 -set JPEG=%BUILD%\jpeg-9a - -mkdir %INCLIB%\tcl85\include\X11 -copy /Y /B %BUILD%\tcl8.5.13\generic\*.h %INCLIB%\tcl85\include\ -copy /Y /B %BUILD%\tk8.5.13\generic\*.h %INCLIB%\tcl85\include\ -copy /Y /B %BUILD%\tk8.5.13\xlib\X11\* %INCLIB%\tcl85\include\X11\ - -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" /Release /x64 /vista -set INCLIB=%INCLIB%\msvcr10-x64 -mkdir %INCLIB% - - -rem build openjpeg -setlocal -@echo on -cd /D %OPENJPEG% -nmake -f Makefile clean -%CMAKE% -DBUILD_THIRDPARTY:BOOL=ON -G "NMake Makefiles" . -nmake -f Makefile -copy /Y /B bin/* %INCLIB% -mkdir %INCLIB%/openjpeg-2.0 -copy /Y /B src/lib/openjp2/openjpeg.h %INCLIB%/openjpeg-2.0 -copy /Y /B src/lib/openjp2/opj_stdint.h %INCLIB%/openjpeg-2.0 -endlocal - - -endlocal - -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x86 /xp -set INCLIB=%INCLIB%\msvcr90-x32 -mkdir %INCLIB% - - -rem build openjpeg -setlocal -@echo on -cd /D %OPENJPEG% -nmake -f Makefile clean -%CMAKE% -DBUILD_THIRDPARTY:BOOL=ON -G "NMake Makefiles" . -nmake -f Makefile -copy /Y /B bin/* %INCLIB% -mkdir %INCLIB%/openjpeg-2.0 -copy /Y /B src/lib/openjp2/openjpeg.h %INCLIB%/openjpeg-2.0 -copy /Y /B src/lib/openjp2/opj_stdint.h %INCLIB%/openjpeg-2.0 -endlocal - - -endlocal - -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x64 /vista -set INCLIB=%INCLIB%\msvcr90-x64 -mkdir %INCLIB% - - -rem build openjpeg -setlocal -@echo on -cd /D %OPENJPEG% -nmake -f Makefile clean -%CMAKE% -DBUILD_THIRDPARTY:BOOL=ON -G "NMake Makefiles" . -nmake -f Makefile -copy /Y /B bin/* %INCLIB% -mkdir %INCLIB%/openjpeg-2.0 -copy /Y /B src/lib/openjp2/openjpeg.h %INCLIB%/openjpeg-2.0 -copy /Y /B src/lib/openjp2/opj_stdint.h %INCLIB%/openjpeg-2.0 -endlocal - - -endlocal - -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" /Release /x86 /xp -set INCLIB=%INCLIB%\msvcr10-x32 -mkdir %INCLIB% - - -rem build openjpeg -setlocal -@echo on -cd /D %OPENJPEG% -nmake -f Makefile clean -%CMAKE% -DBUILD_THIRDPARTY:BOOL=ON -G "NMake Makefiles" . -nmake -f Makefile -copy /Y /B bin/* %INCLIB% -mkdir %INCLIB%/openjpeg-2.0 -copy /Y /B src/lib/openjp2/openjpeg.h %INCLIB%/openjpeg-2.0 -copy /Y /B src/lib/openjp2/opj_stdint.h %INCLIB%/openjpeg-2.0 -endlocal - - -endlocal diff --git a/winbuild/.obsolete/build_openjpeg.cmd b/winbuild/.obsolete/build_openjpeg.cmd deleted file mode 100644 index bf05c6c24..000000000 --- a/winbuild/.obsolete/build_openjpeg.cmd +++ /dev/null @@ -1,36 +0,0 @@ - -setlocal -set MSBUILD=C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe -set CMAKE="C:\Program Files (x86)\CMake 2.8\bin\cmake.exe" -set INCLIB=%~dp0\depends -set BUILD=%~dp0\build -set LCMS=%BUILD%\lcms2-2.6 -set ZLIB=%BUILD%\zlib-1.2.8 -set WEBP=%BUILD%\libwebp-0.4.0 -set TIFF=%BUILD%\tiff-4.0.3 -set OPENJPEG=%BUILD%\openjpeg-2.0.0 -set JPEG=%BUILD%\jpeg-9a -set FREETYPE=%BUILD%\freetype-2.5.3 - - -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" /Release /x64 /vista -set INCLIB=%INCLIB%\msvcr10-x64 -mkdir %INCLIB% - - -rem build openjpeg -setlocal -@echo on -cd /D %OPENJPEG% -%CMAKE% -DBUILD_THIRDPARTY:BOOL=ON -G "NMake Makefiles" . -nmake -f Makefile clean -nmake -f Makefile -copy /Y /B bin/* %INCLIB% -mkdir %INCLIB%/openjpeg-2.0 -copy /Y /B src/lib/openjp2/openjpeg.h %INCLIB%/openjpeg-2.0 -copy /Y /B src/lib/openjp2/opj_stdint.h %INCLIB%/openjpeg2.0 -endlocal - - -endlocal - diff --git a/winbuild/.obsolete/build_pillow_26.cmd b/winbuild/.obsolete/build_pillow_26.cmd deleted file mode 100644 index 9dd1ec791..000000000 --- a/winbuild/.obsolete/build_pillow_26.cmd +++ /dev/null @@ -1,23 +0,0 @@ - -setlocal -set MPLSRC=%~dp0\.. -set INCLIB=%~dp0\depends -set BLDOPT=install -cd /D %MPLSRC% - - -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x86 /xp -set DISTUTILS_USE_SDK=1 -set LIB=%LIB%;%INCLIB%\msvcr90-x32 -set INCLUDE=%INCLUDE%;%INCLIB%\msvcr90-x32;%INCLIB%\tcl85\include - -setlocal -set LIB=%LIB%;C:\Python26\tcl -call c:\vp\26\Scripts\python.exe setup.py %BLDOPT% -endlocal - -endlocal - -endlocal -exit diff --git a/winbuild/.obsolete/build_pillow_26x64.cmd b/winbuild/.obsolete/build_pillow_26x64.cmd deleted file mode 100644 index 450cab2cc..000000000 --- a/winbuild/.obsolete/build_pillow_26x64.cmd +++ /dev/null @@ -1,23 +0,0 @@ - -setlocal -set MPLSRC=%~dp0\.. -set INCLIB=%~dp0\depends -set BLDOPT=install -cd /D %MPLSRC% - - -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x64 /vista -set DISTUTILS_USE_SDK=1 -set LIB=%LIB%;%INCLIB%\msvcr90-x64 -set INCLUDE=%INCLUDE%;%INCLIB%\msvcr90-x64;%INCLIB%\tcl85\include - -setlocal -set LIB=%LIB%;C:\Python26x64\tcl -call c:\vp\26x64\Scripts\python.exe setup.py %BLDOPT% -endlocal - -endlocal - -endlocal -exit diff --git a/winbuild/.obsolete/build_pillow_27.cmd b/winbuild/.obsolete/build_pillow_27.cmd deleted file mode 100644 index 862555362..000000000 --- a/winbuild/.obsolete/build_pillow_27.cmd +++ /dev/null @@ -1,23 +0,0 @@ - -setlocal -set MPLSRC=%~dp0\.. -set INCLIB=%~dp0\depends -set BLDOPT=install -cd /D %MPLSRC% - - -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x86 /xp -set DISTUTILS_USE_SDK=1 -set LIB=%LIB%;%INCLIB%\msvcr90-x32 -set INCLUDE=%INCLUDE%;%INCLIB%\msvcr90-x32;%INCLIB%\tcl85\include - -setlocal -set LIB=%LIB%;C:\Python27\tcl -call c:\vp\27\Scripts\python.exe setup.py %BLDOPT% -endlocal - -endlocal - -endlocal -exit diff --git a/winbuild/.obsolete/build_pillow_27x64.cmd b/winbuild/.obsolete/build_pillow_27x64.cmd deleted file mode 100644 index 88467b838..000000000 --- a/winbuild/.obsolete/build_pillow_27x64.cmd +++ /dev/null @@ -1,23 +0,0 @@ - -setlocal -set MPLSRC=%~dp0\.. -set INCLIB=%~dp0\depends -set BLDOPT=install -cd /D %MPLSRC% - - -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x64 /vista -set DISTUTILS_USE_SDK=1 -set LIB=%LIB%;%INCLIB%\msvcr90-x64 -set INCLUDE=%INCLUDE%;%INCLIB%\msvcr90-x64;%INCLIB%\tcl85\include - -setlocal -set LIB=%LIB%;C:\Python27-x64\tcl -call c:\python27-x64\python.exe setup.py %BLDOPT% -endlocal - -endlocal - -endlocal -exit diff --git a/winbuild/.obsolete/build_pillow_32.cmd b/winbuild/.obsolete/build_pillow_32.cmd deleted file mode 100644 index a1beeb9ee..000000000 --- a/winbuild/.obsolete/build_pillow_32.cmd +++ /dev/null @@ -1,23 +0,0 @@ - -setlocal -set MPLSRC=%~dp0\.. -set INCLIB=%~dp0\depends -set BLDOPT=install -cd /D %MPLSRC% - - -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x86 /xp -set DISTUTILS_USE_SDK=1 -set LIB=%LIB%;%INCLIB%\msvcr90-x32 -set INCLUDE=%INCLUDE%;%INCLIB%\msvcr90-x32;%INCLIB%\tcl85\include - -setlocal -set LIB=%LIB%;C:\Python32\tcl -call c:\vp\32\Scripts\python.exe setup.py %BLDOPT% -endlocal - -endlocal - -endlocal -exit diff --git a/winbuild/.obsolete/build_pillow_32x64.cmd b/winbuild/.obsolete/build_pillow_32x64.cmd deleted file mode 100644 index d182838b2..000000000 --- a/winbuild/.obsolete/build_pillow_32x64.cmd +++ /dev/null @@ -1,23 +0,0 @@ - -setlocal -set MPLSRC=%~dp0\.. -set INCLIB=%~dp0\depends -set BLDOPT=install -cd /D %MPLSRC% - - -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x64 /vista -set DISTUTILS_USE_SDK=1 -set LIB=%LIB%;%INCLIB%\msvcr90-x64 -set INCLUDE=%INCLUDE%;%INCLIB%\msvcr90-x64;%INCLIB%\tcl85\include - -setlocal -set LIB=%LIB%;C:\Python32x64\tcl -call c:\vp\32x64\Scripts\python.exe setup.py %BLDOPT% -endlocal - -endlocal - -endlocal -exit diff --git a/winbuild/.obsolete/build_pillow_33.cmd b/winbuild/.obsolete/build_pillow_33.cmd deleted file mode 100644 index da660ae94..000000000 --- a/winbuild/.obsolete/build_pillow_33.cmd +++ /dev/null @@ -1,23 +0,0 @@ - -setlocal -set MPLSRC=%~dp0\.. -set INCLIB=%~dp0\depends -set BLDOPT=install -cd /D %MPLSRC% - - -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" /Release /x86 /xp -set DISTUTILS_USE_SDK=1 -set LIB=%LIB%;%INCLIB%\msvcr10-x32 -set INCLUDE=%INCLUDE%;%INCLIB%\msvcr10-x32;%INCLIB%\tcl85\include - -setlocal -set LIB=%LIB%;C:\Python33\tcl -call c:\vp\33\Scripts\python.exe setup.py %BLDOPT% -endlocal - -endlocal - -endlocal -exit diff --git a/winbuild/.obsolete/build_pillow_33x64.cmd b/winbuild/.obsolete/build_pillow_33x64.cmd deleted file mode 100644 index 17545be19..000000000 --- a/winbuild/.obsolete/build_pillow_33x64.cmd +++ /dev/null @@ -1,23 +0,0 @@ - -setlocal -set MPLSRC=%~dp0\.. -set INCLIB=%~dp0\depends -set BLDOPT=install -cd /D %MPLSRC% - - -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" /Release /x64 /vista -set DISTUTILS_USE_SDK=1 -set LIB=%LIB%;%INCLIB%\msvcr10-x64 -set INCLUDE=%INCLUDE%;%INCLIB%\msvcr10-x64;%INCLIB%\tcl85\include - -setlocal -set LIB=%LIB%;C:\Python33x64\tcl -call c:\vp\33x64\Scripts\python.exe setup.py %BLDOPT% -endlocal - -endlocal - -endlocal -exit diff --git a/winbuild/.obsolete/build_pillow_34.cmd b/winbuild/.obsolete/build_pillow_34.cmd deleted file mode 100644 index 80b6512af..000000000 --- a/winbuild/.obsolete/build_pillow_34.cmd +++ /dev/null @@ -1,23 +0,0 @@ - -setlocal -set MPLSRC=%~dp0\.. -set INCLIB=%~dp0\depends -set BLDOPT=install -cd /D %MPLSRC% - - -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" /Release /x86 /xp -set DISTUTILS_USE_SDK=1 -set LIB=%LIB%;%INCLIB%\msvcr10-x32 -set INCLUDE=%INCLUDE%;%INCLIB%\msvcr10-x32;%INCLIB%\tcl85\include - -setlocal -set LIB=%LIB%;C:\Python34\tcl -call c:\vp\34\Scripts\python.exe setup.py %BLDOPT% -endlocal - -endlocal - -endlocal -exit diff --git a/winbuild/.obsolete/build_pillow_34x64.cmd b/winbuild/.obsolete/build_pillow_34x64.cmd deleted file mode 100644 index e30e703b6..000000000 --- a/winbuild/.obsolete/build_pillow_34x64.cmd +++ /dev/null @@ -1,23 +0,0 @@ - -setlocal -set MPLSRC=%~dp0\.. -set INCLIB=%~dp0\depends -set BLDOPT=install -cd /D %MPLSRC% - - -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" /Release /x64 /vista -set DISTUTILS_USE_SDK=1 -set LIB=%LIB%;%INCLIB%\msvcr10-x64 -set INCLUDE=%INCLUDE%;%INCLIB%\msvcr10-x64;%INCLIB%\tcl85\include - -setlocal -set LIB=%LIB%;C:\Python34x64\tcl -call c:\vp\34x64\Scripts\python.exe setup.py %BLDOPT% -endlocal - -endlocal - -endlocal -exit diff --git a/winbuild/.obsolete/build_pillow_virtualenvs.cmd b/winbuild/.obsolete/build_pillow_virtualenvs.cmd deleted file mode 100644 index ed66182b3..000000000 --- a/winbuild/.obsolete/build_pillow_virtualenvs.cmd +++ /dev/null @@ -1,10 +0,0 @@ -virtualenv -p c:/Python32/python.exe --clear c:/vp/32 -virtualenv -p c:/Python32x64/python.exe --clear c:/vp/32x64 -virtualenv -p c:/Python34/python.exe --clear c:/vp/34 -virtualenv -p c:/Python34x64/python.exe --clear c:/vp/34x64 -virtualenv -p c:/Python27/python.exe --clear c:/vp/27 -virtualenv -p c:/Python27x64/python.exe --clear c:/vp/27x64 -virtualenv -p c:/Python33/python.exe --clear c:/vp/33 -virtualenv -p c:/Python33x64/python.exe --clear c:/vp/33x64 -virtualenv -p c:/Python26/python.exe --clear c:/vp/26 -virtualenv -p c:/Python26x64/python.exe --clear c:/vp/26x64 \ No newline at end of file diff --git a/winbuild/.obsolete/jpeg.cmd b/winbuild/.obsolete/jpeg.cmd deleted file mode 100644 index 4f16f31c7..000000000 --- a/winbuild/.obsolete/jpeg.cmd +++ /dev/null @@ -1,37 +0,0 @@ -setlocal -set MSBUILD=C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe -set CMAKE="C:\Program Files (x86)\CMake 2.8\bin\cmake.exe" -set INCLIB=%~dp0\depends -set BUILD=%~dp0\build - -mkdir %INCLIB% -mkdir %BUILD% - -rem Get libjpeg -py -3 fetch.py http://www.ijg.org/files/jpegsr9a.zip -py -3 unzip.py jpegsr9a.zip %BUILD% -set LIBJPEG=%BUILD%\jpeg-9a -copy /Y /B jpegsr9a.zip %INCLIB% - -rem Build for VC 2008 64 bit -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x64 /vista -set INCLIB=%INCLIB%\msvcr90-x64 -mkdir %INCLIB% - - -rem Build libjpeg -setlocal -cd /D %LIBJPEG% -nmake -f makefile.vc setup-vc6 -nmake -f makefile.vc clean -nmake -f makefile.vc all -copy /Y /B *.dll %INCLIB% -copy /Y /B *.lib %INCLIB% -copy /Y /B j*.h %INCLIB% - -endlocal - -endlocal -rem UNDONE --removeme! -exit diff --git a/winbuild/.obsolete/lcms.cmd b/winbuild/.obsolete/lcms.cmd deleted file mode 100644 index dd6852c18..000000000 --- a/winbuild/.obsolete/lcms.cmd +++ /dev/null @@ -1,41 +0,0 @@ -setlocal -set MSBUILD=C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe -set CMAKE="C:\Program Files (x86)\CMake 2.8\bin\cmake.exe" -set INCLIB=%~dp0\depends -set BUILD=%~dp0\build - -echo "Removing Directories" -rd /S /Q %INCLIB% -rd /S /Q %BUILD% - -mkdir %INCLIB% -mkdir %BUILD% - -rem Get lcms2 -py -3 fetch.py http://hivelocity.dl.sourceforge.net/project/lcms/lcms/2.6/lcms2-2.6.zip -py -3 unzip.py lcms2-2.6.zip %BUILD% -set LCMS=%BUILD%\lcms2-2.6 -copy /Y /B lcms2-2.6.zip %INCLIB% - -rem Build for VC 2008 64 bit -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x64 /vista -set INCLIB=%INCLIB%\msvcr90-x64 -mkdir %INCLIB% - -rem Build lcms2 -setlocal -py -3 %~dp0\fixproj.py %LCMS%\Projects\VC2008\lcms2.sln x64 -py -3 %~dp0\fixproj.py %LCMS%\Projects\VC2008\lcms2.vcproj x64 -rd /S /Q %LCMS%\objs -%MSBUILD% %LCMS%\Projects\VC2008\lcms2.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=x64 -xcopy /E /Q %LCMS%\include %INCLIB% -xcopy /E /Q %LCMS%\objs\win32\VC2008 %INCLIB% -copy /Y /B %LCMS%\objs\win32\VC2008\*.lib %INCLIB%\lcms2.lib -endlocal - -endlocal -rem UNDONE --removeme! -exit - -endlocal \ No newline at end of file diff --git a/winbuild/.obsolete/t.py b/winbuild/.obsolete/t.py deleted file mode 100644 index 4725df9d3..000000000 --- a/winbuild/.obsolete/t.py +++ /dev/null @@ -1,3 +0,0 @@ -import urllib.request - -urllib.request.urlretrieve('https://www.python.org/ftp/python/2.7.6/python-2.7.6.amd64.msi') diff --git a/winbuild/.obsolete/tiff.cmd b/winbuild/.obsolete/tiff.cmd deleted file mode 100644 index 1f2777528..000000000 --- a/winbuild/.obsolete/tiff.cmd +++ /dev/null @@ -1,39 +0,0 @@ -setlocal -set MSBUILD=C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe -set CMAKE="C:\Program Files (x86)\CMake 2.8\bin\cmake.exe" -set INCLIB=%~dp0\depends -set BUILD=%~dp0\build - -mkdir %INCLIB% -mkdir %BUILD% - -rem get libtiff -py -3 fetch.py ftp://ftp.remotesensing.org/pub/libtiff/tiff-4.0.3.zip -py -3 unzip.py tiff-4.0.3.zip %BUILD% -set TIFF=%BUILD%\tiff-4.0.3 -copy /Y /B tiff-4.0.3.zip %INCLIB% - -rem Build for VC 2008 64 bit -setlocal EnableDelayedExpansion -call "%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\SetEnv.Cmd" /Release /x64 /vista -set INCLIB=%INCLIB%\msvcr90-x64 -mkdir %INCLIB% - - -rem Build libtiff -setlocal -@echo on -rem do after building jpeg and zlib -copy %~dp0\nmake.opt %TIFF% - -cd /D %TIFF% -nmake -f makefile.vc clean -nmake -f makefile.vc -copy /Y /B libtiff\*.dll %INCLIB% -copy /Y /B libtiff\*.lib %INCLIB% -copy /Y /B libtiff\tiff*.h %INCLIB% -endlocal - -endlocal -rem UNDONE --removeme! -exit From 1086ba9e6ea8aa054fc56dded374449660430a89 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 16 Jun 2015 13:35:34 -0700 Subject: [PATCH 0341/1037] Function rename --- Tests/helper.py | 2 +- Tests/test_imagegrab.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/helper.py b/Tests/helper.py index be696b0d7..44071c835 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -233,7 +233,7 @@ def netpbm_available(): def imagemagick_available(): return IMCONVERT and command_succeeds([IMCONVERT, '-version']) -def onAppveyor(): +def on_appveyor(): return 'APPVEYOR' in os.environ if sys.platform == 'win32': diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py index 763d95488..2b4aa8d67 100644 --- a/Tests/test_imagegrab.py +++ b/Tests/test_imagegrab.py @@ -7,12 +7,12 @@ try: class TestImageGrab(PillowTestCase): - @unittest.skipIf(onAppveyor(), "Test fails on appveyor") + @unittest.skipIf(on_appveyor(), "Test fails on appveyor") def test_grab(self): im = ImageGrab.grab() self.assert_image(im, im.mode, im.size) - @unittest.skipIf(onAppveyor(), "Test fails on appveyor") + @unittest.skipIf(on_appveyor(), "Test fails on appveyor") def test_grab2(self): im = ImageGrab.grab() self.assert_image(im, im.mode, im.size) From 6046ef2221b535891765c409b30c3ef84531d170 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 16 Jun 2015 13:45:45 -0700 Subject: [PATCH 0342/1037] dash [ci skip] --- docs/build.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/build.rst b/docs/build.rst index 5125e39b3..bc272e657 100644 --- a/docs/build.rst +++ b/docs/build.rst @@ -5,7 +5,7 @@ Building Pillow on Windows ` should be sufficient This page will describe a build setup to build Pillow against the -supported python versions in 32 and 64 bit modes, using freely +supported python versions in 32 and 64-bit modes, using freely availble Microsoft compilers. This has been developed and tested against 64-bit Windows 7 Professional and Windows Server 2012 64-bit version on Amazon EC2. From 2605f27bd3a60c3113f52a03c25631772bbc0589 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 16 Jun 2015 14:08:10 -0700 Subject: [PATCH 0343/1037] Find all the function names --- Tests/test_imagegrab.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py index 2b4aa8d67..7d156d498 100644 --- a/Tests/test_imagegrab.py +++ b/Tests/test_imagegrab.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, onAppveyor +from helper import unittest, PillowTestCase, on_appveyor import sys From d35ecbef841ee5dd358bac7da11090df82fad799 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 17 Jun 2015 09:02:32 +1000 Subject: [PATCH 0344/1037] Fixed various typos --- docs/build.rst | 4 ++-- winbuild/nmake.opt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/build.rst b/docs/build.rst index bc272e657..d0ee35ee6 100644 --- a/docs/build.rst +++ b/docs/build.rst @@ -6,11 +6,11 @@ Building Pillow on Windows This page will describe a build setup to build Pillow against the supported python versions in 32 and 64-bit modes, using freely -availble Microsoft compilers. This has been developed and tested +available Microsoft compilers. This has been developed and tested against 64-bit Windows 7 Professional and Windows Server 2012 64-bit version on Amazon EC2. -Prerequsites +Prerequisites ------------ Extra Build Helpers diff --git a/winbuild/nmake.opt b/winbuild/nmake.opt index 2d7797ab6..b33421634 100644 --- a/winbuild/nmake.opt +++ b/winbuild/nmake.opt @@ -84,7 +84,7 @@ ZLIB_LIB = $(ZLIBDIR)/zlib.lib # # Comment out the following lines to disable strip chopping -# (whether or not to convert single-strip uncompressed images to mutiple +# (whether or not to convert single-strip uncompressed images to multiple # strips of specified size to reduce memory usage). Default strip size # is 8192 bytes, it can be configured via the STRIP_SIZE_DEFAULT parameter # From 3f2d47d6618a8f11ae4de06990c37e03de0b8c62 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 17 Jun 2015 12:27:42 +0300 Subject: [PATCH 0345/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index e661f14ea..1c3ceb534 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,10 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ + +- Register MIME type for BMP #1277 + [coldmind] + - Adjusted ImageQt use of unicode() for 2/3 compatibility #1218 [radarhere] From 4c02ae4061f6e957eb6308cf4896ef81c2dbd0b5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 18 Jun 2015 10:12:12 +1000 Subject: [PATCH 0346/1037] Fixed _binary handling in _accept methods --- PIL/DcxImagePlugin.py | 2 +- PIL/EpsImagePlugin.py | 2 +- PIL/FliImagePlugin.py | 2 +- PIL/GbrImagePlugin.py | 2 +- PIL/SgiImagePlugin.py | 2 +- PIL/SunImagePlugin.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/PIL/DcxImagePlugin.py b/PIL/DcxImagePlugin.py index 978c90e80..b3f43b4cf 100644 --- a/PIL/DcxImagePlugin.py +++ b/PIL/DcxImagePlugin.py @@ -33,7 +33,7 @@ i32 = _binary.i32le def _accept(prefix): - return i32(prefix) == MAGIC + return len(prefix) >= 4 and i32(prefix) == MAGIC ## diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py index 7b1f4c1ca..842664960 100644 --- a/PIL/EpsImagePlugin.py +++ b/PIL/EpsImagePlugin.py @@ -187,7 +187,7 @@ class PSFile(object): def _accept(prefix): - return prefix[:4] == b"%!PS" or i32(prefix) == 0xC6D3D0C5 + return prefix[:4] == b"%!PS" or (len(prefix) >= 4 and i32(prefix) == 0xC6D3D0C5) ## # Image plugin for Encapsulated Postscript. This plugin supports only diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py index 0660ddeb6..df6a4eb8e 100644 --- a/PIL/FliImagePlugin.py +++ b/PIL/FliImagePlugin.py @@ -30,7 +30,7 @@ o8 = _binary.o8 # decoder def _accept(prefix): - return i16(prefix[4:6]) in [0xAF11, 0xAF12] + return len(prefix) >= 6 and i16(prefix[4:6]) in [0xAF11, 0xAF12] ## diff --git a/PIL/GbrImagePlugin.py b/PIL/GbrImagePlugin.py index e1580e718..b512efd25 100644 --- a/PIL/GbrImagePlugin.py +++ b/PIL/GbrImagePlugin.py @@ -19,7 +19,7 @@ i32 = _binary.i32be def _accept(prefix): - return i32(prefix) >= 20 and i32(prefix[4:8]) == 1 + return len(prefix) >= 8 and i32(prefix) >= 20 and i32(prefix[4:8]) == 1 ## diff --git a/PIL/SgiImagePlugin.py b/PIL/SgiImagePlugin.py index 2b8fcd8e4..6219ecebf 100644 --- a/PIL/SgiImagePlugin.py +++ b/PIL/SgiImagePlugin.py @@ -29,7 +29,7 @@ i32 = _binary.i32be def _accept(prefix): - return i16(prefix) == 474 + return len(prefix) >= 2 and i16(prefix) == 474 ## diff --git a/PIL/SunImagePlugin.py b/PIL/SunImagePlugin.py index e0a7aa6ee..b03924ac6 100644 --- a/PIL/SunImagePlugin.py +++ b/PIL/SunImagePlugin.py @@ -27,7 +27,7 @@ i32 = _binary.i32be def _accept(prefix): - return i32(prefix) == 0x59a66a95 + return len(prefix) >= 4 and i32(prefix) == 0x59a66a95 ## From 1ee1860b52be3aa9877255854ad3f84ca834df06 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 9 Jun 2015 14:36:34 +1000 Subject: [PATCH 0347/1037] Added test for invalid image --- Tests/test_image.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Tests/test_image.py b/Tests/test_image.py index caee70fec..469045909 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -30,6 +30,15 @@ class TestImage(PillowTestCase): # self.assertRaises( # MemoryError, lambda: Image.new("L", (1000000, 1000000))) + def test_invalid_image(self): + if str is bytes: + import StringIO + im = StringIO.StringIO('') + else: + import io + im = io.BytesIO(b'') + self.assertRaises(IOError, lambda: Image.open(im)) + def test_internals(self): im = Image.new("L", (100, 100)) From 93a6f0ebcb2df2e0f18f15950163f99478fffa9d Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 17 Jun 2015 17:58:07 -0700 Subject: [PATCH 0348/1037] Update CHANGES.rst --- CHANGES.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 1c3ceb534..328c96734 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,7 +3,9 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ - +- Check prefix length in _accept methods #1267 + [radarhere] + - Register MIME type for BMP #1277 [coldmind] From a09da242fdaa5bf9114dbacab0d0b9811a1fb0a9 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 17 Jun 2015 18:19:30 -0700 Subject: [PATCH 0349/1037] Updated Changes.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 328c96734..cd4a286cf 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,9 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Fixed polygon edge drawing #1255 (fixes #1252) + [radarhere] + - Check prefix length in _accept methods #1267 [radarhere] From 2d706d74dcbedbec7c5ede1bf3a9723464b95566 Mon Sep 17 00:00:00 2001 From: Roman Inflianskas Date: Mon, 15 Sep 2014 22:24:56 +0400 Subject: [PATCH 0350/1037] add functions to convert: Image <-> QImage; Image <-> QPixmap (see #897) --- PIL/Image.py | 16 ++- PIL/ImageQt.py | 172 ++++++++++++++++++++------------ Tests/test_image_fromqimage.py | 36 +++++++ Tests/test_image_fromqpixmap.py | 36 +++++++ Tests/test_image_toqimage.py | 24 +++++ Tests/test_image_toqpixmap.py | 25 +++++ Tests/test_imageqt.py | 47 +++++---- 7 files changed, 275 insertions(+), 81 deletions(-) create mode 100644 Tests/test_image_fromqimage.py create mode 100644 Tests/test_image_fromqpixmap.py create mode 100644 Tests/test_image_toqimage.py create mode 100644 Tests/test_image_toqpixmap.py diff --git a/PIL/Image.py b/PIL/Image.py index 967a36fdb..6ae0377a3 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -101,12 +101,13 @@ except ImportError: import __builtin__ builtins = __builtin__ -from PIL import ImageMode +from PIL import ImageMode, ImageQt from PIL._binary import i8 from PIL._util import isPath from PIL._util import isStringType from PIL._util import deferred_error + import os import sys import io @@ -1936,6 +1937,14 @@ class Image(object): im = self.im.effect_spread(distance) return self._new(im) + if ImageQt.qt_is_installed: + def toqimage(self): + return ImageQt.toqimage(self) + + def toqpixmap(self): + return ImageQt.toqpixmap(self) + + # -------------------------------------------------------------------- # Lazy operations @@ -2185,6 +2194,11 @@ def fromarray(obj, mode=None): return frombuffer(mode, size, obj, "raw", rawmode, 0, 1) + +if ImageQt.qt_is_installed: + from PIL.ImageQt import fromqimage, fromqpixmap + + _fromarray_typemap = { # (shape, typestr) => mode, rawmode # first two members of shape are set to one diff --git a/PIL/ImageQt.py b/PIL/ImageQt.py index 6b7d4d66e..611019676 100644 --- a/PIL/ImageQt.py +++ b/PIL/ImageQt.py @@ -16,87 +16,133 @@ # See the README file for information on usage and redistribution. # -from PIL import Image +import PIL from PIL._util import isPath import sys -if 'PyQt4.QtGui' not in sys.modules: - try: - from PyQt5.QtGui import QImage, qRgba - except: - try: - from PyQt4.QtGui import QImage, qRgba - except: - from PySide.QtGui import QImage, qRgba +qt_is_installed = True +try: + from PyQt5.QtGui import QGuiApplication, QImage, qRgb, qRgba, QPixmap + from PyQt5.QtCore import QBuffer, QIODevice +except ImportError: + try: + from PyQt4.QtGui import QGuiApplication, QImage, qRgb, qRgba, QPixmap + from PyQt4.QtCore import QBuffer, QIODevice + except ImportError: + try: + from PySide.QtGui import QGuiApplication, QImage, qRgb, qRgba, QPixmap + from PySide.QtCore import QBuffer, QIODevice + except ImportError: + qt_is_installed = False -else: #PyQt4 is used - from PyQt4.QtGui import QImage, qRgba - -## -# (Internal) Turns an RGB color into a Qt compatible color integer. +from io import BytesIO def rgb(r, g, b, a=255): + """(Internal) Turns an RGB color into a Qt compatible color integer.""" # use qRgb to pack the colors, and then turn the resulting long # into a negative integer with the same bitpattern. return (qRgba(r, g, b, a) & 0xffffffff) -## -# An PIL image wrapper for Qt. This is a subclass of PyQt4's QImage -# class. -# -# @param im A PIL Image object, or a file name (given either as Python -# string or a PyQt string object). +# :param im A PIL Image object, or a file name (given either as Python string or a PyQt string object). -class ImageQt(QImage): +def fromqimage(im): + buffer = QBuffer() + buffer.open(QIODevice.ReadWrite) + im.save(buffer, 'ppm') + bytes_io = BytesIO() + try: + bytes_io.write(buffer.data()) + except TypeError: + # workaround for Python 2 + bytes_io.write(str(buffer.data())) + buffer.close() + bytes_io.seek(0) + return PIL.Image.open(bytes_io) - def __init__(self, im): - data = None - colortable = None +def fromqpixmap(im): + return fromqimage(im) + # buffer = QBuffer() + # buffer.open(QIODevice.ReadWrite) + # # im.save(buffer) + # # What if png doesn't support some image features like animation? + # im.save(buffer, 'ppm') + # bytes_io = BytesIO() + # bytes_io.write(buffer.data()) + # buffer.close() + # bytes_io.seek(0) + # return PIL.Image.open(bytes_io) - # handle filename, if given instead of image name - if hasattr(im, "toUtf8"): - # FIXME - is this really the best way to do this? - if str is bytes: - im = unicode(im.toUtf8(), "utf-8") - else: - im = str(im.toUtf8(), "utf-8") - if isPath(im): - im = Image.open(im) - if im.mode == "1": - format = QImage.Format_Mono - elif im.mode == "L": - format = QImage.Format_Indexed8 - colortable = [] - for i in range(256): - colortable.append(rgb(i, i, i)) - elif im.mode == "P": - format = QImage.Format_Indexed8 - colortable = [] - palette = im.getpalette() - for i in range(0, len(palette), 3): - colortable.append(rgb(*palette[i:i+3])) - elif im.mode == "RGB": - data = im.tobytes("raw", "BGRX") - format = QImage.Format_RGB32 - elif im.mode == "RGBA": - try: - data = im.tobytes("raw", "BGRA") - except SystemError: - # workaround for earlier versions - r, g, b, a = im.split() - im = Image.merge("RGBA", (b, g, r, a)) - format = QImage.Format_ARGB32 +def _toqclass_helper(im): + data = None + colortable = None + + # handle filename, if given instead of image name + if hasattr(im, "toUtf8"): + # FIXME - is this really the best way to do this? + if str is bytes: + im = unicode(im.toUtf8(), "utf-8") else: - raise ValueError("unsupported image mode %r" % im.mode) + im = str(im.toUtf8(), "utf-8") + if isPath(im): + im = PIL.Image.open(im) - # must keep a reference, or Qt will crash! - self.__data = data or im.tobytes() + if im.mode == "1": + format = QImage.Format_Mono + elif im.mode == "L": + format = QImage.Format_Indexed8 + colortable = [] + for i in range(256): + colortable.append(rgb(i, i, i)) + elif im.mode == "P": + format = QImage.Format_Indexed8 + colortable = [] + palette = im.getpalette() + for i in range(0, len(palette), 3): + colortable.append(rgb(*palette[i:i+3])) + elif im.mode == "RGB": + data = im.tobytes("raw", "BGRX") + format = QImage.Format_RGB32 + elif im.mode == "RGBA": + try: + data = im.tobytes("raw", "BGRA") + except SystemError: + # workaround for earlier versions + r, g, b, a = im.split() + im = PIL.Image.merge("RGBA", (b, g, r, a)) + format = QImage.Format_ARGB32 + else: + raise ValueError("unsupported image mode %r" % im.mode) - QImage.__init__(self, self.__data, im.size[0], im.size[1], format) + # must keep a reference, or Qt will crash! + __data = data or im.tobytes() + return { + 'data': __data, 'im': im, 'format': format, 'colortable': colortable + } - if colortable: - self.setColorTable(colortable) + +def toqimage(im): + im_data = _toqclass_helper(im) + result = QImage( + im_data['data'], im_data['im'].size[0], im_data['im'].size[1], + im_data['format'] + ) + if im_data['colortable']: + result.setColorTable(im_data['colortable']) + return result + + +def toqpixmap(im): + # This doesn't work. For now using a dumb approach. + # im_data = _toqclass_helper(im) + # result = QPixmap(im_data['im'].size[0], im_data['im'].size[1]) + # result.loadFromData(im_data['data']) + # Fix some strange bug that causes + if im.mode == 'RGB': + im = im.convert('RGBA') + qimage = im.toqimage() + qimage.save('/tmp/hopper_{}_qpixmap_qimage.png'.format(im.mode)) + return QPixmap.fromImage(qimage) \ No newline at end of file diff --git a/Tests/test_image_fromqimage.py b/Tests/test_image_fromqimage.py new file mode 100644 index 000000000..f55bde2f1 --- /dev/null +++ b/Tests/test_image_fromqimage.py @@ -0,0 +1,36 @@ +from helper import unittest, PillowTestCase, hopper, image +from test_imageqt import PillowQtTestCase + +from PIL import Image, ImageQt + +if ImageQt.qt_is_installed: + from PIL.ImageQt import QImage + + +class TestFromQImage(PillowQtTestCase, PillowTestCase): + + def roundtrip(self, expected): + result = Image.fromqimage(expected.toqimage()) + # Qt saves all images as rgb + self.assert_image_equal(result, expected.convert('RGB')) + + def test_sanity_1(self): + self.roundtrip(hopper('1')) + + def test_sanity_rgb(self): + self.roundtrip(hopper('RGB')) + + def test_sanity_rgba(self): + self.roundtrip(hopper('RGBA')) + + def test_sanity_l(self): + self.roundtrip(hopper('L')) + + def test_sanity_p(self): + self.roundtrip(hopper('P')) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_image_fromqpixmap.py b/Tests/test_image_fromqpixmap.py new file mode 100644 index 000000000..e96b3a374 --- /dev/null +++ b/Tests/test_image_fromqpixmap.py @@ -0,0 +1,36 @@ +from helper import unittest, PillowTestCase, hopper +from test_imageqt import PillowQPixmapTestCase + +from PIL import Image, ImageQt + +if ImageQt.qt_is_installed: + from PIL.ImageQt import QPixmap + + +class TestFromQPixmap(PillowQPixmapTestCase, PillowTestCase): + + def roundtrip(self, expected): + result = Image.fromqpixmap(expected.toqpixmap()) + # Qt saves all pixmaps as rgb + self.assert_image_equal(result, expected.convert('RGB')) + + def test_sanity_1(self): + self.roundtrip(hopper('1')) + + def test_sanity_rgb(self): + self.roundtrip(hopper('RGB')) + + def test_sanity_rgba(self): + self.roundtrip(hopper('RGBA')) + + def test_sanity_l(self): + self.roundtrip(hopper('L')) + + def test_sanity_p(self): + self.roundtrip(hopper('P')) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_image_toqimage.py b/Tests/test_image_toqimage.py new file mode 100644 index 000000000..125728bf0 --- /dev/null +++ b/Tests/test_image_toqimage.py @@ -0,0 +1,24 @@ +from helper import unittest, PillowTestCase, hopper +from test_imageqt import PillowQtTestCase + +from PIL import ImageQt + + +if ImageQt.qt_is_installed: + from PIL.ImageQt import QImage + + +class TestToQImage(PillowQtTestCase, PillowTestCase): + + def test_sanity(self): + for mode in ('1', 'RGB', 'RGBA', 'L', 'P'): + data = ImageQt.toqimage(hopper(mode)) + data.save('/tmp/hopper_{}_qimage.png'.format(mode)) + self.assertTrue(isinstance(data, QImage)) + self.assertFalse(data.isNull()) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_image_toqpixmap.py b/Tests/test_image_toqpixmap.py new file mode 100644 index 000000000..b857a66ca --- /dev/null +++ b/Tests/test_image_toqpixmap.py @@ -0,0 +1,25 @@ +from helper import unittest, PillowTestCase, hopper +from test_imageqt import PillowQPixmapTestCase + +from PIL import ImageQt + +if ImageQt.qt_is_installed: + from PIL.ImageQt import QPixmap + + +class TestToQPixmap(PillowQPixmapTestCase, PillowTestCase): + + def test_sanity(self): + QPixmap('Tests/images/hopper.ppm').save( + '/tmp/hopper_RGB_qpixmap_file.png') + for mode in ('1', 'RGB', 'RGBA', 'L', 'P'): + data = ImageQt.toqpixmap(hopper(mode)) + data.save('/tmp/hopper_{}_qpixmap.png'.format(mode)) + self.assertTrue(isinstance(data, QPixmap)) + self.assertFalse(data.isNull()) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_imageqt.py b/Tests/test_imageqt.py index ecab9a956..ec76f55d0 100644 --- a/Tests/test_imageqt.py +++ b/Tests/test_imageqt.py @@ -1,20 +1,19 @@ from helper import unittest, PillowTestCase, hopper -try: - from PIL import ImageQt - from PyQt5.QtGui import QImage, qRgb, qRgba -except: - try: - from PyQt4.QtGui import QImage, qRgb, qRgba - except: - try: - from PySide.QtGui import QImage, qRgb, qRgba - except: - # Will be skipped in setUp - pass +from PIL import ImageQt -class TestImageQt(PillowTestCase): +if ImageQt.qt_is_installed: + from PIL.ImageQt import QGuiApplication, QImage, qRgb, qRgba + + def skip_if_qt_is_not_installed(_): + pass +else: + def skip_if_qt_is_not_installed(test_case): + test_case.skipTest('PyQt4, PyQt5, or PySide is not installed') + + +class PillowQtTestCase: def setUp(self): try: @@ -27,6 +26,24 @@ class TestImageQt(PillowTestCase): from PySide.QtGui import QImage, qRgb, qRgba except ImportError: self.skipTest('PyQt4 or 5 or PySide not installed') + skip_if_qt_is_not_installed(self) + + def tearDown(self): + pass + + +class PillowQPixmapTestCase(PillowQtTestCase): + + def setUp(self): + PillowQtTestCase.setUp(self) + self.app = QGuiApplication([]) + + def tearDown(self): + PillowQtTestCase.tearDown(self) + self.app.quit() + + +class TestImageQt(PillowQtTestCase, PillowTestCase): def test_rgb(self): # from https://qt-project.org/doc/qt-4.8/qcolor.html @@ -48,10 +65,6 @@ class TestImageQt(PillowTestCase): checkrgb(0, 255, 0) checkrgb(0, 0, 255) - def test_image(self): - for mode in ('1', 'RGB', 'RGBA', 'L', 'P'): - ImageQt.ImageQt(hopper(mode)) - if __name__ == '__main__': unittest.main() From 854d343aa5dd12d13db5c0fe79dd6efc6d9f548f Mon Sep 17 00:00:00 2001 From: Roman Inflianskas Date: Mon, 15 Sep 2014 23:01:05 +0400 Subject: [PATCH 0351/1037] add functions to convert: Image <-> QImage; Image <-> QPixmap (see #897); fix typo that breaks tests --- Tests/test_image_fromqimage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_image_fromqimage.py b/Tests/test_image_fromqimage.py index f55bde2f1..56dac0c66 100644 --- a/Tests/test_image_fromqimage.py +++ b/Tests/test_image_fromqimage.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper, image +from helper import unittest, PillowTestCase, hopper from test_imageqt import PillowQtTestCase from PIL import Image, ImageQt From b318595666740b07969b83d6b8a289e8936a4ed8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 7 May 2015 19:55:52 +1000 Subject: [PATCH 0352/1037] Re-added ImageQt class --- PIL/ImageQt.py | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/PIL/ImageQt.py b/PIL/ImageQt.py index 611019676..395aa815c 100644 --- a/PIL/ImageQt.py +++ b/PIL/ImageQt.py @@ -125,14 +125,7 @@ def _toqclass_helper(im): def toqimage(im): - im_data = _toqclass_helper(im) - result = QImage( - im_data['data'], im_data['im'].size[0], im_data['im'].size[1], - im_data['format'] - ) - if im_data['colortable']: - result.setColorTable(im_data['colortable']) - return result + return ImageQt(im) def toqpixmap(im): @@ -145,4 +138,22 @@ def toqpixmap(im): im = im.convert('RGBA') qimage = im.toqimage() qimage.save('/tmp/hopper_{}_qpixmap_qimage.png'.format(im.mode)) - return QPixmap.fromImage(qimage) \ No newline at end of file + return QPixmap.fromImage(qimage) + +## +# An PIL image wrapper for Qt. This is a subclass of PyQt4's QImage +# class. +# +# @param im A PIL Image object, or a file name (given either as Python +# string or a PyQt string object). + +class ImageQt(QImage): + + def __init__(self, im): + im_data = _toqclass_helper(im) + QImage.__init__(self, + im_data['data'], im_data['im'].size[0], im_data['im'].size[1], + im_data['format'] + ) + if im_data['colortable']: + self.setColorTable(im_data['colortable']) \ No newline at end of file From fa1c4bffaf21f7e5656fc8d2bd6b6298d450522c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 7 May 2015 20:26:26 +1000 Subject: [PATCH 0353/1037] Do not attempt to subclass QImage if Qt is not installed --- PIL/ImageQt.py | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/PIL/ImageQt.py b/PIL/ImageQt.py index 395aa815c..480564481 100644 --- a/PIL/ImageQt.py +++ b/PIL/ImageQt.py @@ -22,18 +22,18 @@ import sys qt_is_installed = True try: - from PyQt5.QtGui import QGuiApplication, QImage, qRgb, qRgba, QPixmap - from PyQt5.QtCore import QBuffer, QIODevice + from PyQt5.QtGui import QGuiApplication, QImage, qRgb, qRgba, QPixmap + from PyQt5.QtCore import QBuffer, QIODevice except ImportError: - try: - from PyQt4.QtGui import QGuiApplication, QImage, qRgb, qRgba, QPixmap - from PyQt4.QtCore import QBuffer, QIODevice - except ImportError: - try: - from PySide.QtGui import QGuiApplication, QImage, qRgb, qRgba, QPixmap - from PySide.QtCore import QBuffer, QIODevice - except ImportError: - qt_is_installed = False + try: + from PyQt4.QtGui import QGuiApplication, QImage, qRgb, qRgba, QPixmap + from PyQt4.QtCore import QBuffer, QIODevice + except ImportError: + try: + from PySide.QtGui import QGuiApplication, QImage, qRgb, qRgba, QPixmap + from PySide.QtCore import QBuffer, QIODevice + except ImportError: + qt_is_installed = False from io import BytesIO @@ -147,13 +147,14 @@ def toqpixmap(im): # @param im A PIL Image object, or a file name (given either as Python # string or a PyQt string object). -class ImageQt(QImage): +if qt_is_installed: + class ImageQt(QImage): - def __init__(self, im): - im_data = _toqclass_helper(im) - QImage.__init__(self, - im_data['data'], im_data['im'].size[0], im_data['im'].size[1], - im_data['format'] - ) - if im_data['colortable']: - self.setColorTable(im_data['colortable']) \ No newline at end of file + def __init__(self, im): + im_data = _toqclass_helper(im) + QImage.__init__(self, + im_data['data'], im_data['im'].size[0], im_data['im'].size[1], + im_data['format'] + ) + if im_data['colortable']: + self.setColorTable(im_data['colortable']) \ No newline at end of file From d626679c23dbf9d48edec4730b84c144e8ae6401 Mon Sep 17 00:00:00 2001 From: Alexander Schier Date: Sat, 29 Mar 2014 00:47:17 +0100 Subject: [PATCH 0354/1037] added support for multiline text drawing --- PIL/ImageDraw.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py index 18f9c0c00..11fc52ec7 100644 --- a/PIL/ImageDraw.py +++ b/PIL/ImageDraw.py @@ -257,6 +257,9 @@ class ImageDraw(object): # Draw text. def text(self, xy, text, fill=None, font=None, anchor=None): + if "\n" in text: + return self.multiline_text(xy, text, fill, font, anchor) + ink, fill = self._getink(fill) if font is None: font = self.getfont() @@ -273,10 +276,49 @@ class ImageDraw(object): mask = font.getmask(text) self.draw.draw_bitmap(xy, mask, ink) + def multiline_text(self, xy, text, fill=None, font=None, anchor=None, + spacing=0, align="left"): + widths, heights = [], [] + max_width = 0 + lines = text.split("\n") + for line in lines: + line_width, line_height = self.textsize(line, font) + widths.append(line_width) + max_width = max(max_width, line_width) + heights.append(line_height) + left, top = xy + for idx, line in enumerate(lines): + if align == "left": + pass # left = x + elif align == "center": + left += (max_width - widths[idx]) / 2.0 + elif align == "right": + left += (max_width - widths[idx]) + else: + assert False, 'align must be either "left", "center" or "right"' + self.text((left, top), + line, fill, font, anchor) + top += heights[idx] + spacing + left = xy[0] + + def multiline_textsize(self, text, font=None, spacing=0): + max_width = 0 + height = 0 + lines = text.split("\n") + for line in lines: + line_width, line_height = self.textsize(line, font) + height += line_height + spacing + max_width = max(max_width, line_width) + return max_width, height + + ## # Get the size of a given string, in pixels. def textsize(self, text, font=None): + if "\n" in text: + return multiline_textsize(text, font) + if font is None: font = self.getfont() return font.getsize(text) From 77169b2fdba9546c9f6f3842a48c7ba2ee370b16 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 18 Jun 2015 13:21:39 +1000 Subject: [PATCH 0355/1037] Moved multiline split character to common functions --- PIL/ImageDraw.py | 48 ++++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py index 11fc52ec7..2afe93714 100644 --- a/PIL/ImageDraw.py +++ b/PIL/ImageDraw.py @@ -256,8 +256,18 @@ class ImageDraw(object): ## # Draw text. + def _multiline_check(self, text): + split_character = "\n" if isinstance(text, type("")) else b"\n" + + return split_character in text + + def _multiline_split(self, text): + split_character = "\n" if isinstance(text, type("")) else b"\n" + + return text.split(split_character) + def text(self, xy, text, fill=None, font=None, anchor=None): - if "\n" in text: + if self._multiline_check(text): return self.multiline_text(xy, text, fill, font, anchor) ink, fill = self._getink(fill) @@ -277,10 +287,10 @@ class ImageDraw(object): self.draw.draw_bitmap(xy, mask, ink) def multiline_text(self, xy, text, fill=None, font=None, anchor=None, - spacing=0, align="left"): + spacing=0, align="left"): widths, heights = [], [] max_width = 0 - lines = text.split("\n") + lines = self._multiline_split(text) for line in lines: line_width, line_height = self.textsize(line, font) widths.append(line_width) @@ -289,40 +299,38 @@ class ImageDraw(object): left, top = xy for idx, line in enumerate(lines): if align == "left": - pass # left = x + pass # left = x elif align == "center": left += (max_width - widths[idx]) / 2.0 elif align == "right": left += (max_width - widths[idx]) else: - assert False, 'align must be either "left", "center" or "right"' - self.text((left, top), - line, fill, font, anchor) + assert False, 'align must be "left", "center" or "right"' + self.text((left, top), line, fill, font, anchor) top += heights[idx] + spacing left = xy[0] - def multiline_textsize(self, text, font=None, spacing=0): - max_width = 0 - height = 0 - lines = text.split("\n") - for line in lines: - line_width, line_height = self.textsize(line, font) - height += line_height + spacing - max_width = max(max_width, line_width) - return max_width, height - - ## # Get the size of a given string, in pixels. def textsize(self, text, font=None): - if "\n" in text: - return multiline_textsize(text, font) + if self._multiline_check(text): + return self.multiline_textsize(text, font) if font is None: font = self.getfont() return font.getsize(text) + def multiline_textsize(self, text, font=None, spacing=0): + max_width = 0 + height = 0 + lines = self._multiline_split(text) + for line in lines: + line_width, line_height = self.textsize(line, font) + height += line_height + spacing + max_width = max(max_width, line_width) + return max_width, height + ## # A simple 2D drawing interface for PIL images. From 9546fac7eca6562039d8b5ad5eafa9eeeefa219e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 18 Jun 2015 13:22:04 +1000 Subject: [PATCH 0356/1037] Added multiline documentation --- docs/reference/ImageDraw.rst | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index e6d5c36ee..e030147e9 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -232,17 +232,39 @@ Methods Draws the string at the given position. :param xy: Top left corner of the text. - :param text: Text to be drawn. + :param text: Text to be drawn. If it contains any newline characters, + the text is passed on to mulitiline_text() :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. :param fill: Color to use for the text. +.. py:method:: PIL.ImageDraw.Draw.multiline_text(xy, text, fill=None, font=None, anchor=None, spacing=0, align="left") + + Draws the string at the given position. + + :param xy: Top left corner of the text. + :param text: Text to be drawn. If it contains any newline characters, + the text is split and passed on to mulitiline_text() + :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. + :param spacing: The number of pixels between lines. + :param align: "left", "center" or "right". + .. py:method:: PIL.ImageDraw.Draw.textsize(text, font=None) Return the size of the given string, in pixels. - :param text: Text to be measured. + :param text: Text to be measured. If it contains any newline characters, + the text is passed on to mulitiline_textsize() :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. +.. py:method:: PIL.ImageDraw.Draw.multiline_textsize(text, font=None, spacing=0) + + Return the size of the given string, in pixels. + + :param text: Text to be measured. If it contains any newline characters, + the text is split and passed on to mulitiline_textsize() + :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. + :param spacing: The number of pixels between lines. + Legacy API ---------- From b7335ec9d9396bcf9c14331769ec1a614b1717ff Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 18 Jun 2015 17:51:33 +1000 Subject: [PATCH 0357/1037] Added multiline tests --- Tests/images/multiline_text_center.png | Bin 0 -> 2845 bytes Tests/images/multiline_text_right.png | Bin 0 -> 2846 bytes Tests/images/multiline_text_spacing.png | Bin 0 -> 2844 bytes Tests/test_imagefont.py | 69 +++++++++++++++++++++++- 4 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 Tests/images/multiline_text_center.png create mode 100644 Tests/images/multiline_text_right.png create mode 100644 Tests/images/multiline_text_spacing.png diff --git a/Tests/images/multiline_text_center.png b/Tests/images/multiline_text_center.png new file mode 100644 index 0000000000000000000000000000000000000000..f44d0783a0944986efdc62d4f0bcc7530b74ab5b GIT binary patch literal 2845 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 OU$EoXD23%}hqtl13=Tu}sJ^Cc_Y74%tdX zcFx$B5VA9}XNk;UlJD)D@1Jvh*LnW9ujjh&=Xsy|`rY^M_ukPr%?t&>C&3^PNDyhH zX9)sv!GOIR-yy)3xl$=05O@};r~O-C`szfmk=2wm??!j`1^am`%e%%ADVl%eS|o_Y z#u^k^2TeC{B^bH&5nAIUFI>1Fm$cMLxoDiLte_CX z@ia8#g}UKz=jG+2ZAHbzwmEEX9V5UQY?{2!7tuu1cv$e8LU<$k(g`Y+8n8Mldkj3n zGc@&Jsy4`wuMz9*V@P}+6BF~}$B&mU&s@A%QdwDnM#&|$ zyG2Jw3knK?Bf>b|k=?t$Bl*^%M9xw=tME-tO*}k2pBXzlJ9P6*0)gN+re|PKN7(LY zo~<8P%Fqnj+}y0Zr>v|TclsLBQ&Wi3-qTZ@pMR9Z7*}(7v(p&PJC1ol+!*%R!2p<; zqEPR=tnS>Yb76o3eEVM^y(eqFu~-=K2pwlv*V#|`R=x>Tgv9R8{h+iobf|1YVAg&# zb9wM}RaMo{Iq9`Qqn3t-Czkmrb91LEzuY?|>nkh%MarE0WHOnC2p(nb718NfXJ=^!+H^}rRfOyl$i}yB-D1?*<@}}~24*~yP;pZp zi4jxmp=k_{k3Um^R?;#uq;%R^-udlDW z``jYFY#{#T`Q*+yQ&UreoIdoW%U7;+&iRp4w%*+xP*70V<8V@?A?&pY#RMZhXmD`w z(1OpC(2luI%A?)sRvRslXlJ2qnTLnRI9>V)KfSc{(zRZtmoiln@Tfv}Bl` zm*sWg~x~H&aDJO;(nXPXKrT;QFDux*Jnwkb^Wa8Sp3{=V8U` zW3lI;GEk^?PTz}e4B1%h=+Oi+d2nj#U~hZn^XJ=>Y~g1q8uT-d0t1Oe;xREX`D5TT zK|ZLGl9KmS-9&w8eSEw*3WW*?P~#Lf%gEO)j{wAas%kN8km^4r<{6U-SN?* zT_c)^4ymVJ!Xm_2Qlw@UPi(jmgm0 z*Ei3&?&}Nzry~;5|3x)K4DUb-|qLnwtDjO-;>ljH9EY_>|js zLhS?Z??}s>LPHsdL~IKHjtXgMX(>}HG$1PjKs_od3O`)D&0+}&2`%6MAbQU1wnOu~ zP#7SdH~5y8mtDq0%^Jidq$*VR_77jz1w$GFbpUhq*KX$q81gSvZaA==avXz_(z<^@ zGZDcqKX+#1}Ym7FN-*xmP1-p%yhK1p)1w^c|bKBt{-E#9!jQBmpC(uP!k36{QUg# zHX*6~)95HQC1w6=#xA=ronKKwVRLIMJS+@oCMJ_ePnUlp9ayPCOrq)SwwtXT`$wN(G;=@Bj*4EZ=hsv906JWPXZ)Dy4 zI$3)P+vJRs%W{AN{URW6>ck13EL~2%F1+-bxGCYN`{*$ZJwLy<#<^fY!E#%k^)Xo= zRj!yk%wEXX;buv%SH!~RNMS{?F^T-%YobZ!bW8a;coo1+VB{ zMrXjcM3BCfin~^gj9cxaElhi&thgct?iho-+I8DZ=btzKj6HP)7%>W)op0w%!RsI| z&*K9=3p?@K)`#rBt&Fa`aR$BCgXlMr%T`Jw3Ry)up#5Rg?`J#S$z%AkZk`Yk+1Q*j zQL{-WQC69XTrqDkk^39u=YwNo8bK^i61fc9+X7_msb=rA&1B*s>L^LSr`>OG{iO2) zd6l}TOhj#YxheuN>2mc>1*wGldD`t zth2kd0EqMcx7k)e5Zq5~sPaeWe1abTe46xEojP2ifOF+`_Yzin04!brSd=w~>=?Ig zvf0BP)ca=tTF=e(T!xS>P$gVslxPne^-DB&f zDOOikxBEyV9#5;iL`r6V@qtlg0b`tIv*^b_;Q4l%Soh ztqWbLDiaeE;o;$SMFG7TnmSn@KPdhSf&Sn9B(&iGbjiUYwk-+n`74anH`6QDL5KYp DTe*8% literal 0 HcmV?d00001 diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 88858c717..8414584f9 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -10,6 +10,8 @@ import copy FONT_PATH = "Tests/fonts/FreeMono.ttf" FONT_SIZE = 20 +TEST_TEXT = "hey you\nyou are awesome\nthis looks awkward" + try: from PIL import ImageFont @@ -95,7 +97,7 @@ try: txt = "Hello World!" ttf = ImageFont.truetype(font, FONT_SIZE) ttf.getsize(txt) - + img = Image.new("RGB", (256, 64), "white") d = ImageDraw.Draw(img) d.text((10, 10), txt, font=ttf, fill='black') @@ -134,7 +136,7 @@ try: draw = ImageDraw.Draw(im) ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) line_spacing = draw.textsize('A', font=ttf)[1] + 4 - lines = ['hey you', 'you are awesome', 'this looks awkward'] + lines = TEST_TEXT.split("\n") y = 0 for line in lines: draw.text((0, y), line, font=ttf) @@ -148,6 +150,69 @@ try: # at epsilon = ~38. self.assert_image_similar(im, target_img, .5) + def test_render_multiline_text(self): + ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) + + # Test that text() correctly connects to multiline_text() + # and that align defaults to left + im = Image.new(mode='RGB', size=(300, 100)) + draw = ImageDraw.Draw(im) + draw.text((0, 0), TEST_TEXT, font=ttf) + + target = 'Tests/images/multiline_text.png' + target_img = Image.open(target) + + self.assert_image_similar(im, target_img, .5) + + # Test align center and right + for align, ext in {"center": "_center", + "right": "_right"}.items(): + im = Image.new(mode='RGB', size=(300, 100)) + draw = ImageDraw.Draw(im) + draw.multiline_text((0, 0), TEST_TEXT, font=ttf, align=align) + + target = 'Tests/images/multiline_text'+ext+'.png' + target_img = Image.open(target) + + self.assert_image_similar(im, target_img, .5) + + def test_unknown_align(self): + im = Image.new(mode='RGB', size=(300, 100)) + draw = ImageDraw.Draw(im) + ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) + + # Act/Assert + self.assertRaises(AssertionError, lambda: draw.multiline_text((0, 0), TEST_TEXT, font=ttf, align="unknown")) + + def test_multiline_size(self): + ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) + im = Image.new(mode='RGB', size=(300, 100)) + draw = ImageDraw.Draw(im) + + # Test that textsize() correctly connects to multiline_textsize() + self.assertEqual(draw.textsize(TEST_TEXT, font=ttf), + draw.multiline_textsize(TEST_TEXT, font=ttf)) + + def test_multiline_width(self): + ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) + im = Image.new(mode='RGB', size=(300, 100)) + draw = ImageDraw.Draw(im) + + self.assertEqual(draw.textsize("longest line", font=ttf)[0], + draw.multiline_textsize("longest line\nline", font=ttf)[0]) + + def test_multiline_spacing(self): + ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) + + im = Image.new(mode='RGB', size=(300, 100)) + draw = ImageDraw.Draw(im) + draw.multiline_text((0, 0), TEST_TEXT, font=ttf, spacing=10) + + target = 'Tests/images/multiline_text_spacing.png' + target_img = Image.open(target) + + self.assert_image_similar(im, target_img, .5) + def test_rotated_transposed_font(self): img_grey = Image.new("L", (100, 100)) draw = ImageDraw.Draw(img_grey) From 1111e9fb35d42a778dddcd4e473a6b61d4032519 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 19 Jun 2015 00:49:18 +1000 Subject: [PATCH 0358/1037] Fixed frame position when seeking past the end of file --- PIL/FliImagePlugin.py | 8 +++++++- PIL/GifImagePlugin.py | 10 ++++++++-- Tests/test_file_dcx.py | 12 ++++++++++++ Tests/test_file_fli.py | 14 +++++++++++++- Tests/test_file_gif.py | 17 ++++++++++++++++- Tests/test_file_im.py | 12 ++++++++++++ Tests/test_file_mpo.py | 12 ++++++++++++ Tests/test_file_psd.py | 13 +++++++++++++ Tests/test_file_tiff.py | 12 ++++++++++++ 9 files changed, 105 insertions(+), 5 deletions(-) diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py index df6a4eb8e..772140076 100644 --- a/PIL/FliImagePlugin.py +++ b/PIL/FliImagePlugin.py @@ -127,8 +127,14 @@ class FliImageFile(ImageFile.ImageFile): return if frame < self.__frame: self._seek(0) + + last_frame = self.__frame for f in range(self.__frame + 1, frame + 1): - self._seek(f) + try: + self._seek(f) + except EOFError: + self.seek(last_frame) + raise EOFError("no more images in FLI file") def _seek(self, frame): if frame == 0: diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index bd974b651..35fb95164 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -107,8 +107,14 @@ class GifImageFile(ImageFile.ImageFile): return if frame < self.__frame: self._seek(0) + + last_frame = self.__frame for f in range(self.__frame + 1, frame + 1): - self._seek(f) + try: + self._seek(f) + except EOFError: + self.seek(last_frame) + raise EOFError("no more images in GIF file") def _seek(self, frame): @@ -241,7 +247,7 @@ class GifImageFile(ImageFile.ImageFile): if not self.tile: # self.__fp = None - raise EOFError("no more images in GIF file") + raise EOFError self.mode = "L" if self.palette: diff --git a/Tests/test_file_dcx.py b/Tests/test_file_dcx.py index 7d2ae32d8..d59a5a785 100644 --- a/Tests/test_file_dcx.py +++ b/Tests/test_file_dcx.py @@ -34,6 +34,18 @@ class TestFileDcx(PillowTestCase): im = Image.open(TEST_FILE) self.assertEqual(im.n_frames, 1) + def test_eoferror(self): + im = Image.open(TEST_FILE) + + n_frames = im.n_frames + while True: + n_frames -= 1 + try: + im.seek(n_frames) + break + except EOFError: + self.assertTrue(im.tell() < n_frames) + def test_seek_too_far(self): # Arrange im = Image.open(TEST_FILE) diff --git a/Tests/test_file_fli.py b/Tests/test_file_fli.py index 04c2006c9..1b95f793f 100644 --- a/Tests/test_file_fli.py +++ b/Tests/test_file_fli.py @@ -20,7 +20,19 @@ class TestFileFli(PillowTestCase): def test_n_frames(self): im = Image.open(test_file) - self.assertEqual(im.n_frames, 2) + self.assertEqual(im.n_frames, 1) + + def test_eoferror(self): + im = Image.open(test_file) + + n_frames = im.n_frames + while True: + n_frames -= 1 + try: + im.seek(n_frames) + break + except EOFError: + self.assertTrue(im.tell() < n_frames) if __name__ == '__main__': diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index b15d32097..a38c360c9 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -135,8 +135,23 @@ class TestFileGif(PillowTestCase): self.assertEqual(framecount, 5) def test_n_frames(self): + im = Image.open(TEST_GIF) + self.assertEqual(im.n_frames, 1) + im = Image.open("Tests/images/iss634.gif") - self.assertEqual(im.n_frames, 43) + self.assertEqual(im.n_frames, 42) + + def test_eoferror(self): + im = Image.open(TEST_GIF) + + n_frames = im.n_frames + while True: + n_frames -= 1 + try: + im.seek(n_frames) + break + except EOFError: + self.assertTrue(im.tell() < n_frames) def test_dispose_none(self): img = Image.open("Tests/images/dispose_none.gif") diff --git a/Tests/test_file_im.py b/Tests/test_file_im.py index 24e00b2f0..76bf250a0 100644 --- a/Tests/test_file_im.py +++ b/Tests/test_file_im.py @@ -19,6 +19,18 @@ class TestFileIm(PillowTestCase): im = Image.open(TEST_IM) self.assertEqual(im.n_frames, 1) + def test_eoferror(self): + im = Image.open(TEST_IM) + + n_frames = im.n_frames + while True: + n_frames -= 1 + try: + im.seek(n_frames) + break + except EOFError: + self.assertTrue(im.tell() < n_frames) + def test_roundtrip(self): out = self.tempfile('temp.im') im = hopper() diff --git a/Tests/test_file_mpo.py b/Tests/test_file_mpo.py index 1a0ebc453..c5562097d 100644 --- a/Tests/test_file_mpo.py +++ b/Tests/test_file_mpo.py @@ -99,6 +99,18 @@ class TestFileMpo(PillowTestCase): im = Image.open("Tests/images/sugarshack.mpo") self.assertEqual(im.n_frames, 2) + def test_eoferror(self): + im = Image.open("Tests/images/sugarshack.mpo") + + n_frames = im.n_frames + while True: + n_frames -= 1 + try: + im.seek(n_frames) + break + except EOFError: + self.assertTrue(im.tell() < n_frames) + def test_image_grab(self): for test_file in test_files: im = Image.open(test_file) diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py index dca3601b2..ea3856fce 100644 --- a/Tests/test_file_psd.py +++ b/Tests/test_file_psd.py @@ -23,6 +23,19 @@ class TestImagePsd(PillowTestCase): im = Image.open(test_file) self.assertEqual(im.n_frames, 2) + def test_eoferror(self): + im = Image.open(test_file) + + n_frames = im.n_frames + while True: + n_frames -= 1 + try: + # PSD seek index starts at 1 rather than 0 + im.seek(n_frames+1) + break + except EOFError: + self.assertTrue(im.tell() < n_frames) + if __name__ == '__main__': unittest.main() diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 02a63586c..a88b49d7f 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -157,6 +157,18 @@ class TestFileTiff(PillowTestCase): im = Image.open('Tests/images/multipage.tiff') self.assertEqual(im.n_frames, 3) + def test_eoferror(self): + im = Image.open('Tests/images/multipage-lastframe.tif') + + n_frames = im.n_frames + while True: + n_frames -= 1 + try: + im.seek(n_frames) + break + except EOFError: + self.assertTrue(im.tell() < n_frames) + def test_multipage(self): # issue #862 im = Image.open('Tests/images/multipage.tiff') From d861269a62e8ba0cf357a2fcfba702efe07330e0 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 18 Jun 2015 21:38:26 +0300 Subject: [PATCH 0359/1037] Build all branches > All branches are built by default. http://www.appveyor.com/docs/branches --- appveyor.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index e37b18a98..868f34f7e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,7 +1,4 @@ version: 2.9.pre.{build} -branches: - only: - - winbuild shallow_clone: true clone_folder: c:\pillow init: @@ -27,4 +24,4 @@ build_script: test_script: - cd c:\pillow - '%PYTHON%\Scripts\pip.exe install nose' -- '%PYTHON%\python.exe test-installed.py' \ No newline at end of file +- '%PYTHON%\python.exe test-installed.py' From 731363d76a2c4d40e3b1e70d3fdc576368b128b3 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 18 Jun 2015 22:02:22 +0300 Subject: [PATCH 0360/1037] Add AppVeyor badge [CI skip] Docs: http://www.appveyor.com/docs/status-badges TODO Change to master branch when available. --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rst b/README.rst index 52e9843a8..dd0b85485 100644 --- a/README.rst +++ b/README.rst @@ -14,6 +14,10 @@ Pillow is the "friendly PIL fork" by `Alex Clark and Contributors Date: Thu, 18 Jun 2015 22:05:15 +0300 Subject: [PATCH 0361/1037] Fix alt text [CI skip] --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index dd0b85485..8e562bd69 100644 --- a/README.rst +++ b/README.rst @@ -16,7 +16,7 @@ Pillow is the "friendly PIL fork" by `Alex Clark and Contributors Date: Thu, 18 Jun 2015 22:23:55 +0300 Subject: [PATCH 0362/1037] Clarify build badges [Ci skip] Three "build: passing" badges in a row looks a bit odd. AppVeyor allows you to customise the right-hand side text (http://www.appveyor.com/docs/status-badges), but Travis CI doesn't. But Shields.io allows you to override the default left-hand-side text (which makes more sense) and supports both Travis CI and AppVeyor. TODO: update Windows branch to master when available. --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 8e562bd69..ae3b30147 100644 --- a/README.rst +++ b/README.rst @@ -6,15 +6,15 @@ Python Imaging Library (Fork) Pillow is the "friendly PIL fork" by `Alex Clark and Contributors `_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. -.. image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master +.. image:: https://img.shields.io/travis/python-pillow/Pillow/master.svg?label=Linux%20build :target: https://travis-ci.org/python-pillow/Pillow :alt: Travis CI build status (Linux) -.. image:: https://travis-ci.org/python-pillow/pillow-wheels.svg?branch=latest +.. image:: https://img.shields.io/travis/python-pillow/pillow-wheels/latest.svg?label=OS%20X%20build :target: https://travis-ci.org/python-pillow/pillow-wheels :alt: Travis CI build status (OS X) -.. image:: https://ci.appveyor.com/api/projects/status/github/wiredfool/pillow?svg=true&branch=winbuild +.. image:: https://img.shields.io/appveyor/ci/wiredfool/pillow/winbuild.svg?label=Windows%20build :target: https://ci.appveyor.com/project/wiredfool/pillow :alt: AppVeyor CI build status (Windows) From be5c97e501800949cea288a9aa0741e82257c923 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 19 Jun 2015 10:47:47 +1000 Subject: [PATCH 0363/1037] Fixed typo --- winbuild/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/winbuild/README.md b/winbuild/README.md index 213e9bda4..8a303a3d5 100644 --- a/winbuild/README.md +++ b/winbuild/README.md @@ -7,7 +7,7 @@ For more extensive info, see the windows build instructions `docs/build.rst`. * Works best with Python 3.4, due to virtualenv and pip batteries included. Python3+ required for fetch command. * Check config.py for virtual env paths, suffix for 64 bit releases. Defaults to `x64`, set `X64_EXT` to change. -* When running in CI with one python per invocation, set the `PYTHON` env variable to the python folder. (e.g. `PYTHON`=`c:\Python27\`) This overrides the matrix in config.py and will just build and test for the speciic python. +* When running in CI with one python per invocation, set the `PYTHON` env variable to the python folder. (e.g. `PYTHON`=`c:\Python27\`) This overrides the matrix in config.py and will just build and test for the specific python. * `python get_pythons.py` downloads all the python releases, and their signatures. (Manually) Install in `c:\PythonXX[x64]\`. * `python build_dep.py` downloads and creates a build script for all the dependencies, in 32 and 64 bit versions, and with both compiler versions. * (in powershell) `build_deps.cmd` invokes the dependency build. @@ -15,4 +15,4 @@ For more extensive info, see the windows build instructions `docs/build.rst`. * `python test.py` runs the tests on pillow in all the virtual envs. * Currently working with zlib, libjpeg, freetype, and libtiff on Python 2.7, 3.3, and 3.4, both 32 and 64 bit, on a local win7 pro machine and appveyor.com (3.3 untested there) * Webp is built, not detected. -* LCMS and OpenJpeg are not building. \ No newline at end of file +* LCMS and OpenJpeg are not building. From b5026453501d36cb2d2097dc1aa09623bfa8c0f8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 19 Jun 2015 11:13:26 +1000 Subject: [PATCH 0364/1037] Removed unnecessary comments --- winbuild/config.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/winbuild/config.py b/winbuild/config.py index b237703f3..292bd6a1f 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -27,17 +27,12 @@ libs = { 'zlib':{ 'hash': 'md5:dd70349cedb3981371686e1c9b89a7f9', # not found - generated by wiredfool 'dir': 'tiff-4.0.3', }, -# 'freetype':{ -# 'url':'http://download.savannah.gnu.org/releases/freetype/ft253.zip', -# 'hash': 'md5:b3858f7e69740ac04ef53366aeb172bc', # not found - generated by wiredfool -# 'dir': 'freetype-2.5.3', -# }, -'freetype':{ + 'freetype':{ 'url': 'http://download.savannah.gnu.org/releases/freetype/freetype-2.6.tar.gz', 'hash':'md5:1d733ea6c1b7b3df38169fbdbec47d2b', 'dir': 'freetype-2.6', }, -'lcms':{ + 'lcms':{ 'url':SF_MIRROR+'/project/lcms/lcms/2.6/lcms2-2.6.zip', 'hash': 'sha1:eea25f001246fa2e6b242ac456cecff7483cf061', 'dir': 'lcms2-2.6', @@ -57,12 +52,6 @@ libs = { 'zlib':{ 'hash':'sha1:326c4b6787a01e5e32a9b30bae76442d18d2d1b6', 'dir':'libwebp-0.4.0', }, -# 'openjpeg':{ -# 'url':'https://openjpeg.googlecode.com/files/openjpeg-2.0.0.tar.gz', -# 'hash':'sha1:0af78ab2283b43421458f80373422d8029a9f7a7', -# 'dir':'openjpeg-2.0.0', -# }, - 'openjpeg':{ 'url':SF_MIRROR+'/project/openjpeg/openjpeg/2.1.0/openjpeg-2.1.0.tar.gz', 'hash':'md5:f6419fcc233df84f9a81eb36633c6db6', From c870ee3d62e902a746690ca59e53fa41e2191367 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 19 Jun 2015 11:25:52 +1000 Subject: [PATCH 0365/1037] Removed alternate mirror --- winbuild/config.py | 1 - 1 file changed, 1 deletion(-) diff --git a/winbuild/config.py b/winbuild/config.py index 292bd6a1f..f3939e929 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -1,6 +1,5 @@ import os -SF_MIRROR = 'http://hivelocity.dl.sourceforge.net' SF_MIRROR = 'http://iweb.dl.sourceforge.net' pythons = {#'26':7, From 4a8242970852120aa590dd1bf2b37adec3a73bb0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 19 Jun 2015 11:26:34 +1000 Subject: [PATCH 0366/1037] Updated libwebp and lcms2 versions --- winbuild/config.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/winbuild/config.py b/winbuild/config.py index f3939e929..73cab99ae 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -32,9 +32,9 @@ libs = { 'zlib':{ 'dir': 'freetype-2.6', }, 'lcms':{ - 'url':SF_MIRROR+'/project/lcms/lcms/2.6/lcms2-2.6.zip', - 'hash': 'sha1:eea25f001246fa2e6b242ac456cecff7483cf061', - 'dir': 'lcms2-2.6', + 'url':SF_MIRROR+'/project/lcms/lcms/2.7/lcms2-2.7.zip', + 'hash': 'sha1:7ff1a5b721ca719760ba6eb4ec6f38d5e65381cf', + 'dir': 'lcms2-2.7', }, 'tcl':{ 'url':SF_MIRROR+'/project/tcl/Tcl/8.5.13/tcl8513-src.zip', @@ -47,9 +47,9 @@ libs = { 'zlib':{ 'dir': '', }, 'webp':{ - 'url':'https://webp.googlecode.com/files/libwebp-0.4.0.tar.gz', - 'hash':'sha1:326c4b6787a01e5e32a9b30bae76442d18d2d1b6', - 'dir':'libwebp-0.4.0', + 'url':'http://downloads.webmproject.org/releases/webp/libwebp-0.4.3.tar.gz', + 'hash':'sha1:1c307a61c4d0018620b4ba9a58e8f48a8d6640ef', + 'dir':'libwebp-0.4.3', }, 'openjpeg':{ 'url':SF_MIRROR+'/project/openjpeg/openjpeg/2.1.0/openjpeg-2.1.0.tar.gz', From c634b47577d01acaa64327a599a42d9aa7530387 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 19 Jun 2015 13:23:23 +1000 Subject: [PATCH 0367/1037] Updated winbuild Python version to 3.4.3 --- winbuild/get_pythons.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winbuild/get_pythons.py b/winbuild/get_pythons.py index 71988301d..19c5ddd40 100644 --- a/winbuild/get_pythons.py +++ b/winbuild/get_pythons.py @@ -2,7 +2,7 @@ from fetch import fetch import os if __name__=='__main__': - for version in ['2.6.5', '2.7.6', '3.2.5', '3.3.5', '3.4.0']: + for version in ['2.6.5', '2.7.6', '3.2.5', '3.3.5', '3.4.3']: for platform in ['', '.amd64']: for extension in ['','.asc']: fetch('https://www.python.org/ftp/python/%s/python-%s%s.msi%s' %( From aa35b3623dda50a273b0e0b8c11a6a8e3a87adcc Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 19 Jun 2015 07:40:23 +0300 Subject: [PATCH 0368/1037] Update Windows badge [CI skip] Point to `pillow-b17vj` (https://ci.appveyor.com/project/wiredfool/pillow-b17vj/history), since that's org's python-pillow/pillow repo, not the wiredfool/pillow personal repo. Also, master is available now. --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index ae3b30147..83ea57dc7 100644 --- a/README.rst +++ b/README.rst @@ -14,8 +14,8 @@ Pillow is the "friendly PIL fork" by `Alex Clark and Contributors Date: Fri, 19 Jun 2015 15:35:56 +1000 Subject: [PATCH 0369/1037] Removed unused imports --- PIL/ImageQt.py | 47 ++++++++++++++++++--------------- Tests/test_image_fromqimage.py | 7 ++--- Tests/test_image_fromqpixmap.py | 10 +++---- Tests/test_image_toqimage.py | 1 + Tests/test_image_toqpixmap.py | 3 ++- Tests/test_imageqt.py | 31 +++++++++++++--------- 6 files changed, 52 insertions(+), 47 deletions(-) diff --git a/PIL/ImageQt.py b/PIL/ImageQt.py index 480564481..95819029d 100644 --- a/PIL/ImageQt.py +++ b/PIL/ImageQt.py @@ -18,20 +18,23 @@ import PIL from PIL._util import isPath -import sys qt_is_installed = True +qt_version = None try: - from PyQt5.QtGui import QGuiApplication, QImage, qRgb, qRgba, QPixmap + from PyQt5.QtGui import QImage, qRgba, QPixmap from PyQt5.QtCore import QBuffer, QIODevice + qt_version = '5' except ImportError: try: - from PyQt4.QtGui import QGuiApplication, QImage, qRgb, qRgba, QPixmap + from PyQt4.QtGui import QImage, qRgba, QPixmap from PyQt4.QtCore import QBuffer, QIODevice + qt_version = '4' except ImportError: try: - from PySide.QtGui import QGuiApplication, QImage, qRgb, qRgba, QPixmap + from PySide.QtGui import QImage, qRgba, QPixmap from PySide.QtCore import QBuffer, QIODevice + qt_version = 'side' except ImportError: qt_is_installed = False @@ -123,23 +126,6 @@ def _toqclass_helper(im): 'data': __data, 'im': im, 'format': format, 'colortable': colortable } - -def toqimage(im): - return ImageQt(im) - - -def toqpixmap(im): - # This doesn't work. For now using a dumb approach. - # im_data = _toqclass_helper(im) - # result = QPixmap(im_data['im'].size[0], im_data['im'].size[1]) - # result.loadFromData(im_data['data']) - # Fix some strange bug that causes - if im.mode == 'RGB': - im = im.convert('RGBA') - qimage = im.toqimage() - qimage.save('/tmp/hopper_{}_qpixmap_qimage.png'.format(im.mode)) - return QPixmap.fromImage(qimage) - ## # An PIL image wrapper for Qt. This is a subclass of PyQt4's QImage # class. @@ -157,4 +143,21 @@ if qt_is_installed: im_data['format'] ) if im_data['colortable']: - self.setColorTable(im_data['colortable']) \ No newline at end of file + self.setColorTable(im_data['colortable']) + + +def toqimage(im): + return ImageQt(im) + + +def toqpixmap(im): + # This doesn't work. For now using a dumb approach. + # im_data = _toqclass_helper(im) + # result = QPixmap(im_data['im'].size[0], im_data['im'].size[1]) + # result.loadFromData(im_data['data']) + # Fix some strange bug that causes + if im.mode == 'RGB': + im = im.convert('RGBA') + qimage = toqimage(im) + qimage.save('/tmp/hopper_{}_qpixmap_qimage.png'.format(im.mode)) + return QPixmap.fromImage(qimage) \ No newline at end of file diff --git a/Tests/test_image_fromqimage.py b/Tests/test_image_fromqimage.py index 56dac0c66..957ec9cc1 100644 --- a/Tests/test_image_fromqimage.py +++ b/Tests/test_image_fromqimage.py @@ -1,16 +1,13 @@ from helper import unittest, PillowTestCase, hopper from test_imageqt import PillowQtTestCase -from PIL import Image, ImageQt - -if ImageQt.qt_is_installed: - from PIL.ImageQt import QImage +from PIL import ImageQt class TestFromQImage(PillowQtTestCase, PillowTestCase): def roundtrip(self, expected): - result = Image.fromqimage(expected.toqimage()) + result = ImageQt.fromqimage(expected.toqimage()) # Qt saves all images as rgb self.assert_image_equal(result, expected.convert('RGB')) diff --git a/Tests/test_image_fromqpixmap.py b/Tests/test_image_fromqpixmap.py index e96b3a374..78e4d6770 100644 --- a/Tests/test_image_fromqpixmap.py +++ b/Tests/test_image_fromqpixmap.py @@ -1,16 +1,14 @@ from helper import unittest, PillowTestCase, hopper -from test_imageqt import PillowQPixmapTestCase +from test_imageqt import PillowQtTestCase, PillowQPixmapTestCase -from PIL import Image, ImageQt - -if ImageQt.qt_is_installed: - from PIL.ImageQt import QPixmap +from PIL import ImageQt class TestFromQPixmap(PillowQPixmapTestCase, PillowTestCase): def roundtrip(self, expected): - result = Image.fromqpixmap(expected.toqpixmap()) + PillowQtTestCase.setUp(self) + result = ImageQt.fromqpixmap(ImageQt.toqpixmap(expected)) # Qt saves all pixmaps as rgb self.assert_image_equal(result, expected.convert('RGB')) diff --git a/Tests/test_image_toqimage.py b/Tests/test_image_toqimage.py index 125728bf0..d44a64847 100644 --- a/Tests/test_image_toqimage.py +++ b/Tests/test_image_toqimage.py @@ -11,6 +11,7 @@ if ImageQt.qt_is_installed: class TestToQImage(PillowQtTestCase, PillowTestCase): def test_sanity(self): + PillowQtTestCase.setUp(self) for mode in ('1', 'RGB', 'RGBA', 'L', 'P'): data = ImageQt.toqimage(hopper(mode)) data.save('/tmp/hopper_{}_qimage.png'.format(mode)) diff --git a/Tests/test_image_toqpixmap.py b/Tests/test_image_toqpixmap.py index b857a66ca..dd527732f 100644 --- a/Tests/test_image_toqpixmap.py +++ b/Tests/test_image_toqpixmap.py @@ -1,5 +1,5 @@ from helper import unittest, PillowTestCase, hopper -from test_imageqt import PillowQPixmapTestCase +from test_imageqt import PillowQtTestCase, PillowQPixmapTestCase from PIL import ImageQt @@ -10,6 +10,7 @@ if ImageQt.qt_is_installed: class TestToQPixmap(PillowQPixmapTestCase, PillowTestCase): def test_sanity(self): + PillowQtTestCase.setUp(self) QPixmap('Tests/images/hopper.ppm').save( '/tmp/hopper_RGB_qpixmap_file.png') for mode in ('1', 'RGB', 'RGBA', 'L', 'P'): diff --git a/Tests/test_imageqt.py b/Tests/test_imageqt.py index ec76f55d0..5959bee34 100644 --- a/Tests/test_imageqt.py +++ b/Tests/test_imageqt.py @@ -1,10 +1,10 @@ -from helper import unittest, PillowTestCase, hopper +from helper import unittest, PillowTestCase from PIL import ImageQt if ImageQt.qt_is_installed: - from PIL.ImageQt import QGuiApplication, QImage, qRgb, qRgba + from PIL.ImageQt import qRgba def skip_if_qt_is_not_installed(_): pass @@ -16,26 +16,25 @@ else: class PillowQtTestCase: def setUp(self): - try: - from PyQt5.QtGui import QImage, qRgb, qRgba - except ImportError: - try: - from PyQt4.QtGui import QImage, qRgb, qRgba - except ImportError: - try: - from PySide.QtGui import QImage, qRgb, qRgba - except ImportError: - self.skipTest('PyQt4 or 5 or PySide not installed') skip_if_qt_is_not_installed(self) def tearDown(self): pass - class PillowQPixmapTestCase(PillowQtTestCase): def setUp(self): PillowQtTestCase.setUp(self) + try: + if ImageQt.qt_version == '5': + from PyQt5.QtGui import QGuiApplication + elif ImageQt.qt_version == '4': + from PyQt4.QtGui import QGuiApplication + elif ImageQt.qt_version == 'side': + from PySide.QtGui import QGuiApplication + except ImportError: + self.skipTest('QGuiApplication not installed') + self.app = QGuiApplication([]) def tearDown(self): @@ -50,6 +49,12 @@ class TestImageQt(PillowQtTestCase, PillowTestCase): # typedef QRgb # An ARGB quadruplet on the format #AARRGGBB, # equivalent to an unsigned int. + if ImageQt.qt_version == '5': + from PyQt5.QtGui import qRgb + elif ImageQt.qt_version == '4': + from PyQt4.QtGui import qRgb + elif ImageQt.qt_version == 'side': + from PySide.QtGui import qRgb self.assertEqual(qRgb(0, 0, 0), qRgba(0, 0, 0, 255)) From 33d51d4255593c825454981bc934a5cf51d79c70 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 19 Jun 2015 15:36:23 +1000 Subject: [PATCH 0370/1037] Flake8 and health fixes --- PIL/ImageQt.py | 10 +++++----- Tests/test_imageqt.py | 5 +++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/PIL/ImageQt.py b/PIL/ImageQt.py index 95819029d..52dd7d79f 100644 --- a/PIL/ImageQt.py +++ b/PIL/ImageQt.py @@ -48,7 +48,8 @@ def rgb(r, g, b, a=255): return (qRgba(r, g, b, a) & 0xffffffff) -# :param im A PIL Image object, or a file name (given either as Python string or a PyQt string object). +# :param im A PIL Image object, or a file name +# (given either as Python string or a PyQt string object) def fromqimage(im): buffer = QBuffer() @@ -139,9 +140,8 @@ if qt_is_installed: def __init__(self, im): im_data = _toqclass_helper(im) QImage.__init__(self, - im_data['data'], im_data['im'].size[0], im_data['im'].size[1], - im_data['format'] - ) + im_data['data'], im_data['im'].size[0], + im_data['im'].size[1], im_data['format']) if im_data['colortable']: self.setColorTable(im_data['colortable']) @@ -160,4 +160,4 @@ def toqpixmap(im): im = im.convert('RGBA') qimage = toqimage(im) qimage.save('/tmp/hopper_{}_qpixmap_qimage.png'.format(im.mode)) - return QPixmap.fromImage(qimage) \ No newline at end of file + return QPixmap.fromImage(qimage) diff --git a/Tests/test_imageqt.py b/Tests/test_imageqt.py index 5959bee34..b17d27e71 100644 --- a/Tests/test_imageqt.py +++ b/Tests/test_imageqt.py @@ -13,7 +13,7 @@ else: test_case.skipTest('PyQt4, PyQt5, or PySide is not installed') -class PillowQtTestCase: +class PillowQtTestCase(object): def setUp(self): skip_if_qt_is_not_installed(self) @@ -21,6 +21,7 @@ class PillowQtTestCase: def tearDown(self): pass + class PillowQPixmapTestCase(PillowQtTestCase): def setUp(self): @@ -34,7 +35,7 @@ class PillowQPixmapTestCase(PillowQtTestCase): from PySide.QtGui import QGuiApplication except ImportError: self.skipTest('QGuiApplication not installed') - + self.app = QGuiApplication([]) def tearDown(self): From 54d159bd4219d8190c4c137b93e0303f75bfd597 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 18 Jun 2015 21:20:52 -0700 Subject: [PATCH 0371/1037] multiprocess build creates race conditions in temp build directories --- winbuild/build.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/winbuild/build.py b/winbuild/build.py index 59db83416..817d70467 100644 --- a/winbuild/build.py +++ b/winbuild/build.py @@ -1,6 +1,6 @@ #/usr/bin/env python3 -import subprocess, multiprocessing +import subprocess import shutil import sys, getopt import os @@ -106,11 +106,10 @@ def main(op): compilers[(compiler_version, 64)]), footer()]))) - pool = multiprocessing.Pool() - results = pool.map(run_script, scripts) + results = map(run_script, scripts) for (version, status, trace, err) in results: - print ("Compiled %s: %s" % (version, status)) + print ("Compiled %s: %s" % (version, status and 'ERR' or 'OK')) def run_one(op): From a789c58bf36dd18274b9550a94d3e72d3e19ac18 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 18 Jun 2015 21:43:11 -0700 Subject: [PATCH 0372/1037] working webp build for both x86 and x64 --- winbuild/build_dep.py | 2 +- winbuild/config.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index 55a09c995..48927e4ea 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -185,7 +185,7 @@ setlocal cd /D %%WEBP%% rd /S /Q %%WEBP%%\output\release-static nmake -f Makefile.vc CFG=release-static RTLIBCFG=static OBJDIR=output all -copy /Y /B output\release-static\%(platform)s\lib\* %%INCLIB%% +copy /Y /B output\release-static\%(webp_platform)s\lib\* %%INCLIB%% mkdir %%INCLIB%%\webp copy /Y /B src\webp\*.h %%INCLIB%%\\webp endlocal diff --git a/winbuild/config.py b/winbuild/config.py index 73cab99ae..4cc68e49e 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -72,14 +72,16 @@ compilers = { (7,64): { 'vc_version':'2008', 'env_flags': '/x64 /xp', 'inc_dir': 'msvcr90-x64', - 'platform': 'x64' + 'platform': 'x64', + 'webp_platform': 'x64', }, (7,32): { 'env_version':'v7.0', 'vc_version':'2008', 'env_flags': '/x86 /xp', 'inc_dir': 'msvcr90-x32', - 'platform': 'Win32' + 'platform': 'Win32', + 'webp_platform': 'x86', }, (7.1,64): { @@ -88,6 +90,7 @@ compilers = { (7,64): { 'env_flags': '/x64 /vista', 'inc_dir': 'msvcr10-x64', 'platform': 'x64', + 'webp_platform': 'x64', }, (7.1,32): { 'env_version':'v7.1', @@ -95,6 +98,7 @@ compilers = { (7,64): { 'env_flags': '/x86 /vista', 'inc_dir': 'msvcr10-x32', 'platform': 'Win32', + 'webp_platform': 'x86', }, } From c644bf9455c373a1e3fdc358d347bb7a1a9d2a98 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 19 Jun 2015 15:55:35 +1000 Subject: [PATCH 0373/1037] Do not import ImageQt until it is requested --- PIL/Image.py | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 6ae0377a3..805cb0717 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -101,13 +101,12 @@ except ImportError: import __builtin__ builtins = __builtin__ -from PIL import ImageMode, ImageQt +from PIL import ImageMode from PIL._binary import i8 from PIL._util import isPath from PIL._util import isStringType from PIL._util import deferred_error - import os import sys import io @@ -1937,13 +1936,17 @@ class Image(object): im = self.im.effect_spread(distance) return self._new(im) - if ImageQt.qt_is_installed: - def toqimage(self): - return ImageQt.toqimage(self) - - def toqpixmap(self): - return ImageQt.toqpixmap(self) + def toqimage(self): + from PIL import ImageQt + if not ImageQt.qt_is_installed: + raise ImportError("Qt bindings are not installed") + return ImageQt.toqimage(self) + def toqpixmap(self): + from PIL import ImageQt + if not ImageQt.qt_is_installed: + raise ImportError("Qt bindings are not installed") + return ImageQt.toqpixmap(self) # -------------------------------------------------------------------- @@ -2195,10 +2198,19 @@ def fromarray(obj, mode=None): return frombuffer(mode, size, obj, "raw", rawmode, 0, 1) -if ImageQt.qt_is_installed: - from PIL.ImageQt import fromqimage, fromqpixmap +def fromqimage(im): + from PIL import ImageQt + if not ImageQt.qt_is_installed: + raise ImportError("Qt bindings are not installed") + return ImageQt.fromqimage(im) +def fromqpixmap(im): + from PIL import ImageQt + if not ImageQt.qt_is_installed: + raise ImportError("Qt bindings are not installed") + return ImageQt.fromqpixmap(im) + _fromarray_typemap = { # (shape, typestr) => mode, rawmode # first two members of shape are set to one From 40b659764df36c432002309aa1bea5829d22e697 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 19 Jun 2015 18:23:17 +1000 Subject: [PATCH 0374/1037] Restored deleted test --- Tests/test_imageqt.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Tests/test_imageqt.py b/Tests/test_imageqt.py index b17d27e71..3c977ff21 100644 --- a/Tests/test_imageqt.py +++ b/Tests/test_imageqt.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase, hopper from PIL import ImageQt @@ -10,7 +10,7 @@ if ImageQt.qt_is_installed: pass else: def skip_if_qt_is_not_installed(test_case): - test_case.skipTest('PyQt4, PyQt5, or PySide is not installed') + test_case.skipTest('Qt bindings are not installed') class PillowQtTestCase(object): @@ -71,6 +71,10 @@ class TestImageQt(PillowQtTestCase, PillowTestCase): checkrgb(0, 255, 0) checkrgb(0, 0, 255) + def test_image(self): + for mode in ('1', 'RGB', 'RGBA', 'L', 'P'): + ImageQt.ImageQt(hopper(mode)) + if __name__ == '__main__': unittest.main() From c1b1f184b85672bba72d75a19f2e783ef6dca079 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 19 Jun 2015 18:28:13 +1000 Subject: [PATCH 0375/1037] Updated documentation URL --- Tests/test_imageqt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_imageqt.py b/Tests/test_imageqt.py index 3c977ff21..b0fad6a45 100644 --- a/Tests/test_imageqt.py +++ b/Tests/test_imageqt.py @@ -46,7 +46,7 @@ class PillowQPixmapTestCase(PillowQtTestCase): class TestImageQt(PillowQtTestCase, PillowTestCase): def test_rgb(self): - # from https://qt-project.org/doc/qt-4.8/qcolor.html + # from https://doc.qt.io/qt-4.8/qcolor.html # typedef QRgb # An ARGB quadruplet on the format #AARRGGBB, # equivalent to an unsigned int. From 68ca91f32c05941d63f62080fb1334836c7402e8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 19 Jun 2015 19:24:21 +1000 Subject: [PATCH 0376/1037] Removed support for Tk versions earlier than 8.4 --- Tk/README.rst | 8 +++----- Tk/tkImaging.c | 55 -------------------------------------------------- 2 files changed, 3 insertions(+), 60 deletions(-) diff --git a/Tk/README.rst b/Tk/README.rst index bb5eb9c3c..dc53787f0 100644 --- a/Tk/README.rst +++ b/Tk/README.rst @@ -42,11 +42,9 @@ The Photoimage Booster Patch (for Windows 95/NT) This patch kit boosts performance for 16/24-bit displays. The first patch is required on Tk 4.2 (where it fixes the problems for -16-bit displays) and later versions, with the exception for Tk 8.0b1 -where Sun added something similar themselves, only to remove it in -8.0b2. By installing both patches, Tk's PhotoImage handling becomes -much faster on both 16-bit and 24-bit displays. The patch has been -tested with Tk 4.2 and 8.0. +16-bit displays) and later versions. By installing both patches, +Tk's PhotoImage handling becomes much faster on both 16-bit and +24-bit displays. The patch has been tested with Tk 4.2 and 8.0. Here's a benchmark, made with a sample program which loads two 512x512 greyscale PGM's, and two 512x512 colour PPM's, and displays diff --git a/Tk/tkImaging.c b/Tk/tkImaging.c index 3ebe49d48..494349a8e 100644 --- a/Tk/tkImaging.c +++ b/Tk/tkImaging.c @@ -143,60 +143,6 @@ PyImagingPhotoPut(ClientData clientdata, Tcl_Interp* interp, src_xoffset * im->pixelsize; #endif -#if TK < 84 /* < 8.4.0 */ - if (strcmp(im->mode, "RGBA") == 0) { - /* Copy non-transparent pixels to photo image */ - int x, y; - Tk_PhotoImageBlock run; - - /* Clear current contents */ - Tk_PhotoBlank(photo); - - /* Setup run descriptor */ - run.height = 1; - run.pitch = block.pitch; - run.pixelSize = block.pixelSize; - run.offset[0] = 0; - run.offset[1] = 1; - run.offset[2] = 2; - run.offset[3] = 0; /* no alpha (or reserved, under 8.2) */ - - /* Copy opaque runs to photo image */ - for (y = 0; y < block.height; y++) { - unsigned char* p = block.pixelPtr + y*block.pitch; - unsigned char* s = p; - int w = 0; - for (x = 0; x < block.width; x++) { - if (p[3]) { - /* opaque: add pixel to current run */ - if (w == 0) - s = p; - w = w + 1; - } else if (s) { - /* copy run to photo image */ - if (w > 0) { - run.width = w; - run.pixelPtr = s; - Tk_PhotoPutBlock(photo, &run, x-w, y, run.width, 1); - } - w = 0; - } - p += block.pixelSize; - } - if (w > 0) { - /* copy final run, if any */ - run.width = w; - run.pixelPtr = s; - Tk_PhotoPutBlock(photo, &run, x-w, y, run.width, 1); - } - } - - } else - - /* Copy opaque block to photo image, and leave the rest to TK */ - Tk_PhotoPutBlock(photo, &block, 0, 0, block.width, block.height); - -#else /* Tk 8.4 and newer */ #if TK < 85 /* Tk 8.4 */ Tk_PhotoPutBlock(photo, &block, 0, 0, block.width, block.height, TK_PHOTO_COMPOSITE_SET); @@ -207,7 +153,6 @@ PyImagingPhotoPut(ClientData clientdata, Tcl_Interp* interp, #else /* Tk 8.5 */ Tk_PhotoPutBlock(interp, photo, &block, 0, 0, block.width, block.height, TK_PHOTO_COMPOSITE_SET); -#endif #endif return TCL_OK; From 354eea09352da39055f63a1a1555834c31581250 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 19 Jun 2015 09:59:06 -0700 Subject: [PATCH 0377/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index cd4a286cf..fb29c8c77 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,12 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Automated windows CI/build support #1278 + [wiredfool] + +- Removed support for Tk versions earlier than 8.4 #1288 + [radarhere] + - Fixed polygon edge drawing #1255 (fixes #1252) [radarhere] From 5f143a73a3f07a90651a1318d1a9a2330c369fd1 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 19 Jun 2015 15:32:08 -0700 Subject: [PATCH 0378/1037] lcms2 for everything but VC2008/64 --- winbuild/build_dep.py | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index 48927e4ea..adc27b073 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -237,15 +237,36 @@ endlocal """ %compiler def build_lcms2(compiler): + if compiler['env_version'] == 'v7.1': + return build_lcms_71(compiler) + return build_lcms_70(compiler) + +def build_lcms_70(compiler): + """Link error here on x64""" + if compiler['platform'] == 'x64': return '' + + """Build LCMS on VC2008. This version is only 32bit/Win32""" return r""" rem Build lcms2 setlocal rd /S /Q %%LCMS%%\Lib rd /S /Q %%LCMS%%\Projects\VC%(vc_version)s\Release -%%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:Clean /p:Configuration="Release" /p:Platform=%(platform)s /m -%%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:lcms2_static /p:Configuration="Release" /p:Platform=%(platform)s /m +%%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:Clean /p:Configuration="Release" /p:Platform=Win32 /m +%%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:lcms2_static /p:Configuration="Release" /p:Platform=Win32 /m xcopy /Y /E /Q %%LCMS%%\include %%INCLIB%% copy /Y /B %%LCMS%%\Projects\VC%(vc_version)s\Release\*.lib %%INCLIB%% +endlocal +""" % compiler + +def build_lcms_71(compiler): + return r""" +rem Build lcms2 +setlocal +rd /S /Q %%LCMS%%\Lib +rd /S /Q %%LCMS%%\Projects\VC%(vc_version)s\Release +%%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:Clean /p:Configuration="Release" /p:Platform=%(platform)s /m +%%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:lcms2_static /p:Configuration="Release" /p:Platform=%(platform)s /m +xcopy /Y /E /Q %%LCMS%%\include %%INCLIB%% copy /Y /B %%LCMS%%\Lib\MS\*.lib %%INCLIB%% endlocal """ % compiler @@ -258,7 +279,7 @@ def add_compiler(compiler): #script.append(extract_openjpeg(compiler)) script.append(msbuild_freetype(compiler)) - #script.append(build_lcms2(compiler)) + script.append(build_lcms2(compiler)) #script.append(nmake_openjpeg(compiler)) script.append(end_compiler()) @@ -267,16 +288,17 @@ def add_compiler(compiler): mkdirs() fetch_libs() #extract_binlib() -script = [header()] #, cp_tk()] +script = [header(), cp_tk()] if 'PYTHON' in os.environ: add_compiler(compiler_fromEnv()) else: - for compiler in compilers.values(): - add_compiler(compiler) - #add_compiler(compilers[(7,32)]) + #for compiler in compilers.values(): + #add_compiler(compiler) + add_compiler(compilers[(7.1,32)]) + #add_compiler(compilers[(7.1,64)]) with open('build_deps.cmd', 'w') as f: f.write("\n".join(script)) From 4aa925d749f5377cbb42112e7153eaa333c5f183 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 19 Jun 2015 16:27:42 -0700 Subject: [PATCH 0379/1037] tcl support --- winbuild/build.py | 7 ++++++- winbuild/build_dep.py | 7 ++++++- winbuild/config.py | 14 ++++++++++++-- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/winbuild/build.py b/winbuild/build.py index 817d70467..eee4a4b07 100644 --- a/winbuild/build.py +++ b/winbuild/build.py @@ -67,12 +67,17 @@ def build_one(py_ver, compiler): else: args['python_path'] = "%s%s\\Scripts" % (VIRT_BASE, py_ver) args['py_ver'] = py_ver + if '34' in py_ver: + args['tcl_ver'] = '86' + else: + args['tcl_ver'] = '85' + return r""" setlocal EnableDelayedExpansion call "%%ProgramFiles%%\Microsoft SDKs\Windows\%(env_version)s\Bin\SetEnv.Cmd" /Release %(env_flags)s set DISTUTILS_USE_SDK=1 set LIB=%%LIB%%;%%INCLIB%%\%(inc_dir)s -set INCLUDE=%%INCLUDE%%;%%INCLIB%%\%(inc_dir)s;%%INCLIB%%\tcl85\include +set INCLUDE=%%INCLUDE%%;%%INCLIB%%\%(inc_dir)s;%%INCLIB%%\tcl%(tcl_ver)s\include setlocal set LIB=%%LIB%%;C:\Python%(py_ver)s\tcl diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index adc27b073..7a35066ce 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -95,6 +95,11 @@ mkdir %INCLIB%\tcl85\include\X11 copy /Y /B %BUILD%\tcl8.5.13\generic\*.h %INCLIB%\tcl85\include\ copy /Y /B %BUILD%\tk8.5.13\generic\*.h %INCLIB%\tcl85\include\ copy /Y /B %BUILD%\tk8.5.13\xlib\X11\* %INCLIB%\tcl85\include\X11\ + +mkdir %INCLIB%\tcl86\include\X11 +copy /Y /B %BUILD%\tcl8.6.4\generic\*.h %INCLIB%\tcl86\include\ +copy /Y /B %BUILD%\tk8.6.4\generic\*.h %INCLIB%\tcl86\include\ +copy /Y /B %BUILD%\tk8.6.4\xlib\X11\* %INCLIB%\tcl86\include\X11\ """ def header(): @@ -297,7 +302,7 @@ if 'PYTHON' in os.environ: else: #for compiler in compilers.values(): #add_compiler(compiler) - add_compiler(compilers[(7.1,32)]) + add_compiler(compilers[(7.0,32)]) #add_compiler(compilers[(7.1,64)]) with open('build_deps.cmd', 'w') as f: diff --git a/winbuild/config.py b/winbuild/config.py index 4cc68e49e..08d8920e9 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -36,15 +36,25 @@ libs = { 'zlib':{ 'hash': 'sha1:7ff1a5b721ca719760ba6eb4ec6f38d5e65381cf', 'dir': 'lcms2-2.7', }, - 'tcl':{ + 'tcl-8.5':{ 'url':SF_MIRROR+'/project/tcl/Tcl/8.5.13/tcl8513-src.zip', 'hash': 'sha1:3e01585c91293c532a3cd594ec59deca92153a5e', 'dir': '', }, - 'tk':{ + 'tk-8.5':{ 'url':SF_MIRROR+'/project/tcl/Tcl/8.5.13/tk8513-src.zip', 'hash': 'sha1:23a1d7ddd416e11e06dfdb9f86111d4bab9420b4', 'dir': '', + }, + 'tcl-8.6':{ + 'url':SF_MIRROR+'/project/tcl/Tcl/8.6.4/tcl864-src.zip', + 'hash': 'md5:35748d2fc61e08a2fdb23b85c6f8c4a0', + 'dir': '', + }, + 'tk-8.6':{ + 'url':SF_MIRROR+'/project/tcl/Tcl/8.6.4/tk864-src.zip', + 'hash': 'md5:111d45061a69e7f5250b6ec8ca7c4f35', + 'dir': '', }, 'webp':{ 'url':'http://downloads.webmproject.org/releases/webp/libwebp-0.4.3.tar.gz', From 8a7080cbce0d01212739b788ecb340cd9055d48a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 20 Jun 2015 11:34:34 +1000 Subject: [PATCH 0380/1037] Health fixes --- winbuild/build.py | 2 +- winbuild/build_dep.py | 22 ++++++---------------- winbuild/config.py | 2 +- winbuild/unzip.py | 6 +++--- 4 files changed, 11 insertions(+), 21 deletions(-) diff --git a/winbuild/build.py b/winbuild/build.py index eee4a4b07..eb98965b5 100644 --- a/winbuild/build.py +++ b/winbuild/build.py @@ -45,7 +45,7 @@ def run_script(params): def header(op): - return r""" + return r""" setlocal set MPLSRC=%%~dp0\.. set INCLIB=%%~dp0\depends diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index 7a35066ce..acd6a8669 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -2,7 +2,6 @@ from fetch import fetch from unzip import unzip from untar import untar import os, hashlib -import shutil from config import * @@ -33,16 +32,16 @@ def check_sig(filename, signame): def mkdirs(): try: os.mkdir(build_dir) - except: + except OSError: pass try: os.mkdir(inc_dir) - except: + except OSError: pass for compiler in compilers.values(): try: os.mkdir(os.path.join(inc_dir, compiler['inc_dir'])) - except: + except OSError: pass def extract(src, dest): @@ -66,15 +65,6 @@ def fetch_libs(): def extract_binlib(): lib = bin_libs['openjpeg'] extract(lib['filename'], build_dir) - return - base = os.path.splitext(lib['filename'])[0] - for compiler in compilers.values(): - shutil.copy(os.path.join(inc_dir, base, 'include', 'openjpeg-%s' % lib['version']), - os.path.join(inc_dir, compiler['inc_dir'])) - shutil.copy(os.path.join(inc_dir, base, 'bin', 'openjp2.dll'), - os.path.join(inc_dir, compiler['inc_dir'])) - shutil.copy(os.path.join(inc_dir, base, 'lib', 'openjp2.lib'), - os.path.join(inc_dir, compiler['inc_dir'])) def extract_openjpeg(compiler): return r""" @@ -212,9 +202,9 @@ endlocal """ % compiler def msbuild_freetype(compiler): - if compiler['env_version'] == 'v7.1': - return msbuild_freetype_71(compiler) - return msbuild_freetype_70(compiler) + if compiler['env_version'] == 'v7.1': + return msbuild_freetype_71(compiler) + return msbuild_freetype_70(compiler) def msbuild_freetype_71(compiler): return r""" diff --git a/winbuild/config.py b/winbuild/config.py index 08d8920e9..a80e9317d 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -118,7 +118,7 @@ def pyversion_fromEnv(): py = os.environ['PYTHON'] py_version = '27' - for k,v in pythons.items(): + for k in pythons.keys(): if k in py: py_version = k break diff --git a/winbuild/unzip.py b/winbuild/unzip.py index 18588433c..56e6a768e 100644 --- a/winbuild/unzip.py +++ b/winbuild/unzip.py @@ -1,8 +1,8 @@ import sys, zipfile def unzip(src, dest): - with zipfile.ZipFile(src) as zf: - zf.extractall(dest) + with zipfile.ZipFile(src) as zf: + zf.extractall(dest) if __name__=='__main__': - unzip(sys.argv[1], sys.argv[2]) + unzip(sys.argv[1], sys.argv[2]) From bbf3b52357650fe90058f9d181be15642a6b49cb Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 20 Jun 2015 13:43:14 +1000 Subject: [PATCH 0381/1037] Flake8 fixes --- Tests/check_jpeg_leaks.py | 1 - Tests/helper.py | 1 + Tests/test_imagefont.py | 2 +- setup.py | 1 - winbuild/build.py | 61 +++++++++++---------- winbuild/build_dep.py | 107 ++++++++++++++++++++---------------- winbuild/config.py | 111 +++++++++++++++++++------------------- winbuild/fetch.py | 10 ++-- winbuild/get_pythons.py | 6 +-- winbuild/test.py | 17 +++--- winbuild/untar.py | 8 +-- winbuild/unzip.py | 6 ++- 12 files changed, 180 insertions(+), 151 deletions(-) diff --git a/Tests/check_jpeg_leaks.py b/Tests/check_jpeg_leaks.py index 7204b1edb..1d05cd975 100644 --- a/Tests/check_jpeg_leaks.py +++ b/Tests/check_jpeg_leaks.py @@ -105,7 +105,6 @@ post-patch: test_output = BytesIO() im.save(test_output, "JPEG", qtables=qtables) - def test_exif_leak(self): """ pre patch: diff --git a/Tests/helper.py b/Tests/helper.py index 44071c835..83d86b5d9 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -233,6 +233,7 @@ def netpbm_available(): def imagemagick_available(): return IMCONVERT and command_succeeds([IMCONVERT, '-version']) + def on_appveyor(): return 'APPVEYOR' in os.environ diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 88858c717..168538185 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -95,7 +95,7 @@ try: txt = "Hello World!" ttf = ImageFont.truetype(font, FONT_SIZE) ttf.getsize(txt) - + img = Image.new("RGB", (256, 64), "white") d = ImageDraw.Draw(img) d.text((10, 10), txt, font=ttf, fill='black') diff --git a/setup.py b/setup.py index 0674fa768..f669873d6 100644 --- a/setup.py +++ b/setup.py @@ -758,4 +758,3 @@ setup( zip_safe=not debug_build(), ) # End of file - diff --git a/winbuild/build.py b/winbuild/build.py index eb98965b5..9e5e0ea1c 100644 --- a/winbuild/build.py +++ b/winbuild/build.py @@ -2,11 +2,13 @@ import subprocess import shutil -import sys, getopt +import sys +import getopt import os from config import * + def setup_vms(): ret = [] for py in pythons.keys(): @@ -20,29 +22,30 @@ def setup_vms(): (VIRT_BASE, py, arch)) return "\n".join(ret) + def run_script(params): (version, script) = params try: - print ("Running %s" %version) + print("Running %s" % version) filename = 'build_pillow_%s.cmd' % version with open(filename, 'w') as f: f.write(script) - command = ['powershell', "./%s" %filename] - proc = subprocess.Popen(command, + command = ['powershell', "./%s" % filename] + proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) (trace, stderr) = proc.communicate() status = proc.returncode - print (stderr) - print ("Done with %s: %s" % (version, status)) + print(stderr) + print("Done with %s: %s" % (version, status)) return (version, status, trace, stderr) except Exception as msg: - print ("Error with %s: %s" % (version, str(msg))) + print("Error with %s: %s" % (version, str(msg))) return (version, -1, "", str(msg)) - + def header(op): return r""" @@ -51,19 +54,21 @@ set MPLSRC=%%~dp0\.. set INCLIB=%%~dp0\depends set BLDOPT=%s cd /D %%MPLSRC%% -""" % (op) +""" % (op) + def footer(): return """endlocal exit """ + def build_one(py_ver, compiler): # UNDONE virtual envs if we're not running on appveyor args = {} args.update(compiler) if 'PYTHON' in os.environ: - args['python_path'] = "%PYTHON%" + args['python_path'] = "%PYTHON%" else: args['python_path'] = "%s%s\\Scripts" % (VIRT_BASE, py_ver) args['py_ver'] = py_ver @@ -71,7 +76,7 @@ def build_one(py_ver, compiler): args['tcl_ver'] = '86' else: args['tcl_ver'] = '85' - + return r""" setlocal EnableDelayedExpansion call "%%ProgramFiles%%\Microsoft SDKs\Windows\%(env_version)s\Bin\SetEnv.Cmd" /Release %(env_flags)s @@ -87,6 +92,7 @@ endlocal endlocal """ % args + def clean(): try: shutil.rmtree('../build') @@ -95,49 +101,48 @@ def clean(): pass run_script(('virtualenvs', setup_vms())) + def main(op): scripts = [] for py_version, compiler_version in pythons.items(): - scripts.append((py_version, + scripts.append((py_version, "\n".join([header(op), - build_one(py_version, + build_one(py_version, compilers[(compiler_version, 32)]), footer()]))) - + scripts.append(("%s%s" % (py_version, X64_EXT), - "\n".join([header(op), - build_one("%sx64" %py_version, + "\n".join([header(op), + build_one("%sx64" % py_version, compilers[(compiler_version, 64)]), footer()]))) - + results = map(run_script, scripts) - + for (version, status, trace, err) in results: - print ("Compiled %s: %s" % (version, status and 'ERR' or 'OK')) - + print("Compiled %s: %s" % (version, status and 'ERR' or 'OK')) + + def run_one(op): - + compiler = compiler_fromEnv() py_version = pyversion_fromEnv() - run_script((py_version, "\n".join([header(op), build_one(py_version, compiler), footer()]) - )) - - + )) -if __name__=='__main__': +if __name__ == '__main__': opts, args = getopt.getopt(sys.argv[1:], '', ['clean', 'dist']) - opts = dict(opts) + opts = dict(opts) if '--clean' in opts: clean() - + op = 'install' if '--dist' in opts: op = "bdist_wininst --user-access-control=auto" diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index acd6a8669..a837b31f5 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -1,12 +1,15 @@ from fetch import fetch from unzip import unzip from untar import untar -import os, hashlib +import os +import hashlib from config import * + def _relpath(*args): - return os.path.join(os.getcwd(),*args) + return os.path.join(os.getcwd(), *args) + def _relbuild(*args): return _relpath('build', *args) @@ -14,21 +17,25 @@ def _relbuild(*args): build_dir = _relpath('build') inc_dir = _relpath('depends') + def check_hash(filename, checksum): - if not checksum: return filename - + if not checksum: + return filename + (algo, value) = checksum.split(':') h = hashlib.new(algo) with open(filename, 'rb') as f: h.update(f.read()) if not(h.hexdigest().lower() == value): - raise ValueError('Checksum Mismatch for %s' %filename) + raise ValueError('Checksum Mismatch for %s' % filename) return filename + def check_sig(filename, signame): - #UNDONE -- need gpg + # UNDONE -- need gpg return filename + def mkdirs(): try: os.mkdir(build_dir) @@ -44,28 +51,32 @@ def mkdirs(): except OSError: pass + def extract(src, dest): if '.zip' in src: return unzip(src, dest) if '.tar.gz' in src or '.tgz' in src: return untar(src, dest) + def fetch_libs(): - for name,lib in libs.items(): + for name, lib in libs.items(): if name == 'openjpeg': - filename = check_hash(fetch(lib['url']),lib['hash']) + filename = check_hash(fetch(lib['url']), lib['hash']) for compiler in compilers.values(): - if not os.path.exists(os.path.join(build_dir,lib['dir']+compiler['inc_dir'])): + if not os.path.exists(os.path.join(build_dir, lib['dir']+compiler['inc_dir'])): extract(filename, build_dir) - os.rename(os.path.join(build_dir,lib['dir']), - os.path.join(build_dir,lib['dir']+compiler['inc_dir'])) + os.rename(os.path.join(build_dir, lib['dir']), + os.path.join(build_dir, lib['dir']+compiler['inc_dir'])) else: - extract(check_hash(fetch(lib['url']),lib['hash']),build_dir) + extract(check_hash(fetch(lib['url']), lib['hash']), build_dir) + def extract_binlib(): lib = bin_libs['openjpeg'] extract(lib['filename'], build_dir) + def extract_openjpeg(compiler): return r""" rem build openjpeg @@ -78,7 +89,8 @@ copy /Y /B openjpeg-2.0.0-win32-x86\bin\ %%INCLIB%% copy /Y /B openjpeg-2.0.0-win32-x86\lib\ %%INCLIB%% endlocal """ % compiler - + + def cp_tk(): return r""" mkdir %INCLIB%\tcl85\include\X11 @@ -92,26 +104,30 @@ copy /Y /B %BUILD%\tk8.6.4\generic\*.h %INCLIB%\tcl86\include\ copy /Y /B %BUILD%\tk8.6.4\xlib\X11\* %INCLIB%\tcl86\include\X11\ """ + def header(): return r"""setlocal set MSBUILD=C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe set CMAKE="cmake.exe" set INCLIB=%~dp0\depends set BUILD=%~dp0\build -""" + "\n".join('set %s=%%BUILD%%\%s' %(k.upper(), v['dir']) - for (k,v) in libs.items() if v['dir']) - +""" + "\n".join('set %s=%%BUILD%%\%s' % (k.upper(), v['dir']) + for (k, v) in libs.items() if v['dir']) + + def setup_compiler(compiler): return r"""setlocal EnableDelayedExpansion call "%%ProgramFiles%%\Microsoft SDKs\Windows\%(env_version)s\Bin\SetEnv.Cmd" /Release %(env_flags)s set INCLIB=%%INCLIB%%\%(inc_dir)s """ % compiler + def end_compiler(): return """ endlocal """ - + + def nmake_openjpeg(compiler): atts = {'op_ver': '2.1'} atts.update(compiler) @@ -129,6 +145,7 @@ copy /Y /B src\lib\openjp2\*.h %%INCLIB%%\openjpeg-%(op_ver)s endlocal """ % atts + def msbuild_openjpeg(compiler): atts = {'op_ver': '2.1'} atts.update(compiler) @@ -148,7 +165,6 @@ endlocal """ % atts - def nmake_libs(compiler): # undone -- pre, makes, headers, libs return r""" @@ -200,22 +216,25 @@ endlocal """ % compiler - + + def msbuild_freetype(compiler): if compiler['env_version'] == 'v7.1': return msbuild_freetype_71(compiler) return msbuild_freetype_70(compiler) - + + def msbuild_freetype_71(compiler): return r""" rem Build freetype setlocal rd /S /Q %%FREETYPE%%\objs -%%MSBUILD%% %%FREETYPE%%\builds\windows\vc%(vc_version)s\freetype.sln /t:Clean;Build /p:Configuration="Release" /p:Platform=%(platform)s /m +%%MSBUILD%% %%FREETYPE%%\builds\windows\vc%(vc_version)s\freetype.sln /t:Clean;Build /p:Configuration="Release" /p:Platform=%(platform)s /m xcopy /Y /E /Q %%FREETYPE%%\include %%INCLIB%% copy /Y /B %%FREETYPE%%\objs\vc%(vc_version)s\%(platform)s\*.lib %%INCLIB%%\freetype.lib endlocal -""" %compiler +""" % compiler + def msbuild_freetype_70(compiler): return r""" @@ -224,22 +243,25 @@ setlocal py -3 %%~dp0\fixproj.py %%FREETYPE%%\builds\windows\vc%(vc_version)s\freetype.sln %(platform)s py -3 %%~dp0\fixproj.py %%FREETYPE%%\builds\windows\vc%(vc_version)s\freetype.vcproj %(platform)s rd /S /Q %%FREETYPE%%\objs -%%MSBUILD%% %%FREETYPE%%\builds\windows\vc%(vc_version)s\freetype.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=%(platform)s /m +%%MSBUILD%% %%FREETYPE%%\builds\windows\vc%(vc_version)s\freetype.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=%(platform)s /m xcopy /Y /E /Q %%FREETYPE%%\include %%INCLIB%% xcopy /Y /E /Q %%FREETYPE%%\objs\win32\vc%(vc_version)s %%INCLIB%% copy /Y /B %%FREETYPE%%\objs\win32\vc%(vc_version)s\*.lib %%INCLIB%%\freetype.lib endlocal -""" %compiler +""" % compiler + def build_lcms2(compiler): - if compiler['env_version'] == 'v7.1': - return build_lcms_71(compiler) - return build_lcms_70(compiler) + if compiler['env_version'] == 'v7.1': + return build_lcms_71(compiler) + return build_lcms_70(compiler) + def build_lcms_70(compiler): """Link error here on x64""" - if compiler['platform'] == 'x64': return '' - + if compiler['platform'] == 'x64': + return '' + """Build LCMS on VC2008. This version is only 32bit/Win32""" return r""" rem Build lcms2 @@ -253,6 +275,7 @@ copy /Y /B %%LCMS%%\Projects\VC%(vc_version)s\Release\*.lib %%INCLIB%% endlocal """ % compiler + def build_lcms_71(compiler): return r""" rem Build lcms2 @@ -271,33 +294,27 @@ def add_compiler(compiler): script.append(setup_compiler(compiler)) script.append(nmake_libs(compiler)) - #script.append(extract_openjpeg(compiler)) - + # script.append(extract_openjpeg(compiler)) + script.append(msbuild_freetype(compiler)) script.append(build_lcms2(compiler)) - #script.append(nmake_openjpeg(compiler)) + # script.append(nmake_openjpeg(compiler)) script.append(end_compiler()) - mkdirs() fetch_libs() -#extract_binlib() +# extract_binlib() script = [header(), cp_tk()] - if 'PYTHON' in os.environ: add_compiler(compiler_fromEnv()) else: - #for compiler in compilers.values(): - #add_compiler(compiler) - add_compiler(compilers[(7.0,32)]) - #add_compiler(compilers[(7.1,64)]) - -with open('build_deps.cmd', 'w') as f: + # for compiler in compilers.values(): + # add_compiler(compiler) + add_compiler(compilers[(7.0, 32)]) + # add_compiler(compilers[(7.1, 64)]) + +with open('build_deps.cmd', 'w') as f: f.write("\n".join(script)) - - - - diff --git a/winbuild/config.py b/winbuild/config.py index a80e9317d..ffd8079fe 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -2,109 +2,110 @@ import os SF_MIRROR = 'http://iweb.dl.sourceforge.net' -pythons = {#'26':7, - '27':7, - #'32':7, - '33':7.1, - '34':7.1} +pythons = {#'26': 7, + '27': 7, + #'32': 7, + '33': 7.1, + '34': 7.1} VIRT_BASE = "c:/vp/" -X64_EXT = os.environ.get('X64_EXT',"x64") +X64_EXT = os.environ.get('X64_EXT', "x64") -libs = { 'zlib':{ - 'url':'http://zlib.net/zlib128.zip', +libs = {'zlib': { + 'url': 'http://zlib.net/zlib128.zip', 'hash': 'md5:126f8676442ffbd97884eb4d6f32afb4', 'dir': 'zlib-1.2.8', }, - 'jpeg':{ - 'url':'http://www.ijg.org/files/jpegsr9a.zip', - 'hash': 'md5:a34f3c82760270ee1e1885b15b90a72e', # not found - generated by wiredfool + 'jpeg': { + 'url': 'http://www.ijg.org/files/jpegsr9a.zip', + 'hash': 'md5:a34f3c82760270ee1e1885b15b90a72e', # not found - generated by wiredfool 'dir': 'jpeg-9a', }, - 'tiff':{ - 'url':'ftp://ftp.remotesensing.org/pub/libtiff/tiff-4.0.3.zip', - 'hash': 'md5:dd70349cedb3981371686e1c9b89a7f9', # not found - generated by wiredfool + 'tiff': { + 'url': 'ftp://ftp.remotesensing.org/pub/libtiff/tiff-4.0.3.zip', + 'hash': 'md5:dd70349cedb3981371686e1c9b89a7f9', # not found - generated by wiredfool 'dir': 'tiff-4.0.3', }, - 'freetype':{ - 'url': 'http://download.savannah.gnu.org/releases/freetype/freetype-2.6.tar.gz', - 'hash':'md5:1d733ea6c1b7b3df38169fbdbec47d2b', - 'dir': 'freetype-2.6', + 'freetype': { + 'url': 'http://download.savannah.gnu.org/releases/freetype/freetype-2.6.tar.gz', + 'hash': 'md5:1d733ea6c1b7b3df38169fbdbec47d2b', + 'dir': 'freetype-2.6', }, - 'lcms':{ - 'url':SF_MIRROR+'/project/lcms/lcms/2.7/lcms2-2.7.zip', + 'lcms': { + 'url': SF_MIRROR+'/project/lcms/lcms/2.7/lcms2-2.7.zip', 'hash': 'sha1:7ff1a5b721ca719760ba6eb4ec6f38d5e65381cf', 'dir': 'lcms2-2.7', }, - 'tcl-8.5':{ - 'url':SF_MIRROR+'/project/tcl/Tcl/8.5.13/tcl8513-src.zip', + 'tcl-8.5': { + 'url': SF_MIRROR+'/project/tcl/Tcl/8.5.13/tcl8513-src.zip', 'hash': 'sha1:3e01585c91293c532a3cd594ec59deca92153a5e', 'dir': '', }, - 'tk-8.5':{ - 'url':SF_MIRROR+'/project/tcl/Tcl/8.5.13/tk8513-src.zip', + 'tk-8.5': { + 'url': SF_MIRROR+'/project/tcl/Tcl/8.5.13/tk8513-src.zip', 'hash': 'sha1:23a1d7ddd416e11e06dfdb9f86111d4bab9420b4', 'dir': '', }, - 'tcl-8.6':{ - 'url':SF_MIRROR+'/project/tcl/Tcl/8.6.4/tcl864-src.zip', + 'tcl-8.6': { + 'url': SF_MIRROR+'/project/tcl/Tcl/8.6.4/tcl864-src.zip', 'hash': 'md5:35748d2fc61e08a2fdb23b85c6f8c4a0', 'dir': '', }, - 'tk-8.6':{ - 'url':SF_MIRROR+'/project/tcl/Tcl/8.6.4/tk864-src.zip', + 'tk-8.6': { + 'url': SF_MIRROR+'/project/tcl/Tcl/8.6.4/tk864-src.zip', 'hash': 'md5:111d45061a69e7f5250b6ec8ca7c4f35', 'dir': '', }, - 'webp':{ - 'url':'http://downloads.webmproject.org/releases/webp/libwebp-0.4.3.tar.gz', - 'hash':'sha1:1c307a61c4d0018620b4ba9a58e8f48a8d6640ef', - 'dir':'libwebp-0.4.3', + 'webp': { + 'url': 'http://downloads.webmproject.org/releases/webp/libwebp-0.4.3.tar.gz', + 'hash': 'sha1:1c307a61c4d0018620b4ba9a58e8f48a8d6640ef', + 'dir': 'libwebp-0.4.3', + }, - 'openjpeg':{ - 'url':SF_MIRROR+'/project/openjpeg/openjpeg/2.1.0/openjpeg-2.1.0.tar.gz', - 'hash':'md5:f6419fcc233df84f9a81eb36633c6db6', - 'dir':'openjpeg-2.1.0', + 'openjpeg': { + 'url': SF_MIRROR+'/project/openjpeg/openjpeg/2.1.0/openjpeg-2.1.0.tar.gz', + 'hash': 'md5:f6419fcc233df84f9a81eb36633c6db6', + 'dir': 'openjpeg-2.1.0', }, } bin_libs = { - 'openjpeg':{ - 'filename':'openjpeg-2.0.0-win32-x86.zip', - 'hash':'sha1:xxx', - 'version':'2.0' - }, + 'openjpeg': { + 'filename': 'openjpeg-2.0.0-win32-x86.zip', + 'hash': 'sha1:xxx', + 'version': '2.0' + }, } -compilers = { (7,64): { - 'env_version':'v7.0', - 'vc_version':'2008', +compilers = {(7, 64): { + 'env_version': 'v7.0', + 'vc_version': '2008', 'env_flags': '/x64 /xp', 'inc_dir': 'msvcr90-x64', 'platform': 'x64', 'webp_platform': 'x64', }, - (7,32): { - 'env_version':'v7.0', - 'vc_version':'2008', + (7, 32): { + 'env_version': 'v7.0', + 'vc_version': '2008', 'env_flags': '/x86 /xp', 'inc_dir': 'msvcr90-x32', 'platform': 'Win32', 'webp_platform': 'x86', }, - (7.1,64): { - 'env_version':'v7.1', - 'vc_version':'2010', + (7.1, 64): { + 'env_version': 'v7.1', + 'vc_version': '2010', 'env_flags': '/x64 /vista', 'inc_dir': 'msvcr10-x64', 'platform': 'x64', 'webp_platform': 'x64', }, - (7.1,32): { - 'env_version':'v7.1', - 'vc_version':'2010', + (7.1, 32): { + 'env_version': 'v7.1', + 'vc_version': '2010', 'env_flags': '/x86 /vista', 'inc_dir': 'msvcr10-x32', 'platform': 'Win32', @@ -128,11 +129,11 @@ def pyversion_fromEnv(): return py_version - + def compiler_fromEnv(): py = os.environ['PYTHON'] - for k,v in pythons.items(): + for k, v in pythons.items(): if k in py: compiler_version = v break diff --git a/winbuild/fetch.py b/winbuild/fetch.py index dfedb9835..1ab3e7208 100644 --- a/winbuild/fetch.py +++ b/winbuild/fetch.py @@ -1,4 +1,8 @@ -import sys, os, urllib.parse, urllib.request +import sys +import os +import urllib.parse +import urllib.request + def fetch(url): name = urllib.parse.urlsplit(url)[2].split('/')[-1] @@ -10,7 +14,5 @@ def fetch(url): fd.write(content) return name -if __name__=='__main__': +if __name__ == '__main__': fetch(sys.argv[1]) - - diff --git a/winbuild/get_pythons.py b/winbuild/get_pythons.py index 19c5ddd40..04d8591ac 100644 --- a/winbuild/get_pythons.py +++ b/winbuild/get_pythons.py @@ -1,11 +1,11 @@ from fetch import fetch import os -if __name__=='__main__': +if __name__ == '__main__': for version in ['2.6.5', '2.7.6', '3.2.5', '3.3.5', '3.4.3']: for platform in ['', '.amd64']: - for extension in ['','.asc']: - fetch('https://www.python.org/ftp/python/%s/python-%s%s.msi%s' %( + for extension in ['', '.asc']: + fetch('https://www.python.org/ftp/python/%s/python-%s%s.msi%s' % ( version, version, platform, extension)) # find pip, if it's not in the path! diff --git a/winbuild/test.py b/winbuild/test.py index 69ab7e427..3310fb556 100644 --- a/winbuild/test.py +++ b/winbuild/test.py @@ -7,38 +7,39 @@ import sys from config import * + def test_one(params): python, architecture = params try: - print ("Running: %s, %s" %params) + print("Running: %s, %s" % params) command = [r'%s\%s%s\Scripts\python.exe' % (VIRT_BASE, python, architecture), 'test-installed.py', '--processes=-0', '--process-timeout=30', ] command.extend(glob.glob('Tests/test*.py')) - proc = subprocess.Popen(command, + proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE) (trace, stderr) = proc.communicate() status = proc.returncode - print ("Done with %s, %s -- %s" % (python, architecture, status )) + print("Done with %s, %s -- %s" % (python, architecture, status)) return (python, architecture, status, trace) except Exception as msg: - print ("Error with %s, %s: %s" % (python, architecture, msg)) + print("Error with %s, %s: %s" % (python, architecture, msg)) return (python, architecture, -1, str(msg)) -if __name__=='__main__': +if __name__ == '__main__': os.chdir('..') - matrix = [(python, architecture) for python in pythons - for architecture in ('', X64_EXT)] + matrix = [(python, architecture) for python in pythons + for architecture in ('', X64_EXT)] results = map(test_one, matrix) for (python, architecture, status, trace) in results: - print ("%s%s: %s" % (python, architecture, status and 'ERR' or 'PASS')) + print("%s%s: %s" % (python, architecture, status and 'ERR' or 'PASS')) res = all(status for (python, architecture, status, trace) in results) sys.exit(res) diff --git a/winbuild/untar.py b/winbuild/untar.py index 412ba4bf6..95b1c2254 100644 --- a/winbuild/untar.py +++ b/winbuild/untar.py @@ -1,8 +1,10 @@ -import sys, tarfile +import sys +import tarfile + def untar(src, dest): with tarfile.open(src, 'r:gz') as tgz: tgz.extractall(dest) -if __name__=='__main__': - untar(sys.argv[1],sys.argv[2]) +if __name__ == '__main__': + untar(sys.argv[1], sys.argv[2]) diff --git a/winbuild/unzip.py b/winbuild/unzip.py index 56e6a768e..92d385fa6 100644 --- a/winbuild/unzip.py +++ b/winbuild/unzip.py @@ -1,8 +1,10 @@ -import sys, zipfile +import sys +import zipfile + def unzip(src, dest): with zipfile.ZipFile(src) as zf: zf.extractall(dest) -if __name__=='__main__': +if __name__ == '__main__': unzip(sys.argv[1], sys.argv[2]) From 6d7eb53b52aab1b169cb33a851069db355506066 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 20 Jun 2015 23:52:02 +1000 Subject: [PATCH 0382/1037] Changed gifmaker script to use ImageSequence Iterator --- Scripts/gifmaker.py | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/Scripts/gifmaker.py b/Scripts/gifmaker.py index 28c6fb25d..512b0720f 100644 --- a/Scripts/gifmaker.py +++ b/Scripts/gifmaker.py @@ -42,26 +42,10 @@ from __future__ import print_function -from PIL import Image, ImageChops +from PIL import Image, ImageChops, ImageSequence from PIL.GifImagePlugin import getheader, getdata -# -------------------------------------------------------------------- -# sequence iterator - - -class image_sequence(object): - def __init__(self, im): - self.im = im - - def __getitem__(self, ix): - try: - if ix: - self.im.seek(ix) - return self.im - except EOFError: - raise IndexError # end of sequence - # -------------------------------------------------------------------- # straightforward delta encoding @@ -122,7 +106,7 @@ def compress(infile, outfile): # open output file fp = open(outfile, "wb") - seq = image_sequence(im) + seq = ImageSequence.Iterator(im) makedelta(fp, seq) From 8dc8a1235e1c02a4946cfc28d2754965c941c482 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 20 Jun 2015 21:06:42 +1000 Subject: [PATCH 0383/1037] Added Python 3 support to gifmaker script --- Scripts/gifmaker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/gifmaker.py b/Scripts/gifmaker.py index 512b0720f..bf162eb2f 100644 --- a/Scripts/gifmaker.py +++ b/Scripts/gifmaker.py @@ -89,7 +89,7 @@ def makedelta(fp, sequence): frames += 1 - fp.write(";") + fp.write(b";") return frames From c75a61407d5322023d74c9e5f8004ab884132734 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sat, 20 Jun 2015 13:59:55 -0700 Subject: [PATCH 0384/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index fb29c8c77..fac51d630 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,9 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Multiline text in ImageDraw #1177 + [radarhere] + - Automated windows CI/build support #1278 [wiredfool] From b553ad7a70549b03362d25fda12eef6958168fa2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 21 Jun 2015 16:31:51 +1000 Subject: [PATCH 0385/1037] Further fixes --- PIL/Image.py | 4 ++++ PIL/ImageQt.py | 21 +++++++++++---------- Tests/test_image_toqimage.py | 6 +++++- Tests/test_image_toqpixmap.py | 9 ++++++--- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 805cb0717..a6b08d196 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1937,12 +1937,14 @@ class Image(object): return self._new(im) def toqimage(self): + """Returns a QImage copy of this image""" from PIL import ImageQt if not ImageQt.qt_is_installed: raise ImportError("Qt bindings are not installed") return ImageQt.toqimage(self) def toqpixmap(self): + """Returns a QPixmap copy of this image""" from PIL import ImageQt if not ImageQt.qt_is_installed: raise ImportError("Qt bindings are not installed") @@ -2199,6 +2201,7 @@ def fromarray(obj, mode=None): def fromqimage(im): + """Creates an image instance from a QImage image""" from PIL import ImageQt if not ImageQt.qt_is_installed: raise ImportError("Qt bindings are not installed") @@ -2206,6 +2209,7 @@ def fromqimage(im): def fromqpixmap(im): + """Creates an image instance from a QPixmap image""" from PIL import ImageQt if not ImageQt.qt_is_installed: raise ImportError("Qt bindings are not installed") diff --git a/PIL/ImageQt.py b/PIL/ImageQt.py index 52dd7d79f..0e8cee009 100644 --- a/PIL/ImageQt.py +++ b/PIL/ImageQt.py @@ -18,6 +18,7 @@ import PIL from PIL._util import isPath +from io import BytesIO qt_is_installed = True qt_version = None @@ -38,8 +39,6 @@ except ImportError: except ImportError: qt_is_installed = False -from io import BytesIO - def rgb(r, g, b, a=255): """(Internal) Turns an RGB color into a Qt compatible color integer.""" @@ -55,15 +54,17 @@ def fromqimage(im): buffer = QBuffer() buffer.open(QIODevice.ReadWrite) im.save(buffer, 'ppm') - bytes_io = BytesIO() + + b = BytesIO() try: - bytes_io.write(buffer.data()) + b.write(buffer.data()) except TypeError: # workaround for Python 2 - bytes_io.write(str(buffer.data())) + b.write(str(buffer.data())) buffer.close() - bytes_io.seek(0) - return PIL.Image.open(bytes_io) + b.seek(0) + + return PIL.Image.open(b) def fromqpixmap(im): @@ -128,7 +129,7 @@ def _toqclass_helper(im): } ## -# An PIL image wrapper for Qt. This is a subclass of PyQt4's QImage +# An PIL image wrapper for Qt. This is a subclass of PyQt's QImage # class. # # @param im A PIL Image object, or a file name (given either as Python @@ -151,13 +152,13 @@ def toqimage(im): def toqpixmap(im): - # This doesn't work. For now using a dumb approach. + # # This doesn't work. For now using a dumb approach. # im_data = _toqclass_helper(im) # result = QPixmap(im_data['im'].size[0], im_data['im'].size[1]) # result.loadFromData(im_data['data']) # Fix some strange bug that causes if im.mode == 'RGB': im = im.convert('RGBA') + qimage = toqimage(im) - qimage.save('/tmp/hopper_{}_qpixmap_qimage.png'.format(im.mode)) return QPixmap.fromImage(qimage) diff --git a/Tests/test_image_toqimage.py b/Tests/test_image_toqimage.py index d44a64847..38b83c023 100644 --- a/Tests/test_image_toqimage.py +++ b/Tests/test_image_toqimage.py @@ -14,10 +14,14 @@ class TestToQImage(PillowQtTestCase, PillowTestCase): PillowQtTestCase.setUp(self) for mode in ('1', 'RGB', 'RGBA', 'L', 'P'): data = ImageQt.toqimage(hopper(mode)) - data.save('/tmp/hopper_{}_qimage.png'.format(mode)) + self.assertTrue(isinstance(data, QImage)) self.assertFalse(data.isNull()) + # Test saving the file + tempfile = self.tempfile('temp_{}.png'.format(mode)) + data.save(tempfile) + if __name__ == '__main__': unittest.main() diff --git a/Tests/test_image_toqpixmap.py b/Tests/test_image_toqpixmap.py index dd527732f..95db2e8f7 100644 --- a/Tests/test_image_toqpixmap.py +++ b/Tests/test_image_toqpixmap.py @@ -11,14 +11,17 @@ class TestToQPixmap(PillowQPixmapTestCase, PillowTestCase): def test_sanity(self): PillowQtTestCase.setUp(self) - QPixmap('Tests/images/hopper.ppm').save( - '/tmp/hopper_RGB_qpixmap_file.png') + for mode in ('1', 'RGB', 'RGBA', 'L', 'P'): data = ImageQt.toqpixmap(hopper(mode)) - data.save('/tmp/hopper_{}_qpixmap.png'.format(mode)) + self.assertTrue(isinstance(data, QPixmap)) self.assertFalse(data.isNull()) + # Test saving the file + tempfile = self.tempfile('temp_{}.png'.format(mode)) + data.save(tempfile) + if __name__ == '__main__': unittest.main() From c851316b3f8f478da1a8530101accb46a15513e3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 21 Jun 2015 20:59:50 +1000 Subject: [PATCH 0386/1037] Updated CHANGES [ci skip] --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index fac51d630..4e6cad8eb 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,7 +4,7 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ - Multiline text in ImageDraw #1177 - [radarhere] + [allo-, radarhere] - Automated windows CI/build support #1278 [wiredfool] From 5feed777dc9670b21ccf60bea745661d30f6baba Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 21 Jun 2015 17:40:00 +0300 Subject: [PATCH 0387/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 4e6cad8eb..09a935bde 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,10 +3,14 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ + +- Remove duplicate code in gifmaker script #1294 + [radarhere] + - Multiline text in ImageDraw #1177 [allo-, radarhere] -- Automated windows CI/build support #1278 +- Automated Windows CI/build support #1278 [wiredfool] - Removed support for Tk versions earlier than 8.4 #1288 From 3590902a14529a367cd2f65de22f1e79ae5a99b2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 22 Jun 2015 08:42:04 +1000 Subject: [PATCH 0388/1037] Update Windows badge [ci skip] --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 83ea57dc7..d39070bbe 100644 --- a/README.rst +++ b/README.rst @@ -14,8 +14,8 @@ Pillow is the "friendly PIL fork" by `Alex Clark and Contributors Date: Mon, 22 Jun 2015 11:33:07 -0700 Subject: [PATCH 0389/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 09a935bde..a91e4be5c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,8 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Add functions to convert: Image <-> QImage; Image <-> QPixmap #1217 + [radarhere, rominf] - Remove duplicate code in gifmaker script #1294 [radarhere] From 3e6e50476b50ac8525aed58f47dcebd96774b479 Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 25 Jun 2015 23:17:21 +0300 Subject: [PATCH 0390/1037] DPI should be tuple of ints, not floats --- PIL/BmpImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index 793bd8edd..9a297b3b8 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -103,7 +103,7 @@ class BmpImageFile(ImageFile.ImageFile): file_info['pixels_per_meter'] = (i32(header_data[20:24]), i32(header_data[24:28])) file_info['colors'] = i32(header_data[28:32]) file_info['palette_padding'] = 4 - self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), file_info['pixels_per_meter'])) + self.info["dpi"] = tuple(map(lambda x: int(math.ceil(x / 39.3701)), file_info['pixels_per_meter'])) if file_info['compression'] == self.BITFIELDS: if len(header_data) >= 52: for idx, mask in enumerate(['r_mask', 'g_mask', 'b_mask', 'a_mask']): From 0564979cf9568a3b4422923bd237cb1a324a7150 Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 25 Jun 2015 23:19:07 +0300 Subject: [PATCH 0391/1037] Formatting --- PIL/BmpImagePlugin.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index 9a297b3b8..b109d2696 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -103,7 +103,9 @@ class BmpImageFile(ImageFile.ImageFile): file_info['pixels_per_meter'] = (i32(header_data[20:24]), i32(header_data[24:28])) file_info['colors'] = i32(header_data[28:32]) file_info['palette_padding'] = 4 - self.info["dpi"] = tuple(map(lambda x: int(math.ceil(x / 39.3701)), file_info['pixels_per_meter'])) + self.info["dpi"] = tuple( + map(lambda x: int(math.ceil(x / 39.3701)), + file_info['pixels_per_meter'])) if file_info['compression'] == self.BITFIELDS: if len(header_data) >= 52: for idx, mask in enumerate(['r_mask', 'g_mask', 'b_mask', 'a_mask']): From 36fc250a52164d0678dde9a5009b2a8a00389a01 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 26 Jun 2015 11:01:40 +1000 Subject: [PATCH 0392/1037] Updated tiff and tk tcl 8.5 versions --- winbuild/config.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/winbuild/config.py b/winbuild/config.py index ffd8079fe..71fc8a27b 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -22,9 +22,9 @@ libs = {'zlib': { 'dir': 'jpeg-9a', }, 'tiff': { - 'url': 'ftp://ftp.remotesensing.org/pub/libtiff/tiff-4.0.3.zip', - 'hash': 'md5:dd70349cedb3981371686e1c9b89a7f9', # not found - generated by wiredfool - 'dir': 'tiff-4.0.3', + 'url': 'ftp://ftp.remotesensing.org/pub/libtiff/tiff-4.0.4.zip', + 'hash': 'md5:8f538a34156188f9a8dcddb679c65d1e', # not found - generated by wiredfool + 'dir': 'tiff-4.0.4', }, 'freetype': { 'url': 'http://download.savannah.gnu.org/releases/freetype/freetype-2.6.tar.gz', @@ -37,13 +37,13 @@ libs = {'zlib': { 'dir': 'lcms2-2.7', }, 'tcl-8.5': { - 'url': SF_MIRROR+'/project/tcl/Tcl/8.5.13/tcl8513-src.zip', - 'hash': 'sha1:3e01585c91293c532a3cd594ec59deca92153a5e', + 'url': SF_MIRROR+'/project/tcl/Tcl/8.5.18/tcl8518-src.zip', + 'hash': 'sha1:4c2aed9043088c630a4c795265e2738ef1b4db3b', 'dir': '', }, 'tk-8.5': { - 'url': SF_MIRROR+'/project/tcl/Tcl/8.5.13/tk8513-src.zip', - 'hash': 'sha1:23a1d7ddd416e11e06dfdb9f86111d4bab9420b4', + 'url': SF_MIRROR+'/project/tcl/Tcl/8.5.18/tk8518-src.zip', + 'hash': 'sha1:273f55148777413774aa722ecad25cabda1e31ae', 'dir': '', }, 'tcl-8.6': { From 951923f7556dd37e5ddc98ec92e00d3ec82c212e Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 26 Jun 2015 10:18:07 +0300 Subject: [PATCH 0393/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index a91e4be5c..6838185d3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,10 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ + +- Update tiff and tk tcl 8.5 versions #1303 + [radarhere] + - Add functions to convert: Image <-> QImage; Image <-> QPixmap #1217 [radarhere, rominf] From afa4cadb2327935d6b5ebb036e4f26a831c84827 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 24 Jun 2015 10:35:37 +1000 Subject: [PATCH 0394/1037] Added width and height properties --- PIL/Image.py | 8 ++++++++ Tests/test_image.py | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/PIL/Image.py b/PIL/Image.py index a6b08d196..3740b51c6 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -504,6 +504,14 @@ class Image(object): self.readonly = 0 self.pyaccess = None + @property + def width(self): + return self.size[0] + + @property + def height(self): + return self.size[1] + def _new(self, im): new = Image() new.im = im diff --git a/Tests/test_image.py b/Tests/test_image.py index 469045909..bd5fd3522 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -30,6 +30,15 @@ class TestImage(PillowTestCase): # self.assertRaises( # MemoryError, lambda: Image.new("L", (1000000, 1000000))) + def test_width_height(self): + im = Image.new("RGB", (1, 2)) + self.assertEqual(im.width, 1) + self.assertEqual(im.height, 2) + + im.size = (3, 4) + self.assertEqual(im.width, 3) + self.assertEqual(im.height, 4) + def test_invalid_image(self): if str is bytes: import StringIO From eb73fe3bdbc9f8d29e3917fd82e4ce5e2e84d7d0 Mon Sep 17 00:00:00 2001 From: hugovk Date: Fri, 26 Jun 2015 10:39:13 +0300 Subject: [PATCH 0395/1037] flake8 --- winbuild/build.py | 10 ++- winbuild/build_dep.py | 8 +- winbuild/config.py | 178 ++++++++++++++++++++-------------------- winbuild/get_pythons.py | 4 +- winbuild/test.py | 5 +- 5 files changed, 104 insertions(+), 101 deletions(-) diff --git a/winbuild/build.py b/winbuild/build.py index 9e5e0ea1c..bdede470a 100644 --- a/winbuild/build.py +++ b/winbuild/build.py @@ -13,8 +13,8 @@ def setup_vms(): ret = [] for py in pythons.keys(): for arch in ('', X64_EXT): - ret.append("virtualenv -p c:/Python%s%s/python.exe --clear %s%s%s" % - (py, arch, VIRT_BASE, py, arch)) + ret.append("virtualenv -p c:/Python%s%s/python.exe --clear %s%s%s" + % (py, arch, VIRT_BASE, py, arch)) ret.append("%s%s%s\Scripts\pip.exe install nose" % (VIRT_BASE, py, arch)) if py == '26': @@ -109,13 +109,15 @@ def main(op): scripts.append((py_version, "\n".join([header(op), build_one(py_version, - compilers[(compiler_version, 32)]), + compilers[(compiler_version, + 32)]), footer()]))) scripts.append(("%s%s" % (py_version, X64_EXT), "\n".join([header(op), build_one("%sx64" % py_version, - compilers[(compiler_version, 64)]), + compilers[(compiler_version, + 64)]), footer()]))) results = map(run_script, scripts) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index a837b31f5..827e8d53b 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -4,7 +4,7 @@ from untar import untar import os import hashlib -from config import * +from config import bin_libs, compilers, compiler_fromEnv, libs def _relpath(*args): @@ -64,10 +64,12 @@ def fetch_libs(): if name == 'openjpeg': filename = check_hash(fetch(lib['url']), lib['hash']) for compiler in compilers.values(): - if not os.path.exists(os.path.join(build_dir, lib['dir']+compiler['inc_dir'])): + if not os.path.exists(os.path.join( + build_dir, lib['dir']+compiler['inc_dir'])): extract(filename, build_dir) os.rename(os.path.join(build_dir, lib['dir']), - os.path.join(build_dir, lib['dir']+compiler['inc_dir'])) + os.path.join( + build_dir, lib['dir']+compiler['inc_dir'])) else: extract(check_hash(fetch(lib['url']), lib['hash']), build_dir) diff --git a/winbuild/config.py b/winbuild/config.py index 71fc8a27b..6846669bf 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -11,108 +11,106 @@ pythons = {#'26': 7, VIRT_BASE = "c:/vp/" X64_EXT = os.environ.get('X64_EXT', "x64") -libs = {'zlib': { - 'url': 'http://zlib.net/zlib128.zip', - 'hash': 'md5:126f8676442ffbd97884eb4d6f32afb4', - 'dir': 'zlib-1.2.8', +libs = { + 'zlib': { + 'url': 'http://zlib.net/zlib128.zip', + 'hash': 'md5:126f8676442ffbd97884eb4d6f32afb4', + 'dir': 'zlib-1.2.8', }, - 'jpeg': { - 'url': 'http://www.ijg.org/files/jpegsr9a.zip', - 'hash': 'md5:a34f3c82760270ee1e1885b15b90a72e', # not found - generated by wiredfool - 'dir': 'jpeg-9a', + 'jpeg': { + 'url': 'http://www.ijg.org/files/jpegsr9a.zip', + 'hash': 'md5:a34f3c82760270ee1e1885b15b90a72e', # not found - generated by wiredfool + 'dir': 'jpeg-9a', }, - 'tiff': { - 'url': 'ftp://ftp.remotesensing.org/pub/libtiff/tiff-4.0.4.zip', - 'hash': 'md5:8f538a34156188f9a8dcddb679c65d1e', # not found - generated by wiredfool - 'dir': 'tiff-4.0.4', - }, - 'freetype': { - 'url': 'http://download.savannah.gnu.org/releases/freetype/freetype-2.6.tar.gz', - 'hash': 'md5:1d733ea6c1b7b3df38169fbdbec47d2b', - 'dir': 'freetype-2.6', + 'tiff': { + 'url': 'ftp://ftp.remotesensing.org/pub/libtiff/tiff-4.0.4.zip', + 'hash': 'md5:8f538a34156188f9a8dcddb679c65d1e', # not found - generated by wiredfool + 'dir': 'tiff-4.0.4', }, - 'lcms': { - 'url': SF_MIRROR+'/project/lcms/lcms/2.7/lcms2-2.7.zip', - 'hash': 'sha1:7ff1a5b721ca719760ba6eb4ec6f38d5e65381cf', - 'dir': 'lcms2-2.7', - }, - 'tcl-8.5': { - 'url': SF_MIRROR+'/project/tcl/Tcl/8.5.18/tcl8518-src.zip', - 'hash': 'sha1:4c2aed9043088c630a4c795265e2738ef1b4db3b', - 'dir': '', - }, - 'tk-8.5': { - 'url': SF_MIRROR+'/project/tcl/Tcl/8.5.18/tk8518-src.zip', - 'hash': 'sha1:273f55148777413774aa722ecad25cabda1e31ae', - 'dir': '', + 'freetype': { + 'url': 'http://download.savannah.gnu.org/releases/freetype/freetype-2.6.tar.gz', + 'hash': 'md5:1d733ea6c1b7b3df38169fbdbec47d2b', + 'dir': 'freetype-2.6', }, - 'tcl-8.6': { - 'url': SF_MIRROR+'/project/tcl/Tcl/8.6.4/tcl864-src.zip', - 'hash': 'md5:35748d2fc61e08a2fdb23b85c6f8c4a0', - 'dir': '', - }, - 'tk-8.6': { - 'url': SF_MIRROR+'/project/tcl/Tcl/8.6.4/tk864-src.zip', - 'hash': 'md5:111d45061a69e7f5250b6ec8ca7c4f35', - 'dir': '', + 'lcms': { + 'url': SF_MIRROR+'/project/lcms/lcms/2.7/lcms2-2.7.zip', + 'hash': 'sha1:7ff1a5b721ca719760ba6eb4ec6f38d5e65381cf', + 'dir': 'lcms2-2.7', }, - 'webp': { - 'url': 'http://downloads.webmproject.org/releases/webp/libwebp-0.4.3.tar.gz', - 'hash': 'sha1:1c307a61c4d0018620b4ba9a58e8f48a8d6640ef', - 'dir': 'libwebp-0.4.3', - + 'tcl-8.5': { + 'url': SF_MIRROR+'/project/tcl/Tcl/8.5.18/tcl8518-src.zip', + 'hash': 'sha1:4c2aed9043088c630a4c795265e2738ef1b4db3b', + 'dir': '', }, - 'openjpeg': { - 'url': SF_MIRROR+'/project/openjpeg/openjpeg/2.1.0/openjpeg-2.1.0.tar.gz', - 'hash': 'md5:f6419fcc233df84f9a81eb36633c6db6', - 'dir': 'openjpeg-2.1.0', + 'tk-8.5': { + 'url': SF_MIRROR+'/project/tcl/Tcl/8.5.18/tk8518-src.zip', + 'hash': 'sha1:273f55148777413774aa722ecad25cabda1e31ae', + 'dir': '', }, - - } + 'tcl-8.6': { + 'url': SF_MIRROR+'/project/tcl/Tcl/8.6.4/tcl864-src.zip', + 'hash': 'md5:35748d2fc61e08a2fdb23b85c6f8c4a0', + 'dir': '', + }, + 'tk-8.6': { + 'url': SF_MIRROR+'/project/tcl/Tcl/8.6.4/tk864-src.zip', + 'hash': 'md5:111d45061a69e7f5250b6ec8ca7c4f35', + 'dir': '', + }, + 'webp': { + 'url': 'http://downloads.webmproject.org/releases/webp/libwebp-0.4.3.tar.gz', + 'hash': 'sha1:1c307a61c4d0018620b4ba9a58e8f48a8d6640ef', + 'dir': 'libwebp-0.4.3', + }, + 'openjpeg': { + 'url': SF_MIRROR+'/project/openjpeg/openjpeg/2.1.0/openjpeg-2.1.0.tar.gz', + 'hash': 'md5:f6419fcc233df84f9a81eb36633c6db6', + 'dir': 'openjpeg-2.1.0', + }, +} bin_libs = { - 'openjpeg': { - 'filename': 'openjpeg-2.0.0-win32-x86.zip', - 'hash': 'sha1:xxx', - 'version': '2.0' - }, - } - -compilers = {(7, 64): { - 'env_version': 'v7.0', - 'vc_version': '2008', - 'env_flags': '/x64 /xp', - 'inc_dir': 'msvcr90-x64', - 'platform': 'x64', - 'webp_platform': 'x64', + 'openjpeg': { + 'filename': 'openjpeg-2.0.0-win32-x86.zip', + 'hash': 'sha1:xxx', + 'version': '2.0' }, - (7, 32): { - 'env_version': 'v7.0', - 'vc_version': '2008', - 'env_flags': '/x86 /xp', - 'inc_dir': 'msvcr90-x32', - 'platform': 'Win32', - 'webp_platform': 'x86', - }, +} - (7.1, 64): { - 'env_version': 'v7.1', - 'vc_version': '2010', - 'env_flags': '/x64 /vista', - 'inc_dir': 'msvcr10-x64', - 'platform': 'x64', - 'webp_platform': 'x64', - }, - (7.1, 32): { - 'env_version': 'v7.1', - 'vc_version': '2010', - 'env_flags': '/x86 /vista', - 'inc_dir': 'msvcr10-x32', - 'platform': 'Win32', - 'webp_platform': 'x86', +compilers = { + (7, 64): { + 'env_version': 'v7.0', + 'vc_version': '2008', + 'env_flags': '/x64 /xp', + 'inc_dir': 'msvcr90-x64', + 'platform': 'x64', + 'webp_platform': 'x64', }, - - } + (7, 32): { + 'env_version': 'v7.0', + 'vc_version': '2008', + 'env_flags': '/x86 /xp', + 'inc_dir': 'msvcr90-x32', + 'platform': 'Win32', + 'webp_platform': 'x86', + }, + (7.1, 64): { + 'env_version': 'v7.1', + 'vc_version': '2010', + 'env_flags': '/x64 /vista', + 'inc_dir': 'msvcr10-x64', + 'platform': 'x64', + 'webp_platform': 'x64', + }, + (7.1, 32): { + 'env_version': 'v7.1', + 'vc_version': '2010', + 'env_flags': '/x86 /vista', + 'inc_dir': 'msvcr10-x32', + 'platform': 'Win32', + 'webp_platform': 'x86', + }, +} def pyversion_fromEnv(): diff --git a/winbuild/get_pythons.py b/winbuild/get_pythons.py index 04d8591ac..8ac3b1d4a 100644 --- a/winbuild/get_pythons.py +++ b/winbuild/get_pythons.py @@ -5,8 +5,8 @@ if __name__ == '__main__': for version in ['2.6.5', '2.7.6', '3.2.5', '3.3.5', '3.4.3']: for platform in ['', '.amd64']: for extension in ['', '.asc']: - fetch('https://www.python.org/ftp/python/%s/python-%s%s.msi%s' % ( - version, version, platform, extension)) + fetch('https://www.python.org/ftp/python/%s/python-%s%s.msi%s' + % (version, version, platform, extension)) # find pip, if it's not in the path! os.system('pip install virtualenv') diff --git a/winbuild/test.py b/winbuild/test.py index 3310fb556..84e071308 100644 --- a/winbuild/test.py +++ b/winbuild/test.py @@ -5,14 +5,15 @@ import os import glob import sys -from config import * +from config import pythons, VIRT_BASE, X64_EXT def test_one(params): python, architecture = params try: print("Running: %s, %s" % params) - command = [r'%s\%s%s\Scripts\python.exe' % (VIRT_BASE, python, architecture), + command = [r'%s\%s%s\Scripts\python.exe' % + (VIRT_BASE, python, architecture), 'test-installed.py', '--processes=-0', '--process-timeout=30', From 3f7ec4e98180914e2bae4eea2179e55f3289cdab Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 26 Jun 2015 13:02:05 +0300 Subject: [PATCH 0396/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 6838185d3..534da7fbb 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Added width and height properties #1304 + [radarhere] + - Update tiff and tk tcl 8.5 versions #1303 [radarhere] From 65f5f05b4f66dd283c870e10a6fa89d6150c1512 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 26 Jun 2015 20:14:26 +1000 Subject: [PATCH 0397/1037] Added documentation for width and height properties [ci skip] --- docs/reference/Image.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst index 974d84a6e..ac8b6f506 100644 --- a/docs/reference/Image.rst +++ b/docs/reference/Image.rst @@ -181,6 +181,18 @@ Instances of the :py:class:`Image` class have the following attributes: :type: ``(width, height)`` +.. py:attribute:: width + + Image width, in pixels. + + :type: :py:class:`int` + +.. py:attribute:: height + + Image height, in pixels. + + :type: :py:class:`int` + .. py:attribute:: palette Colour palette table, if any. If mode is “P”, this should be an instance of From 77f45aa4028e7ccc8c53ea62b94ed8a5a5b0e427 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 26 Jun 2015 09:04:04 -0700 Subject: [PATCH 0398/1037] Workaround for clone failing Workaround for https://github.com/appveyor/ci/issues/315 --- appveyor.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 868f34f7e..179c329a8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,5 +1,4 @@ version: 2.9.pre.{build} -shallow_clone: true clone_folder: c:\pillow init: - ECHO %PYTHON% From 26e6bb25ef09dd6b47b7f489fce8decc3bb900fb Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 26 Jun 2015 10:08:40 -0700 Subject: [PATCH 0399/1037] updated tcl/tk extract directories --- winbuild/build_dep.py | 23 ++++++++++++----------- winbuild/config.py | 8 +++++--- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index a837b31f5..2ca4613c7 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -91,18 +91,19 @@ endlocal """ % compiler -def cp_tk(): +def cp_tk(ver_85, ver_86): + versions = {'ver_85':ver_85, 'ver_86':ver_86} return r""" -mkdir %INCLIB%\tcl85\include\X11 -copy /Y /B %BUILD%\tcl8.5.13\generic\*.h %INCLIB%\tcl85\include\ -copy /Y /B %BUILD%\tk8.5.13\generic\*.h %INCLIB%\tcl85\include\ -copy /Y /B %BUILD%\tk8.5.13\xlib\X11\* %INCLIB%\tcl85\include\X11\ +mkdir %%INCLIB%%\tcl85\include\X11 +copy /Y /B %%BUILD%%\tcl%(ver_85)s\generic\*.h %%INCLIB%%\tcl85\include\ +copy /Y /B %%BUILD%%\tk%(ver_85)s\generic\*.h %%INCLIB%%\tcl85\include\ +copy /Y /B %%BUILD%%\tk%(ver_85)s\xlib\X11\* %%INCLIB%%\tcl85\include\X11\ -mkdir %INCLIB%\tcl86\include\X11 -copy /Y /B %BUILD%\tcl8.6.4\generic\*.h %INCLIB%\tcl86\include\ -copy /Y /B %BUILD%\tk8.6.4\generic\*.h %INCLIB%\tcl86\include\ -copy /Y /B %BUILD%\tk8.6.4\xlib\X11\* %INCLIB%\tcl86\include\X11\ -""" +mkdir %%INCLIB%%\tcl86\include\X11 +copy /Y /B %%BUILD%%\tcl%(ver_86)s\generic\*.h %%INCLIB%%\tcl86\include\ +copy /Y /B %%BUILD%%\tk%(ver_86)s\generic\*.h %%INCLIB%%\tcl86\include\ +copy /Y /B %%BUILD%%\tk%(ver_86)s\xlib\X11\* %%INCLIB%%\tcl86\include\X11\ +""" % versions def header(): @@ -305,7 +306,7 @@ def add_compiler(compiler): mkdirs() fetch_libs() # extract_binlib() -script = [header(), cp_tk()] +script = [header(), cp_tk(libs['tk-8.5']['version'],libs['tk-8.6']['version'] )] if 'PYTHON' in os.environ: diff --git a/winbuild/config.py b/winbuild/config.py index 71fc8a27b..f19a881f5 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -23,7 +23,7 @@ libs = {'zlib': { }, 'tiff': { 'url': 'ftp://ftp.remotesensing.org/pub/libtiff/tiff-4.0.4.zip', - 'hash': 'md5:8f538a34156188f9a8dcddb679c65d1e', # not found - generated by wiredfool + 'hash': 'md5:8f538a34156188f9a8dcddb679c65d1e', 'dir': 'tiff-4.0.4', }, 'freetype': { @@ -45,6 +45,7 @@ libs = {'zlib': { 'url': SF_MIRROR+'/project/tcl/Tcl/8.5.18/tk8518-src.zip', 'hash': 'sha1:273f55148777413774aa722ecad25cabda1e31ae', 'dir': '', + 'version':'8.5.18', }, 'tcl-8.6': { 'url': SF_MIRROR+'/project/tcl/Tcl/8.6.4/tcl864-src.zip', @@ -55,12 +56,13 @@ libs = {'zlib': { 'url': SF_MIRROR+'/project/tcl/Tcl/8.6.4/tk864-src.zip', 'hash': 'md5:111d45061a69e7f5250b6ec8ca7c4f35', 'dir': '', + 'version':'8.6.4', }, 'webp': { 'url': 'http://downloads.webmproject.org/releases/webp/libwebp-0.4.3.tar.gz', 'hash': 'sha1:1c307a61c4d0018620b4ba9a58e8f48a8d6640ef', 'dir': 'libwebp-0.4.3', - + }, 'openjpeg': { 'url': SF_MIRROR+'/project/openjpeg/openjpeg/2.1.0/openjpeg-2.1.0.tar.gz', @@ -68,7 +70,7 @@ libs = {'zlib': { 'dir': 'openjpeg-2.1.0', }, - } +} bin_libs = { 'openjpeg': { From 1c7a140298eb2b0ef7bebb1ef4d0e9d48c143527 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 27 Jun 2015 09:10:01 +1000 Subject: [PATCH 0400/1037] Fixed tox test script path --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 80f7edef4..ebc63d85a 100644 --- a/tox.ini +++ b/tox.ini @@ -11,4 +11,4 @@ commands = {envpython} setup.py clean {envpython} setup.py build_ext --inplace {envpython} selftest.py - {envpython} Tests/run.py --installed + {envpython} test-installed.py --installed From 552e7a579b133d384b2285aae8e4041e98640010 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 27 Jun 2015 20:53:32 +0300 Subject: [PATCH 0401/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 534da7fbb..c8f39899d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,11 +4,14 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Fixed tox test script path #1308 + [radarhere] + - Added width and height properties #1304 [radarhere] - Update tiff and tk tcl 8.5 versions #1303 - [radarhere] + [radarhere, wiredfool] - Add functions to convert: Image <-> QImage; Image <-> QPixmap #1217 [radarhere, rominf] From 46fad0fd636b8e39e03f4c9823c2118f91389240 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 28 Jun 2015 12:03:11 +1000 Subject: [PATCH 0402/1037] Fixed typo --- PIL/TiffImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 59de84273..83bbd3f93 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -548,7 +548,7 @@ class ImageFileDirectory(collections.MutableMapping): # and doesn't match the tiff spec: 8-bit byte that # contains a 7-bit ASCII code; the last byte must be # NUL (binary zero). Also, I don't think this was well - # excersized before. + # exercised before. data = value = b"" + value.encode('ascii', 'replace') + b"\0" else: # integer data From ac7b4b825aacc57301b9ccfccf6cf29fa2f116f8 Mon Sep 17 00:00:00 2001 From: David Schmidt Date: Sun, 28 Jun 2015 13:04:46 +0200 Subject: [PATCH 0403/1037] gif doc improved - missing closing brackt - EOFError mentioned --- 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 ed0c3ae5b..11ec60401 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -77,8 +77,8 @@ Reading sequences ~~~~~~~~~~~~~~~~~ The GIF loader supports the :py:meth:`~file.seek` and :py:meth:`~file.tell` -methods. You can seek to the next frame (``im.seek(im.tell() + 1``), or rewind -the file by seeking to the first frame. Random access is not supported. +methods. You can seek to the next frame (``im.seek(im.tell() + 1)``), or rewind +the file by seeking to the first frame. Random access is not supported. ``im.seek()`` raises an ``EOFError`` if you try to seek after the last frame. Reading local images ~~~~~~~~~~~~~~~~~~~~ From 7a3268d4aec2b910b3adc5905017d7e69fdc4d07 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 28 Jun 2015 14:57:51 -0400 Subject: [PATCH 0404/1037] Set default makefile target "make" runs release test --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 4d96c497d..e123a74c7 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ # https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html .PHONY: clean coverage doc docserve help inplace install install-req release-test sdist test upload upload-test +.DEFAULT_GOAL := release-test clean: python setup.py clean From efe925c26f4fb78613b5ed98d488f71a723d03e8 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 28 Jun 2015 15:07:01 -0400 Subject: [PATCH 0405/1037] Fix manifest [ci skip] --- MANIFEST.in | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 30e73c075..ad5bc3bed 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,14 +1,16 @@ + include *.c include *.h +include *.in include *.md include *.py -include *.sh include *.rst +include *.sh include *.txt include *.yaml +include *.yml include .coveragerc include .gitattributes -include .travis.yml include LICENSE include Makefile include tox.ini @@ -16,7 +18,6 @@ recursive-include PIL *.md recursive-include Scripts *.py recursive-include Scripts *.rst recursive-include Scripts *.sh -recursive-include Scripts README.rst recursive-include Tests *.bdf recursive-include Tests *.bin recursive-include Tests *.bmp @@ -33,11 +34,13 @@ recursive-include Tests *.html recursive-include Tests *.icc recursive-include Tests *.icns recursive-include Tests *.ico +recursive-include Tests *.im recursive-include Tests *.j2k recursive-include Tests *.jp2 recursive-include Tests *.jpg recursive-include Tests *.lut recursive-include Tests *.mpo +recursive-include Tests *.msp recursive-include Tests *.pbm recursive-include Tests *.pcf recursive-include Tests *.pcx @@ -59,8 +62,8 @@ recursive-include Tests *.tiff recursive-include Tests *.ttf recursive-include Tests *.txt recursive-include Tests *.webp +recursive-include Tests *.xbm recursive-include Tests *.xpm -recursive-include Tests *.msp recursive-include Tk *.c recursive-include Tk *.rst recursive-include depends *.rst @@ -71,9 +74,13 @@ recursive-include docs *.html recursive-include docs *.py recursive-include docs *.rst recursive-include docs *.txt -recursive-include docs BUILDME -recursive-include docs COPYING -recursive-include docs Guardfile recursive-include docs Makefile +recursive-include docs Guardfile +recursive-include docs COPYING +recursive-include docs BUILDME recursive-include libImaging *.c recursive-include libImaging *.h +recursive-include winbuild *.gitignore +recursive-include winbuild *.md +recursive-include winbuild *.opt +recursive-include winbuild *.py From 3dd3c4e28ca32ddbfd3e20dd277824b104a9550b Mon Sep 17 00:00:00 2001 From: Christoph Gohlke Date: Sun, 28 Jun 2015 20:50:17 -0700 Subject: [PATCH 0406/1037] TST: fix ValueError on Python 2.6 --- Tests/test_image_toqimage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_image_toqimage.py b/Tests/test_image_toqimage.py index 38b83c023..6c79b4c6d 100644 --- a/Tests/test_image_toqimage.py +++ b/Tests/test_image_toqimage.py @@ -19,7 +19,7 @@ class TestToQImage(PillowQtTestCase, PillowTestCase): self.assertFalse(data.isNull()) # Test saving the file - tempfile = self.tempfile('temp_{}.png'.format(mode)) + tempfile = self.tempfile('temp_{0}.png'.format(mode)) data.save(tempfile) From 29cb7d24b80a3db240725d7d7116ee55827322fc Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 29 Jun 2015 14:59:05 +1000 Subject: [PATCH 0407/1037] Fixed ValueError in Python 2.6 --- Tests/test_image_toqpixmap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_image_toqpixmap.py b/Tests/test_image_toqpixmap.py index 95db2e8f7..8fabab13d 100644 --- a/Tests/test_image_toqpixmap.py +++ b/Tests/test_image_toqpixmap.py @@ -19,7 +19,7 @@ class TestToQPixmap(PillowQPixmapTestCase, PillowTestCase): self.assertFalse(data.isNull()) # Test saving the file - tempfile = self.tempfile('temp_{}.png'.format(mode)) + tempfile = self.tempfile('temp_{0}.png'.format(mode)) data.save(tempfile) From fc18f08eb5a3f7ab5b9294fcbb24410eeb29fd2a Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 29 Jun 2015 10:22:03 +0300 Subject: [PATCH 0408/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index c8f39899d..89655a630 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Fixed ValueError in Python 2.6 #1315 #1316 + [cgohlke, radarhere] + - Fixed tox test script path #1308 [radarhere] From 96944e2dd664efb98e25d0e86671420af26fda40 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Mon, 29 Jun 2015 08:57:55 -0400 Subject: [PATCH 0409/1037] Bump --- PIL/__init__.py | 2 +- _imaging.c | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PIL/__init__.py b/PIL/__init__.py index 6d51a5dcb..64f8953d6 100644 --- a/PIL/__init__.py +++ b/PIL/__init__.py @@ -12,7 +12,7 @@ # ;-) VERSION = '1.1.7' # PIL version -PILLOW_VERSION = '2.9.0.dev0' # Pillow +PILLOW_VERSION = '2.9.0.dev1' # Pillow _plugins = ['BmpImagePlugin', 'BufrStubImagePlugin', diff --git a/_imaging.c b/_imaging.c index 09345c0dd..646fedf8b 100644 --- a/_imaging.c +++ b/_imaging.c @@ -71,7 +71,7 @@ * See the README file for information on usage and redistribution. */ -#define PILLOW_VERSION "2.9.0.dev0" +#define PILLOW_VERSION "2.9.0.dev1" #include "Python.h" diff --git a/setup.py b/setup.py index f669873d6..7e9e3a37e 100644 --- a/setup.py +++ b/setup.py @@ -90,7 +90,7 @@ except (ImportError, OSError): NAME = 'Pillow' -PILLOW_VERSION = '2.9.0.dev0' +PILLOW_VERSION = '2.9.0.dev1' TCL_ROOT = None JPEG_ROOT = None JPEG2K_ROOT = None From 24eceff7df828aabc3d9c93cbb173232353a0a28 Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 29 Jun 2015 16:58:53 +0300 Subject: [PATCH 0410/1037] Run check-manifest --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index f94f8376a..2397376fb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,7 @@ install: - "travis_retry sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-qt4 ghostscript libffi-dev libjpeg-turbo-progs cmake imagemagick" - "travis_retry pip install cffi" - "travis_retry pip install coverage nose" + - "travis_retry pip install check-manifest" # Pyroma tests sometimes hang on PyPy; skip for PyPy - if [ $TRAVIS_PYTHON_VERSION != "pypy" ]; then travis_retry pip install pyroma; fi @@ -38,6 +39,7 @@ script: - coverage run --append --include=PIL/* selftest.py - coverage run --append --include=PIL/* -m nose -vx Tests/test_*.py + - check-manifest --ignore "depends/*" after_success: # gather the coverage data From 4e754e9c55aeeb8716d3f544f65a1e541cd82e07 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Fri, 6 Feb 2015 10:58:07 -0800 Subject: [PATCH 0411/1037] Use logging instead of print. cf. #1191. Only TiffImagePlugin and OLEFileIO still rely on (their own) DEBUG flag. I left TiffImagePlugin as it is because I hope #1059 gets merged in first, and OLEFileIO because it uses its own logic. Untested, as usual. --- PIL/Image.py | 20 ++++------ PIL/ImageFile.py | 23 ++++-------- PIL/PcxImagePlugin.py | 19 +++++----- PIL/PngImagePlugin.py | 16 ++++---- PIL/PyAccess.py | 17 +++++---- PIL/TiffImagePlugin.py | 75 +++++++++++++++++++------------------ Scripts/pilfile.py | 6 ++- Scripts/player.py | 3 -- Tests/test_file_libtiff.py | 16 +++----- Tests/test_file_tiff.py | 16 +++----- Tests/test_imagesequence.py | 3 -- 11 files changed, 96 insertions(+), 118 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 3740b51c6..2bd1a3aa4 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -28,8 +28,11 @@ from __future__ import print_function from PIL import VERSION, PILLOW_VERSION, _plugins +import logging import warnings +logger = logging.getLogger(__name__) + class DecompressionBombWarning(RuntimeWarning): pass @@ -138,11 +141,6 @@ def isImageType(t): """ return hasattr(t, "im") -# -# Debug level - -DEBUG = 0 - # # Constants (also defined in _imagingmodule.c!) @@ -386,13 +384,10 @@ def init(): for plugin in _plugins: try: - if DEBUG: - print("Importing %s" % plugin) + logger.debug("Importing %s", plugin) __import__("PIL.%s" % plugin, globals(), locals(), []) - except ImportError: - if DEBUG: - print("Image: failed to import", end=' ') - print(plugin, ":", sys.exc_info()[1]) + except ImportError as e: + logger.debug("Image: failed to import %s: %s", plugin, e) if OPEN or SAVE: _initialized = 2 @@ -554,8 +549,7 @@ class Image(object): try: self.fp.close() except Exception as msg: - if DEBUG: - print("Error closing: %s" % msg) + logger.debug("Error closing: %s" % msg) # Instead of simply setting to None, we're setting up a # deferred error that will better explain that the core image diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index cc1d73f09..52d21e1e8 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -30,10 +30,13 @@ from PIL import Image from PIL._util import isPath import io +import logging import os import sys import traceback +logger = logging.getLogger(__name__) + MAXBLOCK = 65536 SAFEBLOCK = 1024*1024 @@ -95,21 +98,11 @@ class ImageFile(Image.Image): try: self._open() - except IndexError as v: # end of data - if Image.DEBUG > 1: - traceback.print_exc() - raise SyntaxError(v) - except TypeError as v: # end of data (ord) - if Image.DEBUG > 1: - traceback.print_exc() - raise SyntaxError(v) - except KeyError as v: # unsupported mode - if Image.DEBUG > 1: - traceback.print_exc() - raise SyntaxError(v) - except EOFError as v: # got header but not the first frame - if Image.DEBUG > 1: - traceback.print_exc() + except (IndexError, # end of data + TypeError, # end of data (ord) + KeyError, # unsupported mode + EOFError) as v: # got header but not the first frame + logger.exception("%s") raise SyntaxError(v) if not self.mode or self.size[0] <= 0: diff --git a/PIL/PcxImagePlugin.py b/PIL/PcxImagePlugin.py index 40fafa0dc..906c38a35 100644 --- a/PIL/PcxImagePlugin.py +++ b/PIL/PcxImagePlugin.py @@ -27,8 +27,11 @@ from __future__ import print_function +import logging from PIL import Image, ImageFile, ImagePalette, _binary +logger = logging.getLogger(__name__) + i8 = _binary.i8 i16 = _binary.i16le o8 = _binary.o8 @@ -59,17 +62,15 @@ class PcxImageFile(ImageFile.ImageFile): bbox = i16(s, 4), i16(s, 6), i16(s, 8)+1, i16(s, 10)+1 if bbox[2] <= bbox[0] or bbox[3] <= bbox[1]: raise SyntaxError("bad PCX image size") - if Image.DEBUG: - print("BBox: %s %s %s %s" % bbox) + logger.debug("BBox: %s %s %s %s", *bbox) # format version = i8(s[1]) bits = i8(s[3]) planes = i8(s[65]) stride = i16(s, 66) - if Image.DEBUG: - print("PCX version %s, bits %s, planes %s, stride %s" % - (version, bits, planes, stride)) + logger.debug("PCX version %s, bits %s, planes %s, stride %s", + version, bits, planes, stride) self.info["dpi"] = i16(s, 12), i16(s, 14) @@ -107,8 +108,7 @@ class PcxImageFile(ImageFile.ImageFile): self.size = bbox[2]-bbox[0], bbox[3]-bbox[1] bbox = (0, 0) + self.size - if Image.DEBUG: - print("size: %sx%s" % self.size) + logger.debug("size: %sx%s", *self.size) self.tile = [("pcx", bbox, self.fp.tell(), (rawmode, planes * stride))] @@ -144,9 +144,8 @@ def _save(im, fp, filename, check=0): # Ideally it should be passed in in the state, but the bytes value # gets overwritten. - if Image.DEBUG: - print("PcxImagePlugin._save: xwidth: %d, bits: %d, stride: %d" % ( - im.size[0], bits, stride)) + logger.debug("PcxImagePlugin._save: xwidth: %d, bits: %d, stride: %d", + im.size[0], bits, stride) # under windows, we could determine the current screen size with # "Image.core.display_mode()[1]", but I think that's overkill... diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index 214ff9385..9fcc9fe4e 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -35,10 +35,13 @@ from __future__ import print_function __version__ = "0.9" +import logging import re +import zlib from PIL import Image, ImageFile, ImagePalette, _binary -import zlib + +logger = logging.getLogger(__name__) i8 = _binary.i8 i16 = _binary.i16be @@ -129,8 +132,7 @@ class ChunkStream(object): def call(self, cid, pos, length): "Call the appropriate chunk handler" - if Image.DEBUG: - print("STREAM", cid, pos, length) + logger.debug("STREAM %s %s %s", cid, pos, length) return getattr(self, "chunk_" + cid.decode('ascii'))(pos, length) def crc(self, cid, data): @@ -293,9 +295,8 @@ class PngStream(ChunkStream): # Compression method 1 byte (0) # Compressed profile n bytes (zlib with deflate compression) i = s.find(b"\0") - if Image.DEBUG: - print("iCCP profile name", s[:i]) - print("Compression method", i8(s[i])) + logger.debug("iCCP profile name %s", s[:i]) + logger.debug("Compression method %s", i8(s[i])) comp_method = i8(s[i]) if comp_method != 0: raise SyntaxError("Unknown compression method %s in iCCP chunk" % @@ -507,8 +508,7 @@ class PngImageFile(ImageFile.ImageFile): except EOFError: break except AttributeError: - if Image.DEBUG: - print(cid, pos, length, "(unknown)") + logger.debug("%s %s %s (unknown)", cid, pos, length) s = ImageFile._safe_read(self.fp, length) self.png.crc(cid, s) diff --git a/PIL/PyAccess.py b/PIL/PyAccess.py index 4924facd5..cb4f00cad 100644 --- a/PIL/PyAccess.py +++ b/PIL/PyAccess.py @@ -22,10 +22,14 @@ from __future__ import print_function -from cffi import FFI +import logging import sys -DEBUG = 0 +from cffi import FFI + + +logger = logging.getLogger(__name__) + defs = """ struct Pixel_RGBA { @@ -50,8 +54,7 @@ class PyAccess(object): self.xsize = vals['xsize'] self.ysize = vals['ysize'] - if DEBUG: - print(vals) + logger.debug("%s", vals) self._post_init() def _post_init(self): @@ -305,11 +308,9 @@ else: def new(img, readonly=False): access_type = mode_map.get(img.mode, None) if not access_type: - if DEBUG: - print("PyAccess Not Implemented: %s" % img.mode) + logger.debug("PyAccess Not Implemented: %s", img.mode) return None - if DEBUG: - print("New PyAccess: %s" % img.mode) + logger.debug("New PyAccess: %s", img.mode) return access_type(img, readonly) # End of file diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 83bbd3f93..0068efd2c 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -42,6 +42,7 @@ from __future__ import print_function __version__ = "1.3.5" +DEBUG = False # Needs to be merged with the new logging approach. from PIL import Image, ImageFile from PIL import ImagePalette @@ -434,7 +435,7 @@ class ImageFileDirectory(collections.MutableMapping): tag, typ = i16(ifd), i16(ifd, 2) - if Image.DEBUG: + if DEBUG: from PIL import TiffTags tagname = TiffTags.TAGS.get(tag, "unknown") typname = TiffTags.TYPES.get(typ, "unknown") @@ -444,7 +445,7 @@ class ImageFileDirectory(collections.MutableMapping): try: dispatch = self.load_dispatch[typ] except KeyError: - if Image.DEBUG: + if DEBUG: print("- unsupported type", typ) continue # ignore unsupported type @@ -455,10 +456,10 @@ class ImageFileDirectory(collections.MutableMapping): # Get and expand tag value if size > 4: here = fp.tell() - if Image.DEBUG: + if DEBUG: print("Tag Location: %s" % here) fp.seek(i32(ifd, 8)) - if Image.DEBUG: + if DEBUG: print("Data Location: %s" % fp.tell()) data = ImageFile._safe_read(fp, size) fp.seek(here) @@ -474,7 +475,7 @@ class ImageFileDirectory(collections.MutableMapping): self.tagdata[tag] = data self.tagtype[tag] = typ - if Image.DEBUG: + if DEBUG: if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, ICCPROFILE, XMP): print("- value: " % size) @@ -517,8 +518,8 @@ class ImageFileDirectory(collections.MutableMapping): if tag in self.tagtype: typ = self.tagtype[tag] - if Image.DEBUG: - print("Tag %s, Type: %s, Value: %s" % (tag, typ, value)) + if DEBUG: + print ("Tag %s, Type: %s, Value: %s" % (tag, typ, value)) if typ == 1: # byte data @@ -571,7 +572,7 @@ class ImageFileDirectory(collections.MutableMapping): else: data = b"".join(map(o32, value)) - if Image.DEBUG: + if DEBUG: from PIL import TiffTags tagname = TiffTags.TAGS.get(tag, "unknown") typname = TiffTags.TYPES.get(typ, "unknown") @@ -608,7 +609,7 @@ class ImageFileDirectory(collections.MutableMapping): # pass 2: write directory to file for tag, typ, count, value, data in directory: - if Image.DEBUG > 1: + if DEBUG > 1: print(tag, typ, count, repr(value), repr(data)) fp.write(o16(tag) + o16(typ) + o32(count) + value) @@ -651,10 +652,10 @@ class TiffImageFile(ImageFile.ImageFile): self._frame_pos = [] self._n_frames = None - if Image.DEBUG: - print("*** TiffImageFile._open ***") - print("- __first:", self.__first) - print("- ifh: ", ifh) + if DEBUG: + print ("*** TiffImageFile._open ***") + print ("- __first:", self.__first) + print ("- ifh: ", ifh) # and load the first frame self._seek(0) @@ -685,7 +686,7 @@ class TiffImageFile(ImageFile.ImageFile): while len(self._frame_pos) <= frame: if not self.__next: raise EOFError("no more images in TIFF file") - if Image.DEBUG: + if DEBUG: print("Seeking to frame %s, on frame %s, __next %s, location: %s" % (frame, self.__frame, self.__next, self.fp.tell())) # reset python3 buffered io handle in case fp @@ -693,7 +694,7 @@ class TiffImageFile(ImageFile.ImageFile): self.fp.tell() self.fp.seek(self.__next) self._frame_pos.append(self.__next) - if Image.DEBUG: + if DEBUG: print("Loading tags, location: %s" % self.fp.tell()) self.tag.load(self.fp) self.__next = self.tag.next @@ -771,20 +772,20 @@ class TiffImageFile(ImageFile.ImageFile): # Rearranging for supporting byteio items, since they have a fileno # that returns an IOError if there's no underlying fp. Easier to # deal with here by reordering. - if Image.DEBUG: - print("have getvalue. just sending in a string from getvalue") + if DEBUG: + print ("have getvalue. just sending in a string from getvalue") n, err = decoder.decode(self.fp.getvalue()) elif hasattr(self.fp, "fileno"): # we've got a actual file on disk, pass in the fp. - if Image.DEBUG: - print("have fileno, calling fileno version of the decoder.") + if DEBUG: + print ("have fileno, calling fileno version of the decoder.") self.fp.seek(0) # 4 bytes, otherwise the trace might error out n, err = decoder.decode(b"fpfp") else: # we have something else. - if Image.DEBUG: - print("don't have fileno or getvalue. just reading") + if DEBUG: + print ("don't have fileno or getvalue. just reading") # UNDONE -- so much for that buffer size thing. n, err = decoder.decode(self.fp.read()) @@ -821,7 +822,7 @@ class TiffImageFile(ImageFile.ImageFile): fillorder = getscalar(FILLORDER, 1) - if Image.DEBUG: + if DEBUG: print("*** Summary ***") print("- compression:", self._compression) print("- photometric_interpretation:", photo) @@ -833,7 +834,7 @@ class TiffImageFile(ImageFile.ImageFile): ysize = getscalar(IMAGELENGTH) self.size = xsize, ysize - if Image.DEBUG: + if DEBUG: print("- size:", self.size) format = getscalar(SAMPLEFORMAT, 1) @@ -844,16 +845,16 @@ class TiffImageFile(ImageFile.ImageFile): self.tag.get(BITSPERSAMPLE, (1,)), self.tag.get(EXTRASAMPLES, ()) ) - if Image.DEBUG: + if DEBUG: print("format key:", key) try: self.mode, rawmode = OPEN_INFO[key] except KeyError: - if Image.DEBUG: + if DEBUG: print("- unsupported format") raise SyntaxError("unknown pixel mode") - if Image.DEBUG: + if DEBUG: print("- raw mode:", rawmode) print("- pil mode:", self.mode) @@ -893,7 +894,7 @@ class TiffImageFile(ImageFile.ImageFile): "tiff_sgilog", "tiff_sgilog24", "tiff_raw_16"]: - # if Image.DEBUG: + # if DEBUG: # print "Activating g4 compression for whole file" # Decoder expects entire file as one tile. @@ -934,7 +935,7 @@ class TiffImageFile(ImageFile.ImageFile): self.tag.get(BITSPERSAMPLE, (1,)), self.tag.get(EXTRASAMPLES, ()) ) - if Image.DEBUG: + if DEBUG: print("format key:", key) # this should always work, since all the # fillorder==2 modes have a corresponding @@ -963,8 +964,8 @@ class TiffImageFile(ImageFile.ImageFile): (self._compression, (0, min(y, ysize), w, min(y+h, ysize)), offsets[i], a)) - if Image.DEBUG: - print("tiles: ", self.tile) + if DEBUG: + print ("tiles: ", self.tile) y = y + h if y >= self.size[1]: x = y = 0 @@ -992,7 +993,7 @@ class TiffImageFile(ImageFile.ImageFile): l += 1 a = None else: - if Image.DEBUG: + if DEBUG: print("- unsupported data organization") raise SyntaxError("unknown data organization") @@ -1073,7 +1074,7 @@ def _save(im, fp, filename): # write any arbitrary tags passed in as an ImageFileDirectory info = im.encoderinfo.get("tiffinfo", {}) - if Image.DEBUG: + if DEBUG: print("Tiffinfo Keys: %s" % info.keys) keys = list(info.keys()) for key in keys: @@ -1148,9 +1149,9 @@ def _save(im, fp, filename): ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression, 1) if libtiff: - if Image.DEBUG: - print("Saving using libtiff encoder") - print(ifd.items()) + if DEBUG: + print ("Saving using libtiff encoder") + print (ifd.items()) _fp = 0 if hasattr(fp, "fileno"): try: @@ -1206,8 +1207,8 @@ def _save(im, fp, filename): # int or similar atts[k] = v - if Image.DEBUG: - print(atts) + if DEBUG: + print (atts) # libtiff always expects the bytes in native order. # we're storing image byte order. So, if the rawmode diff --git a/Scripts/pilfile.py b/Scripts/pilfile.py index b954114ac..dab240e2f 100644 --- a/Scripts/pilfile.py +++ b/Scripts/pilfile.py @@ -21,6 +21,7 @@ from __future__ import print_function import getopt import glob +import logging import sys from PIL import Image @@ -42,6 +43,7 @@ except getopt.error as v: sys.exit(1) verbose = quiet = verify = 0 +logging_level = "WARNING" for o, a in opt: if o == "-f": @@ -58,7 +60,9 @@ for o, a in opt: elif o == "-v": verify = 1 elif o == "-D": - Image.DEBUG += 1 + logging_level = "DEBUG" + +logging.basicConfig(level=logging_level) def globfix(files): diff --git a/Scripts/player.py b/Scripts/player.py index 43877415a..ac9eb817f 100644 --- a/Scripts/player.py +++ b/Scripts/player.py @@ -15,9 +15,6 @@ from PIL import Image, ImageTk import sys -Image.DEBUG = 0 - - # -------------------------------------------------------------------- # an image animation player diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 896038b9d..8d5b383a9 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -1,11 +1,14 @@ from __future__ import print_function from helper import unittest, PillowTestCase, hopper, py3 -import os import io +import logging +import os from PIL import Image, TiffImagePlugin +logger = logging.getLogger(__name__) + class LibTiffTestCase(PillowTestCase): @@ -231,7 +234,6 @@ class TestFileLibTiff(LibTiffTestCase): """ Are we generating the same interpretation of the image as Imagemagick is? """ TiffImagePlugin.READ_LIBTIFF = True - # Image.DEBUG = True im = Image.open('Tests/images/12bit.cropped.tif') im.load() TiffImagePlugin.READ_LIBTIFF = False @@ -243,14 +245,8 @@ class TestFileLibTiff(LibTiffTestCase): im2 = Image.open('Tests/images/12in16bit.tif') - if Image.DEBUG: - print(im.getpixel((0, 0))) - print(im.getpixel((0, 1))) - print(im.getpixel((0, 2))) - - print(im2.getpixel((0, 0))) - print(im2.getpixel((0, 1))) - print(im2.getpixel((0, 2))) + logger.debug("%s", [img.getpixel((0, idx)) + for img in [im, im2] for idx in range(3)]) self.assert_image_equal(im, im2) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 02a63586c..6780c9792 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -1,9 +1,12 @@ from __future__ import print_function +import logging +import struct + from helper import unittest, PillowTestCase, hopper, py3 from PIL import Image, TiffImagePlugin -import struct +logger = logging.getLogger(__name__) class TestFileTiff(PillowTestCase): @@ -118,7 +121,6 @@ class TestFileTiff(PillowTestCase): """ Are we generating the same interpretation of the image as Imagemagick is? """ - # Image.DEBUG = True im = Image.open('Tests/images/12bit.cropped.tif') # to make the target -- @@ -129,14 +131,8 @@ class TestFileTiff(PillowTestCase): im2 = Image.open('Tests/images/12in16bit.tif') - if Image.DEBUG: - print(im.getpixel((0, 0))) - print(im.getpixel((0, 1))) - print(im.getpixel((0, 2))) - - print(im2.getpixel((0, 0))) - print(im2.getpixel((0, 1))) - print(im2.getpixel((0, 2))) + logger.debug("%s", [img.getpixel((0, idx)) + for img in [im, im2] for idx in range(3)]) self.assert_image_equal(im, im2) diff --git a/Tests/test_imagesequence.py b/Tests/test_imagesequence.py index 1b4bb3c02..d2aa17df7 100644 --- a/Tests/test_imagesequence.py +++ b/Tests/test_imagesequence.py @@ -23,14 +23,11 @@ class TestImageSequence(PillowTestCase): self.assertEqual(index, 1) def _test_multipage_tiff(self, dbg=False): - # debug had side effect of calling fp.tell. - Image.DEBUG = dbg im = Image.open('Tests/images/multipage.tiff') for index, frame in enumerate(ImageSequence.Iterator(im)): frame.load() self.assertEqual(index, im.tell()) frame.convert('RGB') - Image.DEBUG = False def test_tiff(self): # self._test_multipage_tiff(True) From 983c4602b5d015b5a5c77ca454fbe440879d5af7 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 16 Jun 2015 14:44:10 -0700 Subject: [PATCH 0412/1037] Also log plugin opening failures. This allows obtaining tracebacks of failures by plugins to open files by setting the log-level to DEBUG, rather than by having to uncomment the "traceback.print_exc" lines in Image.open. --- PIL/Image.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 2bd1a3aa4..a292e0e79 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -2300,9 +2300,7 @@ def open(fp, mode="r"): _decompression_bomb_check(im.size) return im except (SyntaxError, IndexError, TypeError, struct.error): - # import traceback - # traceback.print_exc() - pass + logger.debug("", exc_info=True) if init(): @@ -2315,9 +2313,7 @@ def open(fp, mode="r"): _decompression_bomb_check(im.size) return im except (SyntaxError, IndexError, TypeError, struct.error): - # import traceback - # traceback.print_exc() - pass + logger.debug("", exc_info=True) raise IOError("cannot identify image file %r" % (filename if filename else fp)) From d20eef450b7f685d4c5aab810d78f036c039f37f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 30 Jun 2015 13:25:00 +1000 Subject: [PATCH 0413/1037] Added is_animated --- PIL/DcxImagePlugin.py | 4 ++++ PIL/FliImagePlugin.py | 15 +++++++++++++++ PIL/GifImagePlugin.py | 15 +++++++++++++++ PIL/ImImagePlugin.py | 4 ++++ PIL/MicImagePlugin.py | 4 ++++ PIL/MpoImagePlugin.py | 4 ++++ PIL/PsdImagePlugin.py | 4 ++++ PIL/SpiderImagePlugin.py | 4 ++++ PIL/TiffImagePlugin.py | 15 +++++++++++++++ Tests/test_file_dcx.py | 1 + Tests/test_file_fli.py | 1 + Tests/test_file_gif.py | 2 ++ Tests/test_file_im.py | 1 + Tests/test_file_mpo.py | 1 + Tests/test_file_psd.py | 2 ++ Tests/test_file_spider.py | 1 + Tests/test_file_tiff.py | 2 ++ 17 files changed, 80 insertions(+) diff --git a/PIL/DcxImagePlugin.py b/PIL/DcxImagePlugin.py index b3f43b4cf..c6c9b8773 100644 --- a/PIL/DcxImagePlugin.py +++ b/PIL/DcxImagePlugin.py @@ -66,6 +66,10 @@ class DcxImageFile(PcxImageFile): def n_frames(self): return len(self._offset) + @property + def is_animated(self): + return len(self._offset) > 1 + def seek(self, frame): if frame >= len(self._offset): raise EOFError("attempt to seek outside DCX directory") diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py index 772140076..e3eaff970 100644 --- a/PIL/FliImagePlugin.py +++ b/PIL/FliImagePlugin.py @@ -90,6 +90,7 @@ class FliImageFile(ImageFile.ImageFile): self.__fp = self.fp self.__rewind = self.fp.tell() self._n_frames = None + self._is_animated = None self.seek(0) def _palette(self, palette, shift): @@ -122,6 +123,20 @@ class FliImageFile(ImageFile.ImageFile): self.seek(current) return self._n_frames + @property + def is_animated(self): + if self._is_animated is None: + current = self.tell() + + try: + self.seek(1) + self._is_animated = True + except EOFError: + self._is_animated = False + + self.seek(current) + return self._is_animated + def seek(self, frame): if frame == self.__frame: return diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 35fb95164..4e8ba9f58 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -88,6 +88,7 @@ class GifImageFile(ImageFile.ImageFile): self.__fp = self.fp # FIXME: hack self.__rewind = self.fp.tell() self._n_frames = None + self._is_animated = None self._seek(0) # get ready to read first frame @property @@ -102,6 +103,20 @@ class GifImageFile(ImageFile.ImageFile): self.seek(current) return self._n_frames + @property + def is_animated(self): + if self._is_animated is None: + current = self.tell() + + try: + self.seek(1) + self._is_animated = True + except EOFError: + self._is_animated = False + + self.seek(current) + return self._is_animated + def seek(self, frame): if frame == self.__frame: return diff --git a/PIL/ImImagePlugin.py b/PIL/ImImagePlugin.py index 589928d0e..0a0a666ce 100644 --- a/PIL/ImImagePlugin.py +++ b/PIL/ImImagePlugin.py @@ -264,6 +264,10 @@ class ImImageFile(ImageFile.ImageFile): def n_frames(self): return self.info[FRAMES] + @property + def is_animated(self): + return self.info[FRAMES] > 1 + def seek(self, frame): if frame < 0 or frame >= self.info[FRAMES]: diff --git a/PIL/MicImagePlugin.py b/PIL/MicImagePlugin.py index aa41bf359..8e3e1b11d 100644 --- a/PIL/MicImagePlugin.py +++ b/PIL/MicImagePlugin.py @@ -75,6 +75,10 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): def n_frames(self): return len(self.images) + @property + def is_animated(self): + return len(self.images) > 1 + def seek(self, frame): try: diff --git a/PIL/MpoImagePlugin.py b/PIL/MpoImagePlugin.py index 9d21728b9..b7e6c5756 100644 --- a/PIL/MpoImagePlugin.py +++ b/PIL/MpoImagePlugin.py @@ -66,6 +66,10 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile): def n_frames(self): return self.__framecount + @property + def is_animated(self): + return self.__framecount > 1 + def seek(self, frame): if frame < 0 or frame >= self.__framecount: raise EOFError("no more images in MPO file") diff --git a/PIL/PsdImagePlugin.py b/PIL/PsdImagePlugin.py index d30695adb..030f5144c 100644 --- a/PIL/PsdImagePlugin.py +++ b/PIL/PsdImagePlugin.py @@ -136,6 +136,10 @@ class PsdImageFile(ImageFile.ImageFile): def n_frames(self): return len(self.layers) + @property + def is_animated(self): + return len(self.layers) > 1 + def seek(self, layer): # seek to given layer (1..max) if layer == self.frame: diff --git a/PIL/SpiderImagePlugin.py b/PIL/SpiderImagePlugin.py index 7de5156b1..f306538ae 100644 --- a/PIL/SpiderImagePlugin.py +++ b/PIL/SpiderImagePlugin.py @@ -158,6 +158,10 @@ class SpiderImageFile(ImageFile.ImageFile): def n_frames(self): return self._nimages + @property + def is_animated(self): + return self._nimages > 1 + # 1st image index is zero (although SPIDER imgnumber starts at 1) def tell(self): if self.imgnumber < 1: diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 83bbd3f93..99c890459 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -650,6 +650,7 @@ class TiffImageFile(ImageFile.ImageFile): self.__fp = self.fp self._frame_pos = [] self._n_frames = None + self._is_animated = None if Image.DEBUG: print("*** TiffImageFile._open ***") @@ -671,6 +672,20 @@ class TiffImageFile(ImageFile.ImageFile): self.seek(current) return self._n_frames + @property + def is_animated(self): + if self._is_animated is None: + current = self.tell() + + try: + self.seek(1) + self._is_animated = True + except EOFError: + self._is_animated = False + + self.seek(current) + return self._is_animated + def seek(self, frame): "Select a given frame as current image" self._seek(max(frame, 0)) # Questionable backwards compatibility. diff --git a/Tests/test_file_dcx.py b/Tests/test_file_dcx.py index d59a5a785..7f804eb89 100644 --- a/Tests/test_file_dcx.py +++ b/Tests/test_file_dcx.py @@ -33,6 +33,7 @@ class TestFileDcx(PillowTestCase): def test_n_frames(self): im = Image.open(TEST_FILE) self.assertEqual(im.n_frames, 1) + self.assertFalse(im.is_animated) def test_eoferror(self): im = Image.open(TEST_FILE) diff --git a/Tests/test_file_fli.py b/Tests/test_file_fli.py index 1b95f793f..a0ea6af04 100644 --- a/Tests/test_file_fli.py +++ b/Tests/test_file_fli.py @@ -21,6 +21,7 @@ class TestFileFli(PillowTestCase): def test_n_frames(self): im = Image.open(test_file) self.assertEqual(im.n_frames, 1) + self.assertFalse(im.is_animated) def test_eoferror(self): im = Image.open(test_file) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index a38c360c9..abf7d547b 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -137,9 +137,11 @@ class TestFileGif(PillowTestCase): def test_n_frames(self): im = Image.open(TEST_GIF) self.assertEqual(im.n_frames, 1) + self.assertFalse(im.is_animated) im = Image.open("Tests/images/iss634.gif") self.assertEqual(im.n_frames, 42) + self.assertTrue(im.is_animated) def test_eoferror(self): im = Image.open(TEST_GIF) diff --git a/Tests/test_file_im.py b/Tests/test_file_im.py index 76bf250a0..602db4b1d 100644 --- a/Tests/test_file_im.py +++ b/Tests/test_file_im.py @@ -18,6 +18,7 @@ class TestFileIm(PillowTestCase): def test_n_frames(self): im = Image.open(TEST_IM) self.assertEqual(im.n_frames, 1) + self.assertFalse(im.is_animated) def test_eoferror(self): im = Image.open(TEST_IM) diff --git a/Tests/test_file_mpo.py b/Tests/test_file_mpo.py index c5562097d..b1d3b7413 100644 --- a/Tests/test_file_mpo.py +++ b/Tests/test_file_mpo.py @@ -98,6 +98,7 @@ class TestFileMpo(PillowTestCase): def test_n_frames(self): im = Image.open("Tests/images/sugarshack.mpo") self.assertEqual(im.n_frames, 2) + self.assertTrue(im.is_animated) def test_eoferror(self): im = Image.open("Tests/images/sugarshack.mpo") diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py index ea3856fce..6492787ec 100644 --- a/Tests/test_file_psd.py +++ b/Tests/test_file_psd.py @@ -19,9 +19,11 @@ class TestImagePsd(PillowTestCase): def test_n_frames(self): im = Image.open("Tests/images/hopper_merged.psd") self.assertEqual(im.n_frames, 1) + self.assertFalse(im.is_animated) im = Image.open(test_file) self.assertEqual(im.n_frames, 2) + self.assertTrue(im.is_animated) def test_eoferror(self): im = Image.open(test_file) diff --git a/Tests/test_file_spider.py b/Tests/test_file_spider.py index 7d24b2fe5..1ddecd365 100644 --- a/Tests/test_file_spider.py +++ b/Tests/test_file_spider.py @@ -45,6 +45,7 @@ class TestImageSpider(PillowTestCase): def test_n_frames(self): im = Image.open(TEST_FILE) self.assertEqual(im.n_frames, 1) + self.assertFalse(im.is_animated) def test_loadImageSeries(self): # Arrange diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index a88b49d7f..1a1bbaef7 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -153,9 +153,11 @@ class TestFileTiff(PillowTestCase): def test_n_frames(self): im = Image.open('Tests/images/multipage-lastframe.tif') self.assertEqual(im.n_frames, 1) + self.assertFalse(im.is_animated) im = Image.open('Tests/images/multipage.tiff') self.assertEqual(im.n_frames, 3) + self.assertTrue(im.is_animated) def test_eoferror(self): im = Image.open('Tests/images/multipage-lastframe.tif') From 593a910e92f983ba9d1652713f144e66e17aea59 Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 30 Jun 2015 07:09:35 +0300 Subject: [PATCH 0414/1037] Unit test and image: convert hopper.png hopper.bmp --- Tests/images/hopper.bmp | Bin 0 -> 49290 bytes Tests/test_file_bmp.py | 16 ++++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 Tests/images/hopper.bmp diff --git a/Tests/images/hopper.bmp b/Tests/images/hopper.bmp new file mode 100644 index 0000000000000000000000000000000000000000..785700d2276bda53f9e8729253a15e138228e587 GIT binary patch literal 49290 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 z Date: Tue, 30 Jun 2015 18:02:48 +1000 Subject: [PATCH 0415/1037] Merged gifmaker into GifImagePlugin --- PIL/GifImagePlugin.py | 59 ++++++++++++++++++------- PIL/Image.py | 26 +++++++++-- Scripts/gifmaker.py | 100 ++---------------------------------------- 3 files changed, 68 insertions(+), 117 deletions(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 35fb95164..4cef91ee6 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -24,7 +24,7 @@ # See the README file for information on usage and redistribution. # -from PIL import Image, ImageFile, ImagePalette, _binary +from PIL import Image, ImageFile, ImagePalette, ImageChops, ImageSequence, _binary __version__ = "0.9" @@ -284,8 +284,10 @@ RAWMODE = { "P": "P", } +def _save_all(im, fp, filename): + _save(im, fp, filename, save_all=True) -def _save(im, fp, filename): +def _save(im, fp, filename, save_all=False): if _imaging_gif: # call external driver @@ -315,23 +317,47 @@ def _save(im, fp, filename): palette = None im.encoderinfo["optimize"] = im.encoderinfo.get("optimize", True) - header, used_palette_colors = getheader(im_out, palette, im.encoderinfo) - for s in header: - fp.write(s) + if save_all: + previous = None - flags = 0 + for im_frame in ImageSequence.Iterator(im_out): + # To specify duration, add the time in milliseconds to getdata(), + # e.g. getdata(im_frame, duration=1000) + if not previous: + # global header + for s in getheader(im_frame, palette, im.encoderinfo)[0] + getdata(im_frame): + fp.write(s) + else: + # delta frame + delta = ImageChops.subtract_modulo(im_frame, previous) + bbox = delta.getbbox() - if get_interlace(im): - flags = flags | 64 + if bbox: + # compress difference + for s in getdata(im_frame.crop(bbox), offset=bbox[:2]): + fp.write(s) + else: + # FIXME: what should we do in this case? + pass + previous = im_frame.copy() + else: + header = getheader(im_out, palette, im.encoderinfo)[0] + for s in header: + fp.write(s) - # local image header - get_local_header(fp, im, (0, 0), flags) + flags = 0 - im_out.encoderconfig = (8, get_interlace(im)) - ImageFile._save(im_out, fp, [("gif", (0, 0)+im.size, 0, - RAWMODE[im_out.mode])]) + if get_interlace(im): + flags = flags | 64 - fp.write(b"\0") # end of image data + # local image header + _get_local_header(fp, im, (0, 0), flags) + + im_out.encoderconfig = (8, get_interlace(im)) + ImageFile._save(im_out, fp, [("gif", (0, 0)+im.size, 0, + RAWMODE[im_out.mode])]) + + fp.write(b"\0") # end of image data fp.write(b";") # end of file @@ -354,7 +380,7 @@ def get_interlace(im): return interlace -def get_local_header(fp, im, offset, flags): +def _get_local_header(fp, im, offset, flags): transparent_color_exists = False try: transparency = im.encoderinfo["transparency"] @@ -577,7 +603,7 @@ def getdata(im, offset=(0, 0), **params): im.encoderinfo = params # local image header - get_local_header(fp, im, offset, 0) + _get_local_header(fp, im, offset, 0) ImageFile._save(im, fp, [("gif", (0, 0)+im.size, 0, RAWMODE[im.mode])]) @@ -594,6 +620,7 @@ def getdata(im, offset=(0, 0), **params): Image.register_open(GifImageFile.format, GifImageFile, _accept) Image.register_save(GifImageFile.format, _save) +Image.register_save_all(GifImageFile.format, _save_all) Image.register_extension(GifImageFile.format, ".gif") Image.register_mime(GifImageFile.format, "image/gif") diff --git a/PIL/Image.py b/PIL/Image.py index 3740b51c6..141d1b264 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -204,6 +204,7 @@ ID = [] OPEN = {} MIME = {} SAVE = {} +SAVE_ALL = {} EXTENSION = {} # -------------------------------------------------------------------- @@ -1669,6 +1670,10 @@ class Image(object): # may mutate self! self.load() + save_all = False + if 'save_all' in params: + save_all = params['save_all'] + del params['save_all'] self.encoderinfo = params self.encoderconfig = () @@ -1686,11 +1691,12 @@ class Image(object): except KeyError: raise KeyError(ext) # unknown extension - try: - save_handler = SAVE[format.upper()] - except KeyError: + if format.upper() not in SAVE: init() - save_handler = SAVE[format.upper()] # unknown format + if save_all: + save_handler = SAVE_ALL[format.upper()] + else: + save_handler = SAVE[format.upper()] if isPath(fp): fp = builtins.open(fp, "wb") @@ -2469,6 +2475,18 @@ def register_save(id, driver): SAVE[id.upper()] = driver +def register_save_all(id, driver): + """ + Registers an image function to save all the frames + of a multiframe format. This function should not be + used in application code. + + :param id: An image format identifier. + :param driver: A function to save images in this format. + """ + SAVE_ALL[id.upper()] = driver + + def register_extension(id, extension): """ Registers an image extension. This function should not be diff --git a/Scripts/gifmaker.py b/Scripts/gifmaker.py index bf162eb2f..c0679ca79 100644 --- a/Scripts/gifmaker.py +++ b/Scripts/gifmaker.py @@ -14,104 +14,9 @@ # See the README file for information on usage and redistribution. # -# -# For special purposes, you can import this module and call -# the makedelta or compress functions yourself. For example, -# if you have an application that generates a sequence of -# images, you can convert it to a GIF animation using some- -# thing like the following code: -# -# import Image -# import gifmaker -# -# sequence = [] -# -# # generate sequence -# for i in range(100): -# im = -# sequence.append(im) -# -# # write GIF animation -# fp = open("out.gif", "wb") -# gifmaker.makedelta(fp, sequence) -# fp.close() -# -# Alternatively, use an iterator to generate the sequence, and -# write data directly to a socket. Or something... -# - from __future__ import print_function -from PIL import Image, ImageChops, ImageSequence - -from PIL.GifImagePlugin import getheader, getdata - -# -------------------------------------------------------------------- -# straightforward delta encoding - - -def makedelta(fp, sequence): - """Convert list of image frames to a GIF animation file""" - - frames = 0 - - previous = None - - for im in sequence: - - # To specify duration, add the time in milliseconds to getdata(), - # e.g. getdata(im, duration=1000) - - if not previous: - - # global header - for s in getheader(im)[0] + getdata(im): - fp.write(s) - - else: - - # delta frame - delta = ImageChops.subtract_modulo(im, previous) - - bbox = delta.getbbox() - - if bbox: - - # compress difference - for s in getdata(im.crop(bbox), offset=bbox[:2]): - fp.write(s) - - else: - # FIXME: what should we do in this case? - pass - - previous = im.copy() - - frames += 1 - - fp.write(b";") - - return frames - -# -------------------------------------------------------------------- -# main hack - - -def compress(infile, outfile): - - # open input image, and force loading of first frame - im = Image.open(infile) - im.load() - - # open output file - fp = open(outfile, "wb") - - seq = ImageSequence.Iterator(im) - - makedelta(fp, seq) - - fp.close() - +from PIL import Image if __name__ == "__main__": @@ -122,4 +27,5 @@ if __name__ == "__main__": print("Usage: gifmaker infile outfile") sys.exit(1) - compress(sys.argv[1], sys.argv[2]) + im = Image.open(sys.argv[1]) + im.save(sys.argv[2], save_all=True) From 7227b4d01d76cc210b2f755808db5c1dd3d0bc2c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 30 Jun 2015 18:07:23 +1000 Subject: [PATCH 0416/1037] Added test --- Tests/test_file_gif.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index a38c360c9..169707499 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -71,6 +71,14 @@ class TestFileGif(PillowTestCase): self.assert_image_similar(reread.convert('RGB'), hopper(), 50) + def test_roundtrip_save_all(self): + out = self.tempfile('temp.gif') + im = hopper() + im.save(out, save_all=True) + reread = Image.open(out) + + self.assert_image_similar(reread.convert('RGB'), im, 50) + def test_palette_handling(self): # see https://github.com/python-pillow/Pillow/issues/513 From 445a8c06fce647249e6a832f595fcdfff1743ad0 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 30 Jun 2015 08:04:35 -0400 Subject: [PATCH 0417/1037] Bump --- PIL/__init__.py | 2 +- _imaging.c | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PIL/__init__.py b/PIL/__init__.py index 64f8953d6..ee3581d65 100644 --- a/PIL/__init__.py +++ b/PIL/__init__.py @@ -12,7 +12,7 @@ # ;-) VERSION = '1.1.7' # PIL version -PILLOW_VERSION = '2.9.0.dev1' # Pillow +PILLOW_VERSION = '2.9.0.dev2' # Pillow _plugins = ['BmpImagePlugin', 'BufrStubImagePlugin', diff --git a/_imaging.c b/_imaging.c index 646fedf8b..932135980 100644 --- a/_imaging.c +++ b/_imaging.c @@ -71,7 +71,7 @@ * See the README file for information on usage and redistribution. */ -#define PILLOW_VERSION "2.9.0.dev1" +#define PILLOW_VERSION "2.9.0.dev2" #include "Python.h" diff --git a/setup.py b/setup.py index 7e9e3a37e..388100d78 100644 --- a/setup.py +++ b/setup.py @@ -90,7 +90,7 @@ except (ImportError, OSError): NAME = 'Pillow' -PILLOW_VERSION = '2.9.0.dev1' +PILLOW_VERSION = '2.9.0.dev2' TCL_ROOT = None JPEG_ROOT = None JPEG2K_ROOT = None From 4fbea3e553d304544228dd7e7a215f772640b021 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 1 Jul 2015 09:18:05 +1000 Subject: [PATCH 0418/1037] Added multiframe GIF test --- PIL/GifImagePlugin.py | 27 +++++++++++++++++---------- Tests/test_file_gif.py | 10 ++++++++++ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 51fd11999..08567fcd0 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -299,9 +299,22 @@ RAWMODE = { "P": "P", } + +def _convert_mode(im): + # convert on the fly (EXPERIMENTAL -- I'm not sure PIL + # should automatically convert images on save...) + if Image.getmodebase(im.mode) == "RGB": + palette_size = 256 + if im.palette: + palette_size = len(im.palette.getdata()[1]) // 3 + return im.convert("P", palette=1, colors=palette_size) + return im.convert("L") + + def _save_all(im, fp, filename): _save(im, fp, filename, save_all=True) + def _save(im, fp, filename, save_all=False): if _imaging_gif: @@ -315,15 +328,7 @@ def _save(im, fp, filename, save_all=False): if im.mode in RAWMODE: im_out = im.copy() else: - # convert on the fly (EXPERIMENTAL -- I'm not sure PIL - # should automatically convert images on save...) - if Image.getmodebase(im.mode) == "RGB": - palette_size = 256 - if im.palette: - palette_size = len(im.palette.getdata()[1]) // 3 - im_out = im.convert("P", palette=1, colors=palette_size) - else: - im_out = im.convert("L") + im_out = _convert_mode(im) # header try: @@ -335,7 +340,9 @@ def _save(im, fp, filename, save_all=False): if save_all: previous = None - for im_frame in ImageSequence.Iterator(im_out): + for im_frame in ImageSequence.Iterator(im): + im_frame = _convert_mode(im_frame) + # To specify duration, add the time in milliseconds to getdata(), # e.g. getdata(im_frame, duration=1000) if not previous: diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 1141e0372..666e1f16d 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -72,6 +72,7 @@ class TestFileGif(PillowTestCase): self.assert_image_similar(reread.convert('RGB'), hopper(), 50) def test_roundtrip_save_all(self): + # Single frame image out = self.tempfile('temp.gif') im = hopper() im.save(out, save_all=True) @@ -79,6 +80,15 @@ class TestFileGif(PillowTestCase): self.assert_image_similar(reread.convert('RGB'), im, 50) + # Multiframe image + im = Image.open("Tests/images/dispose_bgnd.gif") + + out = self.tempfile('temp.gif') + im.save(out, save_all=True) + reread = Image.open(out) + + self.assertEqual(im.n_frames, 5) + def test_palette_handling(self): # see https://github.com/python-pillow/Pillow/issues/513 From abe25b71914bca5a7d23a3316c6c8ce6da7a887f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 1 Jul 2015 09:19:28 +1000 Subject: [PATCH 0419/1037] Rearranged format handler fetching --- PIL/Image.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 141d1b264..62ae9b5ac 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1682,14 +1682,9 @@ class Image(object): ext = os.path.splitext(filename)[1].lower() if not format: - try: - format = EXTENSION[ext] - except KeyError: + if ext not in EXTENSION: init() - try: - format = EXTENSION[ext] - except KeyError: - raise KeyError(ext) # unknown extension + format = EXTENSION[ext] if format.upper() not in SAVE: init() From 63a32a9c5b13d7cbe067831c1196b3929840a907 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 1 Jul 2015 15:47:47 +1000 Subject: [PATCH 0420/1037] Added test for GimpPaletteFile --- MANIFEST.in | 1 + Tests/images/test.gpl | 4 ++++ Tests/test_file_gimppalette.py | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 Tests/images/test.gpl create mode 100644 Tests/test_file_gimppalette.py diff --git a/MANIFEST.in b/MANIFEST.in index ad5bc3bed..70ca05b01 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -29,6 +29,7 @@ recursive-include Tests *.eps recursive-include Tests *.fli recursive-include Tests *.ggr recursive-include Tests *.gif +recursive-include Tests *.gpl recursive-include Tests *.gnuplot recursive-include Tests *.html recursive-include Tests *.icc diff --git a/Tests/images/test.gpl b/Tests/images/test.gpl new file mode 100644 index 000000000..7436a3099 --- /dev/null +++ b/Tests/images/test.gpl @@ -0,0 +1,4 @@ +GIMP Palette +Name: Test +Columns: 0 +# diff --git a/Tests/test_file_gimppalette.py b/Tests/test_file_gimppalette.py new file mode 100644 index 000000000..bb18e8026 --- /dev/null +++ b/Tests/test_file_gimppalette.py @@ -0,0 +1,19 @@ +from helper import unittest, PillowTestCase + +from PIL.GimpPaletteFile import GimpPaletteFile + + +class TestImage(PillowTestCase): + + def test_sanity(self): + with open('Tests/images/test.gpl', 'rb') as fp: + GimpPaletteFile(fp) + + with open('Tests/images/hopper.jpg', 'rb') as fp: + self.assertRaises(SyntaxError, lambda: GimpPaletteFile(fp)) + + +if __name__ == '__main__': + unittest.main() + +# End of file From 6a85d5184c5556a35dfbf5c0d3ed3d0903781193 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 1 Jul 2015 09:09:26 +0300 Subject: [PATCH 0421/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 89655a630..80bc189d8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Added test for GimpPaletteFile #1324 + [radarhere] + - Fixed ValueError in Python 2.6 #1315 #1316 [cgohlke, radarhere] From 0c7cf4070e9abc078736837674e57615d346917f Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 1 Jul 2015 09:56:52 +0300 Subject: [PATCH 0422/1037] Report failures sooner --- appveyor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 179c329a8..6280a97d9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -24,3 +24,5 @@ test_script: - cd c:\pillow - '%PYTHON%\Scripts\pip.exe install nose' - '%PYTHON%\python.exe test-installed.py' +matrix: + fast_finish: true From dba0e7960afd59bb7348bd36260b361939901a2c Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 1 Jul 2015 14:28:34 +0300 Subject: [PATCH 0423/1037] More GIMP palette tests --- Tests/images/bad_palette_entry.gpl | 12 ++++++++++++ Tests/images/bad_palette_file.gpl | 12 ++++++++++++ Tests/images/custom_gimp_palette.gpl | 12 ++++++++++++ Tests/test_file_gimppalette.py | 17 +++++++++++++++++ 4 files changed, 53 insertions(+) create mode 100644 Tests/images/bad_palette_entry.gpl create mode 100644 Tests/images/bad_palette_file.gpl create mode 100644 Tests/images/custom_gimp_palette.gpl diff --git a/Tests/images/bad_palette_entry.gpl b/Tests/images/bad_palette_entry.gpl new file mode 100644 index 000000000..162037184 --- /dev/null +++ b/Tests/images/bad_palette_entry.gpl @@ -0,0 +1,12 @@ +GIMP Palette +Name: badpaletteentry +Columns: 4 +# + 0 0 0 Index 3 + 65 38 +103 62 49 Index 6 + 79 73 72 Index 7 +114 101 97 Index 8 +208 127 100 Index 9 +151 144 142 Index 10 +221 207 199 Index 11 diff --git a/Tests/images/bad_palette_file.gpl b/Tests/images/bad_palette_file.gpl new file mode 100644 index 000000000..c366cc8db --- /dev/null +++ b/Tests/images/bad_palette_file.gpl @@ -0,0 +1,12 @@ +GIMP Palette +Name: badpalettefile +Columns: 4 +# + 0 0 0 Index 3 +01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +103 62 49 Index 6 + 79 73 72 Index 7 +114 101 97 Index 8 +208 127 100 Index 9 +151 144 142 Index 10 +221 207 199 Index 11 diff --git a/Tests/images/custom_gimp_palette.gpl b/Tests/images/custom_gimp_palette.gpl new file mode 100644 index 000000000..08ea70028 --- /dev/null +++ b/Tests/images/custom_gimp_palette.gpl @@ -0,0 +1,12 @@ +GIMP Palette +Name: custompalette +Columns: 4 +# + 0 0 0 Index 3 + 65 38 30 Index 4 +103 62 49 Index 6 + 79 73 72 Index 7 +114 101 97 Index 8 +208 127 100 Index 9 +151 144 142 Index 10 +221 207 199 Index 11 diff --git a/Tests/test_file_gimppalette.py b/Tests/test_file_gimppalette.py index bb18e8026..dfa2845ac 100644 --- a/Tests/test_file_gimppalette.py +++ b/Tests/test_file_gimppalette.py @@ -12,6 +12,23 @@ class TestImage(PillowTestCase): with open('Tests/images/hopper.jpg', 'rb') as fp: self.assertRaises(SyntaxError, lambda: GimpPaletteFile(fp)) + with open('Tests/images/bad_palette_file.gpl', 'rb') as fp: + self.assertRaises(SyntaxError, lambda: GimpPaletteFile(fp)) + + with open('Tests/images/bad_palette_entry.gpl', 'rb') as fp: + self.assertRaises(ValueError, lambda: GimpPaletteFile(fp)) + + def test_get_palette(self): + # Arrange + with open('Tests/images/custom_gimp_palette.gpl', 'rb') as fp: + palette_file = GimpPaletteFile(fp) + + # Act + palette, mode = palette_file.getpalette() + + # Assert + self.assertEqual(mode, "RGB") + if __name__ == '__main__': unittest.main() From 80672b61e8596c7d6dab7b4ef3ef1e4783902f51 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Wed, 1 Jul 2015 14:33:56 -0400 Subject: [PATCH 0424/1037] This is 2.9.0 Fixes #1174 --- PIL/__init__.py | 2 +- _imaging.c | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PIL/__init__.py b/PIL/__init__.py index ee3581d65..179230d7f 100644 --- a/PIL/__init__.py +++ b/PIL/__init__.py @@ -12,7 +12,7 @@ # ;-) VERSION = '1.1.7' # PIL version -PILLOW_VERSION = '2.9.0.dev2' # Pillow +PILLOW_VERSION = '2.9.0' # Pillow _plugins = ['BmpImagePlugin', 'BufrStubImagePlugin', diff --git a/_imaging.c b/_imaging.c index 932135980..1d0c3b7fb 100644 --- a/_imaging.c +++ b/_imaging.c @@ -71,7 +71,7 @@ * See the README file for information on usage and redistribution. */ -#define PILLOW_VERSION "2.9.0.dev2" +#define PILLOW_VERSION "2.9.0" #include "Python.h" diff --git a/setup.py b/setup.py index 388100d78..9062a21f6 100644 --- a/setup.py +++ b/setup.py @@ -90,7 +90,7 @@ except (ImportError, OSError): NAME = 'Pillow' -PILLOW_VERSION = '2.9.0.dev2' +PILLOW_VERSION = '2.9.0' TCL_ROOT = None JPEG_ROOT = None JPEG2K_ROOT = None From efa0fa1b4f4775a109bed56ec1875a8bac6c5944 Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 1 Jul 2015 22:51:38 +0300 Subject: [PATCH 0425/1037] More ImageFont tests --- Tests/test_imagefont.py | 50 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 8414584f9..fd1c8af9d 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -256,6 +256,34 @@ try: # Check boxes a and b are same size self.assertEqual(box_size_a, box_size_b) + def test_rotated_transposed_font_get_mask(self): + # Arrange + text = "mask this" + font = ImageFont.truetype(FONT_PATH, FONT_SIZE) + orientation = Image.ROTATE_90 + transposed_font = ImageFont.TransposedFont( + font, orientation=orientation) + + # Act + mask = transposed_font.getmask(text) + + # Assert + self.assertEqual(mask.size, (13, 108)) + + def test_unrotated_transposed_font_get_mask(self): + # Arrange + text = "mask this" + font = ImageFont.truetype(FONT_PATH, FONT_SIZE) + orientation = None + transposed_font = ImageFont.TransposedFont( + font, orientation=orientation) + + # Act + mask = transposed_font.getmask(text) + + # Assert + self.assertEqual(mask.size, (108, 13)) + def test_free_type_font_get_name(self): # Arrange font = ImageFont.truetype(FONT_PATH, FONT_SIZE) @@ -278,6 +306,28 @@ try: self.assertIsInstance(descent, int) self.assertEqual((ascent, descent), (16, 4)) # too exact check? + def test_free_type_font_get_offset(self): + # Arrange + font = ImageFont.truetype(FONT_PATH, FONT_SIZE) + text = "offset this" + + # Act + offset = font.getoffset(text) + + # Assert + self.assertEqual(offset, (0, 3)) + + def test_free_type_font_get_mask(self): + # Arrange + font = ImageFont.truetype(FONT_PATH, FONT_SIZE) + text = "mask this" + + # Act + mask = font.getmask(text) + + # Assert + self.assertEqual(mask.size, (108, 13)) + def test_load_path_not_found(self): # Arrange filename = "somefilenamethatdoesntexist.ttf" From bfa6b0774143ec891b8460cb339c6aa8cfea8f56 Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 1 Jul 2015 22:53:18 +0300 Subject: [PATCH 0426/1037] flake8 --- Tests/test_imagefont.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index fd1c8af9d..1fd70b3d8 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -182,7 +182,10 @@ try: ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) # Act/Assert - self.assertRaises(AssertionError, lambda: draw.multiline_text((0, 0), TEST_TEXT, font=ttf, align="unknown")) + self.assertRaises(AssertionError, + lambda: draw.multiline_text((0, 0), TEST_TEXT, + font=ttf, + align="unknown")) def test_multiline_size(self): ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) @@ -199,7 +202,8 @@ try: draw = ImageDraw.Draw(im) self.assertEqual(draw.textsize("longest line", font=ttf)[0], - draw.multiline_textsize("longest line\nline", font=ttf)[0]) + draw.multiline_textsize("longest line\nline", + font=ttf)[0]) def test_multiline_spacing(self): ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) From 93eb15bec7920adfd08a47125a6a92f357182b98 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Thu, 2 Jul 2015 05:20:15 -0400 Subject: [PATCH 0427/1037] Commence 3.0.0.dev0 --- PIL/__init__.py | 2 +- _imaging.c | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PIL/__init__.py b/PIL/__init__.py index 179230d7f..665474f9b 100644 --- a/PIL/__init__.py +++ b/PIL/__init__.py @@ -12,7 +12,7 @@ # ;-) VERSION = '1.1.7' # PIL version -PILLOW_VERSION = '2.9.0' # Pillow +PILLOW_VERSION = '3.0.0.dev0' # Pillow _plugins = ['BmpImagePlugin', 'BufrStubImagePlugin', diff --git a/_imaging.c b/_imaging.c index 1d0c3b7fb..895faac54 100644 --- a/_imaging.c +++ b/_imaging.c @@ -71,7 +71,7 @@ * See the README file for information on usage and redistribution. */ -#define PILLOW_VERSION "2.9.0" +#define PILLOW_VERSION "3.0.0.dev0" #include "Python.h" diff --git a/setup.py b/setup.py index 9062a21f6..ba95cdd58 100644 --- a/setup.py +++ b/setup.py @@ -90,7 +90,7 @@ except (ImportError, OSError): NAME = 'Pillow' -PILLOW_VERSION = '2.9.0' +PILLOW_VERSION = '3.0.0.dev0' TCL_ROOT = None JPEG_ROOT = None JPEG2K_ROOT = None From f0e0e7a167b52f4e6156200ddc67c97554cbd120 Mon Sep 17 00:00:00 2001 From: hugovk Date: Fri, 3 Jul 2015 07:57:33 +0300 Subject: [PATCH 0428/1037] Update AppVeyor build number --- RELEASING.md | 8 ++++---- appveyor.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index d9c4d761e..fbfd8f2b4 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -7,9 +7,9 @@ Released quarterly on the first day of January, April, July, October. * [ ] Open a release ticket e.g. https://github.com/python-pillow/Pillow/issues/1174 * [ ] Develop and prepare release in ``master`` branch. * [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in ``master`` branch. -* [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in: +* [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in: ``` - PIL/__init__.py setup.py _imaging.c + PIL/__init__.py setup.py _imaging.c appveyor.yml ``` * [ ] Update `CHANGES.rst`. * [ ] Run pre-release check via `make pre`. @@ -32,14 +32,14 @@ Released quarterly on the first day of January, April, July, October. Released as needed for security, installation or critical bug fixes. * [ ] Make necessary changes in ``master`` branch. -* [ ] Update `CHANGES.rst`. +* [ ] Update `CHANGES.rst`. * [ ] Cherry pick individual commits from ``master`` branch to release branch e.g. ``2.9.x``. * [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in release branch e.g. ``2.9.x``. * [ ] Checkout release branch e.g.: ``` git checkout -t remotes/origin/2.9.x ``` -* [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in: +* [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in: ``` PIL/__init__.py setup.py _imaging.c ``` diff --git a/appveyor.yml b/appveyor.yml index 6280a97d9..6e953c46e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 2.9.pre.{build} +version: 3.0.pre.{build} clone_folder: c:\pillow init: - ECHO %PYTHON% From a06b59bd52f8bbcd311b6a96d6d6ab4a1229331f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 3 Jul 2015 15:03:25 +1000 Subject: [PATCH 0429/1037] Added various tests --- Tests/check_imaging_leaks.py | 2 +- Tests/test_cffi.py | 12 ++++++++++++ Tests/test_file_bmp.py | 6 +++++- Tests/test_file_bufrstub.py | 15 +++++++++++++++ Tests/test_file_cur.py | 3 +++ Tests/test_file_dcx.py | 4 ++++ Tests/test_file_eps.py | 3 +++ Tests/test_file_fitsstub.py | 15 +++++++++++++++ Tests/test_file_fli.py | 5 ++++- Tests/test_file_fpx.py | 15 +++++++++++++++ Tests/test_file_gbr.py | 15 +++++++++++++++ Tests/test_file_gif.py | 3 +++ Tests/test_file_gribstub.py | 15 +++++++++++++++ Tests/test_file_hdf5stub.py | 15 +++++++++++++++ Tests/test_file_ico.py | 6 +++++- Tests/test_file_im.py | 6 +++++- Tests/test_file_jpeg2k.py | 5 ++++- Tests/test_file_mcidas.py | 15 +++++++++++++++ Tests/test_file_mic.py | 15 +++++++++++++++ Tests/test_file_msp.py | 5 ++++- Tests/test_file_pcx.py | 5 ++++- Tests/test_file_pdf.py | 6 ++++++ Tests/test_file_png.py | 3 +++ Tests/test_file_psd.py | 5 ++++- Tests/test_file_sgi.py | 5 ++++- Tests/test_file_sun.py | 4 +++- Tests/test_file_tga.py | 19 +++++++++++++++++++ Tests/test_file_webp.py | 5 +++++ Tests/test_file_xpm.py | 5 ++++- Tests/test_font_bdf.py | 4 ++++ Tests/test_font_pcf.py | 4 ++++ Tests/test_image_filter.py | 6 ++++++ Tests/test_image_mode.py | 2 ++ Tests/test_imagecolor.py | 2 ++ Tests/test_imagedraw.py | 5 +++++ Tests/test_imagefile.py | 3 +++ Tests/test_imagesequence.py | 2 ++ Tests/test_olefileio.py | 27 +++++++++++---------------- 38 files changed, 264 insertions(+), 28 deletions(-) create mode 100644 Tests/test_file_bufrstub.py create mode 100644 Tests/test_file_fitsstub.py create mode 100644 Tests/test_file_fpx.py create mode 100644 Tests/test_file_gbr.py create mode 100644 Tests/test_file_gribstub.py create mode 100644 Tests/test_file_hdf5stub.py create mode 100644 Tests/test_file_mcidas.py create mode 100644 Tests/test_file_mic.py diff --git a/Tests/check_imaging_leaks.py b/Tests/check_imaging_leaks.py index c6d99b8d1..e79429f8f 100644 --- a/Tests/check_imaging_leaks.py +++ b/Tests/check_imaging_leaks.py @@ -3,7 +3,7 @@ from __future__ import division from helper import unittest, PillowTestCase import sys -from PIL import Image, ImageFilter +from PIL import Image min_iterations = 100 max_iterations = 10000 diff --git a/Tests/test_cffi.py b/Tests/test_cffi.py index cea0db093..361b59a39 100644 --- a/Tests/test_cffi.py +++ b/Tests/test_cffi.py @@ -61,6 +61,10 @@ class TestCffi(PillowTestCase): for y in range(0, h, 10): self.assertEqual(access[(x, y)], caccess[(x, y)]) + # Access an out-of-range pixel + self.assertRaises(ValueError, + lambda: access[(access.xsize+1, access.ysize+1)]) + def test_get_vs_c(self): rgb = hopper('RGB') rgb.load() @@ -103,6 +107,14 @@ class TestCffi(PillowTestCase): access[(x, y)] = color self.assertEqual(color, caccess[(x, y)]) + # Attempt to set the value on a read-only image + access = PyAccess.new(im, True) + try: + access[(0, 0)] = color + except ValueError: + return + self.fail("Putpixel did not fail on a read-only image") + def test_set_vs_c(self): rgb = hopper('RGB') rgb.load() diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py index fd0f29470..15d470c90 100644 --- a/Tests/test_file_bmp.py +++ b/Tests/test_file_bmp.py @@ -1,6 +1,6 @@ from helper import unittest, PillowTestCase, hopper -from PIL import Image +from PIL import Image, BmpImagePlugin import io @@ -25,6 +25,10 @@ class TestFileBmp(PillowTestCase): self.roundtrip(hopper("P")) self.roundtrip(hopper("RGB")) + def test_invalid_file(self): + with open("Tests/images/flower.jpg", "rb") as fp: + self.assertRaises(SyntaxError, lambda: BmpImagePlugin.BmpImageFile(fp)) + def test_save_to_bytes(self): output = io.BytesIO() im = hopper() diff --git a/Tests/test_file_bufrstub.py b/Tests/test_file_bufrstub.py new file mode 100644 index 000000000..8cf496533 --- /dev/null +++ b/Tests/test_file_bufrstub.py @@ -0,0 +1,15 @@ +from helper import unittest, PillowTestCase + +from PIL import BufrStubImagePlugin + + +class TestFileBufrStub(PillowTestCase): + + def test_invalid_file(self): + self.assertRaises(SyntaxError, lambda: BufrStubImagePlugin.BufrStubImageFile("Tests/images/flower.jpg")) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_file_cur.py b/Tests/test_file_cur.py index 07bf3a750..d9acb5f78 100644 --- a/Tests/test_file_cur.py +++ b/Tests/test_file_cur.py @@ -20,6 +20,9 @@ class TestFileCur(PillowTestCase): self.assertEqual(im.getpixel((11, 1)), (253, 254, 254, 1)) self.assertEqual(im.getpixel((16, 16)), (84, 87, 86, 255)) + def test_invalid_file(self): + self.assertRaises(SyntaxError, lambda: CurImagePlugin.CurImageFile("Tests/images/flower.jpg")) + if __name__ == '__main__': unittest.main() diff --git a/Tests/test_file_dcx.py b/Tests/test_file_dcx.py index 7f804eb89..62760fd0c 100644 --- a/Tests/test_file_dcx.py +++ b/Tests/test_file_dcx.py @@ -20,6 +20,10 @@ class TestFileDcx(PillowTestCase): orig = hopper() self.assert_image_equal(im, orig) + def test_invalid_file(self): + with open("Tests/images/flower.jpg", "rb") as fp: + self.assertRaises(SyntaxError, lambda: DcxImagePlugin.DcxImageFile(fp)) + def test_tell(self): # Arrange im = Image.open(TEST_FILE) diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index 08d2d875a..931b788a3 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -51,6 +51,9 @@ class TestFileEps(PillowTestCase): self.assertEqual(image2_scale2.size, (720, 504)) self.assertEqual(image2_scale2.format, "EPS") + def test_invalid_file(self): + self.assertRaises(SyntaxError, lambda: EpsImagePlugin.EpsImageFile("Tests/images/flower.jpg")) + def test_file_object(self): # issue 479 image1 = Image.open(file1) diff --git a/Tests/test_file_fitsstub.py b/Tests/test_file_fitsstub.py new file mode 100644 index 000000000..189d002d6 --- /dev/null +++ b/Tests/test_file_fitsstub.py @@ -0,0 +1,15 @@ +from helper import unittest, PillowTestCase + +from PIL import FitsStubImagePlugin + + +class TestFileFitsStub(PillowTestCase): + + def test_invalid_file(self): + self.assertRaises(SyntaxError, lambda: FitsStubImagePlugin.FITSStubImageFile("Tests/images/flower.jpg")) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_file_fli.py b/Tests/test_file_fli.py index a0ea6af04..daaebdc69 100644 --- a/Tests/test_file_fli.py +++ b/Tests/test_file_fli.py @@ -1,6 +1,6 @@ from helper import unittest, PillowTestCase -from PIL import Image +from PIL import Image, FliImagePlugin # sample ppm stream # created as an export of a palette image from Gimp2.6 @@ -18,6 +18,9 @@ class TestFileFli(PillowTestCase): self.assertEqual(im.size, (128, 128)) self.assertEqual(im.format, "FLI") + def test_invalid_file(self): + self.assertRaises(SyntaxError, lambda: FliImagePlugin.FliImageFile("Tests/images/flower.jpg")) + def test_n_frames(self): im = Image.open(test_file) self.assertEqual(im.n_frames, 1) diff --git a/Tests/test_file_fpx.py b/Tests/test_file_fpx.py new file mode 100644 index 000000000..4b3756fb9 --- /dev/null +++ b/Tests/test_file_fpx.py @@ -0,0 +1,15 @@ +from helper import unittest, PillowTestCase + +from PIL import FpxImagePlugin + + +class TestFileFpx(PillowTestCase): + + def test_invalid_file(self): + self.assertRaises(SyntaxError, lambda: FpxImagePlugin.FpxImageFile("Tests/images/flower.jpg")) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_file_gbr.py b/Tests/test_file_gbr.py new file mode 100644 index 000000000..ecec0189b --- /dev/null +++ b/Tests/test_file_gbr.py @@ -0,0 +1,15 @@ +from helper import unittest, PillowTestCase + +from PIL import GbrImagePlugin + + +class TestFileGbr(PillowTestCase): + + def test_invalid_file(self): + self.assertRaises(SyntaxError, lambda: GbrImagePlugin.GbrImageFile("Tests/images/flower.jpg")) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 666e1f16d..33e04cb5e 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -25,6 +25,9 @@ class TestFileGif(PillowTestCase): self.assertEqual(im.size, (128, 128)) self.assertEqual(im.format, "GIF") + def test_invalid_file(self): + self.assertRaises(SyntaxError, lambda: GifImagePlugin.GifImageFile("Tests/images/flower.jpg")) + def test_optimize(self): from io import BytesIO diff --git a/Tests/test_file_gribstub.py b/Tests/test_file_gribstub.py new file mode 100644 index 000000000..84bf8a5be --- /dev/null +++ b/Tests/test_file_gribstub.py @@ -0,0 +1,15 @@ +from helper import unittest, PillowTestCase + +from PIL import GribStubImagePlugin + + +class TestFileGribStub(PillowTestCase): + + def test_invalid_file(self): + self.assertRaises(SyntaxError, lambda: GribStubImagePlugin.GribStubImageFile("Tests/images/flower.jpg")) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_file_hdf5stub.py b/Tests/test_file_hdf5stub.py new file mode 100644 index 000000000..2b7e208d2 --- /dev/null +++ b/Tests/test_file_hdf5stub.py @@ -0,0 +1,15 @@ +from helper import unittest, PillowTestCase + +from PIL import Hdf5StubImagePlugin + + +class TestFileHdf5Stub(PillowTestCase): + + def test_invalid_file(self): + self.assertRaises(SyntaxError, lambda: Hdf5StubImagePlugin.HDF5StubImageFile("Tests/images/flower.jpg")) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_file_ico.py b/Tests/test_file_ico.py index f7b52b124..d2adf8dcc 100644 --- a/Tests/test_file_ico.py +++ b/Tests/test_file_ico.py @@ -1,7 +1,7 @@ from helper import unittest, PillowTestCase, hopper import io -from PIL import Image +from PIL import Image, IcoImagePlugin # sample ppm stream TEST_ICO_FILE = "Tests/images/hopper.ico" @@ -17,6 +17,10 @@ class TestFileIco(PillowTestCase): self.assertEqual(im.size, (16, 16)) self.assertEqual(im.format, "ICO") + def test_invalid_file(self): + with open("Tests/images/flower.jpg", "rb") as fp: + self.assertRaises(SyntaxError, lambda: IcoImagePlugin.IcoImageFile(fp)) + def test_save_to_bytes(self): output = io.BytesIO() im = hopper() diff --git a/Tests/test_file_im.py b/Tests/test_file_im.py index 602db4b1d..01b6fd561 100644 --- a/Tests/test_file_im.py +++ b/Tests/test_file_im.py @@ -1,6 +1,6 @@ from helper import unittest, PillowTestCase, hopper -from PIL import Image +from PIL import Image, ImImagePlugin # sample im TEST_IM = "Tests/images/hopper.im" @@ -40,6 +40,10 @@ class TestFileIm(PillowTestCase): self.assert_image_equal(reread, im) + def test_invalid_file(self): + self.assertRaises(SyntaxError, lambda: ImImagePlugin.ImImageFile("Tests/images/flower.jpg")) + + if __name__ == '__main__': unittest.main() diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index 9768a881d..a97a7e9da 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -1,6 +1,6 @@ from helper import unittest, PillowTestCase -from PIL import Image +from PIL import Image, Jpeg2KImagePlugin from io import BytesIO codecs = dir(Image.core) @@ -39,6 +39,9 @@ class TestFileJpeg2k(PillowTestCase): self.assertEqual(im.size, (640, 480)) self.assertEqual(im.format, 'JPEG2000') + def test_invalid_file(self): + self.assertRaises(SyntaxError, lambda: Jpeg2KImagePlugin.Jpeg2KImageFile("Tests/images/flower.jpg")) + def test_bytesio(self): with open('Tests/images/test-card-lossless.jp2', 'rb') as f: data = BytesIO(f.read()) diff --git a/Tests/test_file_mcidas.py b/Tests/test_file_mcidas.py new file mode 100644 index 000000000..44130746a --- /dev/null +++ b/Tests/test_file_mcidas.py @@ -0,0 +1,15 @@ +from helper import unittest, PillowTestCase + +from PIL import McIdasImagePlugin + + +class TestFileMcIdas(PillowTestCase): + + def test_invalid_file(self): + self.assertRaises(SyntaxError, lambda: McIdasImagePlugin.McIdasImageFile("Tests/images/flower.jpg")) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_file_mic.py b/Tests/test_file_mic.py new file mode 100644 index 000000000..de106bc14 --- /dev/null +++ b/Tests/test_file_mic.py @@ -0,0 +1,15 @@ +from helper import unittest, PillowTestCase + +from PIL import MicImagePlugin + + +class TestFileMic(PillowTestCase): + + def test_invalid_file(self): + self.assertRaises(SyntaxError, lambda: MicImagePlugin.MicImageFile("Tests/images/flower.jpg")) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_file_msp.py b/Tests/test_file_msp.py index f4b1af75e..94fcfd895 100644 --- a/Tests/test_file_msp.py +++ b/Tests/test_file_msp.py @@ -1,6 +1,6 @@ from helper import unittest, PillowTestCase, hopper -from PIL import Image +from PIL import Image, MspImagePlugin TEST_FILE = "Tests/images/hopper.msp" @@ -18,6 +18,9 @@ class TestFileMsp(PillowTestCase): self.assertEqual(im.size, (128, 128)) self.assertEqual(im.format, "MSP") + def test_invalid_file(self): + self.assertRaises(SyntaxError, lambda: MspImagePlugin.MspImageFile("Tests/images/flower.jpg")) + def test_open(self): # Arrange # Act diff --git a/Tests/test_file_pcx.py b/Tests/test_file_pcx.py index 10d17d349..5a1a2e9a6 100644 --- a/Tests/test_file_pcx.py +++ b/Tests/test_file_pcx.py @@ -1,6 +1,6 @@ from helper import unittest, PillowTestCase, hopper -from PIL import Image +from PIL import Image, PcxImagePlugin class TestFilePcx(PillowTestCase): @@ -19,6 +19,9 @@ class TestFilePcx(PillowTestCase): for mode in ('1', 'L', 'P', 'RGB'): self._roundtrip(hopper(mode)) + def test_invalid_file(self): + self.assertRaises(SyntaxError, lambda: PcxImagePlugin.PcxImageFile("Tests/images/flower.jpg")) + def test_odd(self): # see issue #523, odd sized images should have a stride that's even. # not that imagemagick or gimp write pcx that way. diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index 9424bc09d..8dad9822c 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -52,6 +52,12 @@ class TestFilePdf(PillowTestCase): # Act / Assert self.helper_save_as_pdf(mode) + def test_unsupported_mode(self): + im = hopper("LA") + outfile = self.tempfile("temp_LA.pdf") + + self.assertRaises(ValueError, lambda: im.save(outfile)) + if __name__ == '__main__': unittest.main() diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index f438e24cc..dfd83baa6 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -81,6 +81,9 @@ class TestFilePng(PillowTestCase): hopper("I").save(test_file) im = Image.open(test_file) + def test_invalid_file(self): + self.assertRaises(SyntaxError, lambda: PngImagePlugin.PngImageFile("Tests/images/flower.jpg")) + def test_broken(self): # Check reading of totally broken files. In this case, the test # file was checked into Subversion as a text file. diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py index 6492787ec..af1780d9d 100644 --- a/Tests/test_file_psd.py +++ b/Tests/test_file_psd.py @@ -1,6 +1,6 @@ from helper import unittest, PillowTestCase -from PIL import Image +from PIL import Image, PsdImagePlugin # sample ppm stream test_file = "Tests/images/hopper.psd" @@ -16,6 +16,9 @@ class TestImagePsd(PillowTestCase): self.assertEqual(im.size, (128, 128)) self.assertEqual(im.format, "PSD") + def test_invalid_file(self): + self.assertRaises(SyntaxError, lambda: PsdImagePlugin.PsdImageFile("Tests/images/flower.jpg")) + def test_n_frames(self): im = Image.open("Tests/images/hopper_merged.psd") self.assertEqual(im.n_frames, 1) diff --git a/Tests/test_file_sgi.py b/Tests/test_file_sgi.py index d49086c51..9842b3991 100644 --- a/Tests/test_file_sgi.py +++ b/Tests/test_file_sgi.py @@ -1,6 +1,6 @@ from helper import unittest, PillowTestCase -from PIL import Image +from PIL import Image, SgiImagePlugin class TestFileSgi(PillowTestCase): @@ -32,6 +32,9 @@ class TestFileSgi(PillowTestCase): # Act / Assert self.assertRaises(ValueError, lambda: Image.open(test_file)) + def test_invalid_file(self): + self.assertRaises(ValueError, lambda: SgiImagePlugin.SgiImageFile("Tests/images/flower.jpg")) + if __name__ == '__main__': unittest.main() diff --git a/Tests/test_file_sun.py b/Tests/test_file_sun.py index 332104062..e476ebf13 100644 --- a/Tests/test_file_sun.py +++ b/Tests/test_file_sun.py @@ -1,6 +1,6 @@ from helper import unittest, PillowTestCase -from PIL import Image +from PIL import Image, SunImagePlugin class TestFileSun(PillowTestCase): @@ -16,6 +16,8 @@ class TestFileSun(PillowTestCase): # Assert self.assertEqual(im.size, (128, 128)) + self.assertRaises(SyntaxError, lambda: SunImagePlugin.SunImageFile("Tests/images/flower.jpg")) + if __name__ == '__main__': unittest.main() diff --git a/Tests/test_file_tga.py b/Tests/test_file_tga.py index ea94dee64..cccd9bf96 100644 --- a/Tests/test_file_tga.py +++ b/Tests/test_file_tga.py @@ -15,6 +15,25 @@ class TestFileTga(PillowTestCase): # Assert self.assertEqual(im.size, (100, 100)) + def test_save(self): + test_file = "Tests/images/tga_id_field.tga" + im = Image.open(test_file) + + test_file = self.tempfile("temp.tga") + + # Save + im.save(test_file) + test_im = Image.open(test_file) + self.assertEqual(test_im.size, (100, 100)) + + # RGBA save + im.convert("RGBA").save(test_file) + test_im = Image.open(test_file) + self.assertEqual(test_im.size, (100, 100)) + + # Unsupported mode save + self.assertRaises(IOError, lambda: im.convert("LA").save(test_file)) + if __name__ == '__main__': unittest.main() diff --git a/Tests/test_file_webp.py b/Tests/test_file_webp.py index 8c8313dd9..d1c8e580e 100644 --- a/Tests/test_file_webp.py +++ b/Tests/test_file_webp.py @@ -72,6 +72,11 @@ class TestFileWebp(PillowTestCase): target = hopper("RGB") self.assert_image_similar(image, target, 12) + def test_write_unsupported_mode(self): + temp_file = self.tempfile("temp.webp") + + self.assertRaises(IOError, lambda: hopper("L").save(temp_file)) + if __name__ == '__main__': unittest.main() diff --git a/Tests/test_file_xpm.py b/Tests/test_file_xpm.py index 6a6817048..3e3c35e18 100644 --- a/Tests/test_file_xpm.py +++ b/Tests/test_file_xpm.py @@ -1,6 +1,6 @@ from helper import unittest, PillowTestCase, hopper -from PIL import Image +from PIL import Image, XpmImagePlugin # sample ppm stream TEST_FILE = "Tests/images/hopper.xpm" @@ -18,6 +18,9 @@ class TestFileXpm(PillowTestCase): # large error due to quantization->44 colors. self.assert_image_similar(im.convert('RGB'), hopper('RGB'), 60) + def test_invalid_file(self): + self.assertRaises(SyntaxError, lambda: XpmImagePlugin.XpmImageFile("Tests/images/flower.jpg")) + def test_load_read(self): # Arrange im = Image.open(TEST_FILE) diff --git a/Tests/test_font_bdf.py b/Tests/test_font_bdf.py index b844f1228..4d88ae46f 100644 --- a/Tests/test_font_bdf.py +++ b/Tests/test_font_bdf.py @@ -15,6 +15,10 @@ class TestFontBdf(PillowTestCase): self.assertIsInstance(font, FontFile.FontFile) self.assertEqual(len([_f for _f in font.glyph if _f]), 190) + def test_invalid_file(self): + with open("Tests/images/flower.jpg", "rb") as fp: + self.assertRaises(SyntaxError, lambda: BdfFontFile.BdfFontFile(fp)) + if __name__ == '__main__': unittest.main() diff --git a/Tests/test_font_pcf.py b/Tests/test_font_pcf.py index 3cc6afa64..f6dd9265e 100644 --- a/Tests/test_font_pcf.py +++ b/Tests/test_font_pcf.py @@ -30,6 +30,10 @@ class TestFontPcf(PillowTestCase): def test_sanity(self): self.save_font() + def test_invalid_file(self): + with open("Tests/images/flower.jpg", "rb") as fp: + self.assertRaises(SyntaxError, lambda: PcfFontFile.PcfFontFile(fp)) + def xtest_draw(self): tempname = self.save_font() diff --git a/Tests/test_image_filter.py b/Tests/test_image_filter.py index 6a694b3ca..a62431dd9 100644 --- a/Tests/test_image_filter.py +++ b/Tests/test_image_filter.py @@ -88,6 +88,12 @@ class TestImageFilter(PillowTestCase): self.assertEqual(rankfilter("I"), (0, 4, 8)) self.assertEqual(rankfilter("F"), (0.0, 4.0, 8.0)) + def test_rankfilter_properties(self): + rankfilter = ImageFilter.RankFilter(1,2) + + self.assertEqual(rankfilter.size, 1) + self.assertEqual(rankfilter.rank, 2) + if __name__ == '__main__': unittest.main() diff --git a/Tests/test_image_mode.py b/Tests/test_image_mode.py index 6441c7d1b..c20db50aa 100644 --- a/Tests/test_image_mode.py +++ b/Tests/test_image_mode.py @@ -21,12 +21,14 @@ class TestImageMode(PillowTestCase): m = ImageMode.getmode("1") self.assertEqual(m.mode, "1") + self.assertEqual(str(m), "1") self.assertEqual(m.bands, ("1",)) self.assertEqual(m.basemode, "L") self.assertEqual(m.basetype, "L") m = ImageMode.getmode("RGB") self.assertEqual(m.mode, "RGB") + self.assertEqual(str(m), "RGB") self.assertEqual(m.bands, ("R", "G", "B")) self.assertEqual(m.basemode, "RGB") self.assertEqual(m.basetype, "L") diff --git a/Tests/test_imagecolor.py b/Tests/test_imagecolor.py index 5d8944852..d55e73b9c 100644 --- a/Tests/test_imagecolor.py +++ b/Tests/test_imagecolor.py @@ -18,6 +18,8 @@ class TestImageColor(PillowTestCase): (255, 0, 0, 0), ImageColor.getrgb("rgba(255, 0, 0, 0)")) self.assertEqual((255, 0, 0), ImageColor.getrgb("red")) + self.assertRaises(ValueError, lambda: ImageColor.getrgb("invalid color")) + # look for rounding errors (based on code by Tim Hatch) def test_rounding_errors(self): diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index ffefa6504..2e746438d 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -52,6 +52,11 @@ class TestImageDraw(PillowTestCase): self.assert_warning(DeprecationWarning, lambda: draw.setink(0)) self.assert_warning(DeprecationWarning, lambda: draw.setfill(0)) + def test_mode_mismatch(self): + im = hopper("RGB").copy() + + self.assertRaises(ValueError, lambda: ImageDraw.ImageDraw(im, mode="L")) + def helper_arc(self, bbox): # Arrange im = Image.new("RGB", (W, H)) diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index bc81575b6..fbd10d47b 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -93,6 +93,9 @@ class TestImageFile(PillowTestCase): self.assert_image_equal(im1, im2) + def test_raise_ioerror(self): + self.assertRaises(IOError, lambda: ImageFile.raise_ioerror(1)) + if __name__ == '__main__': unittest.main() diff --git a/Tests/test_imagesequence.py b/Tests/test_imagesequence.py index d2aa17df7..3bd6dc582 100644 --- a/Tests/test_imagesequence.py +++ b/Tests/test_imagesequence.py @@ -22,6 +22,8 @@ class TestImageSequence(PillowTestCase): self.assertEqual(index, 1) + self.assertRaises(AttributeError, lambda: ImageSequence.Iterator(0)) + def _test_multipage_tiff(self, dbg=False): im = Image.open('Tests/images/multipage.tiff') for index, frame in enumerate(ImageSequence.Iterator(im)): diff --git a/Tests/test_olefileio.py b/Tests/test_olefileio.py index 1cff273a1..d3842e977 100644 --- a/Tests/test_olefileio.py +++ b/Tests/test_olefileio.py @@ -7,25 +7,20 @@ import PIL.OleFileIO as OleFileIO class TestOleFileIo(PillowTestCase): - def test_isOleFile_false(self): - # Arrange - non_ole_file = "Tests/images/flower.jpg" - - # Act - is_ole = OleFileIO.isOleFile(non_ole_file) - - # Assert - self.assertFalse(is_ole) - - def test_isOleFile_true(self): - # Arrange + def test_isOleFile(self): ole_file = "Tests/images/test-ole-file.doc" - # Act - is_ole = OleFileIO.isOleFile(ole_file) + self.assertTrue(OleFileIO.isOleFile(ole_file)) + with open(ole_file, 'rb') as fp: + self.assertTrue(OleFileIO.isOleFile(fp)) + self.assertTrue(OleFileIO.isOleFile(fp.read())) - # Assert - self.assertTrue(is_ole) + non_ole_file = "Tests/images/flower.jpg" + + self.assertFalse(OleFileIO.isOleFile(non_ole_file)) + with open(non_ole_file, 'rb') as fp: + self.assertFalse(OleFileIO.isOleFile(fp)) + self.assertFalse(OleFileIO.isOleFile(fp.read())) def test_exists_worddocument(self): # Arrange From 309ab1fc3d167b38a463e6bd75438aba4dc3419e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 3 Jul 2015 16:22:56 +1000 Subject: [PATCH 0430/1037] Various Flake8 fixes --- Tests/check_imaging_leaks.py | 3 ++- Tests/check_png_dos.py | 3 ++- Tests/test_cffi.py | 2 +- Tests/test_file_bmp.py | 3 ++- Tests/test_file_bufrstub.py | 6 +++++- Tests/test_file_cur.py | 5 ++++- Tests/test_file_dcx.py | 3 ++- Tests/test_file_eps.py | 9 +++++++-- Tests/test_file_fitsstub.py | 6 +++++- Tests/test_file_fli.py | 5 ++++- Tests/test_file_fpx.py | 5 ++++- Tests/test_file_gbr.py | 5 ++++- Tests/test_file_gif.py | 5 ++++- Tests/test_file_gribstub.py | 6 +++++- Tests/test_file_hdf5stub.py | 6 +++++- Tests/test_file_ico.py | 9 ++++++--- Tests/test_file_im.py | 5 ++++- Tests/test_file_jpeg.py | 17 ++++++++++------- Tests/test_file_jpeg2k.py | 6 +++++- Tests/test_file_mcidas.py | 6 +++++- Tests/test_file_mic.py | 5 ++++- Tests/test_file_msp.py | 5 ++++- Tests/test_file_pcx.py | 5 ++++- Tests/test_file_png.py | 5 ++++- Tests/test_file_psd.py | 5 ++++- Tests/test_file_sgi.py | 6 +++++- Tests/test_file_sun.py | 4 +++- Tests/test_file_tga.py | 2 +- Tests/test_file_tiff.py | 3 ++- Tests/test_file_webp_alpha.py | 3 ++- Tests/test_file_xpm.py | 5 ++++- Tests/test_image_filter.py | 2 +- Tests/test_imagecolor.py | 7 ++++--- Tests/test_imagedraw.py | 3 ++- Tests/test_imagefont_bitmap.py | 11 +++++++---- Tests/test_imageops_usm.py | 4 +++- Tests/test_imagewin.py | 3 ++- Tests/test_scipy.py | 4 ++-- 38 files changed, 144 insertions(+), 53 deletions(-) diff --git a/Tests/check_imaging_leaks.py b/Tests/check_imaging_leaks.py index e79429f8f..a31cd2180 100644 --- a/Tests/check_imaging_leaks.py +++ b/Tests/check_imaging_leaks.py @@ -31,7 +31,8 @@ class TestImagingLeaks(PillowTestCase): def test_leak_putdata(self): im = Image.new('RGB', (25, 25)) - self._test_leak(min_iterations, max_iterations, im.putdata, im.getdata()) + self._test_leak(min_iterations, max_iterations, + im.putdata, im.getdata()) def test_leak_getlist(self): im = Image.new('P', (25, 25)) diff --git a/Tests/check_png_dos.py b/Tests/check_png_dos.py index 762c9607a..c24eeb359 100644 --- a/Tests/check_png_dos.py +++ b/Tests/check_png_dos.py @@ -42,7 +42,8 @@ class TestPngDos(PillowTestCase): total_len = 0 for txt in im2.text.values(): total_len += len(txt) - self.assertLess(total_len, 64*1024*1024, "Total text chunks greater than 64M") + self.assertLess(total_len, 64*1024*1024, + "Total text chunks greater than 64M") if __name__ == '__main__': unittest.main() diff --git a/Tests/test_cffi.py b/Tests/test_cffi.py index 361b59a39..02d1ff7d3 100644 --- a/Tests/test_cffi.py +++ b/Tests/test_cffi.py @@ -74,7 +74,7 @@ class TestCffi(PillowTestCase): self._test_get_access(hopper('LA')) self._test_get_access(hopper('1')) self._test_get_access(hopper('P')) - # self._test_get_access(hopper('PA')) # PA -- how do I make a PA image? + # self._test_get_access(hopper('PA')) # PA -- how do I make a PA image? self._test_get_access(hopper('F')) im = Image.new('I;16', (10, 10), 40000) diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py index 15d470c90..25f70139d 100644 --- a/Tests/test_file_bmp.py +++ b/Tests/test_file_bmp.py @@ -27,7 +27,8 @@ class TestFileBmp(PillowTestCase): def test_invalid_file(self): with open("Tests/images/flower.jpg", "rb") as fp: - self.assertRaises(SyntaxError, lambda: BmpImagePlugin.BmpImageFile(fp)) + self.assertRaises(SyntaxError, + lambda: BmpImagePlugin.BmpImageFile(fp)) def test_save_to_bytes(self): output = io.BytesIO() diff --git a/Tests/test_file_bufrstub.py b/Tests/test_file_bufrstub.py index 8cf496533..05266e9fd 100644 --- a/Tests/test_file_bufrstub.py +++ b/Tests/test_file_bufrstub.py @@ -6,7 +6,11 @@ from PIL import BufrStubImagePlugin class TestFileBufrStub(PillowTestCase): def test_invalid_file(self): - self.assertRaises(SyntaxError, lambda: BufrStubImagePlugin.BufrStubImageFile("Tests/images/flower.jpg")) + invalid_file = "Tests/images/flower.jpg" + + self.assertRaises(SyntaxError, + lambda: + BufrStubImagePlugin.BufrStubImageFile(invalid_file)) if __name__ == '__main__': diff --git a/Tests/test_file_cur.py b/Tests/test_file_cur.py index d9acb5f78..fa4242629 100644 --- a/Tests/test_file_cur.py +++ b/Tests/test_file_cur.py @@ -21,7 +21,10 @@ class TestFileCur(PillowTestCase): self.assertEqual(im.getpixel((16, 16)), (84, 87, 86, 255)) def test_invalid_file(self): - self.assertRaises(SyntaxError, lambda: CurImagePlugin.CurImageFile("Tests/images/flower.jpg")) + invalid_file = "Tests/images/flower.jpg" + + self.assertRaises(SyntaxError, + lambda: CurImagePlugin.CurImageFile(invalid_file)) if __name__ == '__main__': diff --git a/Tests/test_file_dcx.py b/Tests/test_file_dcx.py index 62760fd0c..2c0f90c1f 100644 --- a/Tests/test_file_dcx.py +++ b/Tests/test_file_dcx.py @@ -22,7 +22,8 @@ class TestFileDcx(PillowTestCase): def test_invalid_file(self): with open("Tests/images/flower.jpg", "rb") as fp: - self.assertRaises(SyntaxError, lambda: DcxImagePlugin.DcxImageFile(fp)) + self.assertRaises(SyntaxError, + lambda: DcxImagePlugin.DcxImageFile(fp)) def test_tell(self): # Arrange diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index 931b788a3..6e4c63e4f 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -52,7 +52,10 @@ class TestFileEps(PillowTestCase): self.assertEqual(image2_scale2.format, "EPS") def test_invalid_file(self): - self.assertRaises(SyntaxError, lambda: EpsImagePlugin.EpsImageFile("Tests/images/flower.jpg")) + invalid_file = "Tests/images/flower.jpg" + + self.assertRaises(SyntaxError, + lambda: EpsImagePlugin.EpsImageFile(invalid_file)) def test_file_object(self): # issue 479 @@ -152,7 +155,9 @@ class TestFileEps(PillowTestCase): Image.open(file3) def _test_readline(self, t, ending): - ending = "Failure with line ending: %s" % ("".join("%s" % ord(s) for s in ending)) + ending = "Failure with line ending: %s" % ("".join( + "%s" % ord(s) + for s in ending)) self.assertEqual(t.readline().strip('\r\n'), 'something', ending) self.assertEqual(t.readline().strip('\r\n'), 'else', ending) self.assertEqual(t.readline().strip('\r\n'), 'baz', ending) diff --git a/Tests/test_file_fitsstub.py b/Tests/test_file_fitsstub.py index 189d002d6..cc38b4a3a 100644 --- a/Tests/test_file_fitsstub.py +++ b/Tests/test_file_fitsstub.py @@ -6,7 +6,11 @@ from PIL import FitsStubImagePlugin class TestFileFitsStub(PillowTestCase): def test_invalid_file(self): - self.assertRaises(SyntaxError, lambda: FitsStubImagePlugin.FITSStubImageFile("Tests/images/flower.jpg")) + invalid_file = "Tests/images/flower.jpg" + + self.assertRaises(SyntaxError, + lambda: + FitsStubImagePlugin.FITSStubImageFile(invalid_file)) if __name__ == '__main__': diff --git a/Tests/test_file_fli.py b/Tests/test_file_fli.py index daaebdc69..aa2fa5ae5 100644 --- a/Tests/test_file_fli.py +++ b/Tests/test_file_fli.py @@ -19,7 +19,10 @@ class TestFileFli(PillowTestCase): self.assertEqual(im.format, "FLI") def test_invalid_file(self): - self.assertRaises(SyntaxError, lambda: FliImagePlugin.FliImageFile("Tests/images/flower.jpg")) + invalid_file = "Tests/images/flower.jpg" + + self.assertRaises(SyntaxError, + lambda: FliImagePlugin.FliImageFile(invalid_file)) def test_n_frames(self): im = Image.open(test_file) diff --git a/Tests/test_file_fpx.py b/Tests/test_file_fpx.py index 4b3756fb9..35119a612 100644 --- a/Tests/test_file_fpx.py +++ b/Tests/test_file_fpx.py @@ -6,7 +6,10 @@ from PIL import FpxImagePlugin class TestFileFpx(PillowTestCase): def test_invalid_file(self): - self.assertRaises(SyntaxError, lambda: FpxImagePlugin.FpxImageFile("Tests/images/flower.jpg")) + invalid_file = "Tests/images/flower.jpg" + + self.assertRaises(SyntaxError, + lambda: FpxImagePlugin.FpxImageFile(invalid_file)) if __name__ == '__main__': diff --git a/Tests/test_file_gbr.py b/Tests/test_file_gbr.py index ecec0189b..57b301ada 100644 --- a/Tests/test_file_gbr.py +++ b/Tests/test_file_gbr.py @@ -6,7 +6,10 @@ from PIL import GbrImagePlugin class TestFileGbr(PillowTestCase): def test_invalid_file(self): - self.assertRaises(SyntaxError, lambda: GbrImagePlugin.GbrImageFile("Tests/images/flower.jpg")) + invalid_file = "Tests/images/flower.jpg" + + self.assertRaises(SyntaxError, + lambda: GbrImagePlugin.GbrImageFile(invalid_file)) if __name__ == '__main__': diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 33e04cb5e..c40e5f6b0 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -26,7 +26,10 @@ class TestFileGif(PillowTestCase): self.assertEqual(im.format, "GIF") def test_invalid_file(self): - self.assertRaises(SyntaxError, lambda: GifImagePlugin.GifImageFile("Tests/images/flower.jpg")) + invalid_file = "Tests/images/flower.jpg" + + self.assertRaises(SyntaxError, + lambda: GifImagePlugin.GifImageFile(invalid_file)) def test_optimize(self): from io import BytesIO diff --git a/Tests/test_file_gribstub.py b/Tests/test_file_gribstub.py index 84bf8a5be..dd4ee84ff 100644 --- a/Tests/test_file_gribstub.py +++ b/Tests/test_file_gribstub.py @@ -6,7 +6,11 @@ from PIL import GribStubImagePlugin class TestFileGribStub(PillowTestCase): def test_invalid_file(self): - self.assertRaises(SyntaxError, lambda: GribStubImagePlugin.GribStubImageFile("Tests/images/flower.jpg")) + invalid_file = "Tests/images/flower.jpg" + + self.assertRaises(SyntaxError, + lambda: + GribStubImagePlugin.GribStubImageFile(invalid_file)) if __name__ == '__main__': diff --git a/Tests/test_file_hdf5stub.py b/Tests/test_file_hdf5stub.py index 2b7e208d2..485931b36 100644 --- a/Tests/test_file_hdf5stub.py +++ b/Tests/test_file_hdf5stub.py @@ -6,7 +6,11 @@ from PIL import Hdf5StubImagePlugin class TestFileHdf5Stub(PillowTestCase): def test_invalid_file(self): - self.assertRaises(SyntaxError, lambda: Hdf5StubImagePlugin.HDF5StubImageFile("Tests/images/flower.jpg")) + test_file = "Tests/images/flower.jpg" + + self.assertRaises(SyntaxError, + lambda: + Hdf5StubImagePlugin.HDF5StubImageFile(test_file)) if __name__ == '__main__': diff --git a/Tests/test_file_ico.py b/Tests/test_file_ico.py index d2adf8dcc..70e00c083 100644 --- a/Tests/test_file_ico.py +++ b/Tests/test_file_ico.py @@ -19,7 +19,8 @@ class TestFileIco(PillowTestCase): def test_invalid_file(self): with open("Tests/images/flower.jpg", "rb") as fp: - self.assertRaises(SyntaxError, lambda: IcoImagePlugin.IcoImageFile(fp)) + self.assertRaises(SyntaxError, + lambda: IcoImagePlugin.IcoImageFile(fp)) def test_save_to_bytes(self): output = io.BytesIO() @@ -34,7 +35,8 @@ class TestFileIco(PillowTestCase): self.assertEqual(im.mode, reloaded.mode) self.assertEqual((64, 64), reloaded.size) self.assertEqual(reloaded.format, "ICO") - self.assert_image_equal(reloaded, hopper().resize((64, 64), Image.LANCZOS)) + self.assert_image_equal(reloaded, + hopper().resize((64, 64), Image.LANCZOS)) # the other one output.seek(0) @@ -44,7 +46,8 @@ class TestFileIco(PillowTestCase): self.assertEqual(im.mode, reloaded.mode) self.assertEqual((32, 32), reloaded.size) self.assertEqual(reloaded.format, "ICO") - self.assert_image_equal(reloaded, hopper().resize((32, 32), Image.LANCZOS)) + self.assert_image_equal(reloaded, + hopper().resize((32, 32), Image.LANCZOS)) if __name__ == '__main__': diff --git a/Tests/test_file_im.py b/Tests/test_file_im.py index 01b6fd561..e3d92d1d5 100644 --- a/Tests/test_file_im.py +++ b/Tests/test_file_im.py @@ -41,7 +41,10 @@ class TestFileIm(PillowTestCase): self.assert_image_equal(reread, im) def test_invalid_file(self): - self.assertRaises(SyntaxError, lambda: ImImagePlugin.ImImageFile("Tests/images/flower.jpg")) + invalid_file = "Tests/images/flower.jpg" + + self.assertRaises(SyntaxError, + lambda: ImImagePlugin.ImImageFile(invalid_file)) if __name__ == '__main__': diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index d4929dd58..bb2d7b61e 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -291,22 +291,24 @@ class TestFileJpeg(PillowTestCase): # dict of qtable lists self.assert_image_similar(im, - self.roundtrip(im, - qtables={0: standard_l_qtable, - 1: standard_chrominance_qtable}), - 30) + self.roundtrip(im, qtables={ + 0: standard_l_qtable, + 1: standard_chrominance_qtable + }), 30) # not a sequence self.assertRaises(Exception, lambda: self.roundtrip(im, qtables='a')) # sequence wrong length self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[])) # sequence wrong length - self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[1, 2, 3, 4, 5])) + self.assertRaises(Exception, + lambda: self.roundtrip(im, qtables=[1, 2, 3, 4, 5])) # qtable entry not a sequence self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[1])) # qtable entry has wrong number of items - self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[[1, 2, 3, 4]])) + self.assertRaises(Exception, + lambda: self.roundtrip(im, qtables=[[1, 2, 3, 4]])) @unittest.skipUnless(djpeg_available(), "djpeg not available") def test_load_djpeg(self): @@ -337,7 +339,8 @@ class TestFileJpeg(PillowTestCase): """ Generates a very hard to compress file :param size: tuple """ - return Image.frombytes('RGB', size, os.urandom(size[0]*size[1] * 3)) + return Image.frombytes('RGB', + size, os.urandom(size[0]*size[1] * 3)) im = gen_random_image((512, 512)) f = self.tempfile("temp.jpeg") diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index a97a7e9da..4b7286a20 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -40,7 +40,11 @@ class TestFileJpeg2k(PillowTestCase): self.assertEqual(im.format, 'JPEG2000') def test_invalid_file(self): - self.assertRaises(SyntaxError, lambda: Jpeg2KImagePlugin.Jpeg2KImageFile("Tests/images/flower.jpg")) + invalid_file = "Tests/images/flower.jpg" + + self.assertRaises(SyntaxError, + lambda: + Jpeg2KImagePlugin.Jpeg2KImageFile(invalid_file)) def test_bytesio(self): with open('Tests/images/test-card-lossless.jp2', 'rb') as f: diff --git a/Tests/test_file_mcidas.py b/Tests/test_file_mcidas.py index 44130746a..d547011e2 100644 --- a/Tests/test_file_mcidas.py +++ b/Tests/test_file_mcidas.py @@ -6,7 +6,11 @@ from PIL import McIdasImagePlugin class TestFileMcIdas(PillowTestCase): def test_invalid_file(self): - self.assertRaises(SyntaxError, lambda: McIdasImagePlugin.McIdasImageFile("Tests/images/flower.jpg")) + invalid_file = "Tests/images/flower.jpg" + + self.assertRaises(SyntaxError, + lambda: + McIdasImagePlugin.McIdasImageFile(invalid_file)) if __name__ == '__main__': diff --git a/Tests/test_file_mic.py b/Tests/test_file_mic.py index de106bc14..0a574422a 100644 --- a/Tests/test_file_mic.py +++ b/Tests/test_file_mic.py @@ -6,7 +6,10 @@ from PIL import MicImagePlugin class TestFileMic(PillowTestCase): def test_invalid_file(self): - self.assertRaises(SyntaxError, lambda: MicImagePlugin.MicImageFile("Tests/images/flower.jpg")) + invalid_file = "Tests/images/flower.jpg" + + self.assertRaises(SyntaxError, + lambda: MicImagePlugin.MicImageFile(invalid_file)) if __name__ == '__main__': diff --git a/Tests/test_file_msp.py b/Tests/test_file_msp.py index 94fcfd895..3dbca6e60 100644 --- a/Tests/test_file_msp.py +++ b/Tests/test_file_msp.py @@ -19,7 +19,10 @@ class TestFileMsp(PillowTestCase): self.assertEqual(im.format, "MSP") def test_invalid_file(self): - self.assertRaises(SyntaxError, lambda: MspImagePlugin.MspImageFile("Tests/images/flower.jpg")) + invalid_file = "Tests/images/flower.jpg" + + self.assertRaises(SyntaxError, + lambda: MspImagePlugin.MspImageFile(invalid_file)) def test_open(self): # Arrange diff --git a/Tests/test_file_pcx.py b/Tests/test_file_pcx.py index 5a1a2e9a6..f6342bed9 100644 --- a/Tests/test_file_pcx.py +++ b/Tests/test_file_pcx.py @@ -20,7 +20,10 @@ class TestFilePcx(PillowTestCase): self._roundtrip(hopper(mode)) def test_invalid_file(self): - self.assertRaises(SyntaxError, lambda: PcxImagePlugin.PcxImageFile("Tests/images/flower.jpg")) + invalid_file = "Tests/images/flower.jpg" + + self.assertRaises(SyntaxError, + lambda: PcxImagePlugin.PcxImageFile(invalid_file)) def test_odd(self): # see issue #523, odd sized images should have a stride that's even. diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index dfd83baa6..cb72b2d73 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -82,7 +82,10 @@ class TestFilePng(PillowTestCase): im = Image.open(test_file) def test_invalid_file(self): - self.assertRaises(SyntaxError, lambda: PngImagePlugin.PngImageFile("Tests/images/flower.jpg")) + invalid_file = "Tests/images/flower.jpg" + + self.assertRaises(SyntaxError, + lambda: PngImagePlugin.PngImageFile(invalid_file)) def test_broken(self): # Check reading of totally broken files. In this case, the test diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py index af1780d9d..4890839f1 100644 --- a/Tests/test_file_psd.py +++ b/Tests/test_file_psd.py @@ -17,7 +17,10 @@ class TestImagePsd(PillowTestCase): self.assertEqual(im.format, "PSD") def test_invalid_file(self): - self.assertRaises(SyntaxError, lambda: PsdImagePlugin.PsdImageFile("Tests/images/flower.jpg")) + invalid_file = "Tests/images/flower.jpg" + + self.assertRaises(SyntaxError, + lambda: PsdImagePlugin.PsdImageFile(invalid_file)) def test_n_frames(self): im = Image.open("Tests/images/hopper_merged.psd") diff --git a/Tests/test_file_sgi.py b/Tests/test_file_sgi.py index 9842b3991..e78488913 100644 --- a/Tests/test_file_sgi.py +++ b/Tests/test_file_sgi.py @@ -33,7 +33,11 @@ class TestFileSgi(PillowTestCase): self.assertRaises(ValueError, lambda: Image.open(test_file)) def test_invalid_file(self): - self.assertRaises(ValueError, lambda: SgiImagePlugin.SgiImageFile("Tests/images/flower.jpg")) + invalid_file = "Tests/images/flower.jpg" + + self.assertRaises(ValueError, + lambda: + SgiImagePlugin.SgiImageFile(invalid_file)) if __name__ == '__main__': diff --git a/Tests/test_file_sun.py b/Tests/test_file_sun.py index e476ebf13..16c43b921 100644 --- a/Tests/test_file_sun.py +++ b/Tests/test_file_sun.py @@ -16,7 +16,9 @@ class TestFileSun(PillowTestCase): # Assert self.assertEqual(im.size, (128, 128)) - self.assertRaises(SyntaxError, lambda: SunImagePlugin.SunImageFile("Tests/images/flower.jpg")) + invalid_file = "Tests/images/flower.jpg" + self.assertRaises(SyntaxError, + lambda: SunImagePlugin.SunImageFile(invalid_file)) if __name__ == '__main__': diff --git a/Tests/test_file_tga.py b/Tests/test_file_tga.py index cccd9bf96..459e766d5 100644 --- a/Tests/test_file_tga.py +++ b/Tests/test_file_tga.py @@ -30,7 +30,7 @@ class TestFileTga(PillowTestCase): im.convert("RGBA").save(test_file) test_im = Image.open(test_file) self.assertEqual(test_im.size, (100, 100)) - + # Unsupported mode save self.assertRaises(IOError, lambda: im.convert("LA").save(test_file)) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 98b346016..e1702bcfe 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -86,7 +86,8 @@ class TestFileTiff(PillowTestCase): try: Image.open('Tests/images/hopper_bad_exif.jpg')._getexif() except struct.error: - self.fail("Bad EXIF data should not pass incorrect values to _binary unpack") + self.fail( + "Bad EXIF data passed incorrect values to _binary unpack") def test_little_endian(self): im = Image.open('Tests/images/16bit.cropped.tif') diff --git a/Tests/test_file_webp_alpha.py b/Tests/test_file_webp_alpha.py index f316b71e1..e7df62ee6 100644 --- a/Tests/test_file_webp_alpha.py +++ b/Tests/test_file_webp_alpha.py @@ -83,7 +83,8 @@ class TestFileWebpAlpha(PillowTestCase): image.load() image.getdata() - # early versions of webp are known to produce higher deviations: deal with it + # early versions of webp are known to produce higher deviations: + # deal with it if _webp.WebPDecoderVersion(self) <= 0x201: self.assert_image_similar(image, pil_image, 3.0) else: diff --git a/Tests/test_file_xpm.py b/Tests/test_file_xpm.py index 3e3c35e18..e589a8d26 100644 --- a/Tests/test_file_xpm.py +++ b/Tests/test_file_xpm.py @@ -19,7 +19,10 @@ class TestFileXpm(PillowTestCase): self.assert_image_similar(im.convert('RGB'), hopper('RGB'), 60) def test_invalid_file(self): - self.assertRaises(SyntaxError, lambda: XpmImagePlugin.XpmImageFile("Tests/images/flower.jpg")) + invalid_file = "Tests/images/flower.jpg" + + self.assertRaises(SyntaxError, + lambda: XpmImagePlugin.XpmImageFile(invalid_file)) def test_load_read(self): # Arrange diff --git a/Tests/test_image_filter.py b/Tests/test_image_filter.py index a62431dd9..f75a1891d 100644 --- a/Tests/test_image_filter.py +++ b/Tests/test_image_filter.py @@ -89,7 +89,7 @@ class TestImageFilter(PillowTestCase): self.assertEqual(rankfilter("F"), (0.0, 4.0, 8.0)) def test_rankfilter_properties(self): - rankfilter = ImageFilter.RankFilter(1,2) + rankfilter = ImageFilter.RankFilter(1, 2) self.assertEqual(rankfilter.size, 1) self.assertEqual(rankfilter.rank, 2) diff --git a/Tests/test_imagecolor.py b/Tests/test_imagecolor.py index d55e73b9c..a38c8b070 100644 --- a/Tests/test_imagecolor.py +++ b/Tests/test_imagecolor.py @@ -18,7 +18,8 @@ class TestImageColor(PillowTestCase): (255, 0, 0, 0), ImageColor.getrgb("rgba(255, 0, 0, 0)")) self.assertEqual((255, 0, 0), ImageColor.getrgb("red")) - self.assertRaises(ValueError, lambda: ImageColor.getrgb("invalid color")) + self.assertRaises(ValueError, + lambda: ImageColor.getrgb("invalid color")) # look for rounding errors (based on code by Tim Hatch) def test_rounding_errors(self): @@ -45,8 +46,8 @@ class TestImageColor(PillowTestCase): self.assertEqual(0, ImageColor.getcolor("black", "L")) self.assertEqual(255, ImageColor.getcolor("white", "L")) - self.assertEqual( - 162, ImageColor.getcolor("rgba(0, 255, 115, 33)", "L")) + self.assertEqual(162, + ImageColor.getcolor("rgba(0, 255, 115, 33)", "L")) Image.new("L", (1, 1), "white") self.assertEqual(0, ImageColor.getcolor("black", "1")) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 2e746438d..803677616 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -55,7 +55,8 @@ class TestImageDraw(PillowTestCase): def test_mode_mismatch(self): im = hopper("RGB").copy() - self.assertRaises(ValueError, lambda: ImageDraw.ImageDraw(im, mode="L")) + self.assertRaises(ValueError, + lambda: ImageDraw.ImageDraw(im, mode="L")) def helper_arc(self, bbox): # Arrange diff --git a/Tests/test_imagefont_bitmap.py b/Tests/test_imagefont_bitmap.py index 27141f4b3..a9d745b22 100644 --- a/Tests/test_imagefont_bitmap.py +++ b/Tests/test_imagefont_bitmap.py @@ -5,16 +5,19 @@ from PIL import Image, ImageFont, ImageDraw class TestImageFontBitmap(PillowTestCase): def test_similar(self): text = 'EmbeddedBitmap' - font_outline = ImageFont.truetype(font='Tests/fonts/DejaVuSans.ttf', size=24) - font_bitmap = ImageFont.truetype(font='Tests/fonts/DejaVuSans-bitmap.ttf', size=24) + font_outline = ImageFont.truetype( + font='Tests/fonts/DejaVuSans.ttf', size=24) + font_bitmap = ImageFont.truetype( + font='Tests/fonts/DejaVuSans-bitmap.ttf', size=24) size_outline, size_bitmap = font_outline.getsize(text), font_bitmap.getsize(text) size_final = max(size_outline[0], size_bitmap[0]), max(size_outline[1], size_bitmap[1]) im_bitmap = Image.new('RGB', size_final, (255, 255, 255)) im_outline = im_bitmap.copy() draw_bitmap, draw_outline = ImageDraw.Draw(im_bitmap), ImageDraw.Draw(im_outline) - # Metrics are different on the bitmap and ttf fonts, more so on some platforms - # and versions of freetype than others. Mac has a 1px difference, linux doesn't. + # Metrics are different on the bitmap and ttf fonts, + # more so on some platforms and versions of freetype than others. + # Mac has a 1px difference, linux doesn't. draw_bitmap.text((0, size_final[1] - size_bitmap[1]), text, fill=(0, 0, 0), font=font_bitmap) draw_outline.text((0, size_final[1] - size_outline[1]), diff --git a/Tests/test_imageops_usm.py b/Tests/test_imageops_usm.py index f6eae640b..89c63c774 100644 --- a/Tests/test_imageops_usm.py +++ b/Tests/test_imageops_usm.py @@ -75,7 +75,9 @@ class TestImageOpsUsm(PillowTestCase): (4, 3, 2), (4, 2, 2)]: self.assertGreaterEqual(i.im.getpixel((x, y))[c], 250) # Fuzzy match. - gp = lambda x, y: i.im.getpixel((x, y)) + + def gp(x, y): + return i.im.getpixel((x, y)) self.assertTrue(236 <= gp(7, 4)[0] <= 239) self.assertTrue(236 <= gp(7, 5)[2] <= 239) self.assertTrue(236 <= gp(7, 6)[2] <= 239) diff --git a/Tests/test_imagewin.py b/Tests/test_imagewin.py index 7ceea86ee..8a5bc125f 100644 --- a/Tests/test_imagewin.py +++ b/Tests/test_imagewin.py @@ -115,7 +115,8 @@ class TestImageWinDib(PillowTestCase): # Act/Assert self.assert_warning(DeprecationWarning, dib.tostring) - self.assert_warning(DeprecationWarning, lambda: dib.fromstring(test_buffer)) + self.assert_warning(DeprecationWarning, + lambda: dib.fromstring(test_buffer)) if __name__ == '__main__': diff --git a/Tests/test_scipy.py b/Tests/test_scipy.py index 1632d9475..1d7568148 100644 --- a/Tests/test_scipy.py +++ b/Tests/test_scipy.py @@ -30,10 +30,10 @@ class Test_scipy_resize(PillowTestCase): def test_imresize4(self): im = np.array([[1, 2], [3, 4]]) - res = np.array([[1. , 1.25, 1.75, 2. ], + res = np.array([[1., 1.25, 1.75, 2.], [1.5, 1.75, 2.25, 2.5], [2.5, 2.75, 3.25, 3.5], - [3. , 3.25, 3.75, 4. ]], dtype=np.float32) + [3., 3.25, 3.75, 4.]], dtype=np.float32) # Check that resizing by target size, float and int are the same im2 = misc.imresize(im, (4, 4), mode='F') # output size im3 = misc.imresize(im, 2., mode='F') # fraction From 76d14d6cf142e0888edf42da865d53156078f089 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 3 Jul 2015 09:45:09 +0300 Subject: [PATCH 0431/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 80bc189d8..9a244ea22 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,7 +1,19 @@ Changelog (Pillow) ================== -2.9.0 (Unreleased) +3.0.0 (Unreleased) +------------------ + +- Added various tests #1330 + [radarhere] + +- More ImageFont tests #1327 + [hugovk] + +- Use logging instead of print #1207 + [anntzer] + +2.9.0 (2015-07-01) ------------------ - Added test for GimpPaletteFile #1324 From fa73879d190b9e528ac6aae756b4b0df8d4a21f0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 4 Jul 2015 23:29:58 +1000 Subject: [PATCH 0432/1037] Changed register calls to use format property --- PIL/CurImagePlugin.py | 4 ++-- PIL/DcxImagePlugin.py | 4 ++-- PIL/FliImagePlugin.py | 6 +++--- PIL/GbrImagePlugin.py | 4 ++-- PIL/IcnsImagePlugin.py | 9 +++++---- PIL/IcoImagePlugin.py | 6 +++--- PIL/ImImagePlugin.py | 6 +++--- PIL/IptcImagePlugin.py | 4 ++-- PIL/Jpeg2KImagePlugin.py | 20 ++++++++++---------- PIL/JpegImagePlugin.py | 14 +++++++------- PIL/MicImagePlugin.py | 4 ++-- PIL/MpegImagePlugin.py | 8 ++++---- PIL/MpoImagePlugin.py | 9 +++++---- PIL/MspImagePlugin.py | 6 +++--- PIL/PcdImagePlugin.py | 4 ++-- PIL/PcxImagePlugin.py | 6 +++--- PIL/PngImagePlugin.py | 8 ++++---- PIL/PpmImagePlugin.py | 10 +++++----- PIL/PsdImagePlugin.py | 4 ++-- PIL/SgiImagePlugin.py | 10 +++++----- PIL/SpiderImagePlugin.py | 4 ++-- PIL/SunImagePlugin.py | 4 ++-- PIL/TgaImagePlugin.py | 6 +++--- PIL/TiffImagePlugin.py | 10 +++++----- PIL/WebPImagePlugin.py | 8 ++++---- PIL/XbmImagePlugin.py | 8 ++++---- PIL/XpmImagePlugin.py | 6 +++--- 27 files changed, 97 insertions(+), 95 deletions(-) diff --git a/PIL/CurImagePlugin.py b/PIL/CurImagePlugin.py index 0178957ee..8707eeeb3 100644 --- a/PIL/CurImagePlugin.py +++ b/PIL/CurImagePlugin.py @@ -82,6 +82,6 @@ class CurImageFile(BmpImagePlugin.BmpImageFile): # # -------------------------------------------------------------------- -Image.register_open("CUR", CurImageFile, _accept) +Image.register_open(CurImageFile.format, CurImageFile, _accept) -Image.register_extension("CUR", ".cur") +Image.register_extension(CurImageFile.format, ".cur") diff --git a/PIL/DcxImagePlugin.py b/PIL/DcxImagePlugin.py index c6c9b8773..4c4bf700c 100644 --- a/PIL/DcxImagePlugin.py +++ b/PIL/DcxImagePlugin.py @@ -82,6 +82,6 @@ class DcxImageFile(PcxImageFile): return self.frame -Image.register_open("DCX", DcxImageFile, _accept) +Image.register_open(DcxImageFile.format, DcxImageFile, _accept) -Image.register_extension("DCX", ".dcx") +Image.register_extension(DcxImageFile.format, ".dcx") diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py index e3eaff970..1af9dadde 100644 --- a/PIL/FliImagePlugin.py +++ b/PIL/FliImagePlugin.py @@ -182,7 +182,7 @@ class FliImageFile(ImageFile.ImageFile): # # registry -Image.register_open("FLI", FliImageFile, _accept) +Image.register_open(FliImageFile.format, FliImageFile, _accept) -Image.register_extension("FLI", ".fli") -Image.register_extension("FLI", ".flc") +Image.register_extension(FliImageFile.format, ".fli") +Image.register_extension(FliImageFile.format, ".flc") diff --git a/PIL/GbrImagePlugin.py b/PIL/GbrImagePlugin.py index b512efd25..15282ecee 100644 --- a/PIL/GbrImagePlugin.py +++ b/PIL/GbrImagePlugin.py @@ -66,6 +66,6 @@ class GbrImageFile(ImageFile.ImageFile): # # registry -Image.register_open("GBR", GbrImageFile, _accept) +Image.register_open(GbrImageFile.format, GbrImageFile, _accept) -Image.register_extension("GBR", ".gbr") +Image.register_extension(GbrImageFile.format, ".gbr") diff --git a/PIL/IcnsImagePlugin.py b/PIL/IcnsImagePlugin.py index e0b130e81..060596b48 100644 --- a/PIL/IcnsImagePlugin.py +++ b/PIL/IcnsImagePlugin.py @@ -345,13 +345,14 @@ def _save(im, fp, filename): if retcode: raise CalledProcessError(retcode, convert_cmd) -Image.register_open("ICNS", IcnsImageFile, lambda x: x[:4] == b'icns') -Image.register_extension("ICNS", '.icns') +Image.register_open(IcnsImageFile.format, IcnsImageFile, + lambda x: x[:4] == b'icns') +Image.register_extension(IcnsImageFile.format, '.icns') if sys.platform == 'darwin': - Image.register_save("ICNS", _save) + Image.register_save(IcnsImageFile.format, _save) - Image.register_mime("ICNS", "image/icns") + Image.register_mime(IcnsImageFile.format, "image/icns") if __name__ == '__main__': diff --git a/PIL/IcoImagePlugin.py b/PIL/IcoImagePlugin.py index 778edaf9a..9feafea1b 100644 --- a/PIL/IcoImagePlugin.py +++ b/PIL/IcoImagePlugin.py @@ -278,6 +278,6 @@ class IcoImageFile(ImageFile.ImageFile): # # -------------------------------------------------------------------- -Image.register_open("ICO", IcoImageFile, _accept) -Image.register_save("ICO", _save) -Image.register_extension("ICO", ".ico") +Image.register_open(IcoImageFile.format, IcoImageFile, _accept) +Image.register_save(IcoImageFile.format, _save) +Image.register_extension(IcoImageFile.format, ".ico") diff --git a/PIL/ImImagePlugin.py b/PIL/ImImagePlugin.py index 0a0a666ce..1e5d6860c 100644 --- a/PIL/ImImagePlugin.py +++ b/PIL/ImImagePlugin.py @@ -349,7 +349,7 @@ def _save(im, fp, filename, check=0): # -------------------------------------------------------------------- # Registry -Image.register_open("IM", ImImageFile) -Image.register_save("IM", _save) +Image.register_open(ImImageFile.format, ImImageFile) +Image.register_save(ImImageFile.format, _save) -Image.register_extension("IM", ".im") +Image.register_extension(ImImageFile.format, ".im") diff --git a/PIL/IptcImagePlugin.py b/PIL/IptcImagePlugin.py index 47c7e1936..e810ffca2 100644 --- a/PIL/IptcImagePlugin.py +++ b/PIL/IptcImagePlugin.py @@ -184,9 +184,9 @@ class IptcImageFile(ImageFile.ImageFile): pass -Image.register_open("IPTC", IptcImageFile) +Image.register_open(IptcImageFile.format, IptcImageFile) -Image.register_extension("IPTC", ".iim") +Image.register_extension(IptcImageFile.format, ".iim") ## diff --git a/PIL/Jpeg2KImagePlugin.py b/PIL/Jpeg2KImagePlugin.py index ed3e1530a..b82acdd98 100644 --- a/PIL/Jpeg2KImagePlugin.py +++ b/PIL/Jpeg2KImagePlugin.py @@ -262,15 +262,15 @@ def _save(im, fp, filename): # ------------------------------------------------------------ # Registry stuff -Image.register_open('JPEG2000', Jpeg2KImageFile, _accept) -Image.register_save('JPEG2000', _save) +Image.register_open(Jpeg2KImageFile.format, Jpeg2KImageFile, _accept) +Image.register_save(Jpeg2KImageFile.format, _save) -Image.register_extension('JPEG2000', '.jp2') -Image.register_extension('JPEG2000', '.j2k') -Image.register_extension('JPEG2000', '.jpc') -Image.register_extension('JPEG2000', '.jpf') -Image.register_extension('JPEG2000', '.jpx') -Image.register_extension('JPEG2000', '.j2c') +Image.register_extension(Jpeg2KImageFile.format, '.jp2') +Image.register_extension(Jpeg2KImageFile.format, '.j2k') +Image.register_extension(Jpeg2KImageFile.format, '.jpc') +Image.register_extension(Jpeg2KImageFile.format, '.jpf') +Image.register_extension(Jpeg2KImageFile.format, '.jpx') +Image.register_extension(Jpeg2KImageFile.format, '.j2c') -Image.register_mime('JPEG2000', 'image/jp2') -Image.register_mime('JPEG2000', 'image/jpx') +Image.register_mime(Jpeg2KImageFile.format, 'image/jp2') +Image.register_mime(Jpeg2KImageFile.format, 'image/jpx') diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 5cae90073..f5600c42f 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -728,12 +728,12 @@ def jpeg_factory(fp=None, filename=None): # -------------------------------------------------------------------q- # Registry stuff -Image.register_open("JPEG", jpeg_factory, _accept) -Image.register_save("JPEG", _save) +Image.register_open(JpegImageFile.format, jpeg_factory, _accept) +Image.register_save(JpegImageFile.format, _save) -Image.register_extension("JPEG", ".jfif") -Image.register_extension("JPEG", ".jpe") -Image.register_extension("JPEG", ".jpg") -Image.register_extension("JPEG", ".jpeg") +Image.register_extension(JpegImageFile.format, ".jfif") +Image.register_extension(JpegImageFile.format, ".jpe") +Image.register_extension(JpegImageFile.format, ".jpg") +Image.register_extension(JpegImageFile.format, ".jpeg") -Image.register_mime("JPEG", "image/jpeg") +Image.register_mime(JpegImageFile.format, "image/jpeg") diff --git a/PIL/MicImagePlugin.py b/PIL/MicImagePlugin.py index 8e3e1b11d..053f66c07 100644 --- a/PIL/MicImagePlugin.py +++ b/PIL/MicImagePlugin.py @@ -99,6 +99,6 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): # # -------------------------------------------------------------------- -Image.register_open("MIC", MicImageFile, _accept) +Image.register_open(MicImageFile.format, MicImageFile, _accept) -Image.register_extension("MIC", ".mic") +Image.register_extension(MicImageFile.format, ".mic") diff --git a/PIL/MpegImagePlugin.py b/PIL/MpegImagePlugin.py index ff7c0dce4..04cf755f3 100644 --- a/PIL/MpegImagePlugin.py +++ b/PIL/MpegImagePlugin.py @@ -77,9 +77,9 @@ class MpegImageFile(ImageFile.ImageFile): # -------------------------------------------------------------------- # Registry stuff -Image.register_open("MPEG", MpegImageFile) +Image.register_open(MpegImageFile.format, MpegImageFile) -Image.register_extension("MPEG", ".mpg") -Image.register_extension("MPEG", ".mpeg") +Image.register_extension(MpegImageFile.format, ".mpg") +Image.register_extension(MpegImageFile.format, ".mpeg") -Image.register_mime("MPEG", "video/mpeg") +Image.register_mime(MpegImageFile.format, "video/mpeg") diff --git a/PIL/MpoImagePlugin.py b/PIL/MpoImagePlugin.py index b7e6c5756..7eb8f4cf8 100644 --- a/PIL/MpoImagePlugin.py +++ b/PIL/MpoImagePlugin.py @@ -90,9 +90,10 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile): # Note that since MPO shares a factory with JPEG, we do not need to do a # separate registration for it here. -# Image.register_open("MPO", JpegImagePlugin.jpeg_factory, _accept) -Image.register_save("MPO", _save) +# Image.register_open(MpoImageFile.format, +# JpegImagePlugin.jpeg_factory, _accept) +Image.register_save(MpoImageFile.format, _save) -Image.register_extension("MPO", ".mpo") +Image.register_extension(MpoImageFile.format, ".mpo") -Image.register_mime("MPO", "image/mpo") +Image.register_mime(MpoImageFile.format, "image/mpo") diff --git a/PIL/MspImagePlugin.py b/PIL/MspImagePlugin.py index 1e974d53f..49dd45b31 100644 --- a/PIL/MspImagePlugin.py +++ b/PIL/MspImagePlugin.py @@ -98,7 +98,7 @@ def _save(im, fp, filename): # # registry -Image.register_open("MSP", MspImageFile, _accept) -Image.register_save("MSP", _save) +Image.register_open(MspImageFile.format, MspImageFile, _accept) +Image.register_save(MspImageFile.format, _save) -Image.register_extension("MSP", ".msp") +Image.register_extension(MspImageFile.format, ".msp") diff --git a/PIL/PcdImagePlugin.py b/PIL/PcdImagePlugin.py index 06db5d628..4481671b1 100644 --- a/PIL/PcdImagePlugin.py +++ b/PIL/PcdImagePlugin.py @@ -55,6 +55,6 @@ class PcdImageFile(ImageFile.ImageFile): # # registry -Image.register_open("PCD", PcdImageFile) +Image.register_open(PcdImageFile.format, PcdImageFile) -Image.register_extension("PCD", ".pcd") +Image.register_extension(PcdImageFile.format, ".pcd") diff --git a/PIL/PcxImagePlugin.py b/PIL/PcxImagePlugin.py index 906c38a35..9440d5362 100644 --- a/PIL/PcxImagePlugin.py +++ b/PIL/PcxImagePlugin.py @@ -181,7 +181,7 @@ def _save(im, fp, filename, check=0): # -------------------------------------------------------------------- # registry -Image.register_open("PCX", PcxImageFile, _accept) -Image.register_save("PCX", _save) +Image.register_open(PcxImageFile.format, PcxImageFile, _accept) +Image.register_save(PcxImageFile.format, _save) -Image.register_extension("PCX", ".pcx") +Image.register_extension(PcxImageFile.format, ".pcx") diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index 9fcc9fe4e..a2bc8f679 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -803,9 +803,9 @@ def getchunks(im, **params): # -------------------------------------------------------------------- # Registry -Image.register_open("PNG", PngImageFile, _accept) -Image.register_save("PNG", _save) +Image.register_open(PngImageFile.format, PngImageFile, _accept) +Image.register_save(PngImageFile.format, _save) -Image.register_extension("PNG", ".png") +Image.register_extension(PngImageFile.format, ".png") -Image.register_mime("PNG", "image/png") +Image.register_mime(PngImageFile.format, "image/png") diff --git a/PIL/PpmImagePlugin.py b/PIL/PpmImagePlugin.py index 954832451..d829532e5 100644 --- a/PIL/PpmImagePlugin.py +++ b/PIL/PpmImagePlugin.py @@ -164,9 +164,9 @@ def _save(im, fp, filename): # # -------------------------------------------------------------------- -Image.register_open("PPM", PpmImageFile, _accept) -Image.register_save("PPM", _save) +Image.register_open(PpmImageFile.format, PpmImageFile, _accept) +Image.register_save(PpmImageFile.format, _save) -Image.register_extension("PPM", ".pbm") -Image.register_extension("PPM", ".pgm") -Image.register_extension("PPM", ".ppm") +Image.register_extension(PpmImageFile.format, ".pbm") +Image.register_extension(PpmImageFile.format, ".pgm") +Image.register_extension(PpmImageFile.format, ".ppm") diff --git a/PIL/PsdImagePlugin.py b/PIL/PsdImagePlugin.py index 030f5144c..d06e320b0 100644 --- a/PIL/PsdImagePlugin.py +++ b/PIL/PsdImagePlugin.py @@ -307,6 +307,6 @@ def _maketile(file, mode, bbox, channels): # -------------------------------------------------------------------- # registry -Image.register_open("PSD", PsdImageFile, _accept) +Image.register_open(PsdImageFile.format, PsdImageFile, _accept) -Image.register_extension("PSD", ".psd") +Image.register_extension(PsdImageFile.format, ".psd") diff --git a/PIL/SgiImagePlugin.py b/PIL/SgiImagePlugin.py index 6219ecebf..9b4535ebf 100644 --- a/PIL/SgiImagePlugin.py +++ b/PIL/SgiImagePlugin.py @@ -81,11 +81,11 @@ class SgiImageFile(ImageFile.ImageFile): # # registry -Image.register_open("SGI", SgiImageFile, _accept) +Image.register_open(SgiImageFile.format, SgiImageFile, _accept) -Image.register_extension("SGI", ".bw") -Image.register_extension("SGI", ".rgb") -Image.register_extension("SGI", ".rgba") -Image.register_extension("SGI", ".sgi") +Image.register_extension(SgiImageFile.format, ".bw") +Image.register_extension(SgiImageFile.format, ".rgb") +Image.register_extension(SgiImageFile.format, ".rgba") +Image.register_extension(SgiImageFile.format, ".sgi") # End of file diff --git a/PIL/SpiderImagePlugin.py b/PIL/SpiderImagePlugin.py index f306538ae..6344a1540 100644 --- a/PIL/SpiderImagePlugin.py +++ b/PIL/SpiderImagePlugin.py @@ -285,8 +285,8 @@ def _save_spider(im, fp, filename): # -------------------------------------------------------------------- -Image.register_open("SPIDER", SpiderImageFile) -Image.register_save("SPIDER", _save_spider) +Image.register_open(SpiderImageFile.format, SpiderImageFile) +Image.register_save(SpiderImageFile.format, _save_spider) if __name__ == "__main__": diff --git a/PIL/SunImagePlugin.py b/PIL/SunImagePlugin.py index b03924ac6..f6c598a3f 100644 --- a/PIL/SunImagePlugin.py +++ b/PIL/SunImagePlugin.py @@ -78,6 +78,6 @@ class SunImageFile(ImageFile.ImageFile): # # registry -Image.register_open("SUN", SunImageFile, _accept) +Image.register_open(SunImageFile.format, SunImageFile, _accept) -Image.register_extension("SUN", ".ras") +Image.register_extension(SunImageFile.format, ".ras") diff --git a/PIL/TgaImagePlugin.py b/PIL/TgaImagePlugin.py index 46eafe8d0..75fa1c7e6 100644 --- a/PIL/TgaImagePlugin.py +++ b/PIL/TgaImagePlugin.py @@ -193,7 +193,7 @@ def _save(im, fp, filename, check=0): # -------------------------------------------------------------------- # Registry -Image.register_open("TGA", TgaImageFile) -Image.register_save("TGA", _save) +Image.register_open(TgaImageFile.format, TgaImageFile) +Image.register_save(TgaImageFile.format, _save) -Image.register_extension("TGA", ".tga") +Image.register_extension(TgaImageFile.format, ".tga") diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 9d6e56abf..029690f2f 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -1262,10 +1262,10 @@ def _save(im, fp, filename): # -------------------------------------------------------------------- # Register -Image.register_open("TIFF", TiffImageFile, _accept) -Image.register_save("TIFF", _save) +Image.register_open(TiffImageFile.format, TiffImageFile, _accept) +Image.register_save(TiffImageFile.format, _save) -Image.register_extension("TIFF", ".tif") -Image.register_extension("TIFF", ".tiff") +Image.register_extension(TiffImageFile.format, ".tif") +Image.register_extension(TiffImageFile.format, ".tiff") -Image.register_mime("TIFF", "image/tiff") +Image.register_mime(TiffImageFile.format, "image/tiff") diff --git a/PIL/WebPImagePlugin.py b/PIL/WebPImagePlugin.py index 78a7a5319..6837b53be 100644 --- a/PIL/WebPImagePlugin.py +++ b/PIL/WebPImagePlugin.py @@ -73,8 +73,8 @@ def _save(im, fp, filename): fp.write(data) -Image.register_open("WEBP", WebPImageFile, _accept) -Image.register_save("WEBP", _save) +Image.register_open(WebPImageFile.format, WebPImageFile, _accept) +Image.register_save(WebPImageFile.format, _save) -Image.register_extension("WEBP", ".webp") -Image.register_mime("WEBP", "image/webp") +Image.register_extension(WebPImageFile.format, ".webp") +Image.register_mime(WebPImageFile.format, "image/webp") diff --git a/PIL/XbmImagePlugin.py b/PIL/XbmImagePlugin.py index 454a95534..6916e1888 100644 --- a/PIL/XbmImagePlugin.py +++ b/PIL/XbmImagePlugin.py @@ -88,9 +88,9 @@ def _save(im, fp, filename): fp.write(b"};\n") -Image.register_open("XBM", XbmImageFile, _accept) -Image.register_save("XBM", _save) +Image.register_open(XbmImageFile.format, XbmImageFile, _accept) +Image.register_save(XbmImageFile.format, _save) -Image.register_extension("XBM", ".xbm") +Image.register_extension(XbmImageFile.format, ".xbm") -Image.register_mime("XBM", "image/xbm") +Image.register_mime(XbmImageFile.format, "image/xbm") diff --git a/PIL/XpmImagePlugin.py b/PIL/XpmImagePlugin.py index 517580895..522302706 100644 --- a/PIL/XpmImagePlugin.py +++ b/PIL/XpmImagePlugin.py @@ -124,8 +124,8 @@ class XpmImageFile(ImageFile.ImageFile): # # Registry -Image.register_open("XPM", XpmImageFile, _accept) +Image.register_open(XpmImageFile.format, XpmImageFile, _accept) -Image.register_extension("XPM", ".xpm") +Image.register_extension(XpmImageFile.format, ".xpm") -Image.register_mime("XPM", "image/xpm") +Image.register_mime(XpmImageFile.format, "image/xpm") From d92879f379ae6d8ee8fecae817fa4004b8b06df0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 8 Jul 2015 14:04:02 +1000 Subject: [PATCH 0433/1037] Updated example Tcl version numbers --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ba95cdd58..34277b09c 100644 --- a/setup.py +++ b/setup.py @@ -468,7 +468,7 @@ class pil_build_ext(build_ext): feature.lcms = "lcms2_static" if _tkinter and _find_include_file(self, "tk.h"): - # the library names may vary somewhat (e.g. tcl84 or tcl8.4) + # the library names may vary somewhat (e.g. tcl85 or tcl8.5) version = TCL_VERSION[0] + TCL_VERSION[2] if feature.want('tcl'): if _find_library_file(self, "tcl" + version): From 7e991a804389d27834ff984d9a4cb5d8eea89576 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 8 Jul 2015 15:32:24 +1000 Subject: [PATCH 0434/1037] Allow Tcl/Tk frameworks to be disabled on OS X by setup arguments --- setup.py | 49 +++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/setup.py b/setup.py index 34277b09c..69c65ddb9 100644 --- a/setup.py +++ b/setup.py @@ -571,32 +571,33 @@ class pil_build_ext(build_ext): exts.append(Extension( "PIL._webp", ["_webp.c"], libraries=libs, define_macros=defs)) - if sys.platform == "darwin": - # locate Tcl/Tk frameworks - frameworks = [] - framework_roots = [ - "/Library/Frameworks", - "/System/Library/Frameworks"] - for root in framework_roots: - if ( - os.path.exists(os.path.join(root, "Tcl.framework")) and - os.path.exists(os.path.join(root, "Tk.framework"))): - print("--- using frameworks at %s" % root) - frameworks = ["-framework", "Tcl", "-framework", "Tk"] - dir = os.path.join(root, "Tcl.framework", "Headers") - _add_directory(self.compiler.include_dirs, dir, 0) - dir = os.path.join(root, "Tk.framework", "Headers") - _add_directory(self.compiler.include_dirs, dir, 1) - break - if frameworks: + if feature.tcl and feature.tk: + if sys.platform == "darwin": + # locate Tcl/Tk frameworks + frameworks = [] + framework_roots = [ + "/Library/Frameworks", + "/System/Library/Frameworks"] + for root in framework_roots: + root_tcl = os.path.join(root, "Tcl.framework") + root_tk = os.path.join(root, "Tk.framework") + if (os.path.exists(root_tcl) and os.path.exists(root_tk)): + print("--- using frameworks at %s" % root) + frameworks = ["-framework", "Tcl", "-framework", "Tk"] + dir = os.path.join(root_tcl, "Headers") + _add_directory(self.compiler.include_dirs, dir, 0) + dir = os.path.join(root_tk, "Headers") + _add_directory(self.compiler.include_dirs, dir, 1) + break + if frameworks: + exts.append(Extension( + "PIL._imagingtk", ["_imagingtk.c", "Tk/tkImaging.c"], + extra_compile_args=frameworks, + extra_link_args=frameworks)) + else: exts.append(Extension( "PIL._imagingtk", ["_imagingtk.c", "Tk/tkImaging.c"], - extra_compile_args=frameworks, extra_link_args=frameworks)) - feature.tcl = feature.tk = 1 # mark as present - elif feature.tcl and feature.tk: - exts.append(Extension( - "PIL._imagingtk", ["_imagingtk.c", "Tk/tkImaging.c"], - libraries=[feature.tcl, feature.tk])) + libraries=[feature.tcl, feature.tk])) if os.path.isfile("_imagingmath.c"): exts.append(Extension("PIL._imagingmath", ["_imagingmath.c"])) From 60a3702fd52c7bb2fe2d67038754923dcbc766d1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 8 Jul 2015 15:32:50 +1000 Subject: [PATCH 0435/1037] Flake8 fix --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 69c65ddb9..edfd24c41 100644 --- a/setup.py +++ b/setup.py @@ -464,7 +464,7 @@ class pil_build_ext(build_ext): if _find_library_file(self, "lcms2"): feature.lcms = "lcms2" elif _find_library_file(self, "lcms2_static"): - #alternate Windows name. + # alternate Windows name. feature.lcms = "lcms2_static" if _tkinter and _find_include_file(self, "tk.h"): From 9c20c6d38aabc7be82deb2aa3032b8069315592b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 8 Jul 2015 23:08:00 +1000 Subject: [PATCH 0436/1037] Added service argument to coveralls image URL [ci skip] --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index d39070bbe..169547be1 100644 --- a/README.rst +++ b/README.rst @@ -26,7 +26,7 @@ Pillow is the "friendly PIL fork" by `Alex Clark and Contributors Date: Sun, 12 Jul 2015 17:42:55 +0300 Subject: [PATCH 0437/1037] Update Image.py --- PIL/Image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/Image.py b/PIL/Image.py index 28289d0d9..861599bf7 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -2265,7 +2265,7 @@ def open(fp, mode="r"): :py:meth:`~PIL.Image.Image.load` method). See :py:func:`~PIL.Image.new`. - :param file: A filename (string) or a file object. The file object + :param fp: A filename (string) or a file object. The file object must implement :py:meth:`~file.read`, :py:meth:`~file.seek`, and :py:meth:`~file.tell` methods, and be opened in binary mode. :param mode: The mode. If given, this argument must be "r". From 94ed100bb2c07fb60e4876ae2da3eea374b964a1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 13 Jul 2015 16:46:26 +1000 Subject: [PATCH 0438/1037] Removed ImageDraw methods deprecated in PIL 1.1.15 --- PIL/ImageDraw.py | 28 ---------------------------- Tests/test_imagedraw.py | 8 -------- docs/reference/ImageDraw.rst | 15 --------------- 3 files changed, 51 deletions(-) diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py index 2afe93714..e70991e9f 100644 --- a/PIL/ImageDraw.py +++ b/PIL/ImageDraw.py @@ -90,34 +90,6 @@ class ImageDraw(object): self.fill = 0 self.font = None - ## - # Set the default pen color. - - def setink(self, ink): - # compatibility - if warnings: - warnings.warn( - "'setink' is deprecated; use keyword arguments instead", - DeprecationWarning, stacklevel=2 - ) - if isStringType(ink): - ink = ImageColor.getcolor(ink, self.mode) - if self.palette and not isinstance(ink, numbers.Number): - ink = self.palette.getcolor(ink) - self.ink = self.draw.draw_ink(ink, self.mode) - - ## - # Set the default background color. - - def setfill(self, onoff): - # compatibility - if warnings: - warnings.warn( - "'setfill' is deprecated; use keyword arguments instead", - DeprecationWarning, stacklevel=2 - ) - self.fill = onoff - ## # Set the default font. diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 803677616..5a85f1a90 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -44,14 +44,6 @@ class TestImageDraw(PillowTestCase): draw.polygon(list(range(100))) draw.rectangle(list(range(4))) - def test_deprecated(self): - im = hopper().copy() - - draw = ImageDraw.Draw(im) - - self.assert_warning(DeprecationWarning, lambda: draw.setink(0)) - self.assert_warning(DeprecationWarning, lambda: draw.setfill(0)) - def test_mode_mismatch(self): im = hopper("RGB").copy() diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index e030147e9..9a2a7a260 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -278,21 +278,6 @@ these methods. Do not mix the old and new calling conventions. :rtype: :py:class:`~PIL.ImageDraw.Draw` -.. py:method:: PIL.ImageDraw.Draw.setink(ink) - - .. deprecated:: 1.1.5 - - Sets the color to use for subsequent draw and fill operations. - -.. py:method:: PIL.ImageDraw.Draw.setfill(fill) - - .. deprecated:: 1.1.5 - - Sets the fill mode. - - If the mode is 0, subsequently drawn shapes (like polygons and rectangles) - are outlined. If the mode is 1, they are filled. - .. py:method:: PIL.ImageDraw.Draw.setfont(font) .. deprecated:: 1.1.5 From c7339c244e0254e9c648efd686877657d9df1d88 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 13 Jul 2015 23:37:44 +1000 Subject: [PATCH 0439/1037] Added additional tests for TiffImagePlugin --- Tests/test_file_tiff.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index e1702bcfe..08f1f1880 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -82,6 +82,13 @@ class TestFileTiff(PillowTestCase): im._setup() self.assertEqual(im.info['dpi'], (72., 72.)) + def test_invalid_file(self): + invalid_file = "Tests/images/flower.jpg" + + self.assertRaises(SyntaxError, + lambda: TiffImagePlugin.TiffImageFile(invalid_file)) + + def test_bad_exif(self): try: Image.open('Tests/images/hopper_bad_exif.jpg')._getexif() @@ -89,6 +96,12 @@ class TestFileTiff(PillowTestCase): self.fail( "Bad EXIF data passed incorrect values to _binary unpack") + def test_save_unsupported_mode(self): + im = hopper("HSV") + outfile = self.tempfile("temp.tif") + + self.assertRaises(IOError, lambda: im.save(outfile)) + def test_little_endian(self): im = Image.open('Tests/images/16bit.cropped.tif') self.assertEqual(im.getpixel((0, 0)), 480) From 4666fcf8bedf865a3ace3459da37e471563f3a16 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 13 Jul 2015 20:21:14 +0300 Subject: [PATCH 0440/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 9a244ea22..53524da1f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,7 +4,7 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ -- Added various tests #1330 +- Added various tests #1330, #1344 [radarhere] - More ImageFont tests #1327 From 7598f6c955c2211e2a4acc96bc8d4b31cf8ba943 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 15 Jul 2015 14:00:47 +0300 Subject: [PATCH 0441/1037] Add missing fill param And re-order params to match method. [CI skip] --- docs/reference/ImageDraw.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index e030147e9..a56457be9 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -234,8 +234,9 @@ Methods :param xy: Top left corner of the text. :param text: Text to be drawn. If it contains any newline characters, the text is passed on to mulitiline_text() - :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. :param fill: Color to use for the text. + :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. + .. py:method:: PIL.ImageDraw.Draw.multiline_text(xy, text, fill=None, font=None, anchor=None, spacing=0, align="left") @@ -244,6 +245,7 @@ Methods :param xy: Top left corner of the text. :param text: Text to be drawn. If it contains any newline characters, the text is split and passed on to mulitiline_text() + :param fill: Color to use for the text. :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. :param spacing: The number of pixels between lines. :param align: "left", "center" or "right". From d072ee469c72f13e78f54a4523ff461468137f86 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 15 Jul 2015 22:43:52 +1000 Subject: [PATCH 0442/1037] Corrected ImageDraw multiline text param documentation [ci skip] --- docs/reference/ImageDraw.rst | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index a56457be9..1c9c242c7 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -243,8 +243,7 @@ Methods Draws the string at the given position. :param xy: Top left corner of the text. - :param text: Text to be drawn. If it contains any newline characters, - the text is split and passed on to mulitiline_text() + :param text: Text to be drawn. :param fill: Color to use for the text. :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. :param spacing: The number of pixels between lines. @@ -262,8 +261,7 @@ Methods Return the size of the given string, in pixels. - :param text: Text to be measured. If it contains any newline characters, - the text is split and passed on to mulitiline_textsize() + :param text: Text to be measured. :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. :param spacing: The number of pixels between lines. From 2a3a34d2ff9a691836aab50c0b88ae6c5faa58c7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 19 Jul 2015 22:56:04 +1000 Subject: [PATCH 0443/1037] Treat MPO with unknown header as base JPEG file --- PIL/JpegImagePlugin.py | 7 ++++++- Tests/images/sugarshack_bad_mpo_header.jpg | Bin 0 -> 120198 bytes Tests/test_file_jpeg.py | 11 +++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 Tests/images/sugarshack_bad_mpo_header.jpg diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 5cae90073..7cb280764 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -37,6 +37,7 @@ __version__ = "0.6" import array import struct import io +import warnings from struct import unpack from PIL import Image, ImageFile, TiffImagePlugin, _binary from PIL.JpegPresets import presets @@ -713,8 +714,8 @@ def _save_cjpeg(im, fp, filename): # Factory for making JPEG and MPO instances def jpeg_factory(fp=None, filename=None): im = JpegImageFile(fp, filename) - mpheader = im._getmp() try: + mpheader = im._getmp() if mpheader[45057] > 1: # It's actually an MPO from .MpoImagePlugin import MpoImageFile @@ -722,6 +723,10 @@ def jpeg_factory(fp=None, filename=None): except (TypeError, IndexError): # It is really a JPEG pass + except SyntaxError: + warnings.warn("Image appears to be a malformed MPO file, it will be " + "interpreted as a base JPEG file") + pass return im diff --git a/Tests/images/sugarshack_bad_mpo_header.jpg b/Tests/images/sugarshack_bad_mpo_header.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6e2d75ab62847a721abe90d3051fae49dd034675 GIT binary patch literal 120198 zcmeFYWmFu^+BQ0aySux)50>Bzu7eZY-6aq#5MYoQT!T9#0}L9H5Zv9}gC=MK3{=i|Bds3TmaO6lt0x13jf0%<0)ST6!`}x zd%`s+r2lP`;0i$dhdluB0RTYJ(sA_)aP)HU1_1trm_(HgpDX|X_5ZT_$5lv3L`V!E zA|xy(EhH)}B*OFrge9d#K>z?wIskzAWQ!&wA|jNI`mcV0(&=db#yT+IKW%_0r~vrO zf8eGk#&#wDsw*rcR08}jo=>AJLHQR?l&BKae_`Nu3HrY<3ei)a|64}?Tb3xq|LjTc zsn;mtf3kW0gs1;*oc3roy6M{xkn? zjQV8!cO0M@fTW(L>A(E@7ojp^?oVuCpOF^LF>000<3)c^A1 zQzZO%+@R$DcifG8!02E>% zDlzcS8XyjUgNccSiGhQKg@ucQgG)d~M1YS^Ku=CVOvTE`&c?#X!py-dDagSs#?8zk zq#!H?l988}XBSWgE6J)$%E`+Bad2@7@CoRMi0EXvSh!^VkJF!iVA<1v(SiR+|K$FZ zM?pnH$H2tG#=(6GuYcZqI{)+bf82a})_|y}C}^nY=x9$*2r%OBNJS$?Ct(&=z#!GP z#bog%6N!eEVzDYBddUrDzO#wi`Nd%4P*74))39@Ja&hyBiAzXIfuxm`RaDi~!5W4} z#t;)zGjn?fM<-_&S2zEFz@Xre(6HFJ*YOE&5|iK=nOWI6xq10z$6Mt#?*XVo)`J-qI%BD1QGNr2_hN%w|WtdHeBNTUC z_MW-Hpf3kHQ;m*5*xqt5YfdEVDu`l-Y z98$EaYlvZymFG3T&sXj;W$A_a5Q*&Z+@<3GC7a&GtNES<{bU)abN$vX%+G(?DL;KB z+E-?*`v861D(C%CdmB$Z&wIM4t09Z)$MVo*;f|iz*)P8R&`Lw zXNFrtnY@DeYlCG*Rkx-0YSjmO!DdDvXX629y_GI$LU1G@l1CXnC*6&1E7{!cYo9r9H zx34F`a*y`(B9TaNJ=P!|ju18V$jGV2i@+`v@T&Sy$@U+Bp}ud7Oba)?1&`=l6_+bp z7FnE}i0^IH>if~=Hk`u3Fyh5*ne4b6XqHU~bC%A6Ja;xBU{gP3^QGSv(sVkV*bS9jYClue^acscy z<5$cuOPewbU>_iKJ8Uv3i%w-1A+wP!#~U%b_ftkZ#{kYLA{5 zp1QH?oYg>v9~~>bhofsgQMLJ6kY(IM%Z1$7q}g*-CQ|O?BD-y7t0q)7_b^F4U)7G1 zGm8_t$Amif&cmoQEPVs!_NX;8`Z-<8L1=jIUD#B{kJkY7WML|ljOj3VxypXq@EuVf zU+C!^MkUy44?VAl%!Xn=U{Lg2$*GB3ZF~Xwee<0^?YPG(ZJ^$u>m>=g+|}~%gH!#+ zn%Rn32jy;f#%P7mp~$mMy9Vh$0RQ$dB)!0}rUf-Z>m6y%=y@Q3^n~yyjsPdNs0VB& zgo#k6{6?J6CQO9**(%UZXJ>t``J+}WW~#z*esS!2jLI?))cc#rPesA!aR$@5>SVD6T3c45R(rfDn^xF#|wj^q1Rsgjq(fp=Y>2oi74v(LhN8%DBUvz zs~m>AJif>Us65r(=SnM?4~JgoK<=kh!@7fkD-BTyDX#0s#OzT;vzg(aA)3Hc{xLASY_S+hhK+X=ZME8G;l(Z(0Ag(>F2k>{D^VffklW+k;{WaK(N15u6qX8tW6 zwi69=n6z-2q7P{pGILXff1Jy@*A*L~#-bhpO^t2V4Ju(nPCkSORSkDHt`n%s3Fq4( zAap~V*igERW9^Ry0_*$X8M?m9vo_6LEJuR#1oFsgQZZ73ljnHyNZ~Nc)%@w{+1t>U zG7Sn57-krfb$f!;tbV`W=3?{&-ZpYv%y7o)BVh_AR7FM)do{gx^4m+wfuM?p;dg@7 z^BxXX{H0k1`z{5wr=7u=w7R9gLYs8K@$Q})7Tgz=u{!E;CK6X@KBn1%OKi-;FLI!< z8Pj?{ND#H}%#ORNiqMfmV?rxTn16Y=(6Z;f`OGImRgAH#Fg$Yom;&MFc{r3~igOgC zvxZQE0=%l(qFZk`52-Qn+#DTi=Du3$(iJ`HYjm!XHnnbuYd#n3aQJJW*N81}SiY~6 zN-~mn!Liw$>fNCQ*tBIZiPb=T-v>BIsM~!Gp6mltC(>q{vEh;Vk)e*;1^^Q!d`Xx z7MFhj5h4rj8j~QNisR}dFEW8spMFLn zYwvd*5-R+!^&O%o>dP?vx7{sBDGnEJhdQv#OEjrxxB2XI@v z(Fpt^vhKe=xLizn^>F@J{b1x8Fg?t`aWx|Ebc^m6c618XfgmPEO^bhDT_0s%nS}sC zn?(aax3@K`27QONu~GhOr{K0P1DXY8?95mokVn{G;eWZga~O_{34v}a#-N5Oc^sA) zlRGP6PPG$3^(?jbYD;1;{p9>0=?Ts!=PuKPu~(%yqL%%d8y&v_UFcv9&1T8-xobKx zX%6@2u~!L^oPmFgrhSHtMx-x@ZCn_2{LI_s&Ha!wfARiRlGLo|eGNu)(@Xujji!-{ zcV48k?%Cc)`CjE>dPytP=fS)V6<8IwocXH7XYWRd7;W|T*XZL3+l2qxFZcEVkmF`s z`w$RfmXgh#IW74O&kMrW91N+9Thcq8H5zn;IzD$S+Gl;d(0L|p!XaouNHHDvqUd@n zU$xbt)%iXto61gz&^oh#&cZ`eroG*f09!AFZJ$uz+)vu7tU&+=n%>N?b<{pvWs$*k z%gpzEKPo}i(}n;OY0e_E?0{8ITIhfp1sbWG7HBX@>gUCHRamTR3HBo~t*&RJ}e_OK9WIPISx(4%a6}zeLzPL!j-y_RP zRCZ6zJTEI6#V?CqT8tJ}-(Gxqzw8TvR&o0fL1T+~I6;Y>+W~inuqnbfXi-Gv8OJ94 z<_ey|P!b{0L4`j6P{jnZ`!aK}WcyVRYk%Yw#WT(>=pR7g{Ppus4$8dHT+Q*OGs>Enr%pnnl=xqm2bdmJ#^|K$aD^4l&);0HVacL7l?uJz9r8gsi zQ~Qxsb=(prvSrW|lise$G`|`ChNT&v3rUH#D>2ifpKaeG+__c5MBY~7n=a_=*EOka z{G}q}egp(R;7yx|keB<8Kv3xi#0WJo12CDKYQ{rYYN;nO#2i^oa1`5_ibc3bhy%R< zv{XmGQFL!gtJ;NA_jz%}+(bU4?&Dn>t}tQyG7k~&0j_^6Rt*+uCi}2{A=6tvjS_zQ z2Vl&r^YWv*hc9fiv>c;hv@+7hemi42pWougBz5co7AvOM4Z^i5>4$#nzLZrpCbe9* zafqiAfhp9!6py=}38K=m%}LvqZJ8Q6)%ojGait-+G=GB5N%lMlu!HlqOU*zjLWNRH z+j<~b6C~(`v)aqk%G>jHat{mOKYW^Q!FXC~@%~n`MaC(pqwPkY;S&<|0qAT(qi&W+7u<8+LC>NB3x*2*LYMAGvns4WCSjrOV(&;B>Mxr!;eRfPTSuKl$9^hrG7FNkA*^##9&@aN^Rk*wtQ z+BecE_Uj4XYSa5(2e7T>N6aUoVehYUSn&`VK5aK>F~8^`1htRXizVXO_`Q!+rO!~c zEOG3R2PCrg`;Upup^U{)zOu>ixP|1&k5gn6qm#Hpp4@`#<8p5j+&RnTyvLcM#bZ@3R5&+;E&XK#*d7*+n~%05lf+Mlc>$rqI7#=?(pxqI(cg9N#qz* zkI`v%FtL_9Q_Egq$X7QS*{>G|BP1%f2}##hKLjmNMOvlI@(%SSRDGZ#0lx~uW zSSPw}0w1x}Tk<>tkvxQ>849r}kDWH5ziKO9b9XPFwl*?+xr}S{r#)wVsjrT0360uJ zSi5a1LyPA!=AR)Ep3$>0&{UEDs|XWKxZ<5Aycm5itFZm`wg988t^1Kx5)vRnP~Nt) zCinQ;hWbIb&w@A9F~aURP;b1ECRa;A2z+!X(=v4M%?c_pZK22Koc@BB2p!I&hnNZq0(Z2PTy3qmLf=C{q>Z&9l{OQJ>2N(d^?qF zGH5VbH6oUAX^)%Xy z)-!IXgsKEw`1sgTwX@RwLGq$}j$^(t+MszYW8?}C#3j;q;1yXEfo+vVS<4uWJhj4A zEAo0bOO_q6{bp6+$Mchtz3Q-2rk}|>iFC@Vx!1z0*cg#{mT$jgEyT}xT=#Gq=MV0( z(iJ=S77FG{O4mLUGW^B%82rt;w)|&?&Vq@9NL^QSrhn|*Yzq`Vld3+`-qY3{YJjaA zPqDg>8Wq_?xMR3jWvc=p(*yqJDfIWJ?<0(XLkZ3l2G0J^`*^mL*1m>3m|)lbwC|T_ zA(nU@m@d8cd=~X*)sEpvM3UB6DkLR}*fDW`kGvFpW85E`k=^&o^QFzh<%H8rWP;&N zO62b1Z)SggmcH1lJgOS|={EN{zFXSx&-HXQvt(OHosiqQ*soeJ2YXJ;Q%!TC|3Qe1 z81@|SXXVc>pjcZ^6~Oc_)gATyf2h#!|E-1sP~QJjd1ZQ13!j8lfB_TA-|Fj$^FP(Y zr}IDZrwjnV?BPGk&=CJ!8`zBYFI^P?Y(@v*JX!p$JHBbg__uza{&%fE|1|ESjKuk?*P!k~GiGZ;PQCcOx7hIuv*7ayx^l;UNYkNhd?=eNKZH7vbg# zKF6p~a>h}FA$W7Juugp{FMp>`-9Po_Mvo}9vnJzX<(fj}7jB$#aXS=F#dk&ZsYNH! zmYB^f9LzW!pohhaoIQN&83>47kP?RskDOt!wbEFra-_g7i~+I33lcvEHQip`Kcm)Xrl9nR!Cb1Hv4pMn%scjZ&t*07K4+^Tg-_)sL=@jwBbyyG+tY+XRx>I z+iv@-XC5ZP5sq!1i1Uq%_$hre&CaG2Q`5VF3Jo??pGbv<&*ZtU(e-`@N_!cExhb>Q z3=n;faOp|i8_`}1a>|C9@F>NtR|p;$knE+eytJ2}Uz_%0Mm@yY5Y;Uf(@6r}PB?AZ z4lvtyWlMcIhgkQgx`}dSfMg%82)VMtFKjcJX?D= zbCcqIM=zVd`7Ah3Lq||4Sie}fx}&Wx$Wec`P3)Xz>k+LS$TnFN;i@4CK|7n`Ynv^L zwffN9soh}o!SPevqT~6(u6Jv`s!Qfyvym^pG^c3urO;U9y$Cww+ph7*Kl{aAr{Edr z_xTJqNtCphp)Xq84V@RSirLqui*7Q$F1%@k?XFZm7oZg6-0~Z2b%#{|60RD#qlNKu zb@sz0IFCR2`1}a-6foBfw90m;u!mW4$=D5^%ZyAJ-!-s$w2a?Fu%Md2wNpsre36$k znC(X}|D8q98x|sqDz!#k`(04O+?fvG)xDGd(&;zjfWM}*i!Tqh$QvRjhYZFD#k;(I z3%czHywvA$y;Sf}RQ$w-Ks*js8M=~wtj+5xTKLem%}ZiYw^!--W-o9x@*C2}czwmf ziFh#kY}0i6yBlbuFO9EQ7hu##Bwq7#rI;^WfsSqg#13P#23X(B7xyBh<#*BvP$y_A-tqFaNoC9{%~w%;#vgjUBJo&(eANG)c4!bKcrEl5IA zWzKcJIK$P51!(Qxh7}A6hg#Fk+9N)P!j#aYq=wA9vgmKHUP`D5mJK+xH~0DYvvMqq z9Ep3Yd|C)ti$?8tY6V0!gbull3R-0EIhKz6jCn3t0b}KA216N!dJIBiS`(L2GaG{g zWQzE!N+xzzwjKD!KZKi5ig9cY(ve$U=Lb5qlhK}}KJH9*tjZ^cSAW0!XeOu7wAcGl zU|xQ_4eT)|9Wj7-37u?KQ6#dyuuyorC0&7-SEsH~KZ{vbWKiP`3TPbB*R_sxwm13d`0Cuv=QPDCrI$!b z9h-RQ=EeBF)u_*COevB!)|9a7q-IK*pS^i3sL49pwbS%OV~A-@@VSycS9<}e z=Z>w;;89)dZ<|dak>{h}ZNMl|*JP)=Z)Px7~n%CjVXb)*x$|@C+hK zoIbD3l3;P}mCQ5Pt~}eN+TCW>UvGKR-vq0k`T1K1h9uKD4C|cjzONT>vb2GBtNLql z1l1E|*-hSNW;qViPmyP6^m+}xZXEZcm>N4b5LN3`mrWW`Jrj7Hiw!aXgxS@Gih!9m zg{pfb^1~lS}Hy11@6l6H5O{Q3FEkhq1Zvuc=2+l<`RA`DJj6i-17_xR>-n! zU-tP;qa2S5|H|rdlvS=?Xl3P)C*D|cmXl4Ps;=*1jV0Vrj#~`d#VFa2xOZL7UBPL+ z4yBgKKi>k?1VSRS?r0|4a%$L1(siE}n=zlCBIuLykQ&}@x+=9Y%^Oz=x?| zv~k1SmYSiazEbuakoh(<=$ZNL9{}#^@%I%ftSwlM{VzWEFtH`Q!kbRR0g=ka^4bC{ zAT4HfZ4*i_(kc?o)HwyRe4e7|x8Rqt;zoAf#q_DIr{#lk7dG9#7hZxm-NX3BrEu1& z`V}^vQ**5AiP70s-lSE}S-aoZRaWqNNxRm(FWZ)z5-)0uq>bU>yl)!B^Q4yr&e*cv z##kq)U^biM)GKSGE%t*pBKaddd{ZJ{CXX5wZyzPJ>F7&X+MU>s_JTthBM-E&#rXoA zuU~u&W^$Jp#OE)LKBVqCS%Ce51jF`sR_8#Vp+;#WSp!VJdLixY(Q5~5iFP-yYSH%0 ztJu-&{DJ#?HC5~HV_w}b_K4xjbf6q>k?+9^^>Q{ZR;j8BoZ%j{8D&K>rT{yhgZ&xB zH=Urht6A5cXc-YG@m~#Iu6Q;KMd-4G*VDFN;#8??#=r1qet}q1i zt#coF_i*;~x3R3#q014HGe1v^laoRftGI~aHOQ<{!-uA6AnabfG%b$c&R|uXy*<~A zd+z7wKj9JL+@E8v)0k_ArnO@16_vkQIQvy=F~M^;eB}?IWj(amW3oGs_M)aqQ-6tp z7t1!qoLlc@clca^2~C(;9*0TsDwxCL+nH|IX!d?`@Eg)81v7~*LhE=IqG{9DPh{Y`Vj@uL73vN-kTFY`kO+9+6^c@DF-7Hn$u_UrhPjRIgV{UZ)Z$47ifC zo2O4nS408f5p{uDHpLwc`y*~^Nns8FHTxcds2pVn0!Lp}$XD%*y1PtSGejO6!%h=_A|H@dtpkT!hto&NtzxeE1}FY~F+9kjZX!33i)HoH0O&wAuV* zKAY8aesrB}9M0xb#eSL;zqUN5dEzz_;SsR%aYEXw-Igb2tI|}vSl~NyUPs>{4+j;R zCEuFgw!itv$P+TuXPy)P?udkUgepC%H&TxmyZq6r3vnO5#CH=$PSw$2UO8#E$X|>cd#o4xl+>(qi=??sxdVh5ov^}z~QBxZs)`iVbqVPGHM`r$NvrHAY z)^h`8v6f9MgOyTfTnGp`tL`y<6muuc*PRSuSfDtZN0!acOt*$0vCa1L?2TnJHs4vA zHzLIqKwM3zbfX{kZb`>OZ6TbN=? zw&aCy@TBXs)_@TYG#0w(iHG2e;=Tq9)7RC@2xI!APaS}H3H=uPi3xLCBsJc}Ks9+u zZ%XCVGsEd$#Pa2v;a&GhXMAz6!8v`0Eb!#~fjJ@SqktMcWqwjI%e=W-o`3l%ad+Vx z>6tsT0+oq?#5@CITQR=k#K)7)b)j_{?Nx|^d!zOSW7sPyD%qJaMdX z%e%EyYZ?(vigN@ooVS9F+X1ax%zjBl; zAabL26^`xUiTIK9%3RCWLl%VQ>&AT-&&vDLschk_JoP7kxtFTU8##sgwl`#kw^Z+m z83-|_=h#!K(1=8mUz5u&+ApWlACl{+&P}jQ4y!5*`5PS6xMkRBwyHL(jmXu}uMJIH zMGxZ3^~%8bY&4V@a}c+s!Jm3qER5RDMoV1dNVmU*&{i@7ap)s08F_vfhd zMmX}gVF;-kvDpyWYXg10nL&N5$L0O`#rM=K&c~COJub2SIA8C>qLY2^O>H1h$Iji% zHf(oW`U3gRN@pAb6=yq7Pu=Mx-uE%M9V{Ku@Ibt*zLPehAsIE7I2Hl*`s8U{`;nIl z{&yBDP@=P!`>2Dw;eB1VgY_=35e~&dsv&?uCXy^jCV0-iqeWENm&*`#YRUeqT({H7 zBbMgn{chdUZ@W%f-)zF{p0-|$gSW8yY0`<<=_6KYmVG<_AT}#gzQ#BGiXT$mj9(uE zvOdnj%Dcd#0$-l5UGOH%{ASUt-c+uaT)B&B{A*6FRa21@GlD$D;tzmg?Nr(jcP%&n z`9U@x5N2%^;UPI8UjM3Grs&f=B+z5Vwds&%edOp~Cl?i(y3iO=`sQq4JVYcN8(_l! zuD0#ukXiM}#zy7{$*NknJ*==QC3d0I#WTYB4J$<*?J^p|6l0V;371%VFgUaz%SwJA zM^^kG@XF=JP1kip)|yilOT;MgsnwqANLnPo*i&}Rr3A48261BN9~MX5g4!v5a`k>b zU#+cK-S6(vH#qt0B~tm#<@#M+^JXX_)U7|9X7tqOD44r-GUci;Y+-n8jL6BVw_bTv zJCOd)3A&TEd7SE20ZKYlw(XU*tI+D$Z*}~V_O8*$YL=u%QZNi`>bg3xOb!5)q6Li9 z>yHuT{MdgzyiK?WPwH7N>2{n6 zhp6&=vnXI7p5d6swbG{fV5=z-J%#l8nXI1uWe_)RCk8|B(-&Uxs;XK6E1h8dF3H8Q zjBAX8sjz`!SMp8f463hkTm>IX(VRt#&Qxh1j4O`Dh*KtgVaH~xyJ2TKw!|@1`_3FK z2hJoE8&?@Ia@=R;_9dB0JX-w+bi}D0joOC#j|Ol=2E}%;q~%+ary7XFv(>aMo|uSJ5za$dsIo8+x*RU^k{E>+zX zMi(qESBo{1XyvO*H}L?M=a4{{U3u1e+mb=-=x>jt`m2Y00K2I$S^j&@2%Jk9n_4=j ztnUD7Dlt=CWkdh#{o-GE3vp^i1$N)1uXRhGR%Wd?98d++OPr0d08>BA>GMWtLfYTi zM;x+XSjV?{{!+MeuN{Ww9$D)Kin~_CUD&dJPGNiVa@5}_?+WLyUFmQmP5le=*|*PJ z-=yL=V=lJDtBV!9FU}_!tQ^LuTR$g#FNdjR2$oaW{GuUmdBjmCTA2P!RB_#18NGnf z9C|tLELYppf8ePpV#^P0ge3(~;*%+-Dx8~m0^OP%RPpkYMt>nVL5)nK=yu=23BoiL zxf#*YLkCpZ);HX8y*nQmRRXEg*QL=|6rbOTe+h|_OL-V0MSwA|Pfh62W)7{TVrWmx z4nB0h$?>uM?E721U|aKj&efOgG_fOwCYO?$- zW!0h6P~O37_Aq1BU+tu>q9s2g*pUmZZr{bA&!10Cn*`Rqaz2Kztmwt(MbTn}p1yUP z-~f%V`%@Y&-QX)y-yBzC2N>Ea^&Ym>|rY zk8a7l?_=s~XtpjrXh*MG+dx+^-IlX1V3j4rA#7GQDB4L}#rGocja#!p|62ra7S! zS0|$E;KjL#tkhkVVb<`p)QsqPE@3)k~K71B=EBB zwD^|79wH*JmpyCOtkKU9Up05(M8b067x&<+7=h*bLMhOzZE%c9=2P5)2mT(cxU=kz z&+n}$R>dAt(+Nu7{7F!D{u;NWO4*gx-RFZ-*)<1y9s%XTYq1M{s@`YWosW~cQcdr^ zP&`{}%5LMO0jE#SLSUAw#1&@e+1b*N*DmjQ8ghQ%BIDGef120!6W)IZ;sdyfH z55gfQ5;L*Rl6xrxbDD|!$U-#l0;f=n4ygmYN1ySbxras3WS#LvehWp3)`e5TH<8D>kw(J_7)p>nltG;-KQEJCpzZw)|=9@t`vCg`dd zzJ`7Vo!R9$82+hQtcc~(gad8$aFlaD%*&w4Pl}7ewbVXgN733EtB4WHrkSin({|p~ z#x2Xq4AxeASN&^J=>=sdw%^14mThnKbG`Qt9zOe%OGm%ifta6^NA5EhI58PhHJ`TFoI*fR+O!2NvO75p8ayEJ`_jWsRQq=gfqN}) zH`NBa|I{zFUCfS7uxF)1G=UFrr0JFQrAkkQlS`BQ+y}snvs5E#Tg;YW=B0DenYbqn z6;o8@ux-a)y1S@i_U7ybZXyTdKH7ovZP=HJIamJCT&gS~s@)*qpy^s8hN~fty+Ww%HZnc1K z-7!P?`GNLWtNl`KlR@9Cx;j5eUX3t!bH-2I)aS^v7oRMYeaNex#2Ps^HL9N|7facF z`eUS4-$*rqwJunU?AmscdEYhMXO9PaL!89>b4Z9;Uh5Cvii`J5AZy2)5#JCYrE4~` zLrV5FY7)KVt;IUh?FtZq_O^q>FNMD6X4OrxNTl$)a;P#Yv+GEo@VQCAIm6PK39k5! zu&`C#4(2OE^DmGE0zr#r+#cyg%y^mL@If!?89#~^Z+dI=A-#ZaIN&MY@>j_sNyE6r zm2duWAf>OWNL$VDqPUf&QHIB2+?P;BN!H6c7=3*#A^hj|5vaipjJT?-2+`G8yZq4y-`8jn6TZ zJw+ZUygGQ~xKhhhxanBWQ8I#dRHIuEcIkFWRpsVQv2nEOB+hjXe>|R_8z%-%bTTx-U?3MZsK(r z(HLE~B$HK;>`W=i^y87HKS2Qw`uN*`h3?OdZpw(zsLOWhrU|r1y3F77aNHK&GVv&^zZr9&PzNTB^#?RSbc^ZP?DxW!vvqZp&W?rH6gUd$+p_7DLsEr=31}koD6X@e^ zY)a9sei5f{vwGL-H(UZEiDm>ZR3@5WEQ8ZmN)gj?rzw}Ic@Yt{qwV8}DVoBL#|Csy znv{Whvh55|=$ThQH8sc!YHGnxa*0k2>eLQVIyJgBi*dim8BWc$@z=^*HCkEe}qTqvC&_-&@7mD|rKWRYpKEuDQzk`l8 zn^f>16dfY;drT6cweYr58S8#Zng+z$5M{Atv5&UX3pWyLPh6ZqOw7%=oA)z?&bIzE z6(PO7;x&R;4A#@--gRMd^ngwzzWm#fW(2~cDYYzjk&N)kEw-Zj!!*5x1mfk>;jp1+r!1RS6FV)Ja|o_}LxZM5d)vkpr$JKn_&#gS|d>KVHvQzrUhb zRjs6ImBxmHc4}NcGmS}3X4V@)*)h)?%9a{dJino04l&upMn!Uc+|R!8-bi%VK9I=x zW_0T^to}We4ry65(_nSOMcRs5EHf|2%x1G&$xJL((E-SR-y9?4iuB8vhEX~Ao0J}4 z?WbnxonikFZ8FPt`aC?O8^PSEvkpBai^WG8C z_!h_Y81y!535^KrYiLuvLRQ2}8mD#&!}XH=W`bwZ*YDm0xBYlpq3t*n%8+~3>F%nE z8$O2+iI9*r=?_!xB2RROI;pNPfF>X_UI#M*W0uOAT0kHsnu(~Z%LHY2gRbJNcazu* z{y;Z7iYe=S*w2n-H55Xm~NZ^t{^J^P}n+Ps?@*_0Rt(!6aqE1T;5 z=pLxwHH$vBL@nU=8w^kj$9woG-j>RVbNchp3j0m4@^ypvkErFd?EAm3v1b=~CV^qB)-7|-vjt=tT(yEWPP&$B*POD{oG9woM9 zSc)Bsry^%M7>Z8FucCm7jOIgmrJE0l`7Zr*K~g>Ze}+0C~%oBZ!Ef z2FVHQ4i{M1TknppuHd!!w{JB%u3O5^kVRol-*7e>&KbG|+;3JDC+vdwZVnN}vJ$VB zP!}7f{5Y|@VbCUBjcRr=p7;CN$%aGaE~ zM~Uf^f%<-k>@2Zoq+AgZWf7<(k|z9|T5J~Ko06cKM3x#?T#nSNdhe;tIz^974(aAq z9W&ANf{R&1+Jxm-3hN3d03MB2syH4~+K92A7sL(vx&6-Zr)xK0)2Ndu1a2nOqb)$A zMUr#Oe6T(28jpolmj=0kD9@iZ^RWjw2j!kRWO>ocPTEfr@JelIwB$IV9nnF;UKqTK zNLhOUsqbgM*=bHa6+5!Y3O^Kfb~=)@XxtBD0De z@Tb#X@sL6|)Z@}=-hOg9hP!2#y_cb+o1-3$D(5d=kXLZ0Slm`bw+3mun>eC>%UL`9M>-A=9uON4g66GUeDtPvr0Yo?>4P`yzw9bHQFJ0 zmQ=z&1J`>d3CT&N=wPs1t{KBqcu{)??9IpAup^yByCwj znm_rsfpp)^8?9+3j&Rl^^VPJt3%i(m0_Qi9BSTU{jofN^rdj2(B!Yxis*~+# zN<6|4h*T|WD||Y4th^R9TI`-UX(4L#c)`{}8!}>DeOWiNF)ER#aa1#5WTzPvM)Tgy zq^VA&|26Qy|MkEN_^+uL^j1zfx+J!>6--gL0av_nU?y)cdgz3H_WUUB0JGZEM0XXp z)h)-}dbSu`oOUf;YSkUm`=!KYJ6-74f*1aJy=`^kEjCk1p8X$ysU)3K>r4AeC5jxn zLB~+N^X=;iDTOh+EO_Uu?G$81#tVxRxS!Bvm56#%@~#8rrD`G{@jw;N0TmwTnFdl* z^THr&Vdm&A%45{aWeO7|Pew=(ckpv+9(_Hw`M%0E+)SokMEG8K@M69@TO|-(f!yMZ zvnpPDV>this?5O0M~6qVm;VFxs$6tsxR7>>1i4>0e2DI0VY_VfKr7+E!+}u4bIYzx zJb#DAmh44W%8)?#!Y6rJDUHwF2XgB4ZkZ_-C?R+bz4F$qL$P0EPe%Ps!R7 zgBYlX_#Q!}*(;ieiZm|?^?~v!%-Ku@U1ohdCKGw@`?`9{o8I z^O<>)B-fg1kbNa&SN#(fKec>pX{8?ZM=!Flg>t)=57zc)nGUE|^S`N^=f&|AwBw6{ zeQ*J0tWwWXBv!dqPmZ*A1<^v_qu5By*!ZV9SYm5sPFp_mQ|fgLkmhTQL!j53lEP2tmYoolm#AIIX}vDXX{@AQ(QQZagWI7WS#ylW*C> zxu$wL2!R)b_l?z3;whr-)NZXr*U;pgb#iwMuM!UCaQo3e8TvBi!Ma!PHf)mg2S{ODoXN~Fbu-qyU)cGtG(`lfq)z&l$5NY<@TLzS>AWJF2i;K43k)M3TFB;4}IOhcAGk&94T|%Zv>UFku zCxw=Gm|Rm^ey^6}?cx`=&&e|5tUZ;dxU5;g86Y2CPoe}Ik&pk@A(JD zTf%#PS@SM^#0<%o6X=C( zmP^h({`*CBkLQwlYghW{v#{|Hj<#9>or-~LIif#+L`DwQ{D#H8M@aXH@4 z#XIL4#2`4s*4;FQ}A^EUnr*4>WGJ;o$p= zjZ*vf3`jr^$3|LFu(S%@%MLss>)K)_+!iCFh1sTgL%2QtP5CpnVkAX1TZ|Y9AC&r| z^I1gQo(%R?mG>`R_~_c8U3dY^6IF3^5Q54H#kqaX7+r@5zJ1d?s zW3!a7cZzCwl7}Jr`Ru-)`pFq9$Vk4(Ex1DxVsIdT3ec^TyTRzn`{swZroc`89aD5z z@S0TXgba^VKb%+OxW3t?$yWVUAgQI0EW?7k?uYLDD>i4-J&&R!(T9bt4H?%;gwu^( z@bE7;0wGLt`S@8^CNLQaaQtmaO{^?Dx;u~pF#rO&y$~UT)-npyb7xOWBSJ&HI;(6F zG^){KerKT%!SG}%y#-~jb>w9$9K{c!E;~ zvm&bD7-W0ZH<*aU#2E)TG;Hi=dz(^E45nwd{{Wr?C*HYrxMI0}N#mMLS(7G8v%+9y zW*d*`Rc5?rkIE`=JJd-l>NTNW8-#`}v8eMioz&=VW}f0TXYbA`Y{#Nn8j2z^NuI+b zR8YDjNOC&yp2Df7Qg_h_c{rB}21=ZUrdr%ExKOG|0;`ux4Yu|)ZiLrXflm^Hkt`420RyS!>Q^VQ>)>_)mJby)GAcZQz{bRmGDyS@{~i5xdkhuMoi6du|gj z4y69I>zBIMm*$O6e4*C`RC`vDY3Rtaisn3$7=F>KOqQ1JB32|L41N`}tE|lpx(h=R z@s<^j+Gf1U$(lYLwzamBSG_6bo)u4On%`137q?b1MjB~P4cJt{eWoc(p*7`=!`VFd zL?kN1dv*D9&32j>jyx@)SS`MiUTcK_j4yumtvYBZR5+3IZgW5D{ii1fWc*fTb}^O%2*74Ej&B3RaNsu zWk;4e9B^w0rzd1jGEBv@ypByO7~p}OigPA$wA0Z=#jeL}l{}gzlypXXmu^IH{H&3u z-S5p<5ylu9WOr5N6#d-ROb^<2TZ(h)iFE=T*)+qDYx88`z2^Q{dQ8CG?Xkc3gxkLz1GCmS7+v~1Y@PUIVx5r9Qh z^UJvi0ONy-SF$;()N*#cgl%N_RQxJu5eMMcffJglno?h;40d zWn+*YDA|BMgo^6)pN1YV)Y*KCM7Os*`L{nQBiL2L>M4O&x? z{;J#M1M#ktMew(TJU25(r|B)IT>*!lRP#UaqzkBl8FmX zIUgmpni&`Y>IX{jj~#g9R`GSt#A$5w;C@I$VUO0jVw6`iXEc@V|b;rFH z-ce|XVyq85(29%w;HNR!4xbK85u_a^UX`_go=hIjzX-s_N!Lt?3qMwz)`U5 z*>=b?d4+-L-j5`_p<~Gyrk(aGH$yNLxyM`)RxD#gm8KX33;|WTxmUJ=%c!3xXiz0n z*RiTsP4tb6lCS4ij3V&G0-^a)}6YE zv9~trRY=@UITdEwGHsM#f;-T+xbLAx<#8*i@7}YBA&K>>+)|L>;2&CY%t<_ODW%ZS zJuZt@?hoGQ-_o~qq%RR%{Gq=(yCi4oQt<>LDF9Q@W8Sb_o0M5dWpfphC{!v=dh=IOcD0U)S*slmnFzJGS>!CNoQ!(cOuivm z*gWt8alz<68iga3ZDwa&>Y&|uvFuq~fB>udwZzvJVi)<7l#3b$N8XPFA&j(1zX=9CWVRPx#%Z5gZrVWUVtPLh+6H?bugDCkU^2h|-*$ zmZJO}(=_|P6zjG#MIeghO~fu#t~VY)G9^93DZ>(xw(s zZFMfaDJdx2;sS0grAMmT85Z^vA>fH$=Av~aIPwR%2`n-~mFRuy8f{)hN=fWbX*z{P3VgxNPfD`!nS*&U0Ozr& z*5ss_8zX|Ma0u&CkX1SoDq9Xxh1!KbC@P+`TPOq+%nHas0R7eK)YOZueUXw~o=!j2o7Z-h#tKuW7(-cm%WFXg-|+^?NtPm)QQc>pIdw=(&5s4 z9jDm~IbfPNhnQuIsb=nSde>=Ptr_doj(C!I%yJB&>OE~>rVD#di$*1xVT&Y01eqm17+hIU3=CnF}p~zD{R{1KGfctqtBY< zc4u1zPzww1-Y)4AgIuh!6ekQBM{!dm?{PIAT?B{_OArcsWUk5VynnE z4WzbCGgj^A5z4TWv^73wZA(SDAqYlEA*z>{Eah*PIp}JZsHYxRp$w>#NEsA{Cy`Du z?2xIzBCBO#J6y)PS5_UqZ_ce*W>mo#C+@MV`i(0x%RpgF9#5@64qK2#Ty-W%Y&E0K z?c8yk`qT-+^XtK*UX>3(aK8BA`HF6DS60pzS&o$0T@{_yrtmLjH zHB=s3r8qEC)1^486s(OXwC9TN{Z{4&t8}f~N049$(*w}-H4|!DnzWI1UQ*a+%AUrc zzm%-8zdJy|Q9`rO#l@MhPA+v6jxgK90#18ZrQ6!6wJd{ixdgC8{sOxVGr~R}(((2kBF-W^`Cy;ch|!AQQm0;0 z(P)i$t!?3b(LoY6F_l0KRi8!EE-o74YZw}K7-fHE0o>Z z*f#N(Ue#~I(hIwZ7C=EOk?mQw3v;2Bmfp}OE;H91>U5a`sW{z%(wuE3cGH!Lw`p~F z%CL_u3Bhdkti3}|R9ASep=lF>xxu1W*%{SoyRxOFm)ZxJG{AbVdee&DY3Gj5OtrX{ zS5iP&nC>53RNmW|Jzl1@zkzklSHn_jDP&j4kfX+UagS=d@Rhtd7m5v+idpS0;&J5$ zM#k?#am8&-u4_#SvgBOLRDF3FR*gy!R~!-PPOh^(!lAsmhRW7ko2gPLB2w<|a1;(n zGy(HJ#Gi0V`hcK5Sj{?X$lnN@o!Hpp#sCBl zrCog6K!qR<#N(RbcRMOcEex${C@x{iSfRSkeKISWni7mRW9leZMM1Z@S)NFxSz#q) zUPX0UUEsclaRFol=32Ime8NO{8JYrAAG|sPPgIsgRvjCI%@MNP<-}8O-bSUAa>Zyv zGM%RjgTbmda7uF{V6z{(w4UcJw6iJx^qwjVB+B#vaqa=EjjdHgQt}(o zs}wwuxz61Bjw@uZx#!L;NK#jL4T29^^nZa~B%e(2InZUx*k3D25zc(M2lOK~N!${? zhtTv@5)@DYO>_=EVL%n8Yq;kIG>z@}$mekX02+$dRox1;CUfS!< zY(}h`81vtgTFVb8{{Rp193-BTGWGue1L|i_g85;MrEomtR~uBHLs%9zH(EXPi+O7B z5-wM(62l-qg_nX&!^iNNy(F7&V6%LD5{FmTM8~z^-plqbqyv7*G#j!j!A|{ zL~Z1KNZ?k@X=`X>3X!O<2XixD*EH=0#qRY>sNji58=D1>pdIVxpNZcRC-FD#%S~;f z?)fc;5=uvQ_V%M~ON@EnDrcHJuX5$cD;VJv_7x-uNx&UzigLR>SV2>XZKO!f(67ZZ zVLmb1BA?Dk3zoR2sIJF}5kx`JCdBHA{O2dO+7Np?Fa#l3nN(nrQB zG@46k72`OOd8@IjM^$Hc7NKgcgm1^pJq--66OD&4k~b*tP}?(hs1l9TelxR zaz+PHRqfrxvz(o~x_Z;OOJeQ3!Ald8$BK?=97v>&4HmI-mog)k`O!{SanDY*L`>7M zcSD9b2em6}b2RQx8oDvua-{J^(I|z8%CA}m^&yIQoPZ89S0sFu`Gx@PO3kuXn~^Xo z*>ClJwLQD5FxtZ(TD2xNN2-u*iym1`V@O9_P|)J8LZG{z4-^(+aH5^;DN4+A5lGL! z1O=^~7@(b8pPhyXG$~(loRy0jn89Q0O>-Bv?JS35V?9alNxq_k(!&L?Ol0Jab5lZt zaN>?;knWN?Q6!R|-X_XppmBzBYd6^mgCwc)s~1o|D#^TdR@gJRoMN0b2<~+jn6j`C zg*%yft$i}p$-M^!7~Wrj)Xn05 z4|v9FeG*SI?XbwQ2pw{+N27NY*Z7k`)3t2~*6(8PUhD~(k>=T_W9c0PGOh*q3K$B--uM#L5xHl;6zf8im99o9DT1}HkVE80FDX>p`;U*E@_I!j+Q9f%_t$X%4O*dMzPZ3RR;h2|Df#o5CJ6k_zc5nb+|3Q)ilkyV1aN`p4`z0 z#jR1j<2!0uweU8-eQ{}bsO$QrwX`RC!~i=FVUG1j$DS6~JQF0oF7U>o1k)hhcOBZ} zc=Z_XROaLFOJrkGo7ORUoNb@Pj~VNlgREB?Zl5j4QRb{HPiq-PI%v99r+#MJJu^?y^!+|YlFHI3WNxgZlURN?@y?Io zD^@p?te2&Kb!vy_bJ%-+H9E5x!Z2RyvGV7RJX1cssGUPxoX>RM@$Y3B{oDi7-|JkP zZ<`Bi4{}0>}p~>52@hkR)gT0+U5(iph265{hs^z{ujPBS=em zfME9Zp=wC=vxJi8JoArA3jBa5;;(JXb`n>>#xg1^c2Bgb?Z)HOdeIkSJ3fWm)KyX> z&z5=Ovm~~G3nw6Vu61y2Y>LK4L%TGVkeiJ{F@cQkHA_ONFL>x3ckd^rIHUes z$BxG}X`P!Jnsw}VQK{Yg#p_aPOXfxAB7nn&80%U@#;?5_nJtdOm04LnVAW?0wLJsi z7m8r;=C^;OU&>;!y(=UI`YfE}dt_JA%0nn+Ac8Uos|Kts6bQ%hK93%=ro1QRojz!! z>9xC5z8h<{(QQVfkF~1ol!KhG;Gfchc~$w1Iw&Yg^G}KKJ>I8j3tEw;#~hO0N=6lc zQbOmyG$gNbWqVm3L!}FwGjXe1ypdTXY*kSH@j>a1^<%)kFnto~rSVp?eRro^F=q1| zmW_J?1w^^`?l%Vm+`KpA?+R+zgG$t(!i<35C?nf7&O_mq(L7_S_{R3`E0}z;(IT9X zP5>XE{Od_m?o4LpxudbrX}%wqR&~|2E9YruD?CdU2a(4L4{`l-RDL69vG}V`Iw2z8 zS0!YQH?HsxMD=AT%S2}GOMY!l!mqz;n+UgpHoh`h~cSa>lZX&wh55=TkX&Tkmp&>WI z0%A_xDk(W9VJleJ_%Ev1LGbrp{@u6_51e+|=jJ^2AC+3S@rvCC()Cy*w$m?H$+d<9 zhR1eu`f=-Bj$ElCWonC*y#EadDBI2 zb8=yn%0GBAGBNoY%ig~tVuVtT);hgw#4%c3jWR2bCUCiu;$$UJeR$(F!T8t5eg&|y ziqBuvuC)c3w-khLIUVpjR<2fq(&fqxM)F4<{wnackF6WsAzFPh_S7)*Bl(s=oG-T+ zuSf7l#Xk<;=u^Gr(%7|!$tbvBGyd1==6KZlrFbf$Gj-wq#R)-vtsV-J)+sz%+0po10IT_>{h4Q7{9I;J0x@cLuIU_IVk>U8HvBky;U~!E@!DeL7Vw0j|Z8fWjF&@rs0| z!9ynZb}Q#S`KH2l+(hayL8O)T4NXO=m1&&r0LeMC7c$ca>Sqpk&Xs&^sZS#F-})5Qk6%kuW|79RMISLuG~X& zaKWHx;>lCT74N!#hHaC>*4lmAtd6lNO5k9*0=1`fuYJs#S8?bjvEn@+!th6LA!5q7 zSRq}_?Zr^j*89Z?9;(Z8rpAxD+8LAwBOP0ghtjr!P_t^~YPC7fi+Ziy?}7C#0fDmC zuR($k8zNOaZd3I9s~bh|c8lO`D@}?8x;oXpx_PN;erIq7SoS}aXC-|T8g4q<#Iqs}&*odSbFZ)#FhXQy3j(CF9pI(@j7B30xWVn=MBujyWe;;nm0@X?!6)ps;8q9hRcV1{ma zPkw(&>8D01u3n^0N>1sX3-MFqKCi7>+~~SCf-K>i2_@|3C6JDQ4u{_+yyHoIKGxks zL%!v3pE2bAb)9)A`>_;m{o?kZ(zIz2$&Yu+HvQ90)O1@ZVp%VU+y+ig0jZ4TBv!9; z9$UYc$+1%eAY&?e(_;ZwJw; zt21`bZ+f#TnGlzgMNpaLxoga`km}{vP}?WA!BZGdsTof;8anA zj&}iEk9(R_uA1eER{4>o1Qy%<;y5*dc3wv7kLgZ3-IYcf^D;Xtr?H!Q63Tw^de=z= ziFFT|83Z@!MAh|2OMQy|S+%)Rf7Kod^sN~5>nRwC7@gkzYL=^0lB{hj_a@NnZD4{{ zAds(s4?I;}L6K#Y!Vc1SPFZNIMW(p(*=A;i|7d}2x2EH zYCp1-kQN{swIZFG746ZFE)TdI)p+cnj%>HiN#LJaEv|x6v32aBMl5(EoC;NO<&{5; zCva}=XM7*hwX|OV z_^(tS=z0~;m7l#NWq+Ltj8whgvr5ut-H*Zl038OApG`>6^vjIa7Ng)F6X=?UoqeUq z(Se=O4Z@1Nka`(X{AG73z-_sRZ>O~}so;x1Z%%ZMLq}iJ*Ghu&;jx+09@xoCdfk(`s zXNuiubV1rW8q9=4u=Dc%bze|WOc>*}QeDm2QelXtIO7$yr^r6d8lI(i=9ADaUqxBh z$$m%~Kze7=u&&X*(I6@jx)t=Tpw2q#&gC;98R&6cf5Gn%w}-raY-hV@bj!stN~HXo zzl8gDtqydq&#je%C}kk283cEwrUW-aLlN?ZU(-EnlhiGBTg9HzD=#H_`O5ouQ(KcH z`WiYt{KD7lGacH5E037xaP{}A(9fs=nIS?yDs2rrn0l~%Qsl#X79fn}RYrFj^SyJx z-X+$xIB!4E%yCJSjfOs6**(o=QYqU)p)OZ*FICsCJS}Oc>6i8Xuit!X<^Re)8NQL!4w|HM&V5M(*vH9}aXoTjjde^)ct_6Wp|cnTZO& z!T|bWy%4g2%K$#KE0rlXqd315L#67|3++^)veOF`!(o~6*CU))Gh%#8rRY~$&W~*# zrq@ncBA;{0k@f)Ro;y^|T&niCUE0XxJX_-ZCtlE}Tiqv6x72KxWJx53Gr!cYrfbS{ z{cT{&84b9G2iT59cfJ^u=~2h0c%xCDQG#~@-|bTw zKF^P@YT6C#O20!x#-1neqgdQ{Udkt$?qW9H;1CQrXhZ~y|3+!jnm3GIsnS;_DtV4U6-7bN!jQ(CcYF}lIk@D6oVgawNHBz!NFxb;8NQ` z(TaMrFH#$kc>XLC_}4Y!Jb!CR>#=I7SxD)`FK~t(D)wj$(b~w-j(1kahctVInj7Yk zQF$9rTG~}n=SxkF!R9+ah>RL-o?~__cLIBkm5XSqblj}TOm|q$!14NlSl8*jxlN7V zjl-rjj58hu|xcQXyI%te{aS0cdJ*ly}+eo57+Zgrq zr+$W2BhIunQAu3HnKU4AAzmz~b7m9CqrGD=|BP?lGE6yNIob+A|^O zeJa#j*$H+i-|A7T1$mPg1mN>cw7W~V*{xj40ptZF){2u#>}fXkG``I6;^a)&F|QeC!e(0_t{Gr2LLirOS*`IyQ+QC%*P`#Rr6KKt(wCzjaCM;XYi zV`Z`>w2rPn+5Z5;ZLzNIuI6#n^v!PCd=>Cbvj!Sib^`>nb*pl0N~2Eb=q$V^;awsx zE-M=tlz)F8)`@QXA*FoJK9g_%00T-&4ZR7wGH(7RcpFd3jZ(>vq(B_kIeYQ@!dBas z+B>PX{{V7416>iq)O(IGl--gsZog>VF;h1BJ>#GDh#%Imw7=RG#^+EmYdUj7Z6Uxs z9DQp3sGgV9sneA>^E0?fJPG3~A|aN^oJgYF=nt)BOQTGRb}*2N=2B?WW=m`$QsOxm=nqOsf!s)K+3qVC zy+ycbks0+hBodKweznlsl4mkG!6bqC*4}4TD71|R!9aT8b*gtq6TlDQihT`jeFi$_ z-r*Q?jM9S3W;U_ooYJsZ)U}h$w@f$AJ5uVFtu#MuiAxYME;^5T(wnohF|8ZM<^++D zatB`36!5I@v~0Q0r*C@H<5oQ*!haWa?F&b?(`B~%TyQScnYlRZ4Ru<7$4Lx?Ug^xE z*kZaOp(yFOn@u|+wfD#Scw|0JmBVv_HWDgWemq*-Ttd^@I>MZh(3AA4l{FQ6k?%bY zYh3t=ZK`W4soC96Wpx?-(Nb`u>0P&m{y1q*Xkpb}K{_3~nSS@ZWSWz*8cl4DzeMr= zfvQWsDdQvi#j%R&;MX+S*D+o~z<;zVoj1^Y$!>Vx#UF{j7Sc5tESA;_yUUZZNr?x` z9<{((c*fJij}$)+=6SBJUv1OQ`;k=h$D!+3I!Q=$>bYH8M0!2Xi?q_P$t0ICYDGp` zS34y28K=wkpArb|?d`%_O(17=JZ-HT*;p!6vPkd0vf;M2NG)bDgiyaLSDAcBy1s{5 zv(qiFrB!td<8k@DkF_aD#b`{U%pM=nVZ5=sy}#7~K>G@-{Hu=jdHh>(V{!H$4d2^6 zv=IVb&frg=q7=E8Jc^2c0z6m78t$j!{d8XHrZf;`R6;iA>+4+jjq=X@0q@qiD^%uo zuJI!FxMf%wyZ~yfaacs?s*4<$Pb2ATKDXyc-A2u=RSLRe^<02U>Qs4}~Am*U^9+)lwkj9~L3--t-sG18))_FJqmC?IeRR{NZ(g_az$OCVMscZz+* zSmR=9#M)l9G6VgKsWP`hAvBz$4~XrJ z7{P8~r&3-5c6}!jA^n?eg?!T#oYm)zDb@S3$4> zWBaiRw%gotsVQz=HoANrV6~6xiYk@XxuI#~`S;c^k^a*E07@eU_Cz+!Jxk(;gY@Jr zKD7Y@uE1ORS2KU{^TBWz3G~}kKpFH{YT;t zjIY#*HOrXu*URFk+EI?m=8?jsyD+XbEne&XKDA5PkA2d2toF8fbM}c{!RI8COY8EE zkv5D{=nG*60otUmK~k(JqW}zh*ELK;=esKQ3Ds2&2iFyecWWG(%J;~uVNyz0CS-8= z)@&uWnnZQz5aXKk4+nf>*ZeY`MbLZL2^jMqkSo%mTiW}T8OKtB_^Ig5>i5T974iMD z%Y5R-`VorNeV^<=9-SEA(J{*CkvpB<{6FYOgeTUgrq{`9~|sJ!)xO;1S2I zaLaRLE3;ojfTo`EpT8;36{8T?0Q_p!sP46yZ*qu^I%26#*^veZ2ZK!_yv1X@+>!m- zkS=Y29A#;B3*4RnV*!_^8Lf>jb%yCn{MOI%y8C+7Ct@4j7+xuk+ey^~KWUAURr3&r z=n3d+9j@U3{`Efw716op&Mgs=JFQ-*@&QhJmQiES0j&n{+>`DT7jYIK28HW3Yg{{Ry4iq551 zIdc`ayU=sXbkQtwT*}J4F6Ji!v@A7EUdAB0Zl`r_2RlQYAHtj}wC*cemxqgdPaf$T zS)H762TG%L<1KFPe=_1`wb~`m1MaS;B704w6ZjS1yaY*3Ylp1AuUOx4-}D^0aYixZNnTJ^BV#f zXMx^_YZmUBXbUJ%vI25&XpoODV39~226|9idK6|2jjVd}kxLq}1yxDFq~&93aZO!} zcrJ+z7FXLe@sG2wo>WczY?zak#4@6aRx%=5#7C=DT(y1etccFVlw_QhCx3pE2 z)<+7>5^gE(eXHq@0C+b|(d|mlqd|Ei9l}{7$^C1pin6%sb3v;e?X9#>uol`qq$jah zRDUYbl6mdoX>JzYI8bGqIKZtenU&kz^Q+$yY91x=2-38R%XneVbCH?*>~bD|c}*^o}gnzQTP`N}SuZjC=2o`V23&*G<2; z+-@|`I_f7 z4<7iU`cowO-QYcT#6l0`YJRaw8YtY;*i*KqZPvT1-;CYrSFQ-}DgKo~o@WHfD>g?~ zV#oEY-YM-J4N{C&h_3KQB9SH^KD5ZK;fos-55lo{nvX^&E2|FLp|~0E%?heJVzO*^ z&@eH^DSot@T>YdlSmY zX4Nh77Bje?(yABV$?rsWCGBQ3ka3erLboR$m0j#j`V}R%Q6}{HOCE7ghUW6c)$J!K zu!1w|T@b4DxVaNW<&cm9oaeP$IcXw+e)HfF>0C`;QB#U?vDF0-#}r5~6>w?evBgqa z9eZd3zu6WXl215 z9I@iEZ*KLtNN8UXk_Hq4YNI6jV95gOUh52i0C?Gb5cRFAXX9gkF8FG zwBw-slm&w2+pa4=Ac7+YaOB`qi1}_>wUt&7uNtxXbg35Z4=e)sC!hzdHt(^IJ5I?I zQs~JmuSL#4^r$sLCbnah^A{Lg^{P?O+mi9QpBC~IbY9}EDxkJ-aH@OK(LD_5#?#dH zzXbeL@OOoDds|5D7wt}1R7n5!)k@45W{cpvP3w^^+wPCg4iL<-dnwpY?R{ELFk1DfzoS%lg zO{{3zgmUR~t);@LQ6k10Bj{^eRPpzWuH5=&US56 zS-NKXUa5O5Z`r4B=xU^v>ci(|Y@D6W&ClX%Nn+dB?ShP7x~x7pQBTShMms2{g$gl} zMswD=&-Q%P{Skv^`^;dxfGZ7@sb`@9`zGy zmY6^33&&hkw_>?nwktlfXsv)*zY2)!kY|XbBCAKL5{#J>>${Y!aACU^YJs(TqxWNs zel#U*!LH;_sou9q6@7rHp623SWR=PGIHN(v$c{-TBbi^1dZb=Peq?qBj%lX0B5g&D zec{G9$8TD6h}#1Z*V3MbPI7X;LQ6WRfveW>+M65WhHfXR*^~%kkBS7pD;Yq z-*Pk_vj#W^29ewqJcC!SK_{uDEvwr$=5I+V6+_%d0gotLQ66G zr#PUG#;)f>EZbYjxPqlrjY%5hSiK2Xj*-uXIYe%lMk^+F4=|O&Mj*d8|=9B;(B) z&N|e}4V0yA0p*8p%Q7Eq)CPF-!-MppD;mMwTny2xVWdUx$;AsiK<*PK9e!a+rc9-6 z&3!ug77~L59=|ZA-0dyBRBiQNN=Y7c?4(e8fLN-q+uJmYA;$z9dQy9jmc#t`fuDLTM_o$+vV(CSFBF4hxmgLvY*ItLheK%? za@?>TIi-0NxK#mHJk>HEyvo-v<+%IITc$b9S)SP-fnRYh!y!c^uc>fq)?^F0l%9vu zq>A0-i!o)`ADCk_TII&Xi2=a{!b&+So<(6^%!FGf*bSkW_Ul@fnp=`Q#PX2)a%#EU zRgtXfdnoE#l#5g4L^h^A@Z&WIl|UzcIp=Yyz1hr?ljdVm_A7S|WsOHelTp5@Yt^n} zKAhHyE$(ikt0jlQ9|esc96fh>Z6PFw;m!rfMRufxVoMLe*LzZCb(Q z@xuOep$0iz3Y&Em*x-b&KzOAfH5N#0ki$?a8EU9YG*GpN45Ru`n;NV*#1+I%?M3cnhRa4)1^rwlu^Yrl4DyH ziB@5iG|hnI0ZpqJmF0jO-re*%QrrLt9 za*TU`b6692%GM%9x=7yVfm%UdLK|;XW88SNQlBdsiZwiUHJLs2&BlEDmpSSI#W_a8 z(GYyQ$vqFLq%1S-QK%=bq-E7Q4E-u-W%-3kdTv9h?P(B^k`}6=RnMX8PQ^axwF65t zEO2floNWiFuCBr9JPtqk^ z5#FjXxpAiDt7XWTNPxEo9Ez}voASKklU6fvw23_Z<7)AbT8I^W%O(LlZltsiLCXVM z496-+dwWzjH#4aOw&Uqk*I{3=X6!OpywRRWETpSU|kvy+6xsymhH;)|6bCJWDG#$e@p5&0e>H?qy>QySk3N^sLmC z&Z)jdp`#9$xdU7lS$gdqseF{i$j2n}xYg^sII2~0Dv3%+s^D!yz^iu7Co;xJ!CZmT zmaNV0QbiFf3~j;Z6;$5aXA2kS?Zk%9((m7MM677X0)S;|CqRxUAfT)R7?hLB%V;1`YwHyOfZ= zNGFO6Aa)eNuA_u~a3~Pqk_TFD;=8cpA3T5hslht|#Y~mOYoY#F9Y-{IV2+$sC1fQcpQ^i$9)N-CuUc@@lB1hzngZYy96C83h&1d z?*9Nsfu$$-nBufX+Zh)=FV!C+*epM-WL)Z+jphZ#)UEBD3TdloC2hzqml2LKdevh4 zdQw_~2A5>xypRo`S5$1Cc?w=WazSS&7*QBt2?Nd0)>=fr8aCNTlzgG<8_Q zGUFLM?kwsfsdN%zBa?=D9N&{BGtPAQ{LM&Aw7g$ixX zbCX(A+co24w~|L;$YD^DIVBxR_V@P|x65qmsE3X_)`*jN9(yAucnwh6Z$aCtIsHp- z_KTz?9t;DILs0(!XBiwcZlOm`D%-NqxXH%Nmf?kM;*iZOXV=hGD`{f$&+oIJK~lZ# z&7!&6X&SJ@5kN-f&wiC=-%qxh>NtzK?mX_RNi;;0LTWlRvrOJpn^YVOo`RU#CCm{Q z6GG~|w&T!L^IWK^;w8y^1`U~$6CrsL{njgbIxl<`rFihjCwL< zRVx1gILA(FJ)#N@K>E}_19{Eh1};?l%F>(@ki~t)y;lL8aN;>@wRR z0dl83s?45Ev}Oo)#)F&=4N}zerzVV>iMK-IljupPVsZxJI0B7&mCEm-R$=Ez%v1yI zP#8{mq@JgARgy%B54Yz}5Gd(Qy-U=PMaNDl;y&~zt%ipP&T2)Bh3)mKenGPoAjdsv zU3eeUwJQxPk5$0O=}1=;uVL(k*g?Pol&Ia!JBlb!ppFF_M<=xx8ycgIoKoY8rp(@g z@44T!^Jeb-^C6QH#z!gmx&se(` zMbuttF*ss);MUHT_gB(|TZp4YBRhsmS3I97f`e-BLssPkVEv{wZKv<;tbI+`**}uG zIqge*Ow-oJ0Chl$zcd&M8}6R@>r*Y`hrwT$(x0KUqs%_(qPdJB=L3^ft?jly%y2%O z)mxWyN^|5^EM4j{Lx}B&7#KMe%RBkxm^6~%l<|s5X>&&f%goN6JyA6XbB^ zP`Zq^?XqaZk-y4&9%$w2%pKCOx|O^_(&R=8g+f6;O5|SI#0uMk5$Q(aOG_0bUBh6@ zF+2fVS}vMx%gKKTP)Eubf+@+A+DMXV%w?BP^8B;LRB~$ekrm>jHPri~Zi;(UHrPH@ z9ZSHZVb~v-5wz4(T`Ddd?q=ymn;0~iM(S@fXP$7Q?)nObX(Lruk+7$LXxWKDx1p1F zd`0H?fGE5ke(6V~P=spS{j$V}<2#ioagmRM_+(QF}YG)9L5T zB}g%!eri>>x3O1A$dRNu+m3>-MY+XNyPC2o>t1Mu8W+SseZkaK3p>HFqqt&2AG~>@ z+UQgus#9;OCA|OzWFYa-RdqivG+#qnP}5OV4uY2$ZkRO|H%iEmFVhs@IP2535iUBC zDxNb=anHRSMann<#XNo73b$~unD0xBnpX{n0gtUFInP=|?kLYnPtKi+7o{`qo&^aD zpyQAa1L;5?FDf%lXe1aLagWB608!LAM}kW6)`TY}MuZbr~d7N0ZG{*yphfjmgJK%Dml=l=D=yB3dmAmiF?=GOEOKMQgT|BrpPI z%)QeU7wCm870R-%(l>PVs98sGniE|~eaEb8pGuc>WQ5uQ{&f!R=u3AgUW{{$VxzIf z{+TAK)<2b_hKFR7mD_wuMP$4ESG{igLael7A{hSZCS-@T=FH z(w8KR8aZrhPAqn|o1%q=PtA|5V_n2qGCGPmtj?$Kl>(8_b4j=6!5#b7siP$NlE!wN z`d3E}jK_0b*RdyA9=&KeY*X}M}INc1G<2dVD_bf3U&8smVcd@CGXy%nQ z0=mN_#iO{lQl$O$;}u%(N?5V>MRq+-<4L;-%G(td*~0)qQa$Q_FL@h><3pAA8kbg9 zp43L*u`C;!f;3R%^j@`TnkhLV-JvOo6p{3;R*_kerC<@e=b@p>`V(uK)5ROXDj6~) zOlPhu4ox0NVgCR{c9{PFb-1XzS`p1>sXdmSw(*kK^IT+x8OP&Ix(dj`;zEG-UTMcs z!b!5EF`~w)3Xp@j>sn~;R7Rr%9JMCyOtvUp-48TI18g1X=2R*hc6|w@bWNDlbed+4 zQ~S0bFQ#gp#6xe{?s)zsVcM(I>7^HRX9zIZZbp47#IuO}umIJByAw({b-ru&yX4 z>qlxx1UbMXwIcGsaZQT$6}w`Ta!0i^^aSodsKqb=ng)jw2T_`@ah5_wH1#r)D4sL# z)}wRiD&?t%QWso|{*|A4hmyT1ISK4;8@;;*!k`Y_s${uoP>_m7J$a~#zU=2)T(5D! z0aqV|RVN1o)u`M#g$zjnc_yDSn8r%;-jmQ1D^3{O{KOJ!wYF6HQn}dv6F^EM#!n`n zylv}3M=kq_kb9CTdmK``xqFU^D4Ykmn`CR$FH>6ZT4?hW*(~IqnQoO6Z5t^&Xiefh zGEo$NWluCH&IelMWN#yh<0{|XY8IxP*F#d}l&DlvI{-N~uNQ|e^($jDTcl6bdFnou zcjes1QA<;wmIs1qBXugsaC1!FNKnOy9WrXSHBDJGTY^?XHb_*DO4zo&l5)=`2F2r? z)-^7-CZtrBrOg*odzo4}ak%~5Rn1E6t)xUo0Fjf;Eskl^<#uHkyoOR0!3Vpkp@<4h zC(K-R9gR|4$eAq)9&0*)9Fyv4@uu_h@;l;$#uvBb;Z7ku9!`p^f7M4VC9K zCa!3u7LEHyNR#)Y<=|8om#)rLI6!zJle>30! zxJya4$oZGK>?&Q(X-V0|55HjX;9!wig5X3?18rP<%wbJ3Ij$CHgo~UGDzBgDNUCP= zrKY4{^X*a&8#S5L5#s|L>FRl=#+d$pA8M1eHK8`J_{Yt`r?LDg%W>a9RFTi&icoUL zpK1flR_Ced#V=7v7-O1R00kigp0w8D9e_AfnrO%z(R+o$mj?$FYy%GA-!!AIJX75e zwH$p9)|7PP6eLxU5_mc3)`dO#(QAc)x^$}dB!%M{s*15PDiC|~Q9n>B*2gZb+==s^ zdJ3gE8;77YnKHXBs|R7`HV1Q7W7!cqK+u zZrFYaInHTHX_r!M=!~o14&3RN?>&nwmiXMGJb~$0mou@HfJtz0+o^m^R_Btq=unpA zU=z(=vUN*@1MX)gsUj*qhN3(n@F9!?>MJr^XNbGR>cI9UmgWiDac$&cGE*OoTTMbW zL6CEtW73*T(Zv>WT=9|*Y*iWNF6B1kY3eB0R?u=uj;DdmS%zKw{{Rj%nk;6Pq%V8s zk^&V_csc7;Bw)ENHiJ&x%^YkpV&D}mg(IazxF?gqq>cTbxJarndF$&|Ao)j6#*1|` zh0?KWf60T4)fu6lX@7P2k7`#-aD(M{E!!(v#AAs4NflaE+79k>*ECzO<3D=hEQ4VH zlTv0msdK=^Nl6l~JbtwC{Hd^QTEvNg z+pS0`vMxP1r8pxsELw`W1B1;k8OZ!Wpl3P7IdSVi(y+M{nKXtyNylnKf_8xP#Y-97zTi<+A8}K{h)wGdiSZc`PL~* zlq_-bj=a>cbh?@m%_G7jc*?eMf!ed-mDc6tX22(fq>+QSHH#^9nIN#wy+UpwciLea zLFB3Csw+{Xo6zVq`D6081Yv>St!J!39n7o%B%gY1T&1h9nT_Gy0VE#hy+!6oBl*~Z zIKZs0Z3h@6K(ggFw@`Ze)Eb1QOKBGzA6lks7%2p9%&J>GDrGb+ji!=TBqW>#H7sxC z5d*qL3JfB*oVYF4{ZBEud495Z^-waMkW&}`R8A8M&@Q<|+LoB8M}Q$^9B z?RHn(0V9f86=qMHu1+*!Vc->s0Mytvxg*QZwyNQ z9Y=i$lRIhPkH(uNB0>7oha(xJnNMvA0Opi&+cXI=+5D(D>6*O|o4XyHV;tkXCr+PRGGkGkcdXod9CorQNi!K+kF7AaJ$|(8G{%PBdFfA8Qh4b_ zud#8lG@uJ|0LaHm$_EmV4;3+5op<+iEpN`zlk}lv>PKp(j#(=ayqnHXT69mhcR)=b zQtn6z`c&j)Kz#B#(r-h0Z8LjX2m2l}PEX5|SQ20i9zmsh=ya# zhVG=&$YePRp_#ZR&{WvRW@g`M*4GQVNf=0jq8_!%vXpliJ3-)@wIjBS+E!wr$mXSv zMnjb(9>i1iH(q7fZs!0E+2gp$tDa^6L`d>^%K&LbC!;xPQ;qZ_k86_B+eWzV2U?{) zscBf-ouKw2rCKjTJMujZ`#1~QM30np9Gc0ISV zq9=TGH1Q}=*R?fbX0D9+4(2S`{{R|5Nv4MqMZu|8rYL%`-prLx9CfKj6qv7ZROY2z z0q^fgZVPci&Q58=0B4i^YV;c%PZWdc&%GB z#UVFg-$PO7vE&M1*tsNjq$F;~r2%mX!NT!C;}pr09L$?OAQfKJm#tv zY?0Vji&L4l^d_`zvTkJu*wwe$f*_TTXzk$ z#+Mq&CdkT+F*VHUdUHi?&lJlljP&%QTbs&mF2*2iuLKH`-%z=U!oNg%3HeJL)(u?dis)Lmwzs{EzwaVp@7A`XBwC0}g}BD! z)`=#B<#o!)7fPL_jpB6RHuFNKBRK=7y+m8Ml$tqdv}t2(M=CO&nc&h3 z20{Dg2z|M$zK2)0Rvc~PkM2#mzLeo^1{goh?@2NxQIl?Mp+tmZuVD**6uoKwayJ$utg2Lp-&VEa`h zD>O--l;MuuXjT}#@DCIUVD%S1l&r*YNuezVp~&a0A;CGNuv~D#6&T=i)`81mdaXCU zPc+b5LD;5}I#7vPhkVk5SFkiWn;-+)kxo0*h#2#lhIS==54|*nC80@ka6>xi^@`Xt4!u<%vKvgz~k1d zNiNC~CF(lUP?pv}QYfE2++^2BYkg?)qFi|r2EZz*(upf8ImW6IH6XE?KpN%MT>JA?H9bGd0a8HnH$k2$B<>NGw=pi_ zn$)O#GM?&sQ&E_V^IF?NugvZ|O3R;mk~bEc~Od{?8?URB*6-|`+&l?Y! zTaTqH*l>%m)U}pa*tse*z{gsOYiU9hj|fk8?M2XX}A)<_fS=}g3T1V z+bKOp>Zy4HZ>k#dF?``vb_zOGq<)?F=AvB+eSxq?PCaUr#Da78Pih{eMK)KIf^tVn z6ZfAfAOlY2Npg&iH;Ph$Sf4;DQ55a>ZKcQiri*HH!lLE56p=JdvPCM#o@zE2JPt)J zritHD2kVMlU~yJYH(^v_q)dO4M?g%SatEzFPI1p_mL|yudQqBXTva2lYI}C4%V55P zffyXqyZvfX9mOZmQi`z!1mJtp0Yhi$K|Jx&iv@=Rh2y0+BR%O0Rv>#Wt=~6HL8k zpv5n#Ly#*KPSAagN=IBQ;!PguG0$3%#f3REuVV*$m8E4+27Y5zCF|;IMD!+0QJKpD z(yiGnQe3LbF@Zq`coglXffu3aQe4LdpBeKWU7&3^tnai3Jx+b=2A@&OxtpkK7ZFM= z?c(P=xjjWmre1HHZgN|r9CWA3tVOiDFSXn<5}0{KM1c@X=Fb>};j-sHB`Q!OfaTq=N(&c*vtn?n&c~Yz}#PzJ#1ob~Q zah|m79d1vxHi0Mv9>%MrQOO~WJ_b8bwJJJ05L{WfllN?;f7&%4lR0CxH*M>RX31)8 z_S(%))Tk@F9QLPs7453uPQFN1*@YQ2?H|KFBiBg&!qG3TqHaW{A{39UIMZ@> zHukC>>oq0#QSk-adiZW(j`*ydU%|dE(4%QJeFF9yji zj8A?#RD&Sksmb*fK1W4~cK~orJIu#^o|O#(BuB`nZ$0ShHpv5Ib?HhNbf8qBs&2z>`w{`k z#W%f3x{7H%XaRY~DHH+Tt4jbsAaha5>d}?PbJCI`QnrOXyI9gbHu}@KgH|AMpQTol zh3Qsi^fZzrY=B7mQ9Np-C^+J>>R`9A5l@(Br6aC69Gaz}rHLYk%peni+|+k*M{eLn zF6KR`zd()lE@>ys6J%g*#bH0n&N2K(iybuk3X(F9z0!u2*mdHSmgPz*5E6P5O(4?~ zNr)7lYW=F|vO-qvXGuT2hal0-Vq9BV9)WDJrjruwE}<|p-m+$OQtUC26NA@4>0_>%|216*ese8MW<#NK?pR-J|OeBzu@!J)X zfTVM~G~(0POJXUc+G?EFyH4Ju_Nr0465Ns-pv#@0A8z$+bx^9Ike$4BrN~o!+7^bd z6E4**ryYiB^aL_O!smH#at$P<9m#a;Pql~*k+-4dt7>-g+*ko_SiEN;M^+w`b68Po z_Br@$uMz(Md2KqJ@NjskWi1e9b$#C8^`_RRR5@BcgZ-U!O*c!@ZZ3RTr@}7oCi5Ws zLIT@1jAK6euchp+two$|07Z=b(1VOuToOw|ilgR{I$cKD&Q~6_4aD}d#PM9Byi71d zjD{6fGd#n_p9uUBs@qR>1QF@iKo;|D4#S_IuaNagjlKQFu%GP^M=4PiBA zj=EFk_?XD1LDf+GMMz|U#!a#>xHS$c_cZ2r90jQ#D##CBwB?QlYzJY-dd;V}`J|Sj zi)CSyn~z~iZCh{p!Jo>i=zR8Cjuw-+`F6L^(*2g?XLNb`9Melul|;l5O}ChsXWE4h z2h5BG4HTr1rGlIhL1BT;GfSX0ChpmzcmpSsnx&|3xgc&SGtV@Nu=VZ76fXxS@-&1Y z?|=ZL3JKz(KNr{iGB6Z`XSXfsBsm)h^yV8L&vv)Na#tHpvM2>kf z2PdvLs`3(4@$Xe_4AZ;Ry&JIXGJjf8Ch`j|2;#7M84qPA+J-nz7bn_-Sn370jN=s% zRr4~#IqGO?!&+!)>0&pPf-xBxAlE-4A2wCbA&n7sH>TGvgpfq1JGm7qLZ@)S6g`P; z$=#7W3S#O>&rwlH3W5UXrAn3bA2A$}lsLy~5lW-CA1KXLkuFa5Js#YNyfqBY#d4WA z?N~Q#@&GthJx_dAN!W^!w!+66DqtS9v7$v6*_(8#fSh5vP_A?OnzA*t7?fd%8*sfV zKKehk+cMj($Ily%D808Ol%cV@j|nJ_L>%rzoKPZUkhU^Ur)rlg>RtO5Bb1foa4I>j z2JNalb*UVck&%8PHwsZyF&{Q64i}SJ$ILCD^_;)T4p-i+B9qsF+Ma@mCe!7&Ju41! z(n#B|06b@k8tRIQxzKoAG%zv(3X`5`w`+}SJ+>N|JDOh6(3yaa+=(zZ5&^DaWov03 zHaXgOKGdLQmCCIuxhhG`Rq{!Eo|Uq8D0_mV0|9Csq~@tTN>0SK_VUdzjwL0?&nBLc zN+5ZNAp>pe-ib3?k;5q&HPGn)02~*~G3y$9j;EoF^Hi$7rgEcgi<10B@ZO~5 zrMSGa40D2~HO=at9r$D7YrnMWI^^PfU~S4^eiSN^zM|xmb|7Dae-1SW%4z;Cni+c* zkbhdi@rS{$_(rsebxjw>&0`c^*W?kifG9D<3C!IGWAoR#-$-W2P>Y$kH)6n>M{uj)~ecxq2ft@zn`ru zJDd#u6qbO}L;TKi_$S;{qIAZ`w~f8XqV0pd#;~)_WVOfprj*5C>5nEw9>$nAB*vR- zHyDmtBl?Wf&Yd1XOOcOpMcU{-d0algbH&7U8)@@~!tHVD4GX&mRU$UColuvL08>^8 zfaWIWrxf3GUow)=M-Hb9tUWlUt^oPCq)K`LAoM&^0txF(u^%K5I-0K_${r|Umc?Vx z@lg|%997)rA#9$URas6!6-o3GiGOAXJerae0kU|-XL41CNaKuCRf)|lE(xoM{{S!2 zrGnkuWr0=4Q$tPcK9?BJuesoBU(b6owJ zwZf}OkTK0=V|tFuQ(D?9cljJ{l%6@j0JINK1MX@`LrMd+2afTHTI@!|Z@9 z%*(qUbaRT1M@W#7p1CHAHi^1yEpH&$*kXN!NU{xu&=c)aqhZ?H6`J8xovV}IH5`Z} z8HeUx`RzsBv@ZAD!*ybwY4yR)HUjL=8-3p1)ppRCADRq2@sDeRl(0qu}j$i--A)sYN< z0q;@w2c~M(>{MgazoSU8-Yd#*p+KAq6>gz-@7z5V@->ib^Nzlg}Ie*bLIS_ zJYtzFA3XeLYmu6A)M^*hjtKnws8T_NX2JSZ$t|FU;&hZq6;I29+N<4Lgl@D&+dUay z!#BFSJf2)KY(b6e14 z!K4fh(0*oI3|1~Lsa&g|_0);C^FkvYyc*QD)+K`2H}5tae4yl0cd(WAWb3a(GBY|a z%lERGsNU;Hx=qsBz(_o0Flky#R!pEnEvAJj1Y3;yp{0vQ(wvbDlEzPb^r}*FS^{zv zd^r$rlTvPbFs1&$yBtS*6ArHb!||wE&e`(5 zW3D^(rEcTrY=yL__%YoR?90}omr7!|NmQSDy*4sZYjU&bw~D_nmPf96s&Z)8`$-Wg z_T55AJj*)=Rz2x>9Guo{goqu0e390XSQ2m!I|@XS7I`^tamPwyd99FTa=wK0qD-w3 zr=Y_sY5+3=jEALS>LdOpR{(+cvrg7mI;k{AAId{Wb`VG9NU8g=yYUpd z29_Ad<^w#^6v%&J*i-4Cqhq|#k)`l}O!CM;Ca|QID`?gjPWE08rDtZxH011pF+8$n zKEKMfB8p37$d&V)@OaG_cmM(=@+m#&f~yYAr_LLI~pIo(~k8GUe`9u#80$ z;N+9dT!!ZeRU76#YF6c4Y#w-tRh7W zBvP_fTd{GdmW4UURKp&e@lBT^095mlS7Su9yCXfZ+poPLU=BOfTI|sqJ{^;Al?mV) z(>-wb+v{1*I+)Z}MjiFb{i;MP!B>C*s``MEeHsF-%F1z*+O82vY9$obhA|D23HGRE zJds-27NXlBf4iD;V90~FeD00ccGUkvL(^iMh z0TgHH%_YN;aLT8WUpjSllohv5FPqDi!0Nh_H5 z4o*qNDh+Q{(rAUIH!A|`587uOBC4Z zfaaeFf3ua|M}7?nUqLPPB(yVtZP@Z} z)RFz;NpYmzj2LHyho)#(OfIi-8EvoQQcPi&wkk3%w&cM70F^gouTeTvfSN`uZApkxv`Brr9e|e8mR?2Ao@RoC;H5)|WyPk~(IF7(C}b zw37<~Bn}Du=sUlKvFlFSgV#eH31jO^BK)*8Va?dBGBz`gl&Bd%;Qet^VkC(n4%yhG zk7{v=^U25KLrTSNZ0Il*)gT4SBdPo=16e?t-Qt1H>UX+2Y1o7T^5(5!OBGD?$QY@6 z5jU;ILxxAhY;bXr&{Hl_QEVg;upK!SQCey?$X*!|RScU+8Ro57+Lw7+XDbp9m@iT) zo`hdr4&zIP59|Wg!a))tDjTm#;_cMPrOPNJ_pEtrZAmqD%@{+&A>4ja2O_IUCDGM( ztdHdm-Kp}frVXasA7^lW(CVR1I@O67WNyzV85N!GV=WP8RY+q{zpr|QaCrkCK|;`k zMSfWtAsdiSP%6Kg5qz@8aXe?5dyVRc?)d_-=hmMc(n4@$FM0zZ+5c z?TXF24lNIs-gh9F*q+=~yJ!&rX+hpO`c$^|CUdf~EWB~MZaHShV@13n*o%MyV*q!e zD?>QUWH5nmBxGj?Dr!YXQW=Q{930g*p}L**LR-&0sgmJ~Ng|w`)zWxO#c`_NI4a3B zSs18qOQTL?VLh_F@7}>uaJlCdN+@0}R#}*Zi^^;{=~oE~Pn6l#Jp0WLOOwfy{We9n ziO9fWNE}wqqoqEX1h$hYNn%i?4O5Pn93y3BWG<4e;4M#D5)mX9D+WD{dUwSA0knSs z=vR*-v@^t+d}9QtG)f7pp(nB8dIIJ35Z%ueE|{5VLFa>0dm33MarSCIv}8~EXVR+J zGaX~eCm`gWdQ+A95hkQdr}L{x30}t(=%dBt(Tuj!!J|?26k8D?C)S|8fcfg(0^*cz z-lX#CHu5aP45a+IrI}di?&#R#ty@aq-G$RuK>^H%r*rL4+Rr=uuPZRg>rFc{98zH< z(=Fb^_elI|Np70eR!sApW`}2}oLILc4VUV*%*hmM$b|JsPSgax;Xad+Jy$SK)lF}t+TOcqyYL; zzp}Tl7h6d`-lfdYa}Qk_;HlYkg!>GW?kVE4eML<%Tera#)kr zs?B=sZMJ!f4#O2zzJa1G+Ufe9qp4fk8+VR(kIYXnZQQ=1uXs~I)%C^J8qNtNOC7>r zC}$)JrAe(Blajx>c3O3^#o(PfMBSO;F8jT4UUdLn!+9ZcNyS4}R!3bqb4KXZbVoo^ zMr;lNtm~6H>d?3ilHXsgTKM0#lpe|Mka=RTEft}q4^GY)Dbo%9sfGqA_I zX-H-{$6zVVGXMg%Ij1P2K3iys=an9Op;q7z#;cgXlX>gXrRYo0a{xAsaB4P9pq%qg z;E=H0NgEP`9CZvb4P1_EJCTcs_DTN$j+#xo7qc?&ET)KWz>#y+4QIT8$d{3lf#i@Ph92Z`QeIDQ9oHbp;XnX%#awRK zxm1uf#z`@RRrJMDd&d$m54B0{Rb_h+<88rm4`q_#qY z!8m5gsiMgkkn#r@qm&ei^GNZKcd^G@njyN5XhSc4{_nmE)SyCQ^l-gCn?Lo1I4^H99esiN*l;r7t5|O5@z`MnxS)2o9f7S2{WRTpHu~~D3=y_ZwOltMrP4hg;z~k(82m7n&NKmU zZOd&Ks(%uQ{{V!eP#F8c4nGRIwz#nOyKLpvGw^T3zytE?Q5;vR{C{R21N<)P0mPQs z2fxW$$6?e?GEy=AD)>W4(mWTWc$U&HChFqeIOPlXPE;T2YsGXX4|j67V0k>L7^rdP zlea>nDM~!vWY&wfSDT?GEjzkJxQHNnSlTtim$1uR_VstBDx7~LwdQ7LcZW9&GkFtu(V9`;%x`k~!4> z01I*XQ*BcoaO3asn$KOylD?sH5-Dfo5GpH(gtl>#JLj4e#M7`@;!Wsrm0GP~2{U@HVfE^XiKzaZy7m1$eTZ~^7J)or+~VV9AWAo34R4OMn-M&oj^-WQvxNsfS6OI`2Xw=Rw zTMfDW#k_JkX5G&xH29@SY{aXLqpea*mlw@*=$&g@)O=MbpHP(B>8{@_G30L_?N=?R zUg{c*GH7~Pk~?HL^DWq^$8b$_@3%vaE=yKv_(#CfYIms|DGjVpW6df7Abo4oe1Cam z;J=4=wz^U@FEZd29I@_yoo5bdJxwKKu6g#I;;jQvv+_JirrYWO#qvT^am`{$@O1dT z;(bFyvtJKss<{!c``(7CGmNhmOO~sbb2=}D8jgnv)FjrRmM9KFZNX~uKNaKqerL{d zpw<(QD?4b(y~ylsF0QU4irN=4G8YGL1Pa}-@ZO<)s5PNfNfsY5iS(wR?QyqfV{_r> zf%MIH!v8M=Qw36vzN2g7Puei?cTDJ!1$7UI%)PRcByEdUT{`eQq&Z z%YT>UZnb4@vI4Nk>P-zcijq>)Nm$7z%Gdzs6vAD-{*@N6Mmmiwz@9TtDIh}I0sLqy zsFvSo^8-TV6n*Nn{#5c7c#5zee;sJEL3?V6^y9J0q=15YW16+8FkP%yDe6ypP3x#9 zb0&GFkqYHiJr{#nZxfq=g&_3Ftrm*rv9dI#iEQn_b=*%@^sEg)nCBaMFzZe`+?(Fx z5_irCB!QftO1T!K;w20>G2k3iluXq$*`;l86$43vK<1b2>nvfxMLEC~1j?PHMR_8~ zyO%f$b6Kz!T|NS*06EC@rnFj`MI~fwAt8ef53MpQlJ+ih-2RnTNNrt~ZKPL$Hw^ru zt%9mmbBvCjw3-xGDEP8n!sCjTDgMmKARKPPbK0lWxh*bl=~2fumZ2LB*-gXntNKOE zKk%&eT|aO7K~f3)Xo_n=re5(S%Rj<@2gcbV3#d+hj4IcU*h%6k9U>~$vL*Q7bMPlXbs56L{{Wzh zcf~y>$4~exW2#!(!yVU=3p|RT6O|O!jhu0lO!ECl!54Z~g{JtCB}r_r;SIQTQblku zf;?9k?7-Gjf=@%ToVk%Hj4z5;vD=TWSbvjWApZb&ipu7Mmw)l{KMIdmPyi|=Of5vzbsK2z4I z>tx5MN(bHFT3cf&K3KDB#3`K`(bW972lT4?VvqPnra0#(nuPRX-Nm14-1vUg9@-6x zAh#Q~fx)g%S_ban{{XRVKw%0A?oSZQ@$;B#hchtEns~mU1NHoi@5H04XA~xgYq-1fP#=eo%FDz`V+BRS! z#@)S-wPgmSCdzS9O3dWEK@3mf%|))ZGPEV5PfT>LkTt#1YHre|GC0qEYf8!T>T88} zcP`1|%ZMYh@)W|>$YU5kdo;GbIn^P%c~ueTc>KN2IRdeQlu_2{DSOS8G;b7Z8dZ*= zX{NDgE{hq#O!8`LJ!{3cS{IQ8)!+7gx?m1RB+_YEi*q}VjQ;?nvnphFIH^$pJa9YG z*%(!|$6OpBE667Tppo{D4>T=It#m;t$plhImC19F?^K#ViNm1#$CK?tIt&77T)3t* zNgA*uvkz*r<$lhxAO%lco@jnR?)ntux|41Y1VPw$s_l0u;7Y(!YUtLTx|&`exOuNx zWch~P4@%P4c(Vk3)#kJ1NraM%(B_%j%m+Bny-3#%9ANyzp*5!Fji$9P&3_D*=@j|P zj28E*m)cUiYO6oayo`z>)ORp)aM>8QFukK!V-ipVcLFrc~b#q9&>Rom# z8BAx^p|{#2LDh4`O4v!J>{yM-Vz@Y_Oo1(efx^B)rMR80Xh$gXqltkfhDAfCff%?} z`G9l9C30N#EJ#)~MvPz|;qy>JK6OGzoC>y20-9wd%hB%50lm6(fZ;Q$1e$J2;?P&r30LLoP$L3&owaR|X8YIb$$!QrMm2#4N zBM;!s7seJhWIx#wdE#Nz6$Ik5)0y1rrS8+_^E}2oOY8k%H5*GCi8TKJx@J|#9<63Rkv-z3f-IP2L|obWzjmQ21?g zWq1I|7~+etwt{$bBJ%A|Ow^%1Vqz_F|cJOVxgpuG7_CGZTLdLAnt z^>(U})aR{L+6TBu3u&-tA1R|s7`ME~OkmPZ+lMvLg`nki)FaS}udl`ip>PNHv(0PG zv6PnR-PYs(0E&LdoS&3+`cxV^6XENzw}MF=(Y?!xvPapvmxx+lwfPYc>bS|`xf^*e zO{2mfF^ukaWB042tE(P;Htlv|ol+eZJG-4mH#Ucq0dfs_KBsA^{6X;${>_G8vq>+@ zo{_JArANYvZ=yYKNAT+SHS9&a9$)}0vbS^VT|LeFTwldA%1x>q1b&3}toa;uG*eR1 z?p*OUtsFWf$J_30pxqe=tiTh;YWZ8lNY@&XlHDEDjBq%roA*|x^fYa=rtm`eZ%8)! zmbD!6-N%q&^2=xN#ZThxPe|7FD6h1gF5(H5N)lt*2>Mh~sc5w=P3Indrf!9EE|+0( zZw8!`8Kf_{$4t|rn^lGci6V(2AHYWxr8m&$p+%1oSNh07@kskZ#^qCj+nJ#66nP7oEnlc zC7l_t06kAYDf0Ifth6l6c^j8hz(K&<+|_S9uOf+QxrZa_M3X!9VtL}G3U=jzAB9U@ zg*d391{~&s-7(smMp2900Lk2HIh%KWXpXuSdl7EjLC;D_u2o~&0VCRpF2=Fba;jWs zGpEQ2=~>#_j#s(N5Hd1& z1Db2LTj}Xmw=L>1P|Y$086f0V>c@adJDBy&6ET#vvnk(5=|7h=d##G|$3LAxZw0#` z`x)5Z-E+zMP@UF=Mt0bOQHEgIR~T73YHr$b^ zT?EP8dU{osjjk;&BV{=w8Kq>k4a@pv&)6;Cjqp{5(tir-h?m{9(dRf@gJq}z?k#e|;; zd}Y&co*q9s^#1^iE5G(?(unFJw?Fvlts%CCb+$vkEr8#%j*iSTh%L^4;3BpDCn$f} zx;%0|!3iJt$fwl7r^|Dl@j&1EDQSduvyQuc1q8pd)q;?JW!n;`L6cb*o(Q_uJ|JHBKT4fdt<hQ^H zB(_qk3y6V7P%=RkOGg{DlOB0edW=?1FLkkZxmQR4Y7L&_2ily(jdgnCHJa6G7Q4B6 z{{S9zPtbJDS+wOlQVoT=UCD#T(y_G_K3We$O=>A>$lXcOh|U0Rw7L(?73euQALCV~ zrCD8n zPq^Z*W9wP$ZX~$-ObA^upP1kpg5~aGU@nlfZP^DE*VjYOl(g9A{9~ltYg$5DSUN_C z+ndbpJu3@AX7GKi*Z|H(c&Vtakx-LQQzusOHnn)mJ;d0zQ=cggH&tt|9^Fb|5$Upn z2^&Xh#%}7*V@h#XAn|vIC-FG97dECb4hST3So>9Wka5SQMBUP{)lMlQXr*|_D2T`0 zRiwOZ;i19y;-h7uQib*x?NOHb^1jsYu}zMLKJ_}qYOm4o#L5$_kITalprVNv2(-2FVpI+|AvxlQv1k2zPr^X^Qq6 zb1ul(1JG7q+F7t4?^QtrV|_`Hj=)%z_o~-2f}@k4=S^-l`V~Xq=QSe9o-C8vth&Wi5>@Ig%1&b<1b+6@Dq! zb|pbmp5qk~jrBRErF)Fk?cQhric!5mJAb`gxn~EGQIz%cs)oa-Jq<{86`4dg>Ifqq za2O(-XxmzhDnTPOE){TB8KjX3^(0Jg3_m)ROE@Y|7^KUN#8O7z z+2$fo2Cd5EOKJIiDwe}nxuPv6ws+JCPbO(p4^TQ+X|CuI+W419u@^1n>HuZ1IHf3e z9AvtcH7^QW+-p}lKAkE_CA#^5zfvo<_%Ab$33#T_z$~%4x7P-?geVa~Sz1xbmkwNh_i~h}8J9Ym6MuYiQ-;c+e`!>V}{R83Y{_P9mXFK=| z4OWE}oAaat3I{wpYc%^xp|JZm2A1;E1ka%#VYSIYgV^+g}WYUlM8e7(%w zwXqM0a|i89rJlZQSRULO-2JT)zu^tAk$DSnGBfn0B$RbDo%y5ab=ta#{s=aB{{V?- zKltr){{XVXt9&n1R@@S2U#I!aLim`hlpXiE#C$8$ZLhvOc(T(_fhUa#aIeS&lTH1j zygy;0YFa?kAxl)eZH^ECI@6cC8zSkqQl*T45&RmH!ul}Rp}lEf*csz)y!uy|*l4=m zv332Urs=awWO_;mAB9&WT)9zjs+#7JKCc@_$sCGN)bgZYt)sUQ!h2w1x$Q)> zU>0$=(z@=BGKIz6ybzM3B=!T7T(#pQ_j`<6qwnm!WpEtLk~TVGS(QRSWnw#l=dtwL?t=j_D{{t6C8 z-^Y(b^n0wM=b>R!Vlq3O=8xz*ORX}J zb!CHe_^gc$!}B|>VVIB6>fUZ4JFUOsukCYl+zq<=)b#}t9n%Ny7{V5W zKGvAw3kMI{9QWzan52{AJS6|zJSG4Kv5)7Yo?OeE<6Q39RA-meV`Mloby*Txiser> zrsHUIaygaG+DJzzXmBFQ&vc~ z09`MnMgqAv;W7fJq=Sde!DV=n7jqf&(LnjoT=Hg1%P8u--Z~1b?T1&%lb5EH1Q(Iv zPbOs%yEG{5bj`XxxW*rI#qwGTs+xCsp9%8G&4$1F<>Y;9k@e@xNsrs^fv-{=Fzk>x z#{&=x6XboMt{2_s8Btm{PFBh!^x#@tM115KeCY}vOzvyHBo&4RjmKEzdD zsLIHZlq7<*W%#cr=}Y2MXFjQN3~*nc$K;S`x;)%gG-ygZQQJmotf$nox(KB$*t}CV zFWl7->@i5(<3L0UR@V9Bf2ufg%fsq!?R+d*Li>q?`C0PDi-wp%XT39XVRis+fP6^qNU6I1qeNbePqBr zzuWVcU&DDN>`Oo<^jKr9vxG4FEh2G1M&%E{BAyMssdbZU>xNBtliniZ-nI=vakp%s z1N?3?{f$~&ZiVK+L?1ilUMUC=pvx-ExMX?t8^aH(5fB$>kqkEYPcM8mmk2ob$*DV= zNq>~8ti$_F{oZ1FuV=we8kLd2lK+%lerBn=YY}*M!0jh`Jr+1;{5ruQ4J*7_dmXiZ zu7^6??3)E@_d5S<*qk3pxrG}S_YT`8Y_t5}CmbW<+~^dTX0EoyrRw0^F+Whcy9F#K zOP>SZlfT&}zom>N0yYK;4g|_Hrd)?*%WGCt<$y{*58v@{Lhlo=Wg+^@xNPs6u1gmeu}ODGLa(Vl{{g@|5c|BPX<5Fl_Pu4y;tS!dR!CT&^fn4kh(G?^z2f$c zUy8JExlF`;R`1)v?3f29*YO;YAt1C1>Rdo@hr?QY(018b2 zF{U$12+c)T%lnO3&{{8ifwnb7()-;;-&eG<>OYEoaPiUn8 z6$~tt0G})tR9yd1wV<&6lePp(8~xi&#dFNWJSQ^sD`~6sViV@uQE%y2F>Ap|!ht~e zZl)bV86)5X9^={3#SuBS;NOJqx1vIuX2`RU_=4*N{{$ppiNS7~EL0P*+q ze*G3NX4BnL^=}c4B@4R_6iTl?9l6{fQ(1Scw6R$m4tq-a#5yi0L%naFGQnr`Phy&i zdn_Dm`^)cK06sq!nrY=?6QVHW-{>KzK569XmJrJ4g4ctZqOKnJ^e`?R&|st?qSl$k zv_Y3X9oZl0bOZ^Yc!hnfq2N42t{xb0`jswIox`7mrYm^2iY>V5TEA27IG=4OliRlA zVmlm~EB`*rm#%8hmU1W}(3Ob$y1RMpB3{H13Nd0mSH}Fuex^un940uT|4p01LYu zM=lz&rR11t3s_CVKK+MZJA2DSKGk{>3fsgtBmcAb=w0{G4_a8 z@zzw_Cmu)2#V#&i7bh{)9Mm$OHUxj@%CQGaO#e4fsZQNmbC1|`VXq@y5NW)@te?6w zNS0veMprYJyWKcs_qy6rQR)j7zLoWgSA>?^%fu&`bb;?#>v^K3TF#bj9+3Cdx%U>y z`?|Q*%*Ohdx-l|Ctdhan$L-)d{o0EE>|?}G7q9Qij}9~ZsOtVv{$Nm=y7=;KGL zMyP#nQ;NClvliRmEI(+CKA|>}kv>dV6}2SCK4QP5+SQt-+*`=G69%=AnRF(GXAr(a zh_L0R%jfvh^Ed7+k>9ZjTW}Beo$K+aP<97^U$2aw>^~{yhVPQj&oxb}tGT8y_(6%{ z4qOrh&twt>{-|U<5MIrK|AB0`^`55NgwsOy*e-9_3>Rt!N0=bgO>h!u!`nR-q1qa7 z78B6I#Th<9S2aQ5+mC#o7?OVa2cUfZ{qfux{92U)v7Kx3@fEiwStEUaLWkYJLMh}A z!1YDWHVEU>NFqs#<9%zKP-F?_4@{Rpba9RPiYocbSG_8p-Y1d$fW9(twwqebdsV(i zC)`iflfu`#+8sh3kuWdO!Wk zpR$z^a;^=YUbwAW+rFXY$&GuJ2J(hX7A(xPF<7(&U@o$JVk_1cnVBfnX-tl@!|u2J z4%5w6E_pDE+VHc#+AknYR)J{xYs7H0SLnSDIO(s_IlthoCvDkBb48_HJJX^`R{0J}-y z_QBdd4CVRybK5&cp|cnMoTV_g4U_I*I=`M0rNTjzS=8KOarW6LgXCeBSBAlS%LMxc zVJr_Kds4fwM|b1mlxB-L{O^G;zkfT&Oa|P{cPg!XS92l7Jt6S#4PKxY-FRLCy)q`8 z?l8?H^v#Vm2M2Yu^Yw-1cb(Yc4asUSI|vjXD&WR2!i%2R$R@U3G-O5 zlRsD7MeVb&Pn0HB7_i>Bz2viWWsX2yb1n9k3}QL=VKMdMQQ%WIRN!M13KDX=8Nvoy zF&=EoGMWO}fHuD(Ux}@m_Qw@oj*7<$T!ZM<<=Bg{;?6amo+@XjTftMJRE&GanHpNG4`X%u>_i8yYmZXrifj6S3|t;p)DDFHfW`DE|uQ7Rz%;OV6IqJGnrB8WYi z^4W^yh7F01xZ5w4B1NC_og8P0Rac3CHZ>`%4%$ZzID0fIt!Z zEAGYQThN@Ft{^uiafwybhKeMhwnNC!Z45p3A)dFUIGPt<=nhjvzG?$i=jUR7$y^O3 z&Cj;@-V=P$YUIEl2Ez{xoD-W0p!N_Z?&~nw znKqgI0T5rdppt>b^xo2zO34F9n>KOf~lm z{V{p-BuLMX?6h}o2|UKBV(fArV(m@VQUffK$}T0}RQ7;Ik{eeHM}{kI*jMsnib{D3ZZnURmIn8Nc}YP& z^ErK38Yd9to0~lPAW6D)1YCaN_^};dE5nnJM#oQx+ow@^d~UIl@txa%N_EO+dA*7g zuhrx9{mga`LyrowIXhD)ieRu8)Yq6{)Ikc6(QJ+5ARPwWtID zOBq4}@Ws~q(~dYx#&Lp;z@wZPZmYMkJpNZ#P&F6;)rglXBwba)#jlS5&6_hblPXhg z$i(yWXg08H2q-G`!fI+CknY#b{Z>FZe}<$nxxFFuDo&|E6T$gC%4cC=KRp>q5>cKd*Ee&V)Pe7L zu^|@N^D}4O+6O=S^FbHwpzVR z274VnMb$3fC?2Isn<8fGC|jL_q&8tY{2jLgDl7?vzPh!^2i&x;DXbjD#LMWa@smcr z(*a5O7>k~KO0TmU3U&0C70;{1u4G8pC9BJjk+J5c+e*BAm9Iey56jF(F((u)A2YzO z6tbYIM@+*Rqip?v8Iy2U=;+^vdCR30t32$fv*{6=u~)0;uGStm&By&=XSNmBBpI9p zc1?N%8oa@@m2@rs;S;Wri30@gVY&k=If-2%()TqAnEl^aug_-{%9clABD^}l;d;JM zqaXsm$)N;AL^W`B@K+*z^n(=bPTMD{y-Om5JAKUFRmogClY}9RutUaz6rEKzjp(%X zHhw&NvJlp!q4EMF0KXcLspNH-B#noT!A=?$PH)~!rh6BUm<}n)4gXY51O^MBB7&TZ zA;CaiMR(Ru9(7Zqf&PE1b4kbAJ8W!)@Z4 znX`LS6}B!LmP0jx`4ybpMs7C{BFc?~XQUN&K8rEe8J3CcAnKfK#Eu+l4J%r~%&c}X>Lk8eUj=zFPcJ|5?W!`QaT z+=#g~VSEcwElUS8T^SYF)4ye_>zXwD!p;H1C4-?OHFVG;kjNf79}#3V&olC2cw2kY zdazIoS(fLsFydvV-^b6;$$RX*_0Qg&a0h^JR|AQ>*G_ zM3UEk0BZVh=bEib@T>NpqB)XRn>4NUntS$5TU(r2`lNptDJ_dky~4Jmt}tD1pDW_L zjPi{Xxq~bDl-)FNqZYKlNgzwW)xK7~^)2x0>xPA|Lw|HoZEZn0xP~|u_ z6nh`Pp}VTtvvG}{a8r5I=6Wa0TabXmQvr7d#MX`WNN6F0GCg3KZ=D}i_F&D_ywHTH zCImNoX}VdaCI-AAy(AmKm70KyCK`tmq(2ta}b@v-*nW#y%r*&Mai6y_U*se&GhFt3;taXDMg zgL3*o#jYR7`1=m8D&;84<1?@P3G$g*%r2b2aW>%G*SQknZQKm7)OZsmZF23&6Ge)B z;M?p?s`EGr8N~gEm=BVA{6y2c=iUmx~ENyDvsmN~hq9cY_!T@Km zle8PFt^@=%r8-Ro6n+h}rCD@k&wdV%GgcM^+&4O;ojWDK+Q{U6V)J)pTdY^%o-cju z7;r>$jZ~k<8?Ja_3OkY%sH%PhY{NnO+vZ;%_WnYDjJTh)mFtr32J*nd9pE1%=-LhC zC(k|FZc}DUmV+~0BCONf517WBGy8Ea;T#CGV3fI&5!AWc$tJY1A6_xC^W$Ol!Eywz zwlQ{7l@*<5Ly!$$$ITb)iPZ&n^HW))q}gaE$lMI;{Az9qnZ*VtsY~EGs$`|>;8u-IAB_|z1l_C5=x7Z@#s`KV;5%!_)#O}V<()e2Ya)vsQirO-PjReRO7Nx3qV8V7dV#oXOa z_7ukh7yh3v;@3cA@zHcd7w*es>F34o=P>jej~2*{C9!$jIrDPcnmmd(8b=AmV(HpK z_>u*!LO(ff2c5v?dxL^|cAKMiw(f!R+k?u{c?&j=lC_I3kCEQrck?3Mn_Mum=c@r18!JGnGl%3AjC3E}O;0bQfM}IM;rg40Csa z-gakp)dv`v;<)UlExfZT3yozO&l04nO!4Jw@A0sjg*Ss*L@4xM?ZjrJA!7iv1^?9r z(z&6;++CEsPJFd1%ZqsS4JqL(fo;@Yibz+o8g#5B*o`bZK)!r**fg^-W8j^%!JkJv z%cu>po|$iLuEu!fgnJda51B3mHi>h<-B|le24fvYdgTV9Pixoqo78}ZPR>t)9d=ye z$c}O$23s`!o@O8W9XWh_cgZQ@Dp=5{hns|s^zTOYOu@%GgGUUrgvqPVD5ABU$Y_oyoJ(ix#TQa=E*fq&hwFg6A{f6ul#~MzLbwlxXN~Jw3C{ zy_WCpDF^DD+A$W-*~j4odgYa-a?|}X@fDCvkV1bjDhjAmP`sn;02Def8GQ+M0eb<%tZx zE;i3^qqt9F8uHCN1Rr?}Z-kV6bcl`<9^<@KhrQKXm zv3Koudhp~ImcpK?^G+_Vg@TML1H~fwHEbmN6HxyLTo=M@tnSsW7@nba@WoEE!;mZw zZC&&$pFQ1Lg^vH{kj6Wu*iGEII+!Z`4|)kmmgGfh`U=?i9*kPAYFBqZD}geM5S<)# z*i}4<-7F}6=N2<;Z-?jz%qX=1Cj1&RIp#&I z&bH3k%I#B=zq*L4M7770G!aiVX5fJ_;$R!Ve_QDPZ%5b`jezvQwmJ!bE5T(@w?Iu5 z;yE`CQ}0J7+woWbt5}KYTf%cCNhYM^A;rlBgPA_Ya)wcgUd~_G2CHe9#G^TAKEe8> zS=B2i8o#*#F_cCs&vI$`b(Nd$`Oh-w zw$!JBh}D=L)dpOSG7^2R8Rv{(P**$GCRASr>xZ%0!2Xz7!F?ceI7`chl5<;%_AA4% zW>OUL2~SU=3=9`(Vgj z)H5$?LHsrnDQ?%BwK?;}Hcivbjh-qm0dA2|scRDdC@h->S$3=#S_`jC7f(VtocUyz zk#x1PL3`9-nI`=p%FdFw;1!f>%O0G+sxBYiJI}rs4)>J2RE&|UiKiRF_Vu?`^+>Db zt19&=;Yl8bU{G?#!IF z!7m*#zb71(1p4ryDzA$lnPVi&r`oG8kUm&HqHipa;`3r>i_(lKLuS+Nv1J5O^S^jd zTi2DcuaC&*<%a8`vjXs@6IE>qdtznuau;zD3BO>l4coL88tPkPVw!T$I%rweh|hiD zd?raQeHCd*;A-o6(j`inw5`25j3Qg;mnq$=%)vSOa9nMCGZ}n=?xw}OlYXg`({H3poO-bv+?SSOBUz9g z8Ch8GFLa@anWm&Vw#WkdHr5XI=txWHz4g>e2Fy_qUYUg9Z&|eNE3C(kGLU4q z55e;8#^@A@{V%I6{6Pp^YV}NU#n~9LYuh`_)xwK_-ZA%2-MaCZ_DPkM$gFVBtI*yy zz@cmusm~KY`Fhz*KF_f2;RU&2rib8(uD&p{e4Lycv>W!J zm43E|#G_G`XwrTwIHHy@tg+olI?jK~=Zn_jWkm_|Yd+8d?|6{yj<#kYOd-j4evAi> zc@Jl4%AYmj*8$&C>wQhTyS~m4KGV6m@oE0Ca-pS@Y9lFlMhR$Ca(RN|h2yJTl6?#o z1SYP_9^%1ts{6`FeUVLMYuLIZu*ps+oo@Azf7)1)@OkSFk?uvp3l5DrDyEa$;mEPO zr7CJAqb-2Hpu91*=59JCb(fSWueMCzVZZ4!ISlg21~mO+iuLc4t^X+niJlx2VzvCX zj;fb!g&Jm(YvRPsHU%)_#AY~}qzbL8*Y~59%ofM+Q$IlUcxzRPI zvo5dT|H(cDbX{sO&swlVSTTo#%WIL)U~Ak97MztZS5MQT7(HZm{C5mh70&4k{L5sE1#m zZJeolrj)m~lb}Z4X7gYu?kc^bRtM3J%o=(_$R7}tswFjRnmL&btH;$V&}PO2QK~e?+H^)>*Nj z4f;9x@0NSS!#(15ZFn^!(crMUI^scC!JCj-J9+mqFxr`UgR?!6O>*OF&u=vyNgxzs zxOO514M^ORq(`n{q>$(vD=(!RWHYCIz@bu`GvNjF7Nift#9*15n(S&`o48e*#e<{va98o15>l%xNYOGxy;x1A%kM zmstNd3s{6~f1MNR8AR~cd7;6*g9UCyQ@7{$zqX^r#?kk$+yA7Yz?J{f187w~UT`=4 zn|#4C0$=oB|6_nx{D0M!RhEz<`%8()G|XMd*qPY@09Xj9f0H9v6aHOqAhZ8nZy<~Q zRc|06{wdQ+$bZTmYw@r1gUkP=2VkxJ0YHZbcmWEaAU*;h(IKGFA^xlZ!T<PAl-=E`J6fioh2P1M#==cizFxY z_4j??<==Pz%TDlEgMfm9goc8Ffd-Ed2>-t{6&f7|<0G3WET)Pv9JwOUMTV99%qn0xD`6S~_}8E^Z!PK7R4f5|UEVGO}u4)HO74+0cu8wim*wxX8N_5~H%kopD8}}1ZYf$AHbt09zEUN_{e(<;_gT== zp+BpZ2G9m{X%+G>G6$iw)yd5@oVzDI_BO&rl}5koR!@c?!fiY6I}ghX#sz~Ziw zKJTZ9eTNc8;@3vDUNT7DVbi&~7E^pEc{!IrGWxxNu()jt{ln570V1M63YqV;Dx!FA z9I9ELfs<1Qm?U_u7kM4R^Xk^R`!r?c0csrgyb6ujqiPipe= z&&*ACxeooJ4YpNJ1GbZcL{0e;#D4&jwuKB`Xd7u$*w-urx*M{`z+wNIN|_!LsY&T0 zt%ql~!*2`?SHA{gt?f+`zpNp$B1{&TmTkWA2PW-P`F@aCJsVtftl7PWeqfD83Lon> zmxK^@3;8fYQ4fVzgKR4`^D0%k97}WKt`RwrQ-q0U8sB^4Qj)Xv;ySP4uw+g#`I;Kz z6tRAxK`RcmGzWKNGp`sY-Kq>nN~ia5_nfTNJnSM*DK&+%BjN4@Kw?J~&e+wwoi##`<73%&Q9vax380ntp&6nOYQ>oR&mT`ar%7?_1A`u+c{}BZ6l6DH0hQa>|QkF zUA=+LTC3a*GyoC^;n`Z1`Zi7R{Uj+OyA*7u`8WQ6Fm+cKD*Lz;L21CM5|6$ zGoo4Y4xYEaQs6uEPeV-mqWYYrC~N3u^}2T5GSFj1vVH!hMFbROdi22N0yWe7?Eo}M z-SW~VFVZ8HF6@`b9_pMalDjBuVen1h1ceGTF2kFbTl|gf_IkWr3E7dqwvm((T{*WO zuI8wZ0vk5Rx9o+yVn+Razk4~8?XHWUF>EETqe;nzxA?70>;(&iyqSVd5FsrR?ejDY|O zvLMkxcOi~z=B&bF7j2jX-L|-4A_Rz=V7yV{2vo&a55Ah&eVwdRYH#Q{+_tCJJqaIGIL)jNdtf9sR_6}-#2|t9=;~-8!)+(MgSI6G)+9mgs zbVZUET0oMyu=76oE`H81(rAgH7X59R6TtW0;_U#c?~Hk#d2NgDuR<9h-9NvpDZ1mV zB6Yj>XVGk3Z04;)pW~4DUHaCgTZn(cH^q6(0!qfy1k}d@+&N6aN1oVI)RL>56#(y zSaAWN!9nmRgNl-pGQJ_xTU?xT-NF@&R*j!3u^O+GHle{@#c;uE6gjRFI`!ANpfu1# zKivCF#sL%KmUug@vRF7J5QX0~vAZR^(SvY#G=K%IM*IO8!uhtE0G543o3{v)cHSIOIuN}=#$|Hv^2XwjzGzc4sE&3{9Fotel>#?>_OZg9SG{xwLR*ROe z-m7>fP*MWPzXe#Go0-2}$8_0m8KTR6-I--Z0f(V8aSuPU!8Y$%Fy)kwn!$tGi6At9 zV0x-+n2H1J!k?g@9{27>{ zwzlNm*N*hks@@aYvu6u&&gC$wczs)UBNeJoXw~XqhYD0~?Dn!d zJEl$#E^=(U6YLzUBv~}PRfcOm#ZRU$vPVyrWL*8k-{te@Tt_Abv2{+oT`V(xI-KW^ z*VSVioNVD>U{HDxy6AfF2dMKWtin|1ndY-42)0+9Z51Fm6~{rveMw(p2FYA}p&i=t zf2x^6IepehBjnl)Mkz9+$~#bB2E3nSP6|#2(zkQA(G6bQGP4Z8*Ep24hsw;PHoaEb z$3m-214@uy?%n`koD6#xRV3VG-ST&M`v~FdEn9=YkCmQ}xj(z|Nlur5A1D{N0R8XY zqx(>g%y7rrJU%TGuixy)Mx72H!Oze)8~^G~_9yaAGx%*+g*Ub z=LLl(MSEu2wx`^*%*u-QFXOfuEvYHpbmz<2x@q0+Tu90KqWeQ*TCLKi(+|V@D)v^2 ze)U~}VR;`abgaypJ}f+B!%MF_AZ)x2fd&Kk1yA;>>!j*#gy4PlB={dgto#s1^zj&nnUr)#^u`3i z(<-jMAGal#XO2bCiUY3PgwPH;PqlwTW12X?Jt8-CR8z@xYxWM`I| zPtHSCQ7N`pnG#C%#>wc^Z2^kXEh~!g+;EE1`)x^96J8GIFa!{}z0u1BB9F@1>os<$b_^yaTpTmK-9-YFp&bvoiSki)_H@e>8@RL#k8qMdNbIxg|4tZ%QR3ds` z!uca3R;`i|j20#H;tEg@Rmlgzqo+tS+rGbI>sm!}^L8?-%4!^b-Mu39T2zyW&I%$~ z1V1QAy5tW4hxh%<9{_D)@&`Xskid!aXxd7yUI+lPYNRXm4V6uB~yzs?mk7~^|JCjKQ2Z*OBLlENX`b0nd2N2 zBB?=$uf@Ixl$nr$`q;NVglOq}WWx}#TFlUUNO@xuOA&E{4wNmYDIm77h9(sE4sTk~ zcfEUe)AFf_-+<~J-}z#VtI;&K3tb5s_zE%+I0rZ{ee(i4YPCgmlLl@_7h@{H%&S6b zXd$8jhBxt^WJc~ExcoZxX4Dsdjw*Ytsz)|dh_#=)EB7&5I2;nQ$52k0YG#$62yG(F>UU291pen*92mLeoh5V;0WL z#XukWN47DxWOUOtic7c(wht&f&UrXAG|TF= zD_3i!X5mCDQ{{U7&m9&Oz@Z%EXaSl&JZbQDIvJMo`ncV~U7lq%`1ts96x)s1K zbqK%K>|-VB&5?5#k0|&^&Q6`QZ}BX8x>x|(N|W%&iVgr-V&oc!Ng7Vp+~J527s zbk_s<&K9?w-)kCLqW1hR1T?X$kME_+Q;-JnHn^HaD5Z;`g8)Qsd_=F`dQgHabO(!V zMqRn9EU~O~{VOTpe<}xg?3JQQY|Buw1ne@B6NbM;5%qc zZYGJFiFrT&n6P5cWWNOSA!06FBT2w~JY^ohdjzQUVk2XgoEF`)ge_G(o1rqY*rmra z{ocb>x_cm8%OHT?)5lO^+dgl489cJ*x|;4CIJ+V}MF|i%EW0gQO;1KyA%x^h4<83r#*gfsxJT_%r1R)<)X;R z=qD(Ok4BJ}jq_F%D6#w|eAq31qGX&Mg`A`S(PYo;Fte9`-J@aoV$RrGu9}%CH}f3YT57Q29dMa}PS&>nys+_6yD4RkQ$cEQwLE5mvVjfh z=Mqj=65VB9)-52!`RD^lto7Ag{|wl%x}R#jxH?2LDgYsl_xYen_PI&Xk{BK451?NV zM`X&;-QRWy8%KGh>>c`paISal_p9HAvleFcAIb+{#U=ZAUX}CvLtLMgxAK0R2x#pO zMeg9Mv6M|X@IZ80tq8ol%xz!gHF>kYUKOPxA8>4W>?Z7TaZBshA( zsz-z_u>_tl{Q;lZx^jMMeDRyk9{{ea&dmy9-admPvR8V)Vh@K^1{IZta2H#LaC^3J zCthoy@3PN}Ws&c;u_wo`&-Wp6naiJ!Of)72fKC%Z0ouYSu41tbo(k)rQpH$@2Mi|+ z7$@hqHDB0wE1OgF9tyaWa=DM-7`K!1UoTwhQA%Q6^b!Q+^bkg@ycG)+3=dcV*qKqK z9SvGrh)DMg3Q#Koh* zaL+ns0A~|zn&~5JZ<%Dd8M_hi7aJa>+4V4yM^$tNf>BYSg?r_4i+8(4FTc92Lvn>R3tQ9EId41 zEF2sHVk%MsLJA@r95O~S3ThfUdOCbkCRQd|Rw`OLS_n8eI0SeEOauf>S^^vb+W&U> z|E;F~U$dG%m;UG9+v)l8$D;FRgYYa#E!iv60hW-Ja$dhyrkoRU?5k9$x;Ri|zRlVE z7F#x6?^_-d1jb3aX?VvSxxIaHxWWPMwbUjpM$%tMvWIt`J zSz|lATO+RWJ-&=uMrzD!i-|^E#)h}HeEpibXM3gD7(WQHKpB_q*Jh3MSvdo7R?Xz#xt@|9$5))1Iqa=%C1y}@qc6}*qL zjr9;WL%_u5(? z$jNA@uygj3iY_bBqzNcOx}taT@vRbG{o0RJNcL?a*06c5yBp!B+ZcT{7EVOJG3ryG zkC4}N*>E)!mf0|1uVXeL4eMjq#dr?8XK{Z(k%j^?=%{`yn#`rxkj0qX)V44lM<1eh zL|e1vr>b^z-v>2EI(X#KcXWlRr`Wp0=Yj_3Gcoqq_t@i7^vu&1F23$CL^RG3+!ffd z*K?-kbL}_eGuVu~c1?Gi!VHhs|6?nyY%wS9E+)eWZYi0b2E<~!Cx-_$NC=_N|m3w==FeM+J7 z*BD}nv3E2}rrFeM%qsG}9UN?1|h;&2?&?Jk89ajK+^L1V5l zLT`Nq27fC~0;1f&wOCHL>F16i9zsCaM7 znX*>e90lf*1dk;ZSb=ugz7-^5H!PEqyyxm3-tmb$oZM?2Dhg!W=XTqn5$`S54kqZq z@nO%4o}Gxx6;3}IXxA|Z1&dd+^j;RKqGgan$UKcZh!cIfEUFNnYI#_$gVjBKF}-yT zH9^*`K2f@HEa~4pu@||bTIx+Hg3{V+^YQPrn({cGZH1l59}|%O#Ki;C)>Ug;G*#SRP#( zo)UtwyM2oH$K@B|HeL8Jzp&pLVKY+`P!i6R9uvPwc%YuYs2MQlf3i#I2G*%VfpmUT zak=VBUt?NE$n^AsAIuY=1-LeX#TXUz@izsKz6k<+VI-hcH^*jc6wm( zaQ!$%wnSQ0?!{-`9|wlL*vsoz&^yT9h%;lLbr{OGMoMzIIXu?OImb4Uarha?%>Ln6 z=DQDOoJCdE9g|$)m#IdhAm`1p-*6JA$7<9AYhqRP6uiUE$D$<0qr4=|mu|94F4AUb zL75J|yER&>+V#DWB&lhg*^;A};s{L@yP@lv96(|Y< zIdHKwxX;yXi>yW3bz>+#3v%8|XM>sX8*H+bGA~74D?lkmxHqfbdu-x)Ps%KEb2M;b zD3g-Q3eaVt#Gd8QK18Ik&SJ8o)=KXXgT|^zEE^-aaZf6wE$DLu9n(T9JPHpNR@!E% zt4^+C1T#!T^%D2*K}zX@^9h0B)w}l!s}XX;2D)|GoF$8qNrlak3Ribmn75wX*g|g; zl&*~6LF1$8O^K=#4DVnEIF*$Oh3{eQ?Q3E~u~pU&m~R=X5rp3PWch)5OLRN5#SP!G zdGWeG$AgUzvO^$2%;+z_e&fgORg4n3>gzAu@&up@SrIOX7}iiNyIeT&+$(u?#1gam zpDqgFo;Znb#rDv@xQr9*aKs(f@Vm$?+cT}KTfeh5fKiDx@Mik;$!=c1y{a&$Ih`V( zB_UVy2%xmrWNUQfH3mp=?40}bZ4Q1L6mpn8VkgScBxqT);O-zaoiEcSRA?3m;gBfO(f&}#CWJu_SdqfARNi;W2!W|PAeYqyoU>lu_*)+|&>Z*y$1 z^FB-37&bG6~iQV{g249Y&PT!uSy#pwt*DP zLXj}r)*D?33^L)Dh#lLnF2dfJXs$7l=dhotv{}P|VC;DfYhboH6SJ;uuB3|yG*aPt zwyBnGzf0qj*{AHg7|KMfd*3mg7|FZUBM zNGt)yCZw99&6@Yuh&|>u3}EYOJM+v)^p8D=E)~Zb`!4 zu7yE+NLJEg_wifWdb7cGZdU(s0JY;egKq4aw!95DoCFQrE8Roe4wLzXJz}Gq+D>D&O=OwNTt`_85_LxSb0go4rhe&1;UEi=chcsBR(2#)CqMW>-^QPt7 zsW1-fhGU+FQ;j}`%zfq>7Q%4c?^wp^zHdUa)L(j~*Au(+ZRn}>MvpN^F3Pe`-X($Z z4U{Pf%}YiEblA}d-nG^;hN?y3iw>XXDCnl3NERIX&+nT%TGKS6ZrCq1DuRu`0c7gJoXVg>;6h2-QjmNmDZsWDs z*&)~gKQQfzuIaerwNgS+NU>%x=W*h+^qZ@AW1TL3WM;~YRU#!GsNe9mv1M&^*4L`7 zY8!lKztX#He^Qb3d1jERz*qNifzC1Bw9?lmQf-{dYPR-%9k)o?S;lvQJJ&sPI$Up? zZVPnBK~mc2Qg+b$DvH#QqJRo00HS~jC;*~>3Mc@k84zbI1psDWY9PYUQaS@yVouCsH~=vU214xnZ$3#M`2dou0@uH&)Us|%M5U{jD=}^D}2qH zNmZOXCLb%c=2ZKDV=a#L@_&e6yVA8eqrHg6QOf0qT8ZAk z-%}Rrbg8*jsUv0*kj>3~nYp~z7Nb+nz3S;r9B*+}}#yZk= z=r8oq0A*C3wWK4di;C)ZIzFQ?NixOab=pX5G=ECuJWs60 z84%lD7nh%#sl|0R`E(&Aa;Jo~8DY2<+ZC0$1djE4#E&Jl{`n$U_YMH8oww>}n|_Bg zegsOACn3*%)lJz$5HXSMS;-Z$5gkLbbu_V0ERP|XvI5~jsTQn9BrZ-V-XCE4#8O5| zlsU$6xcX5|+E-yVwmWTWTTo?(%V^!>1Rr|hrMXifEAqcX?^?~=NqG&oU?m;%-mEYS zr)5S+WBltjkw|8<8YB=%y1b3;s4u$QOKDUxujjEZb8<-n#EA+t|(Tt#_2!VcmaI47k;BxWP~=Qt;f9x7J4 z4chl$=p6wcfz4Kc7IOT2qv=M(UgEJVNC%%vtT}uTaZMrgCWDES`=_oePQo~(xWw4< z$5MM$MRX;$jjQc4-qb;;*v4d7+b*ZD^{cn~?XA%`KOu|-^ix;1jfuW(jEzD|b8I}f z+~5LMv)JcIc~G2(#sx*XgR%D26}cfr02ELGMF12~0Yv~5PytnMtwqF46}B-_HwtOk z4g*lr&6H+2Ci7IDNIfghFYlqVxoq4tfF44Qp4D@`jH4u(*XW)mL1N3Ms~};^5n5Lk zmlsj3%SzA3EA2`Z8dfTBy1F>&BR0CS%as|=Ue(a}a^Wvpd13-7v$b6GG}~gPw6WFL zStNo;uWu2zFWp=U;qoL`u-q~FvTZ857Ve?{$zJq!rBf`HBqtgs=u=50*$UEoS zpz(pyJW~b5*V&)WQ^RqsRzUnC*|2C1eLVkc?)pprtDsw+Rf2w;RD`>Pe`i zxrJlcvW>^RX5z-`SsAyp22H9nj`dpBZ2a7Rp3%g8y2BGc{Xl@)+do=G*` zJi2C|p}-(2X2u=TWipuc~f$%wYRY$^%CW4c zY)a&xrA(dGkXuqyh1Q=x>ZL_ zgrlye*0XmE>=7hV%G?lpRy>jJV#;?=^e*e66mIDGC`jpw&_W*3(>)GoCwF1mn}oNs z$jI4>k4w4{>kt44CxUUs3rBIv=(VXG`lOMtJC%tU^rlH9GO~?@cAOe+R~p|?mIjJU z5rsUA)diUmnF5jl>M9C)GB;A;n`>?BOfsR2A>@NfFH$9Op^C;-b~zPTyEK``)%NdJ zgM9~v+;TdKYN{Cu^zWZa3cZ!fHtvh^ZRGc>x@F8Z*M!WWl#RIernKCa&WlXct)qL( zY1k^rzbB_k=3yxs+(j_wB&i;iZCz+gZ+ny`VtD@V0OqXN$R5Ibes_K0QLU_j?tR5Y zbdnTM0Yv~5PytmhZcVlcQLv7=rj^}<-H?_SCUhIQBxbHrC1waHq%fvP+ef*f0mJL= z*0lPb|$oL|43Tc)YBW2et~T(GqjV;wtBYp-a0MmF_&HjAsZkNvf8bgmt;zCV>^D zl-Co@x<&w@V~l%LTDF&Of1^nf$c3Fa1%c+NHqkO`W_+{aoi=?lS9r|qtgLXyip+;z zzP5}YxqZ92$9m3EX^^9(g}2liWROY&7CGjjx3ohzkYJK?u$ttPcQ#kGnY1UCU~TSV zQ5hTr=M_e6E+m&LlNtUj@+or~UfY%KY^~;aV}f(L_}dt$CA8Dl=tm$8!OJR;BPW>3T$Wu-_mhL2wU3dkWK+n^rsO;&T)1xR=V3 zRPz*JSaX`7A-IvLzG;5(%plOwktWB zI2ofG{f$&hi5KrE4W345Rt`((%{y!1DFe$2N1@JZMrKKE%c%K>298vX9Evc2jz}5r z%}G3-U|qT6ueBz{(!{oCo=af-!;{57aKK^6#}u^{(jt+;9UJg8^EOdociI~O15ePK z>#46}q%_yitZ}rA2P6)Ly`RIL8;-+Ap5sw<^Cd&H9u03!Jy5S^W8B?X>X(q|*M49! z$K|L|oupS2;w=vELM|oR>%S<&xT~uRYK-A2N==-+gi6KF-!)Y&on=0G;P(~D%#pKt zmTluzMlNzgWM|Tx@(JZ3p9%-dLXcgfK4pKjmB4RIQp;?_g1~L+PHCrMx1q19+eQ7I z6tONHKmhvIbVQ;K#;(HKVo4+nxgC3oMEPBeXOc04OVDdqar&-)#cd)#AY)-2YOY-! zCM&hNgPLnlmEMK>%cPq1ShgIfImcRa#Md_vv_lNc0i?kZ>K9V|qst&4EaN%qDy&Y8 zE8_#X6fnDNe)3KW0H=;Bxs75CBXk3ytkTy)oVpPgEUdwL4DnT2&fJdmcNwFjvX^M2 zRUnR}b*j^CisnV_noE0qFgvR9MfjllL-z9Q`6R~a8}$|8Ci=Gaoatr6E;|2 z9yq4E3FwB%!<=(Y(&Pr=!6X8-U$GKxqwd-(qymZnD4+t6&;?m;lrT`klYnYF*u~YP ziz1w;$YEBrcPHXa89D{ThUP;ng^dQ?ze=+vr?&SSP6){QRn1-8l=)SS2(<`aGR#7O z)}*z%j^q;&1xG?V)^0Y`yP`=YNdExV9joekS0Unm68)WuS^n@2>S@Jy6rHv(wasGP z;J1?h00|6$072v*dgDB9ntT=_;@0(Ko%k!9)T*!i<{Lmi2#(R`*kgwM%yLlIRrwP9Rp4?V_?Y5!h!k0Hv#0dFgc@Ofghc|5yH3hEB zs)kSD@a6{q@{HkT_NET=r3xrK=@#M)$-QbElG;a8+PNI-FKzy}Hd8 znqhU=aKMVu^1X>yTA~R;d99EFaPl9Nw1XnOO4r2@YEi{`s9na9 zM!SrA``2ALrmT#md)Vf+U4F$pUb=;?%wjxegTOV9;=dP1tlUW%kn98GV7yT$WoFGG z*)7g=I>er09qsNaAG9%T&Cl1haLw1!v!+|pp-Gu!^#`Z5 zG|91M@toNsWp^RyJJTZ{Ylww!nFo5P=v&&zx32{j)-sF@(d{@G=Ben?IK4s~oxI~U zH`c)Apj=30l1D!&Eu5OF;lbR$H`1wD3tJLHCIL7hy~RrIvPMz59`&D6V|BQUQYgp- ze8U;f6-Q`7P@MOtv3E=-mvaFaAqR2mNpM-@QcoDqHA!eou={CAV7Ujq{pavn_e7jTV?mV{;||eb~UQpA7_#!_91l83a~x+Qy0Nb4KkVyt8pD z5eXeddEB}&k5OdW<~KV~k;P1V7v^wYH_|U5yL(8bYk-4s0PwZvZzD+inTu?0GupDL zHkN~eO}1}okX&EH-(^#pr6 zW{zN_JXq)JT&1Cw-1+Sh5vjo>arjlr*RZr|Y3w@rDpakOAHgU zpONybO>44IS9usb)Hsdut2Qd#F(FA(r`N4BDyxB0w!+bq*hgz6${Cse0Noucq_=A| zi!i8#dy~|fN-oze$t@7Ew)A4`3U(%`-`OmeQL-UCdj7Pc?IS;ko?FRQ86xv!Li}ga zwl?T_i_T}goM2_!7_OBrWua@AsxZ7=9B|%-wS-3*9h;?VXj*&fS~^acNiugS=rLK; zv)He)bT%pFKqhqtWEcyc4LS8&c!7uP>Y=$~nsG`bDL#e{r*UUELvDw2ADca^HZcU3 z3YY+Q(~=o5K;YLs*hTANwg@N78(w2GOV9qjqeJSWevv(r4VkBIym+tQwfirX4G3}x-^&=LR}i1n%{GRGMJp7b^rY3eNBE^*1Fv}G3=Z@Z65 z5SrAxZyL^$oP!$j!@X6t0t<;7J5a7v)vI3Pc56c0kF;p}U*_`P`PF?qM-|5886i&< z4@M}igC5pKSsXAde}=9lo4S7J0HJVCdd<`|aK6Pkqx(7n`%53;?N#A}%-qPqR37x> zD{4basN3JZ5U3dLI#qdB3=VUVRHTAx+6+PoI6W!yFaQb@u@6K#)B!la>}ZqcPN#xt zt8Nk8g*i-~l^~c70*nf#`vR7_jd?&kVEri;Mt4Tza5)rQhOfEzwH4F>MF12~0Zm{C zAorjSV^z1339!KVWlj%jp`z;XX^`GM?7}!xo$hcebh*)<%<+$i9~N#euVuE;B8}qv ztvhrl+MV#b#_`K_{hzInW+yPo*bhp&(TpsU)XI%XIde;6*mUWkyM{>t0uuPZ@9$k6 zm|&2|(XiMsTOBJ7mpAHb6i1J40Z&S)do`*lhDlO4U_zX7Ds^QZP4OGYVj+W&Ap(K<0ucd38JsUlQM%!PQ~8{&2IwFHPg6F*hD8BpIY=wEl6lrNqEq- zi*lkUiR{Oru6V;!WRiDAx@bS}k!#Z}ni#GlgXeNSUB}kAkBA-w)HHh=dkd-ITWO}m zQ{w=(3Bj!+4t5iEi{^GbviD9~C3wjKL^xtkU!`M7xtINXxkP7dqmP305q(zKsIxKsPPnB*Nn3yR6z$fUF< zZ6$;e1Z0YA{LD^sS8cSo{KadFlwseVY07P`Sdx@hrj_lSA_V(1fS~Q3wN)IfMF8iu zLRQg+mnz;(y%`-yQUms0Glp<`9ltu8J@T1d;cg1Epnmv*>iVA~h6W&> zGJ1Q|amTh?%DY>j1Jb3jx6u-n3<~x?rDqt{HhuoMTpE<@O5BFxLmLH5kF8U8!m(!s ziRsdt1oa^j5HbZQXS~8~$zFg{Ng`#wriPZ1yd?x=;jz-SQqo8RGs%@5k7{ek)+@1{ zcXT2-w~d{blb(jV4-IOzO{Fcw3=QURVt$HiNVN1WbnP3mwySwH?8_v~jLLEHj@6C~ zl3Wy70MBn~&A!Axc?+lwd( zWLS^Nq#RYfQ2DOzL^U{8alr^rAN_i=x}!klRD&5A89ggE70|U&+d*ySigt53LKm)m zD3$CZDkIP5#YVjg(XC9IrdebxqaBE-t`$H6GrKiWS2^CU7 zw2x*tZiUS*AvCYIBMa86+@nAnap-?KQL=Uhx}uWH`>$N~sKas&PtvXE$R1==86$Vl z(^^f(_h7C$%_bH;{{W)91QbvKMF2J2CNtdPt6Vu&55^Sy$J&|`XB+V+#=2jJH1%m` zj$4M#*h6w_=06qqyHN2B%XzXqHitjx;PtM$apZ?HIcikqmn=?J;`S&LV*)Y!3rT0G zEQc^1vJT|(waq83*|#p2J&)ms#9y#Q9-FKnlguDTCVhQt)h;z_t9yo97{j4Z@8zBd zqR*o#P1{sItu;Gzo(Am0q4geyv|4N_MK#LxK5F=>@QYK^C2d1WnXar?BgsE}3i1@Syhc7; zL~S@AkGqP>w35}1g+4`VmaX3BdmE5TXO7ip&oM^K&6>he>}txU<~fS)kLAuut@~|h z79@oriDOZWo=r5;TA9xM2VF^Q0BF(VX5^m2vslBtl~4)knugPNLcfs()b~*c)-@YP zIZ;+)xn^M_E-*O<)|>Jj`L$*E;fd~HdxQ>-Fmu|e!)gYhED>#J*|#py(0Wp}(P~M1 zsGG$aRPotc$RjZ3~YG*&i_(P`H5M>uRV zvB*D4%--He5|SBxX-)~dk7h>Y)MB6sv~;LMZ3!=G ze78KDcc@xyc2t6rv88Wttr`*XzjgPh;C+{*GUtEs8aF(rH3?Xila%xrFvaR9klyy3V$-1X~L zH5q)1saKFm%AKSJqDz%sjX}LiSeMT%!0ll~{&Fr+iXj}3YIo4i&dj%ZD#7Cfh@q+m$F zTpUwPFhe7amSFpi4_|tsazzft7S)vP@vnF{^am_AdwPubt*oasJlpWX@&RZuH z;eQlgDclE+m?MyQDb-=~$Yc zskaLWQz7FZQms|Znb4ujv65YhF6>#)IVP1PD=2u5*$*DZxo>x=>P8&VvD8|_E8D9p ze((T83iNM=emZ{+%`K*|?{O6P2_ydi%Rg_WTq-A_#a5rQmHQnf#-XU&c!mq1e2Zr* zgXO|;@{``ZTHnR#b!;u|!ZQK49gQ~b?r5Q9boV^N$I|M$b=-H6Od~7E5%m0Ov|kio zUejMq)a~JU7;W6VbMHqw>Pn1~Jn}h~(^Q^0V#1arZ8+krO*=_$yr@uk$E9NA_eFCx zW7~cMYj&2t8uK zaoyUfE)O{7ns#?IvMgWfv+8zt5zjIh`l`24R(=~=-`se4>UH|(tSGN=aC0Mu;gY3ZAuY*}TMMw~42ik%;32lUk;%I7PQ( zs?ok3__JJPHrjvKU}65UB;=-NKpvXpL*U@qfR!FoN835 zEe|ccyqzwKlbzh)bQF?K*TGvQHgHrPD~alKRHewSPoe(nS%H((16!J_BpRHtwmo>J z-qtyl+p$#2u{>^aRPrlk@ned6?m-O*V14mNE!fufIkmama5n`VMrq^&5O!{JipjeM z%&l=WK@>*cygV9?+TcW++?@0yH8&Ku6)w%h@hYHIt5LwESndRa#ZtN*yM?$xFV4K@ zgPtjM36QYz+n?eTtlWy`Ra1{}bJ~*C+;Y)a`}&ISG72aFu7Acp zD$~3xroGmoDu=--7I}K8LC#7jiOoVTPoeV-@~BKC1OsYTRoqtUaUy3}5DVIB%}&#hL99m61fYl>1mIx*!|Cl>p@>X!DX zl?*ZlVUVYQYc6A<`>55i)*28JQ?Un3R-_ueyzW&oxOZ-u_N=U7JIiu*ytp$GvoK&W zfNOWazAw1Y^|VXJX`z_x0lO3FTScpCLRUwl_@7XZQ(0rVm_ZSb3cz=-9kF1yP8G15 zzVPeToSo2|T6ej-tOya{STiAU^Eo`#-78F1mELTfxFK`av7Oe%r*)~TXQ(ZWYmyIOu03&a8SLg!IiDM$=8!t$763G4jCRF!rW5 ziflExFCmd$D+c}ACf*qO)v9*)q%}0X#q&Hf#eN%}-&eR6Ntl&;IC6qB)QQP8Hc@kX6%cV-ePP_PV6IIFr&l|9Ti@YrbYB#6!X#pzn4 zlGM6wrtXej?WBzTnrKW$GqsTQ4N%2nEyQw62?(u#i_J^k$jv6VI)&3d$mUD*WUd!j^a5);YCdR2LMG*->EhZ)6UphwU2LcA&udXa!*>y z>!{j&2Nn!*-iHJ1?X&{G)AJN|)Ry=$(jb*aACdH`nk~Fnw*@XF*rStD-A*1oGYm``DKhrA>Y7b%{jAYB?QWs z6OSacM6oA>=}BhpxzUw#{EstDQ%AM89rdI}kHM zcVc%VeCKG%9cjxNVDIFSO%&FbA7gE{LoA%c2d8?`p7JlVff)`@P&-ytHCh)2;`K3Z z)n&TepemeXR@&Ud3W$au<>CD(OSxCp=*YUcmE<{MMN}*ss^Pz#IICzxozKu3E6_3u zLO>ZO08j^+d`9@Ir}%Li(@ooVsS}N${HgA1=I;~uhf&w{7Q5AM9#`0bYb4 zs8i;9v(Kqj)v3z1M=O7+tVNbFmi|?RF*h%l=hPnc!8Z4~=u)SzFzDhK>4EKAdTy5U zjq(kVV}sDwB;|9dd%Bk9k_ZQvYyrXfiSJeJEI!#8k`a;zB9v1~>=gN%xrHt&RwNh3ExzX4*=nxfP`1G!e!>}|OoEK88MmXCfFv+a>%;c>rqp-QWv%T{? z#yKF3qtdXge#K{V5~LvRIKdgIa&71?Yi?+2lE4fiD3c?pBc)P_-+Q{qz;@=ev6QFG zK?|SlJ^2iKlUH@!Ug~1D)*m1PK4Nf1OOn0Wbo5p&_#;A}QoeJjUNn&bla|RQwsie& z9UE4f8--IGM+nTh$f}BzqjonH7jx0H-Fv|orXM;;5^0cv7yxUE_T@R$lIi7$I3~mga=F8zi-vAQ zu3c&ZSg)fA1YcE8!aSdLo1bJCtyNB z^)+S#9@$rZ9FLcpmmM`G=#7>{WM)sidvvJKoRh|LS?oJmlF78TOOoD)rBRwm1D&OD zPCJG4(3Tk)k(J95c*yNZ3^Cg|ef>ck_opNsE{K7OS%1}(&jgA|Acj1Ym76D_qVJ%s zrlhj7K9l8`&H*7$tu2+Pw1pldEjzAGMtG@{Z5I??T{`OWAV~7D-==A@*d%#31ThCZ zaY<};sPabScNY*xZp#`N*BHngpT@ESnG_)$9tLYmoO7C+(6bG@Fl??+bONr!8rvg2 z@w*>(v%b0wq@qovX}222uVZertTD?vqq8>{C!f;3sqpv1UkmA$=u*NvnFtw@VqZUj z;<}+tHFk_swy5l_t!?d7Wwa4OzLB6&`cZWgPZLJ6v_XaoCtt$0B*?k&?YXj+J2z{4 zm&f`~-hAh-PHV`%Gih3OlVxiRf@|$cW4q6}khvb<6M^`8(ahthMkw4|$6<{1 zsn>{x1~JyDB!c%MDp+*yRow_1hfLy~lGsP+4HecwM_1J>^ouvTzn(Z^QJlMDKGpN@ z#9xbdx~s`yp&zo@;D(U(ljwU_UK*1AB(_H;BBOu0j_mPko2`v0xO2UZOB2?xH4Rcp zfsO;UF@kDqRdaVQ4Mxb%gf~J-J#tN1jxxt+2aiEqa%Z5UZ4QG@)1ZbGpHN@`C!x*-Znf2uIN9={ zWP#urk#I@CIHW`#TQM8~f@pC}QPk>uJK*0DY7iyHmt{0jJ~qjnppRPX?LG=>SGOL1 zv8<{C);N$H{xov7tZKO+batNud?Onymyuju$~nxEoCEAL#dW$bfwV0bP>S}#)W>xi zY*`DEcKUK_c*({*x6sMT{S57U;jWFXc&Gb%>PwM(upj~cKnJ%<=k-s5x>f!3*H3?R z9n)YOV15-2HymZlp@`oGwT&(ZT?<}P801GZd=JD{73aba7|nF|M(0m?+W`GPI?<># zYZ8=sxrXW=2>efXXbM}}MItXjE_a{Ky#D}I8vcuYCYyP4HKocf-z~=&_RV17%AJr+ ztE(9!PrPVlo9xoas4`gwb4-pWj!=f);aH~dS~HAREnONYxgqgtKQV=OU$O zC>LyGj2^kDbqgztcZsJCB9LVX?kQTvckX*1!0!OTsmT_zeJoctNye=0gCO4X<3uXuXH+!U_*Ix4V|pFH!>`6#HF3s-~cP;uZf;BvGG2s70t3VcDBKh zUH<@&+)-t1K~B=K=C`-AT(OEa3vcKLT8`QvWC{la@@tB6=z3J*sYg=KHqpC9=Cd*A z-j&O0_Hx5?&hFBw=*F>9*Fw3IW?M&`oN{W;m3nOC*&+SIcl51Vx+v%*Sm%Iqxma*& zU$?o_62^4|p~}CgsY=L-k>-g4Pa>dLvOOzm+U9oCzk1D+o#WJsrJ^`>Y{$DK9pzhT zJZ7tBFq3Z84?58N~+&%(WQv5 zTpZSRtMT7VwTY72&E&f5gy3eqnBie3q_jBYT4`vtJX^+oG}OFRFPW)I`xKl)56V9Z z<1h6g2popM`9Pw*RF@=nMtFKn%bHuWow~V_?eieO2Ot`1sbVreD&d>5JxD^@7xep9 znnD+^1e(=jZ($P=^U4i!sX03uxpJA;65Q$z1F6_fRY0pz={IF_^+npt2ktZjq z8HQVPws^`PL*A=168QraQM8rIwTl)I%Jc4kjH%})w`9o4+lM2Qjm)2L0X3WV<&xtBSI@r3GesEsWsqx41c30l_^3 zR&Jmc-d`jP%a53m#cvdv(9004%F#??R#TS9$IZ?vG98hSHwUQgRk4-qkD`7Hc%?PX z0N6?uFDx*Opkpi8iu7vGSH7V|02ELITW~oT$9kz2p%XbtRpEb01~{gb!(!kdjDiUD z6^r6e3~3%CwUQlH&M8ZwK`t=rJ)5YcOroT$&mh&l8ESqX)=at{skn`&YdmdNMhcs9FTsrdl;q7&~Ficac*Zv;a?3}SXe`@g=5%bY9Zh-Cv|7~7(>@z5?Zn9Xgm!KR-I#PW zE~)Tm#1>XBcWphj`yaa+{G^X+#Z#=_n;LV$tv5I=Lq^ne>!GLW_7lfBH~_CXtQ})Z zp4Rw7aIG5!%KW`WRHI^CU9QbP53o%rq(^bwc|Gb+G^37+R8%&$1p36D(o-`yc?o6Y zDK&Oyd7&|f^4Ui$oDoXbdK=06wqog?9K6+Eek8ftFfw`kE2-1`1Yy!;)_g$oT4|6; zwpg4UuVPobNwvhHu_KMSMq=A@UJ9;tT$2bhbJj6KDAQs-#N z1ox_1B#fg68985ZYObYyZKl~xcY88JZxWVgV}Jml(D}RKm&A$uMjF=9&9088fx8Vq zrbnl>d0f{rT;p&8A<;nKR91FYHNw+!o0N%dP?kM{in$XlzFinIdIMZCTAfvDG^|x- zmVL~sh{x8d+}N;%neLEeax;-o)}o}>Q=YhwWQ?GO1B{OKUC@r)R?%l&tX|P?n$}M$ zatsHidsg(&t1ldb$TcimF(Q$^SlDIMeeYV-f?xQT7HozG7^3z#swEi}Uf?zc27bKN z3z_4Tlv{irgyNb;4q6q6DoUt6`J{_(KX_QT=~PQ??l}V%!5@uXu^+sUfsd4vPUUT( zQpQ|~hDlxh`&IZBK3+l3YL|OxZ5L&!6Hgk=v<4o4(Iw1ko7n9my;a?exsoW$F`dZ; zfb^$8#FC{?9<*v2J%pWrk#WbpNeqs%0uE0?Y3-pex!3rU#g`YdNVf0fz<*lfu5N@| zn3J`+-Q3rwC(5jO(M`*k#=O-T-)v2gdXriBCqDJZDKyVQlytfu8Xdnd8LJURyQ!?^ z+d86M^V%C>92j3yQQF%}a?DU~ZuH}5tFm7AW!UVmq9N5vsO#)1>gp3P^3)NIG7TC@ zXk~uHs_!NQ`F~7~m5Hib?7A$faKk5#l&LF7hj}{_>FzC5FpuUVt}8*OSvogf=ZaNT zoyt*+R)V`vB85UQJt`-jY!Kvz91bfxD-`y*nRO#cAdjn(2h`V9Yl|xtw~T=)01CcW zYa@DU(Ye2Acjll{a_hnOt2%-Un{Tv6KYJezJw;SBVO~7Q#?Q->*VdS=m*I%5nZ<60 zd+=Xhw$S`!Qu1Gt`adKw;45b%`B&2*f&m90_p2MXL1?5QvN%L6+yF9aD?60Chs=x+ zGDa9+&;<1r&AeAov{w`cY&lHbL*KYN@?K4fgMvqLt8ww{V0qb71 zrg)C#6>Du;FSK4Pcv&sMK4I;jO5faW$ykJ^X1?POU$D}3JwEz7O9?M^ow<@*cljf8 z@9Ha;(Y`NgJ|EP6$Kq=_FCe#%%Y~pBD8rAN{A&lwuG=bH@sd{1;kDltYB&BO8ui7% z7Y^h@7#u2OU{|X_Yo}|TAGOr=Jx1QoPzY0OyFih8kDK1n-AP;l!yNUkEgMA>YIf0I!Smclz7WIjR|MnlSn5`O>OB_U!5VI>;ftM35-V%S z8I{xlmB+1S=yn=Dso|T8D?4*8w8XcQ2;Aj*1IIvdT@>g>JCf=(B*$plUZdc<`&*sq z_UAbJEZhPOb(a4C@R<0n$5yh|H0wpPl}bnRfKcO%dex~YLeF!Rr7D(BPNzHJ&l<__ zp3LZeC)2O|wBp=D31x3^z*M>q#EEVqmAo6_8|k$>)-OELN4MrY_v$N1d^c{#L}{e? z6FWZv>fSB!MAzxA+fSmw0SKyM0o?ceE4k6Mh_taWj!7kXTXQp>X-e1AVLdb`_|wIj zcZ9UIyVM_at;8@dFDrThUorUi#u}H1?!>ya&zBDE&{(PYSJ08(rOTCEi#bU}96Ze> z#J*+0+`#eaPRIZkKQ4LZxTPm8PgPo+Y>P=Eh|0vr-!%kM2xNaeCZe$gNE$QDk+t zoR&s7LQf)=q*&Pp_k;jTRM1R|zA26abj?+@I4d^Zg;zkXMoR(EnvO?e!#ojIjIG={ z^x*o^LkjQv-9=AOT589=yokzFW--afK~^k=-!ku@uodvH1?+-xMd*dL?e6x{HvJ}yu2dCMxYp>5nZuF* z&sx&<(CqsO7VT#+-IhEct^0YEixpw9h8rE}Y$mO_n|Tt$DOChUdJ|Q89D`e}jtg6H zvVv5UVjHLsX!|@m8bZEe`(gO0~9YmvOc!NgQ{;u82-NwwA_Rk&L;cx$0jJyg{h= z*3KDqzb4LGM%?O$1)KaUz0}*v(C*s6hLg%+8B?6O7##g;C*-s(ZOnOB!mToI25EY& z)~3c?Pf@tp0F-TMo~Tjro}BaMy9=@wTFalwN{h&@=R2xZaEd~ zxBmcUe+77RT-0P1(p$o;V?_Cg9-rsEZ3;GbVD(Rw$niYy<9!w4(Jms>lzjeLosvkW zrtmsv9qSvwTD8`NDruI#Woc0FGW5nc+n(aFl$W&BSDKGAN3!TzitGB5i=8&zF6~uB z&nlh6BO_@eky|$!h-u_aJ{abf1Bo4u)lbH{Q%_V(5^vtn^Zx*fej2dxZOx^=qpMom z-9o#gw=w~iJu8*)jQTHxWw-lX;e%1LoWUq(`B-Ne$E9lubsqZ>EKM42NgnXpg__ul z#<_)UNZsa_0de)O9r2IH2rPA3G|dN3lKqG(w@OCEZ(7ep^#?x3gln4qrKsxDUEkeF zb9BV~(l$hS?7o7sAxR6ovo`q~x$RulXMGObJY02SrCye!8 zXc~7ZNf{fsIUTW9?X4ExCAz#jf4IHJy*8|@jH=5`H!NFfkePP7?QHz4d)3+F^OO?V zVaPQ$>Ss!om%LX=wzG-P&Zh%C>925(cn5>dD`>0gan*y1FIwK%zz*oo&Hdv~dz*H{ zzy=3uk7n<3V?Km7=_53I2~!EiMKI+Hdc`2 z5uTh?mqr)^CYwfS_c(kPe3D7^siKRHmAtObj#bc-IJUk|deMnV0ON|nTIf<*3n1DE z$4^Ry^T)k7>^o>%vTdZ`V|RMi(=H5hv6n2Maal`N4K2&x==ZM0P^LTj(`B^Tw5xys z=QRnt*u}_%tMRvMA70f)?qC&HXU)&!O`}LnTFfn{Bw)ry-r21rXlA$Ej;iCTlS^i2 zB9p@-WT*zK%$-b!%Ojq9)y0j*<;@*@yH}OZ%;z9htdS8C0&1Dn80a$K4egGlMgXf? z9O{S?R^|E z(zh>`z{-H|>)ckcb~*V}ossq)je0dLCerjNPd+7+Y3-3$Xc$&rp{nWD*J94{=^>g# z+@bgcXR$m}O5uBI%?Mo`2bRHg5OQkm9H)!t)4WZtXw79j z5$W?xv&jT!kO9tIcKmBk!=3<}#CnC)y2_aJXqby!5t%>Q`cyj7OOnMxF{b&Wso}Tk zHD43nL8l9XEvINwXP~#JWbirCHisT^&5I^KA^9w)*Fq!B>@jpA7hGMGRupE)28WM}nd?#&h|KB$c!kDbJm& zY&zdX)4V69Ne74?$5U7Wn^>C*CeinR2d_A`#qJ+&jjn1cK7qlw=!BK)8bXye($&IUcd17!k!hnSJgDTc|t)c5*v<%y5xRz z`6;KN&ZgwwIoL_4X!=sz>w1;c5G~s654>UH9Wm?0eC6>6;zosV<4yQ_ZsFYJt}li{ zNDojm)YW`CrYR*mXmHJ^LoTGZ*SB)q>LiJGyo8=T>h0tGr&bRdJb4&p-JJ2(x#~(? z%Y;;JtV69Xt#@q&wf3oU+FS!{cIze)N4Tn2daKDF+3opJ!$?PJxlOq0X-#U{Ac$;H zk(|;?KGY#dE;KLx#c+ahC>DK1TH++Ybjz{=WeF<{X!dxWsmXbmv zjE;k?QY#+atGvE&IO2xyYaJ9?QTI%(cToERh+!birB%bFV6j}Pz>W4i3Rik|lrJ^SC z@CWHu;ao-tNJMBmfSeaK`LQJ>UZcedq6NcVAX>z94d?q_N67MHqg+wxkLnK zy-aSA+Rk&r`2blELj{drMb6Bl-x)@UAQ58A6jXo%xg!P7;FH^ z1EJv5A+i}sBRzJS)-2;oms8(=XAc!#>Y8=_i>bc$u_|rZkId1W@%UG?)v|41tS=JB zZ!{RZh%zrAfszGOH_%dcMg%?}o;ht(NsdXZq8O1`2*~z0uCiTDHMWXmQzE#}%iG$V zmZU~^85XYtA!bPvjGvag=f*Zxy2O%e7nc5Qw0|@(mCG{@0mTur)KhJ(4>7gVZhy4z z?=H0KsH0@bR720UM*_N?d&jbAJ`je(MAX(Bw71(NlXX|eLGMus#_c_gTohx>-lq@n zjx9IC_UWu$v(J75Fa>fLo~P?x_29eR0%n(3^P+<8Pmr11AprInse~ToImSzsbTob- z_>5>i5xdlErH$@nPb$*~au?kA| zJI5&;Oj~NktIm1>UQw#qOQvdiw~3==SnhT~X9S12I4q;Kc>JnuahbOo{{V*cdz&32 zUxG;W7jHGLZd2q;{{Xgl{40U@v2$Z(sVw?^>~k&F4(nmSU#P09Lf-c=m$&3@n_8Uj zito~06HbcS<|~+-62N`sI2~$^z2ZBqV?fcgWkrhK!z8#lG85}u@|xOOohna|rE7F7 zZKi}q8JOi{ZroMx4EV>zS|O15cKMjIZkF0*-4p)+fu{!;zGta~xus-!9-1zEdo{Jy z{P9Pqh;cQL7>)$z=B_v54~G0dr?s;9QtI01#u!}rY&dVHYPr+B!D|ve@^rv=GA;?kQp%M?_2LiK{u5`vc zvGWOMI01OaT6_>qDu!Hh+?tCOqSVl~(_;)KnfG~rHqlbt+(u62W#{;FgHn+<_L8zH z%Kl<*CkLj|d93DQx5zg)LUBd<9ng)TV~@UV*E2h@B)P#Rpa@KgoaN33y>=&LdG)T& z!@_nSXO2&`L{#tKp1muixw^is7x>0k;BpZWsp~1M6BZ7UCFi!<^D;%#`k| zO$?!*?*QYm?^bR1LFNybM=0bUTHc#+N1Zg3tjhNhI{~6iEPMt#X}{^s};x%+lqFvVm-^Ejmj{11R9X+42)(>H(#5jK1NcF^%(`#r`lWh z>Pq(92|tAwQd~RjwY0dA0M1q>Khm~TCv8yICE#TrnWgNlt0}=pNi&qRkL3W2x2-wi+Swq3o+}%>o7(!J#1MHkJXzb4 zF-fppffMJ;bg5OLP(0A1pW^RO^%B!VQrGN*eC3nla=cLyR|CEIZt^E*d9Lzlp7A<;FP7ZZLW6Nor>$b0boP?d}Q_@-WJuQCW@| z4lr^0*6}>~CZ47J4_&?R-mz_}X|pm(8v!C?{bFa4Uqe}ZZSefj#pf)sNI={eV+OW_ z9JVu*xlNm2ie4SMjFh@mKkZZ>@U^@<_Kzw{sF-&k){cykJmgV)NzkT_^!={dD|Pu| zQPsUoa=IVH2{h}uh0NBsHZ0}F7!8YA&2lT5#ko%VSoF(p6zF%>amx+Wwk2Gs3^P`A zv0*u8QjRnCO)bsMJBY2`(n!FKP^sjO1$^=2trNpH+KO0dT8w8~Cv=jz+ZyAkG~*v3 zOP6UE{3YU@KTFi$@Os5(c2F0V{{Uz(r?!1-q?I&#WqVm>wz#!@(kYCF!+&%dmp2(` z3Rb&Yeq}2tCGi%qX{TB_T_(*;j;y>E#s~ASL2Ip2)-;9&jg$-mMsNi;JH1GcbIZIz z@dsMG)Gq9h6tsjCK3)Sk_4-znI%kMAk)_>gmk6;$H>70npkoA&TBj{EL1!)5FN*wQ zqsgURCaCk}&9>F<^lLdIv`EB^=Q1^2IW@W$E3s1FU%m5Z zWVo13GGbOnz+vfBv`3aX=PB5SodleLux*E{#CjmgJji=1_D<{df z1ZO=eV-vPDl3I|#;j4Rn_Y)C2?%V*z1z5SVhft2#v?!q%lg2m|vTt*Sa#Cz{?aIcg zkbv0h%}-{ANETTDKu=mpX>q};*^-NejzNF`2c~nHTg^f;0?p*EdS?Qr$(_{U%Z<T_HPZu~Hb0@k8?q60jYr<)GX0ImUSuwi`Ar?Uz-z zYc|5%pxqOmhuXP2fwE_b{`wLK9<{V3p}}2RS{1`!;NvxZ)*&DsWJ}LNd(}DHRAQGg zHP)LSlguNY%4;R`=t&`ypI=(h){<7Z(nt=PgCDy)M?so~WmvKfcJu=^jY_SHl#It@Bkvt-dXc`FXsJ8;>7&%1bA-`+6ANc9}WP zvYeG`u>#LV6eB5ABJ$)Ca+2Hvs<@(&!?kC!yhYxMQo~5lDh{T zERI6w#gdys zCbx<;XYGu7Q`8dKMg$Jj?kFv8$nG85PC@C=9x97TE3gW1qpvjXYZ&uri3Ecn@<*jb zHQb7J1Oy&2Np5e*t!6mHV;PK)G0sgus^IYDdt)_AMPr6(w{$e^noWwJH%i&?#*cNc z-brI=fFdBt^K{RpTvs-MXUoicmVxkl!S81i>T_M(T$LNwH~{yqt4sK6;5#V+x6%Cf z1O8e9=C_S2JKWdnbBuW+^fhjLFW}n?S#G89{h};tSnh~d9i<&7RMlGhNbsGc`gs9z zRaL(uTXNjOc$$l6sp!^z6wsxHIi=8Sp_PCo3~EaIR;8wkrdfGrXIyATx(Am@~o z*>PZre(Xp-iYc;B1S9v7#~A+rX@I7eU?^R9hex$IxYaDs`Wcq6uYM+YLeMvvu3IxN zRZOw|RidRg&_^+-e01=qRZJot)6{ndqpQJ+?3Vft1z_r<>!T<*p9kf)F%N|!n-Wpe(l8}4(ro-pwp=`v5O z-r>5Sd{;ku_c!thF0N;GU++jwA6#%=;<0|pnp<3~wh2eExUF=?V6HoxdK>& z0qe~|iZYU0q7<~X^BIZ8dD-bxO*CnM4sbEYY8trIyOdf$cwbtXCD=ycz@eg7x~?(q zvNo@-DGAE@(^?VDc2&9k&elBc=M@@CWCd62^s8#><))b|aT(`|-afm5C)&EHu&k+r zgcvxXC3CKlT%*;3x8a(R$t!`8ntL1UiknEe8Bz}1=A1c;k6hDa>`l~s!2T6#C1QY( zaoUhJp2eu;mJ+f?#a((4Tt2C7GU_)B)4Q1)aiKdaG)sb$)TYTVX>Gez_zwea;VL3A0Jaez8|(p&=~ ze4BRzo=rkp5Y?GQTg#EM2h4hku_Wmgxpo`bdwW$W>#3(L-HJB4?2j_r3lIsUd@h_Lkvv740{f0w08*OAx=Bwb)~8slc}mQwI4DmRf8~6GBeOq z1DUWJBaX+ZtrAwbl&+nbe!|#XU)kHVzFJJ-UCG7=u50M;25J8Q4}2$Y=3Crb$#hg1 zW|DEajc-YFE1FHTk~&2ApWvHwv99gY9DJuW9G@CI5pN?$Z*wftVV)RJ2n>C&cHskONMM*xX~v{1!HPrpj%bH3uz))ydij8YjB1d=$Rv1~NW9zO6M zITa*sBXGcE zkKy&F%F5EhYWAZ1MDVzc&|rhm)@(biWIM+rkF8R6RvLwwvEj&r-Ai&tK;dh2f>)lo zrwxwi$K69%a7jNeK~uo2>CWyd`WjogFWFh<4qF3mGgjf#oso5npzn}b{-^Ef8Jr2rz52fu}0ou!0tUMyE5Y@v?x0)^N>l}J9eqp zZZI*&>sItu2CNY_*|G`8O0alhdr}uBjAYj4KxIc610``wYj1_~CTu6A4q6M^D_9jG zGP9xpK)^lfGxN-16SsC z5AvQs1WYi>M?BKG zi+t>-8_gn+@(>R=sdw!ShQR~1Q?b`ZotB1d*AqTOj#+cl6+8~Cpo|Yo%-q zQc{z$V`L0_r)a8<4@fi=VOv-qUI6SR8#4?o7~kk(22DxG(SDU ze>#P=n>6VoF!mIr>=t&=f%V%^{{Sk=$JlXE9e{3K>Ycrf}iE-(kl^g1} ztPcpI*ijb_s8(0hfv;|)KQyFvz^2PA{{ScxH%wD)UqUoa$YXCK=5{CRO%awDU~x-u zz4Q=3Go7GuPgqWQ<$XOUZo_Y(s|h6My#xkqvBeKobIW2#L=S|OUnJuo*43g}T0^=v z9%1$86gQ=b-Dor|#D_cgeJad*!(qNtoK#8nB}FDB<3@|QALr@KXHLM4$t&;l#}zAc zC#em@jPa6t4&Ca2Q!oG>pq@=>uqDv33y^T~>&k#~?KY z^E0jzFi7fq)bA}v8=G1@47Sm#BLdjmeJgGoMs~usPo_9EcDBc(vbiGLqO#>jJoDP8 z+Mt#KixYhb9e^WhjC+c!aelDJvP*(Z*rSTPwvoPKyazBHb$hq;BS(Ydp zBY<;G*AxtkzY*#;EKistAMB26HLhl7F7mT6>P;rCp>nfU6YUL-9kKPK7cK!lW1n7W zr5jqr>CloH+1LzdQC)NYN$V>wRX3Yo?` zlPJZTZ5!OigBX6ss7}^pp(>h+CI~A5ymB9 z!R=A%GCi_FkAPf}#ZA@C`f3ZSTqVB5ju!)f&DyEl-b~iTH?dbaBi^p*naL%q)S|$i z`J_<8zu`%clDPcs@IUVSRN8Qycs%u~N2?em6pZO+RV7P|`c;3mqKuP5Oigt!>5P2W+>f+|pY9nkkkq!A$Owq_<29iyY?i13Xr)4lo%D#M_+oo7QVB^^ELzlhl81>1wk%}ZL` znaIvYz`-XK6!M&a2N}gR+?(7$+*F7`EvAW- zL-K>k%|xG6Z&F;F8dmW_fFlJ8qZ!3!UQ8L?6l4zm)Q%L})Pg;-L$`K+rDucl6nq?Y z$9ia`t0s%g5tTivnn6zN>FIK>q;sseG^>-jtY$0uBK^X~6(I)Yz*C$O!~>rv%8y z@l&}nR$^=&O$1>4w8qwffr@s_bv%00dLj}k43_m7rIeEGr( zJq;oBCxGoa=(Q@{zHt#Clj&JB*mB(yqRPiCdiONTgmsfBGPvtUp^UU4d2UubFkm}i zRaqv?fUzaB!K>v>63~wz!7N59q?g!Xw~)MX-jZDfpx?JE$~MQ9Zn-@w(@emmk>hgm z2^DrNX{r|IQ@ovawEIu{0~M1fGBIhSk;(q^nr4!l=vQcz=ZuPSs^qVKYInB6<{ps( zMMZXbP)-2!rE=-JvKZprdF$<3cDh}>vZ!W!9-}>KWj2v|-Ik`&Ed-%~bx=O&J!+ZL zqG|T2aUZ$nlBn&m%_@#8bLn=$NmrB*JDSJZH)A~K6>4`hg!!F~Jthz?K_HxrXVRIe zsTcAA(*RPn+0ztMa}Zo}nukB#@^*=FR5xdrP&`&EPEji;q$&31EC zk27Y~oRVBFe$GTBedE%b_G@?*o&r>{&z4O>moqYI+A}XzxpjXjBxMJ!Q;CFmFO$V; zqKl4&s4rubZ8<~lRPV%-Ps3(4`=EEFYcohSdycb1xG^lCb9*d6O=RLu!U2+*VLhFXX z#Z~mPGkWY{&aE2}BH)veP)I&fLY8kq%}Us)#k6fhq*;wQZYGj#z-53q{3&C+js*hB z*>?KKGEGZOtpXQr=EbajWMWeaK`RfN-mQWyz>RZ;``p%Pu~fTy5!_vfrQDCajy6_n z%FQFX?d0;cRx*NjBUce`ef$7P6++(aWSN1iLFN{7~oQY&P`8SfH3Bi0CSoy3gQ8S z#XNEiKB8hOjGT&g7aj9T&4ZQ3^&PQ5=QT}&)TtT481?H*Amrm2t9zVQuz;NUQ-Q$g z!8Fnx>{x;}en>bKW+>r-vhS8X$mv1(C^Ev>yVtpznW&jaN_eV6hAe!NwwwA!h zAXF_JEHI3BA8K)SLR08IMTf~MVO(Pe*0LVf6^*j&r1A%0Rn652q-M#LJ@~1hnn!hs za_rp}w9%`wqPmx7wVnaF(**Vgt2&(WxT#MRLh8rvE zS=X!cjic*LGg@sS`gjev5OOPK&P0nOjJL0)OqJPEWYXOs1cT3|Q!D zE(rFh{J01Fvr>IPrd>T9$?Y5WimEUwrmUN9j(eaqbvmi*sgW`eezg*09Osc*Cvs)E zD4Jden;>6fR_vy8C|r}c;~un0D+R5o)Y$6ZVO53Ka$}HBK~q6}4gAbhVER`#Esj}6 z>f|@(6@1Tb(*Stc!4(JgdR``M`6C_oaPJY0X6v+n^DFpO>a9XH1`DZEnLTMpPO!CQkd7^yTv|Ut;05 z^Yp1TOIc@vF$rDDdKyw@Fx0JYsNRIA-hf2E*sIf&W795Ng9MKs^V3WGgW~FT{ z#A;oZDD%%fN79Q`hBw<7$_}h(ze3aGjhUcs6}EsrP&(CBx)aFEjg?cyX7n*qzQOW` zECA13)oEk%SI>t#hgB5QDxT#Vv_c!@7^vWg2@3;`Df1n1jiOn$khIDaWOu7X z0oU}V)rCnoGI*WLTVn?#@x@;Aqf!*G$QUHjFt2`NH&J(h319=M--@Moi?~BS-p2-< zUvgoi<|;1eh$?YN@)zoP?@jJ{5J|g$$RpCFKyk_ZDqA*aTo4a6Fy@}fOn4Z<#}x20 z(uBov*!HC(iY_r%jN*cMphK`RN&qJr>qnRg#yzRSkOd1;KzdL|P7gHpKsPuX3Q&Gp z7R4vN5@8X22?DCjAuLtg z4DxFz>St?gLn)7T5xB<{1Gxhk$g9|icPnmE7=w)Z)DGYt59L&~6G?;I90jNugC3+) zux^GBgsFamuf0ugJP}E~0*1#?M3wdx+(~Df&dUPBJu_6ME08ivbtKirXgJxG*x>uq zOpTQQ6O-DLA2qZ@%zV5Oc<-90d5@H->qzLQW`wkJmJ8I?;J|e_C)S*hrqUcp$0noe zM=_Jscsy__OG_uXmmYL+DLn5uqfVxfjm>*~6_!xZ+{YnP$Q6&RsgC~uY{<&nKnJm? zi@Rq;8c3kY9z!wd+M_86&e)4F=ohV8Y|5qYpj~OR!2(a^NU<>aw+@wF7iio(eMcgz zn&m~dz2auLd1c=r%XH(4($lpcv&x&sRYB-EsA$o_Nm&=6)jYdlaWdtYkQ7$UmD*g} zERip4k&cxOByi4~Um~^D)C}e+RWYBuI*P1SB+ru|E1vusNwD~KL~~4F7{J}zJ?f*l zlO%virkem9tDczVqcc2-@;24)o=K%^8AC^@UVCS9qoUFsqZXl;z1JpHlXpgh>?Pq+UtwRW7g? zllOYm-&2;Sy*FYOxsS@^{Jqa=sXUt>0DQFF(0ewWnNoQ}1yvXz=QJnW!2IZxj_Pr= zY{w@Dnv_%MYo_9U8h*F9-5ZcGk99jR2~r3nf{f#6b{nq$9UUZm22%`4moXFaI_ z&U#RUD)t_gCV2W)H(tbkA9T}PkU$jEIUcN< zC|%9~=9QtCHUd9hqN|x~$+}Nc%m(T+jg8L)8iIIT7_*MN;-^GTh+?}EP6j<{Z1JA7 z-lkHHrAf#GI2?LaV!-o~DXoO!HP6b&wMA+qoDvr*JBkr}x@rvF06$tmADI6DC;^D; zMaTyCTqO&+WuOrry845@V10sS|u1hnC04JV$Ra^Dj99(nH zN@(e)7DH_&zY)khVVE#8GYSm!mJAtA)pE@WP%NzOi%28VJj<#{55 z&b$5`_7$R93!3rN>VDrgp=h?+j40iWa3&qrHwcps!-#2%Roq?l)3b+{}0l%$q_C zj`+oE2PptZ_-tUdJ5bQ(?$yeIS6BH?3H@nr9QhqdC#_Pks&NdD+k6M>ws+4Yd+NM@a6jNN?NOe2OG^K0H zbqw-+gdTua1iHM}%l?pxU!ATy3c7mgZAzWh$`eyV71*`F7wDN?pU^dX(d~VG6Udf_rqNnT@)JE4u@qdRH5@>?HR)qZ^au zI5?=@a1YCll+j$el%D2AkxKA$gN~Ibmn?YZk~(8f+LA^|J-Ia|?mz&0({Dow$CR-d z0ebUKM&A8|WA78`Q?Zoox+RKOI-jLINKus8QfA(p$FCI{u_JCW zC|5U>TGXp_rZc!?A@5bh>TQfBKbtuXRTtda61dWP@lHdKa%gfoBFR)^*wm_X(xr*& zJbgM;i=Mq`iyoZ^1Gk)0(2{U?tKU=!j$_R{9<>)C?kG9n^``P0+L{~Ccp1sLtkf;BPgFrHVpE(2 zHC)1~a9@w5XqAcEVnh+dq^3&|%~yFt@_JEyN=d!OG`JjR9+biJ5^+}A+_bEREMq*; zem20TB&e+0yEma8rCT%e0Dn5MBLH|5`q)(^sEXqPnC+jNk&kLDW{~)#%8{Na%0@rHo>u%5;`GuxAN7fh%x0;;$7Dg3+S0 zw>iM9-llCUSg&xacK%*6%6a38=CxfqTL{dB(4lS>M{3d%OG8?6N!gikFyM-xCmxk; z9d^?~#k}58$WX`7)#$DksE;8)U7Hpkw_T;j%sr%jP1tZ`qVXJr`jmS;(6>;05E#g@Ji3X zP!CFO$mVnB(6vjp*>4*x*s%wq9K6CO3fJtn?`V4x(YH)N&x(7TQ(n(M?BPvz|9HhQyiwH1l2nb z*!|f!?@BY86s(FIr!BzEKaxZ%^*)f!;fF3AU>3ahi4egJoM?sQq)66 zbB@##$0M3s0~BYeroT+nLn;z@?@%0;!8ogSM;x_fi)j)m18ogfj-ewk!hkC|)4RBw zFS%+vnD&h4b9bcPN#c@LIp-F&DNYI;s5w39*87MB&pwq~tw~X~rKXo7w@g$_L!k|f z4%8x3xlQ5PP6;QqR=5l!B#xCyqM09g`}P|VaOTIYfHp-63r}fL_;c!74@lU zqo$nsR;Mv~vfH>>6rnX*30@fkhhfh+J!;mdCuwSpD0PV%U9S%8{m`eSTe^+aT1WuN z`^tM%@}zR|axxf<4u~aUuib&-wJlQeDU__L@Z*5gny^c4O&Oi$h>LND>5hV_L2UO9 z>w5`wQID8+tnXq}xlv12(W72;mXjaM{RQBJIHp*^>s9E7;e?zJqElE)~adk|ek4}{vmf3;V)KlnnxhE2+oc>OV6EgPkjYaL1Fy zMIvHCQ!EWJJIg}Fy{oobKu{XL8bt!-RI_7}YF4I-D%V5Wa(N3#C;jHrj+~QAV<{^; zAZhK@cT533{M8>fc1Z72cFwFc8oOj?B-25_;;P4QkrN&cahiaW&ML@u6lebcuTm>x zgN~Ig2IHe4PI`)*5=R)#3}||`DcL0YQ%gZKOs6U+Y~*&O)E>jR?M}{ma6Rdvi=URB zr!?4dj{2H60n(&wLU_$cIs;V^3=PhC08{s5Q}hkS=bZD!9eVVo&^9Quoa4Br9oxOl zQNyUJKN(Ls^QtNew2w;B9j1u4t|u9N(5aGJ!+9HUFcORTj^2D3#l1a!S6#U5Khb( z063_AVA|%( zUYB7#(lktAmM@!T?B=cO{{U)PB71$sM+^sg%bNR}#!37|Q>DqMLO$9|ZPju)ti{k0 z8JTXHM^JdjLMoHx*v-z(_AlG%meEGuOl3$JE!5LzV2Z64Up!#aY?(W5%F|l3xKN=V zInQd^wYMv3jV!r*b)?%TojB?c%IM^0r=>|QqbjLYY+!b%GHteIL8#nZNMmS~-0_e) zR)Dg(c**kc$JVK7p{rKX#y!$c9EAvCar`2xiI@Txh914BO5={if@jW7Ni^W>z~Oo8 zPWBCuRI979)FB1rx8QM8DR%sOOJrenRFh?#cOv zaZ$(y&`V>U^$l2YcXleYhkqSCYPojbobgV^juzBp!JRYL+M5G~OfkV7%@*iwN?O=? zkP=l+Nb8De=bmvzjoTRD_NwHa*c8^H+)nN)s0~fivlfaLVVtYMDuklk3uu9(kg`_7s9e zNFRzwNsY*%nB%8PXuIj4(0wybW5!M>M7MI9DcU>x)$3bGCEMmRf_nB8P3fV_r&7Vv z?VV4M=W}sU&7{c~8>Ikr#wvNAa~^J8%F|msIT+jk$s@3<7H-~H-n&5PO(w1mN<$M&CWVQR0ywFpAdcXg&(LYy zg`a5!2srho$s;%$h~k~DCfbn$jEvQ3I^!749J-HoGiDDWr3+QY*i;3E?pqf!e^In!9W3k1OvofWv|5T(vCThP@2AZa6N@l@&&9 zXh_BwP>9I;*rt{CGEr>3rO$|2SgGd>#M38&7jxxIyU*~iN}YO>D3vx#5i0SveQ{T< z+Yt@Jk~&dvQS3u*Q_dnx?mY*3BvBvC-81P<*9YW23z(vixvn8sY*`sDIk^m~SxbJo< zF*ziCGApZB-KQu$3gVmG-@0nmV`Kv8UN1*>3(n;>}spZS7-}t;MWzXZVP#y_ugh zlhC_$CX;G~r-nG!rWJbEHK<1lOwyyIa5&0uJ887u$3!P>*^t-qvob=hD=+->1*)^% zFmIaPQQwp`r93R&sWVS)9)yrx#UKg}8`7ry6)Cv8GSt&oL;3;fQXxJ1R5hV# z-D)_oJt@nR!8Fp)NfItIj`biRPxPdgqhhOk_@@>W>^o>U5@}B~fer&BtpsDG00i+$ zGl5Mj2wWabH|8TJrAiWd*cXnpK0xWur8KU`)QkPzX?Ky-@##a}$17JX>9+`yvND{L zkkztwXZNR`paWU8$|jLod61G&{Jry3{Pdf0sRVmftyZQkD|AUvPoI-QNz zyJTeJ;QcC3aB#)XiA`vHspU=9dNnLBsl0qRhV_f0wy>hlSR@+ z`UxuGxTtoPd=r6F7jR4%6nu8|rw9z0#SQ8-t}&u$B`W#poK?uxTSRu?U>Yyf=%(z< zLb(9vijf?rI2BEr&NpH;!w^6N2bzJIi0_(7SraG`5KmeM-WfO<nX5fv@FHhTf zZ!|a272T2)^Id+e8V!yPbLm*lNxLH%+iMut4g!$Rf&A*JwN_9OgqiD()Zpwm%ap%o zVI;BnVp!paIcXWO&!DT97Oif9ZGmz}-U6aiSFx0&+?f*3CNRW?98$+TamZDjLk?<2 z*!aS|!p=|3Mg~PUPq7^fcsLVoPlPVcob@ zd!Zm=fR!!DrPNJdLTeT!N*tVbrb^DNLFwyFyAIr?e=&n8c3czQx&=uV-z0`kqM5fg zhdXZaz03J3&+{DPpys61ZO9SINZ*a5Rr6U{nWuRb903`q8MY=fQuoj|WHQ`D$L310 zZ*v+D-!}viifuG(n@Y^kj`9;Jm7FMVQ&w&6gSZhqZIg}-OkZ;sPg20&zYM>XHf45+ zvKs(r6qB)?oy@&r;^NO~h?R-PMhAMQZ>U^1S;_W2s_k{5(+Y8s7O8kl1j`|I?V9>` z;djDs3u@jR(sd6IuiLJkFnqZW3J+{n)M3?;h)G%~_U$vm-X78IQX4HU&KQ(n0T}65 zhL@!>=j>LSf9sg8i?nlC}t0!o;}AVLF5tj7^BPp)QxMgC~i9bw1A&pDO$pA+78I2Q}|?4P4yc>obi)Mnibdw zQ-MxkJ?gd-MC+#^Bj<8Ej+CO{8Dqjv{Ab5}PcVRtb6O(xul zZ9Pb2k~TYcAqTG_qK#t7Sj=cUoYt|6TZXnRK@cgpO23DCSz0iKK;-*WZp7D8l;bPM zA4;Vt8Rr@Gr)Eplk#5IYMv!AYs-m62*%jULcqDUEB%6KB4kd0ke)i5W?Mx_ll6p}X zq+c$Bn*)>5g^`tVhw`em*sZBw%P!1W+n(o|)3=DGjc{89N|FHPrRZrzS(po!K~hBq z_}Pgg2N?CF-HwUNurvWsu*t_G$gke+e~Eva8&2ksXm5nmo!!p zUL=j4pbGDGr`V8g;Z01Iqp4=Db9Vu!n8$!4Uis#wjv|{v9tXWm&Pgks9k!ZgM0o;* z##9c1tUAVuq2>lonN!lTX)x6ox|&WVEevfU`hivBw-35ig~RnbhAG)S!%kAfA0YXT zAQT5NE>RwzfZ%f1_?x+wlq<8G3+?E zVx#zuarL8BiG`~bKQP0OY7|kM(b&Y2ZS;o&1 z+yitq-B^52y0p1T!hopTf+@iIRpzNg=`6prtR%LOV{6U@ zS2C95sLCwzo8Q_009CuVl1)Ef(_)E^QX?ip`(~+J{?8sej00(Zq}(n)f=qugL}3Q3 zX)281{7vDVcft3D*TmL;XD#<72PpplLMtt2LVi|X&Z2NjLwI}Y=$TI^EP3=bGO!kv}O(y{^NHzq7l zvA|Evm>*u1c4O5OeVW!mv;+)ym3kWlRn&} zu`_!xqRkEycGQgN*O8yPt^Vm0*S5G-`J!xlQXe`f`H~go*j0HI+cTp(OM9)kT@^RojEjM<6}1k4m+ua*=H3lauXDAdjsNu;T7Z5W|d|ijqPy zaZ=Hx`wbX%A&1hKz^LbsY|x5pQC1@OJ5GC6g^Wcs>(Jcr-YjZ)7q)WE^wvPH2HfEyg=hp`EoG zZpJo*fyVLLyT28`lVfKf8?brw31xxgIXo1;;*>!1j?*H$m#`Hw(`}1 zb_@0CO4ciu#J3k9HWLkj)~mh7RELjtc;M9~rPy-oaDvv>a{gSBD|OmMbQaN{unYoQ zZU#?Umb#MU)3ZiY5wt)@<|4jMSE&^=komKd{pH(=H+LbfXLFLBBo`7w5P*zEO(QPB z*P7Do*6k|{W7mp=pPN06T14I40rso*WBw&Kz~h`%Y-t$VQdspEqP@4d5wTd4YXRKU z>Ezw5%$qR8ZX3FeY9zXsB&78z`2jJwXFTGkwY2lhq%ly5xB=d#GASz|7THo3m(9-y zD^_o8n!?W2?uzZ4Zc5cHE{K$^WIs)}xzwg;w8&(*mNnlZp!+G9`u;u|<%*Wc}mpL9#bVZNQANO`vr=liWvrunrY zw;duz`VN-t8XKA8_cX@v^kI*esd0`sEoj~YZ+c(styyE@C6T=W z6y(*kFWPw#$#1B{Kx+w@cPoQP4C>fxc~o^dT2>Qx7;ht13QGcgREn0~SlyTg%XPt{ zO|Cmx172ICr5Z(69;T&PV{VS5pF={6T9C(K7?izrP+U>4uR8<+1P$))4DRkWXmEEO zd~gfF-8Bpz+=k#zaF^gtaEIW*LLTqFTkqYv@0?TT{dBkF0AfxL zCt>kMma#MCBY4b>(d_QMNdBQ(e3moL!J~p8t_K~gWYC>DcnGOWmPLjGum`282%B4< z3M6VsERiXIi#t5k_5a|AN*p>J2!7QY@_Dg0%JwGKA(o$(yF-dLv3JJ~PT;#^ZB z{q77$k&b>`0S;B+CPhQb<23M-5?-e{7bMyD0ViPR3^sa5?Uh$)nEBsfd_ErQX9;r$txrQ@iuP!8kPw!9cLd%qW;#Tyauuf@ z#~9Slfe4qlhEk%I(1SQ+S+f-eSPVn%-ilR<7xz@jRQMKbL+CX}hXC)wlG#2fR7;#l zo+?co)^mCi4V1_*5IVd1EfiZebLck=3zp`)nZ!y>%sy5%vd=GozRFZi{(RDS6Tv77 zjSbUIdsln~t@vVp%#lPk!w(m9d}conw2X5)b6085#vNI-iH<(K-D%IJXSYaDl`kuil@^LwjZdf1@*P79&Rs~oUm?Lv zCo-RnGLb{ov2{4HT(s$><*M?y!;|d=rp`8dDU=*Y0?o7}_TXhFX)NqgA@GIH%)Q~? zEFvkriHeu^OUwNY>dRT$&_@QrM);}#y@ys3KVDe)`=17VIY{bjuI{-vv>Z6bUP0xj`!JsT>J1+e0bkXFVZtIZnLkeoX-LP@_& zZx%E6>mJ;oRr=r<-5$3=eY6sr(6c68igpZte0J)fC<$FE$!Qw=_^KL26I8W6=s;RT zn;KH^^HxVnHWjumkNFiILzwiSMsf#WVdJV7JheY5y{yVVpC{_Jsf95k1(}{`Vlha_ z<(ytIBj5U&jjjQl1b-GuQ^Pdm3F|Mfzxtyh z_e$QjDblf=71%hZRIm%YrOK3Ez`7wBI1xrH4;-y1&bDe6xI@s1`ILfW$?>;;2qJe@ zFf!z?b?Q!4zUIdHG`G@ihP1>ZaO&n_UJ0P!94TXhQ(=``u9R^5EXS$3aTDjs^AfGH zp0~UQfFt7NiErc-l^n#9N^^#3w}u|P)$s7eF&H=wkJxIwi_y~XyB4jiPX30kO+(@y zQ$g>Yc&ah#p8|j^7Hde|j+B&X^pCqO1D2G&$+XkiJxoztN zC)G$P-!xCk>-Skd44Bc7-sIhWRb!|9bC7L^cP7gme;PVG`({8HQ^6qjNivYP0hHJ} zR0aESb{N;No+we&q4G-S$U|?VUK$S`FQ;jl`Y|_aQj+Ac>iRiuF+P2u4<`q}eI2aA zitZqy0A9CG5W!Z+CtP5)G`^YDH+kYY3U!W`v6X{QZ7T9N({J5^)bAo-2$sL@qhGr7 zw}z<4ryCHEjOV`fxTbR>JfB2OQ6${DCriGLbTNJfci)1(E-9`!x%smXELH_UpHaL| z@K1Sq4MZ{ENpEbvb|iVrw4(*FVs(%a0Iu}P$Flb zo$4qYaWgV(4>D4osSh{XJn8pG`^-cetTsuLA+8vE>W=su7~|Nl&r>;wlH+s={qBz2 zJU1YbN%mNp&lhp!EtApWzPhgDPKfCB9wb_+zy>)OPMZJ_c5`p80Cf=-+|Hm~o=+qr!x-EK=_oT!;}-LZ;Ef;R-?!SX z-F!D02f+sQn?bPhh;@Z!X2BX$bUR&$&tdeEUQCl|Z4krkb8f3l*RD-v(UpH|u$w+Z!7Taw+>N%YXN?u(dZ!ZPQeD5r6i66N2c z(nCxvn5!O-hV{=g^Ul5Y5r%x`fBbBssA-s6Uziud39svcGM=pr{%mUQgCf(enQGB` zYHD}mO7h)&(J4_ZRE7LVfVnq;AdfDN9P}Lp%BAH8t;!NsINO!z)sBWItgxg3p z28XJB5gtA=OcBxP8lh(o8pCwH5mH^)bqy@kaC=an+Q|KSRTpjM-dv(h($6@SekQ!A zdMf4H^gWtHa9d%xyaIu>Gmbb-dv!i(XxJ^z*}P{6RN=&9?0hy%myqbofdqc!d|13K zcQ30@1=&x9ut(8I00{MG}*8{{8*hipd+Kk2^8Lr z_&%-#$<$!+4GE&ed*)hYZ7#^V*`UrwpgOW0gm_?m1b<^dY`=z@d>`RxWjTijBGFQ* z^+<;<(+${hJ$GwLWKdZUjFa0Z`FU12)Zqs-Tl;5e7su#)DiRo!2#2wi`A~ab&%3OO zog2jE_18DWHakGr60A6#8o*MI;c7U9J* zTKN>=T~$8UXvAMeT|D1aIT80Pxk4PqPqEht5hXvz-lon-yTxy6P};~1HvHPjrm>OR zW1-MPyqMCDOAxhu`|%mEiGEA5@z|SYzgmUE0CUoJnLQjo%t0I=N(|m4b$&?Hd+Abj5~gA zgpq1hlNwc|b6ORP8TxRU@_k|1&slcKD=d1@k!oNQ*_mXR6(@T4x6Gk#(z*_#6%(U!S48e}lQREO1Mm+L z5ov1&6uNq_LmlVbKCJwh42>ksrb(n>;HqNIC@uxR3srJ=m%aaRc(Y#pA0S~~{RAAX zlqs#dlMxNxVP6G;W}47{S$8xE_g$qncmCM<5tiCZkm<*zOdDmRr+2sN2QRIoOeK6h zL{FdFY|M;=Bw{sZ8AZlr`MSaz-T{?wGgXlA=;%@PnO04why?KQM=uj&1JeyPK9li- zZ%PBV=}Vhb&H&HL6)LH$`ua(}J@7Bu808|FXvjl;3oXkgg;j85Ib7xA-z!W+pO{JA z)}gHH*}rt~?S5Dfi^SC6RjzWO54Mm(tA1%f#d2o9*s5Xl#QrUF_w@3QaKxN#*Sys1 z4H=PwS|r{FA9UG9`_m>L@-tM4tPPtYI9J!AUzw&PUs?aI`!=# z4x2ykrz3tLd90y2ILVuYO_nEqWfEau>~h;2nqA@Rb-{Hws(dfiF0nB>&b)<<=%?kv z6@k*q{!&p)!(iM@po76iouDC!!gtX5`XxdLqp%D&56;DtfnH`8&uEg`>9>&w`G-r} zASc@SJUEE!7Qni9lsvufRA~Rce)exV{?IgRkb?Vp!8BWkiSHNFxP!sn!hW2eNj6rv z)_MN^sEd*MHs(9N!iwULYb;)l5SD80#1dQ9H$(o#JsaTtH+9Pe`MBLC*9CtuUHk1o zb((kRmydhT)8ED={#3P+b*DG412gq*5d7h~gBQ~F+>$6g?fgpokRk&$Mi}?D`T;o* z22mJzCBNlaB=ntmtFREb3S}Xbj!l+2ITpWh40uT%62+6=ceI{A_v^40u?v5r;l?OH z+i7dhy-tadEw6@I&;52T44&X{old}Ak`qDlW)ETYEbUTQ1Z#)}yUFbaZ&vtzH90xd zEb?Jcn|lK-$b*)gXj4#sa0kTw>PQX=5x9fxf*yQ}FezRnW{TtzmRBWt6uUI3dSA6? zOG*-K)w`4xoIFm&S4vM9Vz?m=?zHA>BvUJ@nEOWTvHJwXC_>30<&rNz;;BWx0vHV9 zN{VZ_tUV6?!#CS6mL8GO)@79!hV#b#n}E}SgxI4BC=)~CjY#=qfjKa7t5lNttDc>t zm#X1yRpqvi&Yus<-_Ck+pE!5IF0d3=Gs0%DjU`8pL{BLAq}-M*J_AKvDiQ{)SF#x> zH9i0i5n@{EI_jlYZ6mR%ti%?0xMP!Fvk8GO>@m4|@}Ch=dDfLxX-zij42jLHTR7W; zrwMs;nn?5Iw7XYQrowNA;HuYRLy6PEMGE{eni_RN;_@ zsiNwQ*ld}h@CR)V=ssz6f*vmXrCU6gacqB1MHHl?;esvlQo{YP(9fTamSg4JTQ)xf zi*uX{#ii4-%5m2*!E?(7ZQV$FF{BKZFw*qhG@5A!zehxL|Fowu-{Rn*vuS7gq} z=dIlY;)z8q>8pD*dga-9^x|n>KJL^fYvkWVZ*8QR2+>>lN`5zI@2$izbS~$ZVl&$_ z{esvMmJj5BID;CdJX@vpM{_O&PaN83C|=pV^he8UqSU?$%VL!5h~avQaZ`P<7e}l6 z2gsv%X0;7anRB{xS>>0TV^5}S?rd2e+vqkrXo@jbNXrp4ICB@r6CttQt|bHql3l*Y z25y#-B>L_0I+5Q_Gv{qLRXSI>^Hm9Cg*in)g-cbfaYqrBS@C&$S zTJdt6TX@=RCHiH3ik=@TSQ6Iq)F{)xjYc;)Bq{J2-@S@4V`4FC#liK`LLKD7{a5Oq z3NTBg2x#mR8Xu5e6eMX5dP%iK*&&M4QpO_A^-(QGAMHubsQX+4?>|(i8GgtwWRsVU zaiC;~d-YuQ-3PA}n7Jn(gV0Za?~{TEFTr(h6-Dj$);vSkJD>Q1gaJ%wXjftDsz|5W zdmW~9>sWDCpOSE$U;%^77pR`6nzS?JR3qr8$_kAQQguO`b6sxa^n8{R(BGd@aMgQs z26>|G|Eh^JU%nl4cMU+B2JTs&i{jU)AjbwN4>l_<5LcAdfv@Wr$WL=_DrZ{S=?M%P zO@_d>Z!sCwn9(U1*(p)PF#PGyRg<;H9Zl=q@nwYUR4_i*K)>gqzHq~`4A+6(=4)XURTyU~WW9Q0f#@Nv@~DptmVzi9*AFVrY|A2obkRy*KAY;Qt8-^410YbSJW;@_JyJ97hY>CmkzYjo*T1{h~wmTXXU8Ws=#pgoFi)d)(Gm-Qc+i z3Lw1x*m2e*FR<9+#EYv}(BcP;d-y>)xty$rl&(aXhkla=DYq3w$Jvx=ROGE)1XYGH z>4HSn&>6Cr+@kaWP11Z?(GcCxO?IR!mKK|);sC;$JJml1!z5$$J7KU9LzZ5XQguT* z)uHq<49RtKhpK|A2X?789|M*N)c<`_HaX6zOdk5 z6El{PDU};X%}|upc<52hLAvvqQSsIF=MS~gS5|MV6H*dYgjF9AO#2P`LN_C_3)lAc zOptD=`(lR^o>zeMgVkg&W6|xm7zIvYn#!xAwP)1_h27s0^2h2AmzMY6E~AIsDPG47 zX;5lbj1qTYR1Y&-g3&YqZ#j+YzJ^!P<6(`5lEUL;f%xB=t^%XeAFr-oTJ2~GlH9|rCj4TRcS23C~v42#fl(LLWG5McVt}zr`3>v_p z0b0Si1JHg=d0c6~4)$S+A>_(UhOt2s%Px%uJlI)ut*QrOF^Zt_L8j6~JJRH|L21}- z0aT`AM;pG--dOo{?_*KKkv(lS0~xQlgp4wUuhT*9VL53Cy4Qto2OfQbVZCB*j6h{< znWF7re?B8Q)A9G}-|iY@b)_R(TUrLGM#v4$h!UeaCC>cK5qMoQZ}ck8Owtga3QJS5 zg|L`0xVx&R=ndRF2Zc+TQS)OZ(sg!sLFn1sOFjgK$pt1;6&&! zYEwm(H(`jX5vDfr+5X#E6nI=(v)_;PM`S@OxqE#>17MiSfq%eYp>FPi80Ucds%Pu7 zJ+^o2W~_9gL%GfD05WW5m!*HPp@x@{W(M^{6E_9hmjU7F(~^#+L)taE%3VI8PqBfa z+yp@|gIVs4$&alrG4>CqE@eFRS$v2wQa)2cT3YIAQpaPbn?kqq`)vyde^929E@R4m z6OTWXaZPXMw5QX}Z?&k}Z`0Ekc@|#ZJw19aQAzG!1>e=mUW$hWh-0gX$J9C9eo?7FhRYw8JYbfZt2+F!5L0GD=tA>Q5=CdQA!zI)!2GWA3WctOK8 zh&9xj@2V~w!o(^|+eJxhm#@g*MeK#Dvq~vRC3F`nq~NSk7z{)?B}cu|K+9QCLVvmo zB8j4_m{mJ>{`_Ep0TQ)e1tDx^@)k|Gtv?g@=b<`ek;*7z=Hw3VmzQ-V*ll#AAd|z7 z-r!<(cQF&aYn?P|WKBMFF!Q7?3%c}=aa%9~m(t)9*p2^@*xM;>dhGt0_52S|qWbNQ z0cuLUqHvnjT9Q<6pUj-!Sum@lu~BO&t3|9^ugY{+CTq7cLhg zi5Ona$zA^FCwx&WB@$bsRX8Jy)=)9C%SUO7%|~UAeScm_g6(KVbfYDMhF%@ z>8&tdZ4oJzG5HVB!m{z_21Rg4sMiN}=3)S)iPur5I)@xPZy1sZID3+iNb6`r->ES`tAa zo(`5i6)2k136KD)y8k5q4!cb37K*?U-z*sfBF{aE3%TC0sPszcnfngqyvuwR%C@9r zTfZ{0m!-vSZ?sNjKtaMaRe5jILT!m#d0(RPBT&>K_93fmeWUA}zJ5a=hW^}QDN~CL zlYiAnW}j~JEB?$%aWMJ*6iD1(EUQxYl|RK{4|+di-iSm}mq|0*AXj6uNG?Y8M!Z_% zyP--lOiZJWv)ugN=~Vp>;Ou~?` zgVz0W=y=q@(6yCLv=}h8SjV%_{sSBY?Mrs1BYz;=(i&3{CIWHT@y8cX^k|BW2_o=| zxGSEq7YsFOaEu_6TE*DFik``}P5OgX?`>{1x+6(Xra#J_##rgpkK!m0GD**o*(Yl_ z;e3Y52h&bbJa;#y7@i}4i4_o=Z#yr<@U|d<9K>BRTSZ$wCScZ`tBf6JrZ{hOYr)LicUk)**RJ{jP-ToSWG{XY87oOgMF!Nn&D%kONH` zV}5#f7jN1De_{sc>TjFy%pDoVO!-mg^^3^xJ0=PUjPOpknfH?3mw@!~$Pm!fX#I6I zav+j#aT|+VV49W_wY_~K+kjGilISDsi*fvRo5f5pY98_%S$t`R0}9e96%{J1lO zLZ0qD5_5q=pEv9(X!rHzLFZcTI=2Z(iKeP`yGJaUAm`e>KpaCm+CzB!TSE?DGpwoY zA0Iw-*s_WUGS8nXqx@TsbW2EIQp2WUoh^uJv@!7GlQ_!4WkiZaiSy}R8e^Am`1)ZUK-BC)ZeF<;Ie%7seS$o83&U)YK& zm-7AB?Chj7q`NS5lZR>7L*2{ps~%1J0*dm=>?0Lnqb**Rc16jpnA6)w199vZZr8xG zn5G<)5^Yr_>F*EK%BGF{Z?(G|?-HkqAD~`=Gl6enMmd4$?Sdx;V*?b8=4)3tJ#4Ok z?!^4hJH-#nUmu#b1mzjdQ8*Rl9E?%(o03ttx`d>Yo6j*R)U|)u4c>?$gJ*ZsuM=N| zr3@NW3bLxMs6>9RjByE4&*HpP{t1#yz5lG1?d6!CtToFN@Zs?-L3%cs)lGC-u7%BJ zZA9_MKL4x&1+^A0Gu}Yb?-wU&f*LM+%&Samb2j5cIA7rWs;?&rZ5-S%Dazd6sx4H@ z0_93Xwlr7K6I&oFYVof|9pT^6AB*#m*}e|7;oJ9E7Ot5nU8-%DhVRx!Kuk|&T06m3 zrksSK*LCoZI?35_Fqsm&g)svpm@Hi~9t?(xwy){~6CPONI69yyADHbq50bZkBdQ40R7;wFDKHlJ*a>NS#XmKuvb1GW zwqfEPHaIwJZxAvvAYn@=OB+#tYXVG^o_AruU)8Q&$|OrCcv_HRHbmEQkTY?C^I2D* z79+~ZcxC+u_-mi|>5?J7@gc1K!Qy!7HA`&Quv6%>C)0eWmknCO#tJ@xS7rvR42@2@ zw4c!E6 zwA`k+m}onFX=wuryzZ*@TS2FVa2Aspqfc{Mx$DlfUX==<|t43lC zU5muH;0d}uf1a5~J?? zltPXWBk*Dm+~8BN+66~l6Q?>qy-U$$TCZry;84|$+?UaS{W03w?LwJYWIM`Hc!%D3 zvsh;2OcG9lj=lO#8gfV_JS^%^b2(H+>;vn2-4An>D=~VWQz^4xAh&Vx@!H|urTD0Z zP&C#qrI|+(qlPiy+>PfGLzISvJEbe?chIO&$K{sT3ib(6YkZwvwTjO@(Y)oEj@6C$ z2avqX(!7yHvHEkwq@_DwsnVvO^A;J4*oqE1gO`XFJF)lBo0s5e|JpN+8XY;W<{l2l z7PmbtY*lPqTj>*s=Ndg!(f>-!L$U_!NN_*sO3CB4e+--rq9A@iubX{+gRQ;w&^8ybnMSf%AXCt;@W?q zJ8^hu3i!1$B=+m*EnZS=yZrtk5WX+0>dUtgfhS^EDfqd(=Wiym81faVVlhHj(&s>f zvS$veP@}6j^xVG7x+iRis+e`z#VB5ulWyvJQPoP(o*#o$Uyd? z_?M9I7u;0--qCT=R5R{>fPf{|XZj>DJh|M(1EaSI$Tfjal;d2`A7)nvu`!Cu6mR2k z1{Z5w+M?dKGga@L2|85FgI8lV`>b?~F#2lcysh@Ij*ktFtOUPelm_QKYY?`QhZOmy zTkB1^&BIX=9&2CJI-N^$D^|)o&M9tfB)DrY#{Eo%V+?)hSfnKYtTs;Hw4UbOf%S6l zIai-R#b22Duu@0Jm|Du$5Pr6uj$AN06|`IH-#)^ET$NCE?);0uftE?wNSAdxB^|&> z-pvw{*rSjg)X&{}x-`4!e5P22EY@K*+{p=dspYZYngprE>URM9T?BNxBF z-)~^k(WY)Y(R2-zm20#u0;-e`aEB0Af-1_5iftL@7Xzo1JMJl+G*l(x7>^eV99HgM z>ah%%K{!)U3-sK<{adqT5n8Ol9pAM_}{Cu+^2Oqzi9<;JJYK zfDc|OvDMCm{A;sh@(Xq5^z{&HlKh1reQNrxtiX-E+@DODoORISl6Ilu_3_Wnu*^MY z(Z}+NV&{*kT`^)zwGr|>lO(csPrC+;7+&oC9kWX}U((5_ zr*ab+K!OWC)^~A?X#;FsqBAz%5_j@iBIp^hZ{S&wjL=}Dgxv5U*bCKFC6xH9IC{1t z_1BU*FnE?Gu-p>Mq`^06I_ubzLs_!MZq znZHA3kFF~>=Tx{!-_+OU{7{kh9LZwTmi&Go+>-PrPG>;k`(;6#jlK=)?PDc)^`O=m zqijbD2fQhGxjO<%oiJrdtTDVge@T7OyNtX32dD;R5?yc_^EF5E&aKb=ExdTY6#YVY zdY#r8pZNEd>5E-7aHq`2v1%9yhebN8vng- z^Y5ra@e+X-WxBU52z)YmpSaSd`nD}#-|dt!b|G%3hB6skNxPcWr4Ya9%f0b96;=f) zukS%cxg=ZhTE1Jw^WLR>Q6X0Y3GXh*Sy|TW7T{e5rpm$33MuEwK10XnC9_qT+g=^y z`V7o#TC8E)gCZ}#cMhB9U$+-J(peNy0T(XgLCr2Y8BIz%b$7g@e(B8?LPtP&18&X& zQxJKsX17esVCQ#2na6j}86Fm=@GWADZ33TF@7$m~V5Wi5` zFxeFW$B&lpLvgXx?IYZ``X$_>7hE=kN^X@EIdQnYq^zAhHs{v%L-tc&41lo!C38)i zAA@v3mTrkMPiPK@`|g?Xt$dPbN1r;$d_FGJRODg&-oLK<2lS8pyv~3E_ul)l!zXpO zfSG-A`UmlyTtvb=gx8lFFK5Sl+0b3sE_9rf%>sd3=O z3H~L8YIl&gZK0R_D$ycD>^!t= zv8~Hg`kI%p`b?WPMr{XP08e!S)YLZZ@PxW@0=8=y#LBdW?mg3E2d8;zpi@N(F5=0=W+Np0gUhFb{|5^J2@S z%7Jom#cOuF0KTjIa0W}(IB0x=8-2JSg*vDY`v^=lYIo0R1-(D(YAVh0Wu4vSM4pm0 zYMKi$cfK6!CR_+OA{3F4QN^rbG%ssg5R@)XE~|RTA0!}H4J_HoB|m2PE?l$$XY|*n zqREB)82US!N7UvxaF{HvXj0Bup@u=xp5M~7Ox@+@Q8%+sq{<>pMDTOzDF4c$wL7q4 zm4GH^F@MNm+)469m%ZjsG;v+kP_b@Ii9kn$(gq_=LL-=)?nxE+1q*gj^07@6u3z@&yp@kyi7vk=F z&W?JqW@UENTdDU4SY zPt(Rr^ve?>4vZCP46*bappgJ$#GG7sk!Dwxnlj2^W)f}nncW+}v8PR88D}ZSrpgoy87Ei5 zm0W~+uj@2NjW^AE<`Q2EI&SK-ei-UgQ@j`d@Q}jWgtO7TNE4*OGq11H&hyr85Otui ztnguUbcmU#(;BbHC8;d^t&zFvfV(LU6aQgnNxgw%CBhdIP(JCq+`14ASmTiJ_vF=B zaK>O2hF+fH7=~+-AP!T;I6mc7vB|c=QQW@un4jg9Pbi@tmvX6z-HUG$;ZYw=oaZ;< z9#%p$r%=`kTrgGyFkR86#G~6PTMW>f#=@?(CAMDL3dghE9yR8NIxaug(T0D5#O+i( z$ZzeJ#RMo*cC@FnB)KFpot{JS6D1X|b=vNx!b|n-R*k!JO9zq-)(T`~2mvTRYn1=T z-uNyC5OTuK$%UxrQK>UCwt|ksCJGK#ePS~CL_j5P-#72^xP^9{i3H{0PO%31yaiTz zFDaPq=vF5tM01{}=)5_ZO1S+zsRCod4ufsU<*_P7hvUgP*e8!x@;R% zxYK>9Em*q~26W_aQ+=J;ihUK(^0g|J4N{IKYP$Tua0^!!%I^Xs1F8FBn+^n8p zub-Tljy)%pkGQxS4zW7hG99-yz+^^g&F7ii!&F0{?VZFtM6I5bXxBc7_33leTpiqR z(tDCSN*!4uoaR(qpO2B5!#+E#zTjPUKN+)as%Tu>{k{~LW^WNibg-LC>pK20>Tc() zV~P~O!S=1It&w|u$h}>MHT5*qB;2K}r1E_W0KfqRE1nDt;V+zwV->w0w+!^x++!)C zw0hPe{@6>!MOyfnXwanaE;)^(A0S^?fNa^=96dhhf+Bb_7DSvE)D%@3!Zbu5O-mkE zu*6jL!h0E5TTXZW8fwYfmvcBqD6YN*raWQRsy{VF~aS*QeBX~KE`1WD`Q3%v0g4U~<^;O+L#^gj{tLBj zfBRvib;3G(Mhh0q2R??>Qn>YwFJ6zYZItDe&hF}kKeeyL)yUviR6q&WkQUCVF?(;W zrflT)#H#gXA&LoQD)SWl$2X3ED`csVd{Uqc`^VW?3anyBot%&8CUr=1N<3Y5i_t1} zx4X?zm0yNLJo+0~SG9_Qs5-^4mBW{)Zo;$_ZR_6;y1LI28?n<74n#&2X@{59yO$l_ zylxK3BzY*E5o#g#r^$oKMuB*vfn#iP>9ta78{J8XQx#PTgTT_U`^99rY%lf_qgdx5 z_4uwT{D{2b#E<5wqr;k7g`wx}DPI=KTnyOG7za)EuWUb1KwL$c)f(iA&8(j1oOfsH zhzXbWnK?ecm$Qfzxb7V;FEaOiCyyqL9E7uu0iGxe;Zjpn**FoVm7;{3+TSHlyRoSu z!b*CRqnWS4AGB@Af_vYqKKsol+Z>6K{Eo@+lZuh{JI?+uVy(^;|az^8+)vbRpZ`=A?e!)m0-5O)Nc#I;SxzUUXH2 z4aqFa>0Ym*_VGiMNwBLWN)+Ncei@+{X-~Ky2+i$ojc;Te`Kemm+P=OLfLq;aVLTyG zI`!x8i<;jImLEOLdz=?apTb&*HW*275$l}RG!2dM4pUK-8^Yl$EcJA2S(dO2m&aP% zC)O+#9x3NnulLo|6^)V)M8&s%ztxI~_SqYB7qBcu;vco`Zg#b(K6sbPn(vwRlC8C{ zOf@YLGCErNYAwp^mw~^77b~0`&>%gbaDbg^-bM{7eHtV39?iCZ3Olzvn#&MVAa6rG zp8)EhYi1tikFZ1Yme4z6czHhWna--5 z^32?V`zOtKF=IVNRmG8+g>DKxlV8LBwC_lw9yM~@)xmjatpu}YjIkRdQ}(&p)asv6 zm)_DUjmB=I_7$|XE9uR<*VP-qd%sK1^bzO*QXAm!=yK~{6%B|n3TXMx0Ubx}d!quY zlSKk8fKhwsy-~!5<@qgnI-4$WjLdu!-FsX>kYx!g0QVD|?Ef5=WRtzxBTS{ZB?wn* z!#z~A6}sn~O8zi%8_^eCGW6=Y^UUZqbq*YQD9eJ0>l1<+z2$j*FLjOPTnrBTy5A~Z zEu*HtRKU!9q<2iiJ7wBo*=5p|rUKNU^i~?tzb88til8&i8ladHC_uj`K>8}MtjyHD zn~-DFt|uq~L*DN=knZmf{Yy>o<&j^E@wbHUIaU~1-?R$x?1U3@RbbHsBFOj@5g}7o zov{N!kuj<$cjrdaXTKqIC5ug@F5&!Wu9#iQMc|ikRJV~u)j(6$whMa~V38#lX2Y7L z@pms~Va*q*rAx&u+ez-jMoi`bFGDalW*1DP zAxMXEfsSxcm3PKN@3x2kfL`pSmqMrg6i42U&C9vmJ~PcqB_M96$d_mjvnrf>0JD3B z`nXQ#Xd%X!kuO+0^(^hhG38}n(++tX&06RW3s={q9Z&{ch=+-9wFxWAKdMW-y@vmH zfJ11m8~qbpsR{NaOpqIP^)023ii|v4Be%#9{d>$NB3L#ShODj1fi9dnQe!zCU|GC* z6xG7P^;NQ0=O2Lh>K`CF(;Gi!b;RQ|YjPNVOzPp~O**p0LqK3y4x%n!%bKmqqqOaa z`En(kxE(j+{{9J^v`L1k71wcQdTTQ(p04&i7v^xz1S_j}L3;2OYh4W=PH1Ej8|vXL zZ#N2z==#zB10#TSdu`Xd_?Y93NRHnUnl5mibmFh@Nao3^@HQ7sVjkyjK?w0$mVj5> zGfYVdWU#ATK28@7jK`8&(z_bZ&)-50RbU-VN zz}Uw-TpjLP^Y^vxyiA`v)=gN;(!3D-gXfwZ+s4L}v%KtW8GOE(Bc}B`p|5SmYw>J! zvE_3jFbF>Ai%^?01e+`FN>Onq&OnzT>SZ91mtJzTck8~u?~WGEclc0(B>UiSHZPQn z5-ag5IT*ea?lIPKHYrUG4=h@@J{Or0ggz!toQuD!>yOp7ZXrp`VQU#wH3}Itia9S<6K*j!4Nv9s4<>9pWt*(I?$oub@~~yfgAKcHroGf zs48DJJmIT#@g^R{iNmAe)#p(n+)_wEzAt!mE7*a0(G7CBoUPeN(8ur(KZ|IGA}9x= zDSYyH?}N*?Jg-3u4-a3$N)`UU7}Eb|QvcNzIsE-Y+BPy|4FL7&W>K|B6~BN^d+5Yx z1ybkP&gFKd#0ZY^`m1D3<>GLh6dcZ$>VBVF@4)BxqcF91SD<#uTB0yelWf*(U1~hl zWn!JsCy(RS8jfu&&9vgD%3dv*+D7I(H_sGfX_g&BOpav8(J}rNmlbie3&8YKRXO5E z%qtTw_es>8{{caW$Nu79Vzd71GKQb3GmQj(?K3_g5uQFMSc%mU)Lw7by_)?cd z>lpgmq|o;&ViDfO1&e!K(JPoZMO?8`b(OkOmZ#b)k7TKqjZL}bvZg6xlh!g0`m1HJ zrmT~rpIV`9Nb8^-H+8*t@SCWre{mX%Q*a)q?m0u!>hD$~yxZS8XR^$?BZL)(V4yuAgAI9o?Z*39AGaP3d1Z=oei@rUlU%^(Q^?wv|y66l<%oJ9WmZ@&x zxue;^B-m^u{yZA9e%ZtMNi6-C9QATSjJQ!3ddb^Ex_=XM9EUB>1N&7tb0pMo0?n4$ z^HqT4+`&fq&fy@z23-8}p8xS0_}`YqfA{C*4=&Ndh7_Njbj8+3g<)mrpjaWDKaV>( zF9G88cnbo+m%8(s#ArO4_OWBnqR}6PtMd#;-xp0!u`_`=>=~Jow57=^zFJd%I4eSj zyuiDBG%--l4FjTW6YbVrow+}>h0L&6+RTg>R*y#(MF!?|*Mcdud~9%{`;N5vu7-9d zDk_{miF2f4WhJ9JY}TW;37D=yZ<$o=RmCl9T{cbFu^#>54T&K%RMWq;V0i&61|K4wfWM=o}4Rj7k;$ zt=EU{4IY0P$v?fPA*wnFTN}qJ#eP#Vk)%IP&yR-&aJW6H(F1ynV3!&{sxB3wEL2-*2KF$XJuCr zGJ>~VcfvfC&%^Hw>KhWaL|9bQVeE38Y+iIaqxxWuWosESA*9V9-<|{#0O{SNFsghj zhF!3BrI+P=aUH{SWm?kF%N2=MZ2t$qI@NCz6qOQk^dks;ae>&)7~1P`Fu!H5$*xXK zLXo0*s^;}mE;vL{QC8D+x>j%mjE%|IvdS@8Ro!wW?`n?d^)rFS!e3M7-Bn0|?fQ5~ z&+@o{q-oOeb_O)jN&|p{Ee-C;|Kl$IcWv}PehxFOv4Pkq;MuFLXV8o9`4iixT`~tI zw(=@NH(y$7$j!U%uiqr-jlaHN`hmh=kU*--+Z1k5=zB6(U~)%u-Ul>q)aioT8agmA zhG4nWiJx;B#6W6UqSYBXZkE*#odh{f3bOo${6t>R^*fM!k*daG=CGphZ!2GJZUk=b zH^+Te1!qK~x!E{Pl+|CUbirq4YfWaRtWY8+*OAq`Yj3ya+6?GxpD|ljBBrpAl%h8s z5|MrstRdWwaq2*t{6$@=bMm32j?F7W{g2waqFDTExs$L)21@wFZ@o*JvjM;>w3Ozj z^zY$XRzf*fCWXZhd1E{8GjP&GZwxhI-T1ZwC3Rv|J3i-5#CnCcW&%ccwNy*iSEkXt zQ3rY@4Iwlg&My(<>2ub!wjPNlmtk(PjPX6f3lgxDSVKw5Z^8fP-@E_ca)7(e_{y5{ z>Bg#@vc{9uOH7@dFgY1IrDD`3$`%XhM!6vJgLql7;Ih(=teeMYfi~uK;dWFA)cILi z+aE*c^X!XkL@g%Z4y461#c=bAH4hr4(1Ie#P6g%DvxVREz^>4c;%;)Bcn$osGE}}A z7j@C${=xCG5~PibOFdU(>_?4J!C*;%uvpsj{ggMl`NTt@=#zZ^bXcN+QPN78_X}I* zl*zfHUZF|~q?OFJ&G1QHF{6osZ78>KE;msqt`^RH-f!WUPIoU|!hy(HQ2&h2Ns6io zaow^;Ty>G{yjY*L)H7ZXHK(H58ub(+Tk5j$@JGF!Ln^62bo7w7rr=@fU{#0zESqp! zUPPzT-5|b?|L5ucLza_+UC4rRV$uircOChtK9SFEcv%v95_@^YOR_@i7egA6%tDe% z#)#&!06dMuPc1EJvNZV|NqoW6JaEqcGnV|1js7jj{I73394zehd%}N5;;k$BaKV65 z&!H_cufiJJ-A)!Ht3`T^WDs((v&sAZpqDJTdJ&2`QH$R|C_xw43~s>Gfg zR8H*)a_27uaMKsjzeHt=Bu`qLkdwF16`0Y!ch6ajXR?dY*!PxahtNjz;mzkg;L6mG zQX-fqx&i5{<4KmJ1_$IQxyIzn@uYV4dWiz?@875<%zLP=Hh>@F=f}D=?B;T)oM_0y zxFB$WXB};(g14_Y^=4O<$MK}Z! zVIffN|GwIwG4K66Yv%}m5ve2D=|+Gcw!Yqw)13X^^Q>co?O#&N^wm=-n@{()GSPVQ zZVOFB!V+$%yE(chUCQ2;M+NV`&d5!XB`YPw$@2i7{xh%pzvbJ&?>4UF&`_ufFHVLp z%m?*@9n1a4?3vk0GnHh(y(K2=2wcBXFgH;RJ{~pi{tj8b7#6Hh@u2=~DM#W{)Dw>j zHv|~6AEz#$y&Xnjboy4Etf7?xxi-hW3K3dRG3YfJlBAj*U#w(NlOF%=*x3T63QiRD zntR9F^LKfTI^jiYiZ~Oo#uBIvvY;0HAUd=P_|JGwSk0+xi7fA{r{6wn?5d+8=ElRZKK4U@N8;6+x$10Uy^gQ1Tyd(I z>}s)7*YD(!z?|fAmF&xw>)Ose<9NW$(-y~m#;-W8_2QYctO*B-jJryixHK3wGw}71 z2*noj;}>gheDYxac|CIBnf{K&zAM-*f%)maOHkOITib6eSQEi=`F7jSJ3Jw-oMxWS zj)@n|J=PJkEpo?~vpmv1Q=T@Qcs5ffVZpuMm0y0FTP#e zyR_eMn^oGnC!VS+Pwqa*EvUic`A<3IxyRkphZeeTO}g!KOVc(m=owGbO_q7aFGJNf zr%GSkF7FenxjI57_NaI2M9!iEzk?UHc|59^vwq6)$$f=hzPzVy&oWe9x+g&=<=ywy z7fuVhIv9vA7Zoa(^0MYM^pttR23#DZvDhV9>fwPUH%_iyoTj*ZB7-b&Oh@FwqI8z$ zrn=fpRnyj9ImEzIP^ohDwe?G#e5F}phc%zy2{Z0a>v~?gtYY82Pbp3BwRSQ;dy&0m zdf23UXKK3?!X-~U?|QvhFEx4Il1fvJ7j+6(^i4A!cSbPXd46fHql|$}&-bHwd(K?m zwd3*YTPu7ERn7$6Ntg8v64tr(o^w+Wch|fL$0P;pE*+4nMIKf`46qQxV)%a(0JV%9 A!2kdN literal 0 HcmV?d00001 diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index bb2d7b61e..25c599a17 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -353,6 +353,17 @@ class TestFileJpeg(PillowTestCase): reloaded.save(f, quality='keep', progressive=True) reloaded.save(f, quality='keep', optimize=True) + def test_bad_mpo_header(self): + """ Treat unknown MPO as JPEG """ + # Arrange + + # Act + # Shouldn't raise error + im = Image.open("Tests/images/sugarshack_bad_mpo_header.jpg") + + # Assert + self.assertEqual(im.format, "JPEG") + if __name__ == '__main__': unittest.main() From eee98f8043353040eefb645a1c8fc9704ff99940 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 19 Jul 2015 20:27:53 +0300 Subject: [PATCH 0444/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 53524da1f..c3f7ebad8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Treat MPO with unknown header as base JPEG file #1350 + [hugovk, radarhere] + - Added various tests #1330, #1344 [radarhere] From 31edcad8f84b7a7e9bde085f34f00c3c010270b2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 21 Jul 2015 18:23:55 +1000 Subject: [PATCH 0445/1037] Set landscape max-line-length --- .landscape.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.landscape.yaml b/.landscape.yaml index c869da5b4..ddd9cef32 100644 --- a/.landscape.yaml +++ b/.landscape.yaml @@ -1,2 +1,3 @@ strictness: medium test-warnings: yes +max-line-length: 80 From a90e72076a60670b10db9ce7b87f243a4fa132cc Mon Sep 17 00:00:00 2001 From: Anton Vlasenko Date: Thu, 23 Jul 2015 11:29:26 +0200 Subject: [PATCH 0446/1037] Fixing typo to have proper testing --- Tests/test_file_gif.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index c40e5f6b0..70438eb03 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -93,7 +93,7 @@ class TestFileGif(PillowTestCase): im.save(out, save_all=True) reread = Image.open(out) - self.assertEqual(im.n_frames, 5) + self.assertEqual(reread.n_frames, 5) def test_palette_handling(self): # see https://github.com/python-pillow/Pillow/issues/513 From 1589052fb52742ad14bc2161920ae747fcf38173 Mon Sep 17 00:00:00 2001 From: Nicholas Jones Date: Mon, 27 Jul 2015 12:17:49 -0400 Subject: [PATCH 0447/1037] Fix setup.py for Solaris/SmartOS --- setup.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setup.py b/setup.py index edfd24c41..74fdf9724 100644 --- a/setup.py +++ b/setup.py @@ -305,6 +305,10 @@ class pil_build_ext(build_ext): _add_directory(library_dirs, "/usr/pkg/lib") _add_directory(include_dirs, "/usr/pkg/include") + elif sys.platform.startswith("sunos5"): + _add_directory(library_dirs, "/opt/local/lib") + _add_directory(include_dirs, "/opt/local/include") + # FIXME: check /opt/stuff directories here? # From 9e994b9e4212524abaccbf2d07ce0abbeacbf0ee Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 28 Jul 2015 19:59:52 +1000 Subject: [PATCH 0448/1037] Changed indentation to be consistent --- setup.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index edfd24c41..02baf8c12 100644 --- a/setup.py +++ b/setup.py @@ -302,12 +302,11 @@ class pil_build_ext(build_ext): self.add_multiarch_paths() elif sys.platform.startswith("netbsd"): - _add_directory(library_dirs, "/usr/pkg/lib") - _add_directory(include_dirs, "/usr/pkg/include") + _add_directory(library_dirs, "/usr/pkg/lib") + _add_directory(include_dirs, "/usr/pkg/include") # FIXME: check /opt/stuff directories here? - # # locate tkinter libraries if _tkinter: From 87d00fe441b74d1da981236c9232adbb07fd2d8b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 29 Jul 2015 20:01:23 +1000 Subject: [PATCH 0449/1037] Catch TypeError in _getexif file seek --- PIL/JpegImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 7cb280764..da8c6922d 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -423,7 +423,7 @@ def _getexif(self): # get exif extension try: file.seek(exif[0x8769]) - except KeyError: + except (KeyError, TypeError): pass else: info = TiffImagePlugin.ImageFileDirectory(head) From 3a25b1cd9bb81a2bad8531d2e3da4c8d1847f148 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 29 Jul 2015 23:38:26 +1000 Subject: [PATCH 0450/1037] Added test for _getexif TypeError --- Tests/images/exif_typeerror.jpg | Bin 0 -> 52510 bytes Tests/test_file_jpeg.py | 6 ++++++ 2 files changed, 6 insertions(+) create mode 100644 Tests/images/exif_typeerror.jpg diff --git a/Tests/images/exif_typeerror.jpg b/Tests/images/exif_typeerror.jpg new file mode 100644 index 0000000000000000000000000000000000000000..550859a0abbe0b4282d03de812eee46ba7e97f5e GIT binary patch literal 52510 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 literal 0 HcmV?d00001 diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 25c599a17..371b7037b 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -166,6 +166,12 @@ class TestFileJpeg(PillowTestCase): im = hopper() im.save(f, 'JPEG', quality=90, exif=b"1"*65532) + def test_exif_typeerror(self): + im = Image.open('Tests/images/exif_typeerror.jpg') + + # Should not raise a TypeError + im._getexif() + def test_progressive_compat(self): im1 = self.roundtrip(hopper()) im2 = self.roundtrip(hopper(), progressive=1) From 50a3c32d4c3a1f96ca5598ba3beb5562f7024b26 Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 29 Jul 2015 22:55:01 +0300 Subject: [PATCH 0451/1037] Test on Python 3.5/3.6 dev --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 2397376fb..caa9746c8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,8 @@ python: - "2.7_with_system_site_packages" # For PyQt4 - 3.2 - 3.3 + - 3.5-dev + - nightly install: - "travis_retry sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-qt4 ghostscript libffi-dev libjpeg-turbo-progs cmake imagemagick" @@ -107,6 +109,9 @@ after_script: matrix: fast_finish: true + allow_failures: + - python: 3.5-dev + - python: nightly env: global: From 45efd748640b2b77458736760be0935cf47cb9da Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 30 Jul 2015 22:23:57 +1000 Subject: [PATCH 0452/1037] Added Python 3.3 to AppVeyor --- appveyor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 6e953c46e..0e3224540 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,6 +9,8 @@ environment: - PYTHON: C:/Python34 - PYTHON: C:/Python27 - PYTHON: C:/Python34-x64 + - PYTHON: C:/Python33 + - PYTHON: C:/Python33-x64 install: - git clone https://github.com/wiredfool/pillow-depends.git c:\pillow-depends - xcopy c:\pillow-depends\*.zip c:\pillow\winbuild\ From 63d62b4192e9cf5de682d5ed952d8d48c24107a6 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 30 Jul 2015 22:07:03 +0300 Subject: [PATCH 0453/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index c3f7ebad8..e55c9e501 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Added Python 3.3 to AppVeyor #1363 + [radarhere] + - Treat MPO with unknown header as base JPEG file #1350 [hugovk, radarhere] From f5df0b86fcf92b6bcc9f777bfa2621d379b973a6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 31 Jul 2015 20:59:59 +1000 Subject: [PATCH 0454/1037] Fixed PSDraw stdout Python 3 compatibility --- PIL/EpsImagePlugin.py | 7 ++++--- PIL/ImageFile.py | 3 +++ PIL/PSDraw.py | 8 +++---- Tests/test_psdraw.py | 49 ++++++++++++++++++++++++++++++------------- 4 files changed, 46 insertions(+), 21 deletions(-) diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py index 842664960..e82656fae 100644 --- a/PIL/EpsImagePlugin.py +++ b/PIL/EpsImagePlugin.py @@ -376,9 +376,10 @@ def _save(im, fp, filename, eps=1): pass base_fp = fp - fp = NoCloseStream(fp) - if sys.version_info[0] > 2: - fp = io.TextIOWrapper(fp, encoding='latin-1') + if fp != sys.stdout: + fp = NoCloseStream(fp) + if sys.version_info[0] > 2: + fp = io.TextIOWrapper(fp, encoding='latin-1') if eps: # diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index 52d21e1e8..c55ace55b 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -461,6 +461,9 @@ def _save(im, fp, tile, bufsize=0): # But, it would need at least the image size in most cases. RawEncode is # a tricky case. bufsize = max(MAXBLOCK, bufsize, im.size[0] * 4) # see RawEncode.c + if fp == sys.stdout: + fp.flush() + return try: fh = fp.fileno() fp.flush() diff --git a/PIL/PSDraw.py b/PIL/PSDraw.py index a090a8f64..d4e7b18cc 100644 --- a/PIL/PSDraw.py +++ b/PIL/PSDraw.py @@ -16,11 +16,12 @@ # from PIL import EpsImagePlugin - +import sys ## # Simple Postscript graphics interface. + class PSDraw(object): """ Sets up printing to the given file. If **file** is omitted, @@ -29,12 +30,11 @@ class PSDraw(object): def __init__(self, fp=None): if not fp: - import sys fp = sys.stdout self.fp = fp def _fp_write(self, to_write): - if bytes is str: + if bytes is str or self.fp == sys.stdout: self.fp.write(to_write) else: self.fp.write(bytes(to_write, 'UTF-8')) @@ -47,7 +47,7 @@ class PSDraw(object): "/showpage { } def\n" "%%EndComments\n" "%%BeginDocument\n") - # self.fp_write(ERROR_PS) # debugging! + # self._fp_write(ERROR_PS) # debugging! self._fp_write(EDROFF_PS) self._fp_write(VDI_PS) self._fp_write("%%EndProlog\n") diff --git a/Tests/test_psdraw.py b/Tests/test_psdraw.py index 9606a4392..427c6c707 100644 --- a/Tests/test_psdraw.py +++ b/Tests/test_psdraw.py @@ -1,25 +1,17 @@ from helper import unittest, PillowTestCase +from PIL import Image, PSDraw +import os +import sys + class TestPsDraw(PillowTestCase): - def test_draw_postscript(self): - - # Based on Pillow tutorial, but there is no textsize: - # http://pillow.readthedocs.org/en/latest/handbook/tutorial.html - - # Arrange - from PIL import Image - from PIL import PSDraw - tempfile = self.tempfile('temp.ps') - fp = open(tempfile, "wb") - + def _create_document(self, ps): im = Image.open("Tests/images/hopper.ppm") title = "hopper" box = (1*72, 2*72, 7*72, 10*72) # in points - # Act - ps = PSDraw.PSDraw(fp) ps.begin_document(title) # draw diagonal lines in a cross @@ -35,14 +27,43 @@ class TestPsDraw(PillowTestCase): ps.text((3*72, 4*72), title) ps.end_document() + + def test_draw_postscript(self): + + # Based on Pillow tutorial, but there is no textsize: + # http://pillow.readthedocs.org/en/latest/handbook/tutorial.html + + # Arrange + tempfile = self.tempfile('temp.ps') + fp = open(tempfile, "wb") + + # Act + ps = PSDraw.PSDraw(fp) + self._create_document(ps) fp.close() # Assert # Check non-zero file was created - import os self.assertTrue(os.path.isfile(tempfile)) self.assertGreater(os.path.getsize(tempfile), 0) + def test_stdout(self): + # Temporarily redirect stdout + try: + from cStringIO import StringIO + except ImportError: + from io import StringIO + old_stdout = sys.stdout + sys.stdout = mystdout = StringIO() + + ps = PSDraw.PSDraw() + self._create_document(ps) + + # Reset stdout + sys.stdout = old_stdout + + self.assertNotEqual(mystdout.getvalue(), "") + if __name__ == '__main__': unittest.main() From ab9f995dd846e403e39cf6f4f3c17978fc904d76 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 31 Jul 2015 19:13:20 +0300 Subject: [PATCH 0455/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index e55c9e501..ac3c41e29 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Fixed PSDraw stdout Python 3 compatibility #1365 + [radarhere] + - Added Python 3.3 to AppVeyor #1363 [radarhere] From 7c0554c74e449fd3a7d34dc4349837f325ee0f4b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 1 Aug 2015 17:44:13 +1000 Subject: [PATCH 0456/1037] Added support for ImageGrab.grab to OS X --- PIL/ImageGrab.py | 45 ++++++++++++++++++++++++------------ Tests/test_imagegrab.py | 5 ++-- docs/reference/ImageGrab.rst | 16 +++++++------ 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/PIL/ImageGrab.py b/PIL/ImageGrab.py index ef0135334..c52189145 100644 --- a/PIL/ImageGrab.py +++ b/PIL/ImageGrab.py @@ -18,31 +18,46 @@ from PIL import Image import sys -if sys.platform != "win32": - raise ImportError("ImageGrab is Windows only") +if sys.platform not in ["win32", "darwin"]: + raise ImportError("ImageGrab is OS X and Windows only") -try: - # built-in driver (1.1.3 and later) - grabber = Image.core.grabscreen -except AttributeError: - # stand-alone driver (pil plus) - import _grabscreen - grabber = _grabscreen.grab +if sys.platform == "win32": + try: + # built-in driver (1.1.3 and later) + grabber = Image.core.grabscreen + except AttributeError: + # stand-alone driver (pil plus) + import _grabscreen + grabber = _grabscreen.grab +elif sys.platform == "darwin": + import os + import tempfile + import subprocess def grab(bbox=None): - size, data = grabber() - im = Image.frombytes( - "RGB", size, data, - # RGB, 32-bit line padding, origo in lower left corner - "raw", "BGR", (size[0]*3 + 3) & -4, -1 - ) + if sys.platform == "darwin": + f, file = tempfile.mkstemp('.png') + os.close(f) + subprocess.call(['screencapture', '-x', file]) + im = Image.open(file) + im.load() + os.unlink(file) + else: + size, data = grabber() + im = Image.frombytes( + "RGB", size, data, + # RGB, 32-bit line padding, origo in lower left corner + "raw", "BGR", (size[0]*3 + 3) & -4, -1 + ) if bbox: im = im.crop(bbox) return im def grabclipboard(): + if sys.platform == "darwin": + raise NotImplementedError("Method is not implemented on OS X") debug = 0 # temporary interface data = Image.core.grabclipboard(debug) if isinstance(data, bytes): diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py index 7d156d498..bdc9edfad 100644 --- a/Tests/test_imagegrab.py +++ b/Tests/test_imagegrab.py @@ -37,11 +37,12 @@ class TestImageGrabImport(PillowTestCase): exception = e # Assert - if sys.platform == 'win32': + if sys.platform in ["win32", "darwin"]: self.assertIsNone(exception, None) else: self.assertIsInstance(exception, ImportError) - self.assertEqual(str(exception), "ImageGrab is Windows only") + self.assertEqual(str(exception), + "ImageGrab is OS X and Windows only") if __name__ == '__main__': diff --git a/docs/reference/ImageGrab.rst b/docs/reference/ImageGrab.rst index 117be885b..22ea72797 100644 --- a/docs/reference/ImageGrab.rst +++ b/docs/reference/ImageGrab.rst @@ -1,21 +1,21 @@ .. py:module:: PIL.ImageGrab .. py:currentmodule:: PIL.ImageGrab -:py:mod:`ImageGrab` Module (Windows-only) +:py:mod:`ImageGrab` Module (OS X and Windows only) ========================================= The :py:mod:`ImageGrab` module can be used to copy the contents of the screen or the clipboard to a PIL image memory. -.. note:: The current version works on Windows only. +.. note:: The current version works on OS X and Windows only. .. versionadded:: 1.1.3 .. py:function:: PIL.ImageGrab.grab(bbox=None) Take a snapshot of the screen. The pixels inside the bounding box are - returned as an "RGB" image. If the bounding box is omitted, the entire - screen is copied. + returned as an "RGB" image on Windows or "RGBA" on OS X. + If the bounding box is omitted, the entire screen is copied. .. versionadded:: 1.1.3 @@ -28,6 +28,8 @@ or the clipboard to a PIL image memory. .. versionadded:: 1.1.4 - :return: An image, a list of filenames, or None if the clipboard does - not contain image data or filenames. Note that if a list is - returned, the filenames may not represent image files. + :return: On Windows, an image, a list of filenames, + or None if the clipboard does not contain image data or filenames. + Note that if a list is returned, the filenames may not represent image files. + + On Mac, this is not currently supported. From 63ea8ff5ebf7db8342668ca418dc301e41ed3791 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 1 Aug 2015 10:55:04 +0300 Subject: [PATCH 0457/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index ac3c41e29..f54722e36 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Added support for ImageGrab.grab to OS X #1367 + [radarhere] + - Fixed PSDraw stdout Python 3 compatibility #1365 [radarhere] From 6ffa876d33f8cd2b0fe708074368a85e74c78596 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 4 Aug 2015 15:37:41 +0300 Subject: [PATCH 0458/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index f54722e36..d630dfb08 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Changed register calls to use format property #1333 + [radarhere] + - Added support for ImageGrab.grab to OS X #1367 [radarhere] From 457d39832d4f433a59c78598849eef3d073cf7e5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 5 Aug 2015 20:54:33 +1000 Subject: [PATCH 0459/1037] Added support for pathlib Path objects to open and save --- PIL/Image.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 861599bf7..87d77ef09 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1653,13 +1653,15 @@ class Image(object): may have been created, and may contain partial data. """ + filename = "" if isPath(fp): filename = fp - else: - if hasattr(fp, "name") and isPath(fp.name): - filename = fp.name - else: - filename = "" + elif sys.version_info >= (3, 4): + from pathlib import Path + if isinstance(fp, Path): + filename = str(fp.resolve()) + elif hasattr(fp, "name") and isPath(fp.name): + filename = fp.name # may mutate self! self.load() @@ -1687,8 +1689,8 @@ class Image(object): else: save_handler = SAVE[format.upper()] - if isPath(fp): - fp = builtins.open(fp, "wb") + if filename: + fp = builtins.open(filename, "wb") close = 1 else: close = 0 @@ -2277,11 +2279,15 @@ def open(fp, mode="r"): if mode != "r": raise ValueError("bad mode %r" % mode) + filename = "" if isPath(fp): filename = fp - fp = builtins.open(fp, "rb") - else: - filename = "" + elif sys.version_info >= (3, 4): + from pathlib import Path + if isinstance(fp, Path): + filename = str(fp.resolve()) + if filename: + fp = builtins.open(filename, "rb") try: fp.seek(0) From cf4145e2c9c838336f84dcf5e67ced3b2e4cfc22 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 5 Aug 2015 21:29:24 +1000 Subject: [PATCH 0460/1037] Added test for pathlib --- Tests/test_image.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Tests/test_image.py b/Tests/test_image.py index bd5fd3522..fa7f8ec06 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -1,6 +1,7 @@ from helper import unittest, PillowTestCase, hopper from PIL import Image +import sys class TestImage(PillowTestCase): @@ -48,6 +49,14 @@ class TestImage(PillowTestCase): im = io.BytesIO(b'') self.assertRaises(IOError, lambda: Image.open(im)) + @unittest.skipIf(sys.version_info < (3, 4), + "pathlib only available in Python 3.4 or later") + def test_pathlib(self): + from pathlib import Path + im = Image.open(Path("Tests/images/hopper.jpg")) + self.assertEqual(im.mode, "RGB") + self.assertEqual(im.size, (128, 128)) + def test_internals(self): im = Image.new("L", (100, 100)) From 7775ff3ac73f0d6444432bafc8ee140d14cfbb3d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 5 Aug 2015 22:32:15 +1000 Subject: [PATCH 0461/1037] Updated docstrings --- PIL/Image.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 87d77ef09..0c254c61d 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1640,7 +1640,7 @@ class Image(object): implement the ``seek``, ``tell``, and ``write`` methods, and be opened in binary mode. - :param fp: File name or file object. + :param fp: A filename (string), pathlib.Path object or file object. :param format: Optional format override. If omitted, the format to use is determined from the filename extension. If a file object was used instead of a filename, this @@ -2267,9 +2267,9 @@ def open(fp, mode="r"): :py:meth:`~PIL.Image.Image.load` method). See :py:func:`~PIL.Image.new`. - :param fp: A filename (string) or a file object. The file object - must implement :py:meth:`~file.read`, :py:meth:`~file.seek`, and - :py:meth:`~file.tell` methods, and be opened in binary mode. + :param fp: A filename (string), pathlib.Path object or a file object. + The file object must implement :py:meth:`~file.read`, :py:meth:`~file.seek`, + and :py:meth:`~file.tell` methods, and be opened in binary mode. :param mode: The mode. If given, this argument must be "r". :returns: An :py:class:`~PIL.Image.Image` object. :exception IOError: If the file cannot be found, or the image cannot be From d958d4853dfb6bdc3f4251aeb07331dada23d622 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 5 Aug 2015 16:42:20 +0300 Subject: [PATCH 0462/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index d630dfb08..2decc2966 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Added support for pathlib Path objects to open and save #1372 + [radarhere] + - Changed register calls to use format property #1333 [radarhere] From f6d11a2803aa0c04a9470e94e658e28a767c0426 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 6 Aug 2015 00:23:14 +1000 Subject: [PATCH 0463/1037] Only use fast rotate operations if the expand flag is in use or the image is square --- PIL/Image.py | 2 +- Tests/test_image_rotate.py | 14 +++++++++----- _imaging.c | 6 ++++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 0c254c61d..77408e0fb 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1621,7 +1621,7 @@ class Image(object): if self.mode in ("1", "P"): resample = NEAREST - return self._new(self.im.rotate(angle, resample)) + return self._new(self.im.rotate(angle, resample, expand)) def save(self, fp, format=None, **params): """ diff --git a/Tests/test_image_rotate.py b/Tests/test_image_rotate.py index 38391adae..26c0bd729 100644 --- a/Tests/test_image_rotate.py +++ b/Tests/test_image_rotate.py @@ -1,19 +1,23 @@ from helper import unittest, PillowTestCase, hopper +from PIL import Image class TestImageRotate(PillowTestCase): def test_rotate(self): - def rotate(mode): - im = hopper(mode) - out = im.rotate(45) + def rotate(im, mode, angle): + out = im.rotate(angle) self.assertEqual(out.mode, mode) self.assertEqual(out.size, im.size) # default rotate clips output - out = im.rotate(45, expand=1) + out = im.rotate(angle, expand=1) self.assertEqual(out.mode, mode) self.assertNotEqual(out.size, im.size) for mode in "1", "P", "L", "RGB", "I", "F": - rotate(mode) + im = hopper(mode) + rotate(im, mode, 45) + for angle in 90, 270: + im = Image.open('Tests/images/test-card.png') + rotate(im, im.mode, angle) if __name__ == '__main__': diff --git a/_imaging.c b/_imaging.c index 895faac54..7fd2da88e 100644 --- a/_imaging.c +++ b/_imaging.c @@ -1550,7 +1550,8 @@ _rotate(ImagingObject* self, PyObject* args) double theta; int filter = IMAGING_TRANSFORM_NEAREST; - if (!PyArg_ParseTuple(args, "d|i", &theta, &filter)) + int expand; + if (!PyArg_ParseTuple(args, "d|i|i", &theta, &filter, &expand)) return NULL; imIn = self->image; @@ -1563,7 +1564,8 @@ _rotate(ImagingObject* self, PyObject* args) /* Rotate with resampling filter */ imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize); (void) ImagingRotate(imOut, imIn, theta, filter); - } else if (theta == 90.0 || theta == 270.0) { + } else if ((theta == 90.0 || theta == 270.0) + && (expand || imIn->xsize == imIn->ysize)) { /* Use fast version */ imOut = ImagingNew(imIn->mode, imIn->ysize, imIn->xsize); if (imOut) { From e271471f8e2e6a28d2f71cfe6a94659dc4c42b6b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 6 Aug 2015 00:24:08 +1000 Subject: [PATCH 0464/1037] Flake8 fix --- PIL/Image.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 0c254c61d..3d95befde 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -2268,8 +2268,9 @@ def open(fp, mode="r"): :py:func:`~PIL.Image.new`. :param fp: A filename (string), pathlib.Path object or a file object. - The file object must implement :py:meth:`~file.read`, :py:meth:`~file.seek`, - and :py:meth:`~file.tell` methods, and be opened in binary mode. + The file object must implement :py:meth:`~file.read`, + :py:meth:`~file.seek`, and :py:meth:`~file.tell` methods, + and be opened in binary mode. :param mode: The mode. If given, this argument must be "r". :returns: An :py:class:`~PIL.Image.Image` object. :exception IOError: If the file cannot be found, or the image cannot be From 79f588b0098e6c50691109e24c99751b7714f2a5 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 9 Aug 2015 11:27:24 +0300 Subject: [PATCH 0465/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 2decc2966..8076894be 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Fix fast rotate operations #1373 + [radarhere] + - Added support for pathlib Path objects to open and save #1372 [radarhere] From b1e081bfc2cea3ddfd070d311cbd9ebff7f6380b Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 11 Aug 2015 14:15:08 +0300 Subject: [PATCH 0466/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 8076894be..c135798fb 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Test on Python 3.5 dev and 3.6 nightly #1361 + [hugovk] + - Fix fast rotate operations #1373 [radarhere] From e9f0d53382f3ccca35cce7a8d0cdba22fc8f2099 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 11 Aug 2015 23:37:41 +1000 Subject: [PATCH 0467/1037] Disabled Travis coverage scripts for nightly build --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index caa9746c8..fc687d5ed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,12 +35,12 @@ install: - pushd depends && ./install_openjpeg.sh && popd script: - - coverage erase + - if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage erase; fi - python setup.py clean - CFLAGS="-coverage" python setup.py build_ext --inplace - - coverage run --append --include=PIL/* selftest.py - - coverage run --append --include=PIL/* -m nose -vx Tests/test_*.py + - if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage run --append --include=PIL/* selftest.py; fi + - if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage run --append --include=PIL/* -m nose -vx Tests/test_*.py; fi - check-manifest --ignore "depends/*" after_success: From 93a8e43ddf1ff33961d2d50f369b6440ab4a49e4 Mon Sep 17 00:00:00 2001 From: Sandy Date: Thu, 20 Aug 2015 15:25:08 -0400 Subject: [PATCH 0468/1037] ImagePalette: Add param documentation --- PIL/ImagePalette.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/PIL/ImagePalette.py b/PIL/ImagePalette.py index 5aabaa138..f80259f04 100644 --- a/PIL/ImagePalette.py +++ b/PIL/ImagePalette.py @@ -22,7 +22,19 @@ from PIL import ImageColor class ImagePalette(object): - "Color palette for palette mapped images" + """ + Color palette for palette mapped images + + :param mode: The mode to use for the Palette. See: + :ref:`concept-modes`. Defaults to "RGB" + :param palette: An optional palette. If given, it must be a bytearray, + an array or a list of ints between 0-255 and of length ``size`` + times the number of colors in ``mode``. The list must be aligned + by channel (All R values must be contiguous in the list before G + and B values.) Defaults to 0 through 255 per channel. + :param size: An optional palette size. If given, it cannot be equal to + or greater than 256. Defaults to 0. + """ def __init__(self, mode="RGB", palette=None, size=0): self.mode = mode From e623f96f57ac2c82b1765e7f37df8cfc3b5031c8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 21 Aug 2015 19:45:12 +1000 Subject: [PATCH 0469/1037] Fixed typo --- decode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/decode.c b/decode.c index 6299d9124..29c0f9a1b 100644 --- a/decode.c +++ b/decode.c @@ -754,7 +754,7 @@ PyImaging_JpegDecoderNew(PyObject* self, PyObject* args) ImagingDecoderObject* decoder; char* mode; - char* rawmode; /* what we wan't from the decoder */ + char* rawmode; /* what we want from the decoder */ char* jpegmode; /* what's in the file */ int scale = 1; int draft = 0; From 1f8a37841c91e45f48587319e5e14aee9f575fe8 Mon Sep 17 00:00:00 2001 From: Anton Vlasenko Date: Fri, 24 Jul 2015 11:14:20 +0200 Subject: [PATCH 0470/1037] Testing that animated gif preserves all important headers --- Tests/test_file_gif.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 70438eb03..5c83ba8e4 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -95,6 +95,21 @@ class TestFileGif(PillowTestCase): self.assertEqual(reread.n_frames, 5) + def test_headers_saving_for_animated_gifs(self): + important_headers = ['background', 'version', 'transparency', 'duration', 'loop'] + # Multiframe image + im = Image.open("Tests/images/dispose_bgnd.gif") + + out = self.tempfile('temp.gif') + im.save(out, save_all=True) + reread = Image.open(out) + + for header in important_headers: + self.assertEqual( + im.info[header], + reread.info[header] + ) + def test_palette_handling(self): # see https://github.com/python-pillow/Pillow/issues/513 From f64bc891d470ee4a9c0e2b3840f4dabc41aa86a8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 21 Aug 2015 22:09:05 +1000 Subject: [PATCH 0471/1037] Changed GifImagePlugin to include use image info params --- PIL/GifImagePlugin.py | 21 +++++++++++++++++---- Tests/test_file_gif.py | 2 +- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 08567fcd0..45fc34021 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -24,7 +24,8 @@ # See the README file for information on usage and redistribution. # -from PIL import Image, ImageFile, ImagePalette, ImageChops, ImageSequence, _binary +from PIL import Image, ImageFile, ImagePalette, \ + ImageChops, ImageSequence, _binary __version__ = "0.9" @@ -317,6 +318,7 @@ def _save_all(im, fp, filename): def _save(im, fp, filename, save_all=False): + im.encoderinfo.update(im.info) if _imaging_gif: # call external driver try: @@ -347,7 +349,8 @@ def _save(im, fp, filename, save_all=False): # e.g. getdata(im_frame, duration=1000) if not previous: # global header - for s in getheader(im_frame, palette, im.encoderinfo)[0] + getdata(im_frame): + for s in getheader(im_frame, palette, im.encoderinfo)[0] + \ + getdata(im_frame, (0, 0), **im.encoderinfo): fp.write(s) else: # delta frame @@ -356,7 +359,8 @@ def _save(im, fp, filename, save_all=False): if bbox: # compress difference - for s in getdata(im_frame.crop(bbox), offset=bbox[:2]): + for s in getdata(im_frame.crop(bbox), + bbox[:2], **im.encoderinfo): fp.write(s) else: # FIXME: what should we do in this case? @@ -591,7 +595,16 @@ def getheader(im, palette=None, info=None): # size of global color table + global color table flag header.append(o8(color_table_size + 128)) # background + reserved/aspect - background = im.info["background"] if "background" in im.info else 0 + if info and "background" in info: + background = info["background"] + elif "background" in im.info: + # This elif is redundant within GifImagePlugin + # since im.info parameters are bundled into the info dictionary + # However, external scripts may call getheader directly + # So this maintains earlier behaviour + background = im.info["background"] + else: + background = 0 header.append(o8(background) + o8(0)) # end of Logical Screen Descriptor diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 5c83ba8e4..dd91325fc 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -96,7 +96,7 @@ class TestFileGif(PillowTestCase): self.assertEqual(reread.n_frames, 5) def test_headers_saving_for_animated_gifs(self): - important_headers = ['background', 'version', 'transparency', 'duration', 'loop'] + important_headers = ['background', 'version', 'duration', 'loop'] # Multiframe image im = Image.open("Tests/images/dispose_bgnd.gif") From b8ff91ab3bcc788c612f5d879e0483d295c395d9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 21 Aug 2015 22:10:13 +1000 Subject: [PATCH 0472/1037] Corrected version number when saving GIFs --- PIL/GifImagePlugin.py | 13 ++++++++++++- Tests/test_file_gif.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 45fc34021..1287f2a52 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -537,8 +537,19 @@ def getheader(im, palette=None, info=None): # Header Block # http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp + + version = b"87a" + for extensionKey in ["transparency", "duration", "loop"]: + if info and extensionKey in info and \ + not (extensionKey == "duration" and info[extensionKey] == 0): + version = b"89a" + break + else: + if im.info.get("version") == "89a": + version = b"89a" + header = [ - b"GIF87a" + # signature + version + b"GIF"+version + # signature + version o16(im.size[0]) + # canvas width o16(im.size[1]) # canvas height ] diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index dd91325fc..0ac67cd63 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -24,6 +24,7 @@ class TestFileGif(PillowTestCase): self.assertEqual(im.mode, "P") self.assertEqual(im.size, (128, 128)) self.assertEqual(im.format, "GIF") + self.assertEqual(im.info["version"], b"GIF89a") def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" @@ -266,6 +267,34 @@ class TestFileGif(PillowTestCase): self.assertEqual(reread.info['background'], im.info['background']) + def test_version(self): + out = self.tempfile('temp.gif') + + # Test that GIF87a is used by default + im = Image.new('L', (100, 100), '#000') + im.save(out) + reread = Image.open(out) + self.assertEqual(reread.info["version"], b"GIF87a") + + # Test that adding a GIF89a feature changes the version + im.info["transparency"] = 1 + im.save(out) + reread = Image.open(out) + self.assertEqual(reread.info["version"], b"GIF89a") + + # Test that a GIF87a image is also saved in that format + im = Image.open(TEST_GIF) + im.save(out) + reread = Image.open(out) + self.assertEqual(reread.info["version"], b"GIF87a") + + # Test that a GIF89a image is also saved in that format + im.info["version"] = "GIF89a" + im.save(out) + reread = Image.open(out) + self.assertEqual(reread.info["version"], b"GIF87a") + + if __name__ == '__main__': unittest.main() From 2d9f091a302dd6e94f4f1d29fbe340a1cdae2f62 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 22 Aug 2015 22:29:23 +1000 Subject: [PATCH 0473/1037] Improved handling of getink color --- _imaging.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/_imaging.c b/_imaging.c index 7fd2da88e..e49c5431f 100644 --- a/_imaging.c +++ b/_imaging.c @@ -480,13 +480,31 @@ getink(PyObject* color, Imaging im, char* ink) /* fill ink buffer (four bytes) with something that can be cast to either UINT8 or INT32 */ + int rIsInt = 1; + if (im->type == IMAGING_TYPE_UINT8 || + im->type == IMAGING_TYPE_INT32 || + im->type == IMAGING_TYPE_SPECIAL) { +#if PY_VERSION_HEX >= 0x03000000 + if (PyLong_Check(color)) { + r = (int) PyLong_AsLong(color); +#else + if (PyInt_Check(color) || PyLong_Check(color)) { + if (PyInt_Check(color)) + r = PyInt_AS_LONG(color); + else + r = (int) PyLong_AsLong(color); +#endif + } + if (r == -1 && PyErr_Occurred()) + rIsInt = 0; + } + switch (im->type) { case IMAGING_TYPE_UINT8: /* unsigned integer */ if (im->bands == 1) { /* unsigned integer, single layer */ - r = PyInt_AsLong(color); - if (r == -1 && PyErr_Occurred()) + if (rIsInt != 1) return NULL; ink[0] = CLIP(r); ink[1] = ink[2] = ink[3] = 0; @@ -526,8 +544,7 @@ getink(PyObject* color, Imaging im, char* ink) return ink; case IMAGING_TYPE_INT32: /* signed integer */ - r = PyInt_AsLong(color); - if (r == -1 && PyErr_Occurred()) + if (rIsInt != 1) return NULL; *(INT32*) ink = r; return ink; @@ -540,8 +557,7 @@ getink(PyObject* color, Imaging im, char* ink) return ink; case IMAGING_TYPE_SPECIAL: if (strncmp(im->mode, "I;16", 4) == 0) { - r = PyInt_AsLong(color); - if (r == -1 && PyErr_Occurred()) + if (rIsInt != 1) return NULL; ink[0] = (UINT8) r; ink[1] = (UINT8) (r >> 8); From 60902efeb0b6384286bedab8c69ee05f6ee90105 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 22 Aug 2015 23:03:11 +1000 Subject: [PATCH 0474/1037] Added test --- Tests/test_image_quantize.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py index e8183eb48..986a0a6cc 100644 --- a/Tests/test_image_quantize.py +++ b/Tests/test_image_quantize.py @@ -28,6 +28,10 @@ class TestImageQuantize(PillowTestCase): im.quantize() self.assertRaises(Exception, lambda: im.quantize(method=0)) + def test_quantize(self): + im = Image.open('Tests/images/caption_6_33_22.png') + im.convert('RGB').quantize().convert('RGB') + if __name__ == '__main__': unittest.main() From f211601ecdc70134e1cd5a5cac49c11a74dcf721 Mon Sep 17 00:00:00 2001 From: Charles Merriam Date: Sun, 23 Aug 2015 18:30:39 -0700 Subject: [PATCH 0475/1037] In tutorial of pasting images, add to mask text. Using an RGBA image as its own mask is a common question. It shows up in a dozen Stack Overflow questions, e.g., (http://stackoverflow.com/questions/5324647/how-to-merge-a-transparent-png-image-with-another-image-using-pil). Adding a sentence to the tutorial gives people a chance of noticing this. --- docs/handbook/tutorial.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/handbook/tutorial.rst b/docs/handbook/tutorial.rst index 365c8e7a8..d8587eb8d 100644 --- a/docs/handbook/tutorial.rst +++ b/docs/handbook/tutorial.rst @@ -193,7 +193,9 @@ For more advanced tricks, the paste method can also take a transparency mask as an optional argument. In this mask, the value 255 indicates that the pasted image is opaque in that position (that is, the pasted image should be used as is). The value 0 means that the pasted image is completely transparent. Values -in-between indicate different levels of transparency. +in-between indicate different levels of transparency. For example, pasting an +RGBA image and also using it as the mask would paste the opaque portion +of the image but not its transparent background. The Python Imaging Library also allows you to work with the individual bands of an multi-band image, such as an RGB image. The split method creates a set of From 88fca0f55531777d578831dd42d39819beea90dc Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 24 Aug 2015 23:23:07 +1000 Subject: [PATCH 0476/1037] Fixed palette issue when saving --- PIL/GifImagePlugin.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 1287f2a52..bd45e2dbc 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -301,14 +301,17 @@ RAWMODE = { } -def _convert_mode(im): +def _convert_mode(im, initial_call=False): # convert on the fly (EXPERIMENTAL -- I'm not sure PIL # should automatically convert images on save...) if Image.getmodebase(im.mode) == "RGB": - palette_size = 256 - if im.palette: - palette_size = len(im.palette.getdata()[1]) // 3 - return im.convert("P", palette=1, colors=palette_size) + if initial_call: + palette_size = 256 + if im.palette: + palette_size = len(im.palette.getdata()[1]) // 3 + return im.convert("P", palette=1, colors=palette_size) + else: + return im.convert("P") return im.convert("L") @@ -330,7 +333,7 @@ def _save(im, fp, filename, save_all=False): if im.mode in RAWMODE: im_out = im.copy() else: - im_out = _convert_mode(im) + im_out = _convert_mode(im, True) # header try: From e88e90b8ede189221199c429340d3242fc9dc318 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 24 Aug 2015 21:56:23 +1000 Subject: [PATCH 0477/1037] Minor improvement, one less copy --- PIL/GifImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index bd45e2dbc..d78e55f1f 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -357,7 +357,7 @@ def _save(im, fp, filename, save_all=False): fp.write(s) else: # delta frame - delta = ImageChops.subtract_modulo(im_frame, previous) + delta = ImageChops.subtract_modulo(im_frame, previous.copy()) bbox = delta.getbbox() if bbox: @@ -368,7 +368,7 @@ def _save(im, fp, filename, save_all=False): else: # FIXME: what should we do in this case? pass - previous = im_frame.copy() + previous = im_frame else: header = getheader(im_out, palette, im.encoderinfo)[0] for s in header: From 1b8d12b048dd1d4c7da834ae3c44c9661e24e47a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 24 Aug 2015 23:51:02 +1000 Subject: [PATCH 0478/1037] If only one frame, do not use image from sequence --- PIL/GifImagePlugin.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index d78e55f1f..83169bf21 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -345,6 +345,7 @@ def _save(im, fp, filename, save_all=False): if save_all: previous = None + first_frame = None for im_frame in ImageSequence.Iterator(im): im_frame = _convert_mode(im_frame) @@ -352,10 +353,14 @@ def _save(im, fp, filename, save_all=False): # e.g. getdata(im_frame, duration=1000) if not previous: # global header - for s in getheader(im_frame, palette, im.encoderinfo)[0] + \ - getdata(im_frame, (0, 0), **im.encoderinfo): - fp.write(s) + first_frame = getheader(im_frame, palette, im.encoderinfo)[0] + first_frame += getdata(im_frame, (0, 0), **im.encoderinfo) else: + if first_frame: + for s in first_frame: + fp.write(s) + first_frame = None + # delta frame delta = ImageChops.subtract_modulo(im_frame, previous.copy()) bbox = delta.getbbox() @@ -369,7 +374,9 @@ def _save(im, fp, filename, save_all=False): # FIXME: what should we do in this case? pass previous = im_frame - else: + if first_frame: + save_all = False + if not save_all: header = getheader(im_out, palette, im.encoderinfo)[0] for s in header: fp.write(s) From c6b13d294fb64242f0599614e5efa48ff46c749f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 25 Aug 2015 22:27:18 +1000 Subject: [PATCH 0479/1037] Flake8 fixes --- PIL/BmpImagePlugin.py | 4 +-- PIL/CurImagePlugin.py | 3 +-- PIL/DcxImagePlugin.py | 5 ++-- PIL/EpsImagePlugin.py | 9 ++++--- PIL/FliImagePlugin.py | 4 +-- PIL/FpxImagePlugin.py | 5 ++-- PIL/GdImageFile.py | 4 +-- PIL/IcoImagePlugin.py | 4 +-- PIL/ImImagePlugin.py | 4 +-- PIL/ImageFile.py | 2 +- PIL/ImageFont.py | 3 ++- PIL/ImagePalette.py | 4 +-- PIL/ImtImagePlugin.py | 5 ++-- PIL/IptcImagePlugin.py | 5 ++-- PIL/JpegImagePlugin.py | 20 +++++++------- PIL/JpegPresets.py | 56 +++++++++++++++++++-------------------- PIL/McIdasImagePlugin.py | 4 +-- PIL/MicImagePlugin.py | 5 ++-- PIL/MpegImagePlugin.py | 3 ++- PIL/MpoImagePlugin.py | 4 +-- PIL/MspImagePlugin.py | 4 +-- PIL/PalmImagePlugin.py | 56 +++++++++++++++++++-------------------- PIL/PcdImagePlugin.py | 5 ++-- PIL/PdfImagePlugin.py | 4 +-- PIL/PixarImagePlugin.py | 4 +-- PIL/PngImagePlugin.py | 4 +-- PIL/PpmImagePlugin.py | 4 +-- PIL/SgiImagePlugin.py | 5 ++-- PIL/SunImagePlugin.py | 5 ++-- PIL/TgaImagePlugin.py | 4 +-- PIL/TiffImagePlugin.py | 33 ++++++++++++----------- PIL/WmfImagePlugin.py | 4 +-- PIL/XVThumbImagePlugin.py | 4 +-- PIL/XbmImagePlugin.py | 4 +-- PIL/XpmImagePlugin.py | 5 ++-- Scripts/pilfont.py | 4 +-- Tests/bench_get.py | 6 ++--- Tests/import_all.py | 5 ++-- Tests/show_icc.py | 6 ++--- Tests/show_mcidas.py | 5 ++-- Tests/test_file_tiff.py | 1 - Tests/test_imagedraw.py | 4 +-- winbuild/build_dep.py | 4 +-- winbuild/config.py | 8 +++--- 44 files changed, 168 insertions(+), 173 deletions(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index b109d2696..d9aaf193b 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -24,12 +24,10 @@ # -__version__ = "0.7" - - from PIL import Image, ImageFile, ImagePalette, _binary import math +__version__ = "0.7" i8 = _binary.i8 i16 = _binary.i16le diff --git a/PIL/CurImagePlugin.py b/PIL/CurImagePlugin.py index 8707eeeb3..3825e098b 100644 --- a/PIL/CurImagePlugin.py +++ b/PIL/CurImagePlugin.py @@ -17,10 +17,9 @@ # -__version__ = "0.1" - from PIL import Image, BmpImagePlugin, _binary +__version__ = "0.1" # # -------------------------------------------------------------------- diff --git a/PIL/DcxImagePlugin.py b/PIL/DcxImagePlugin.py index 4c4bf700c..f9034d15c 100644 --- a/PIL/DcxImagePlugin.py +++ b/PIL/DcxImagePlugin.py @@ -21,12 +21,11 @@ # See the README file for information on usage and redistribution. # -__version__ = "0.2" - from PIL import Image, _binary - from PIL.PcxImagePlugin import PcxImageFile +__version__ = "0.2" + MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then? i32 = _binary.i32le diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py index e82656fae..34cccf5a7 100644 --- a/PIL/EpsImagePlugin.py +++ b/PIL/EpsImagePlugin.py @@ -20,12 +20,13 @@ # See the README file for information on usage and redistribution. # -__version__ = "0.5" - import re import io +import sys from PIL import Image, ImageFile, _binary +__version__ = "0.5" + # # -------------------------------------------------------------------- @@ -36,7 +37,6 @@ split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$") field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$") gs_windows_binary = None -import sys if sys.platform.startswith('win'): import shutil if hasattr(shutil, 'which'): @@ -187,7 +187,8 @@ class PSFile(object): def _accept(prefix): - return prefix[:4] == b"%!PS" or (len(prefix) >= 4 and i32(prefix) == 0xC6D3D0C5) + return prefix[:4] == b"%!PS" or \ + (len(prefix) >= 4 and i32(prefix) == 0xC6D3D0C5) ## # Image plugin for Encapsulated Postscript. This plugin supports only diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py index 1af9dadde..a07dc29b0 100644 --- a/PIL/FliImagePlugin.py +++ b/PIL/FliImagePlugin.py @@ -16,10 +16,10 @@ # -__version__ = "0.2" - from PIL import Image, ImageFile, ImagePalette, _binary +__version__ = "0.2" + i8 = _binary.i8 i16 = _binary.i16le i32 = _binary.i32le diff --git a/PIL/FpxImagePlugin.py b/PIL/FpxImagePlugin.py index 9d338d9da..d369e05fa 100644 --- a/PIL/FpxImagePlugin.py +++ b/PIL/FpxImagePlugin.py @@ -16,12 +16,11 @@ # -__version__ = "0.1" - - from PIL import Image, ImageFile from PIL.OleFileIO import i8, i32, MAGIC, OleFileIO +__version__ = "0.1" + # we map from colour field tuples to (mode, rawmode) descriptors MODES = { diff --git a/PIL/GdImageFile.py b/PIL/GdImageFile.py index 080153a9f..ae3500f0c 100644 --- a/PIL/GdImageFile.py +++ b/PIL/GdImageFile.py @@ -23,11 +23,11 @@ # purposes only. -__version__ = "0.1" - from PIL import ImageFile, ImagePalette, _binary from PIL._util import isPath +__version__ = "0.1" + try: import builtins except ImportError: diff --git a/PIL/IcoImagePlugin.py b/PIL/IcoImagePlugin.py index 9feafea1b..0b8f4691c 100644 --- a/PIL/IcoImagePlugin.py +++ b/PIL/IcoImagePlugin.py @@ -22,14 +22,14 @@ # * http://msdn.microsoft.com/en-us/library/ms997538.aspx -__version__ = "0.1" - import struct from io import BytesIO from PIL import Image, ImageFile, BmpImagePlugin, PngImagePlugin, _binary from math import log, ceil +__version__ = "0.1" + # # -------------------------------------------------------------------- diff --git a/PIL/ImImagePlugin.py b/PIL/ImImagePlugin.py index 1e5d6860c..dd4f82900 100644 --- a/PIL/ImImagePlugin.py +++ b/PIL/ImImagePlugin.py @@ -26,12 +26,12 @@ # -__version__ = "0.7" - import re from PIL import Image, ImageFile, ImagePalette from PIL._binary import i8 +__version__ = "0.7" + # -------------------------------------------------------------------- # Standard tags diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index c55ace55b..d892ba584 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -33,7 +33,7 @@ import io import logging import os import sys -import traceback +# import traceback logger = logging.getLogger(__name__) diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index e3fb6f503..7bd81c95a 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -275,7 +275,8 @@ def truetype(font=None, size=10, index=0, encoding="", filename=None): # According to the freedesktop spec, XDG_DATA_DIRS should # default to /usr/share lindirs = '/usr/share' - dirs += [os.path.join(lindir, "fonts") for lindir in lindirs.split(":")] + dirs += [os.path.join(lindir, "fonts") + for lindir in lindirs.split(":")] elif sys.platform == 'darwin': dirs += ['/Library/Fonts', '/System/Library/Fonts', os.path.expanduser('~/Library/Fonts')] diff --git a/PIL/ImagePalette.py b/PIL/ImagePalette.py index 5aabaa138..6461600ee 100644 --- a/PIL/ImagePalette.py +++ b/PIL/ImagePalette.py @@ -237,8 +237,8 @@ def load(filename): p = PaletteFile.PaletteFile(fp) lut = p.getpalette() except (SyntaxError, ValueError): - #import traceback - #traceback.print_exc() + # import traceback + # traceback.print_exc() pass if not lut: diff --git a/PIL/ImtImagePlugin.py b/PIL/ImtImagePlugin.py index f512eb801..1ca2c25b0 100644 --- a/PIL/ImtImagePlugin.py +++ b/PIL/ImtImagePlugin.py @@ -15,12 +15,13 @@ # -__version__ = "0.2" - import re from PIL import Image, ImageFile +__version__ = "0.2" + + # # -------------------------------------------------------------------- diff --git a/PIL/IptcImagePlugin.py b/PIL/IptcImagePlugin.py index e810ffca2..323177039 100644 --- a/PIL/IptcImagePlugin.py +++ b/PIL/IptcImagePlugin.py @@ -17,13 +17,12 @@ from __future__ import print_function -__version__ = "0.3" - - from PIL import Image, ImageFile, _binary import os import tempfile +__version__ = "0.3" + i8 = _binary.i8 i16 = _binary.i16be i32 = _binary.i32be diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index c02518cf2..95a057754 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -32,8 +32,6 @@ # See the README file for information on usage and redistribution. # -__version__ = "0.6" - import array import struct import io @@ -48,6 +46,8 @@ o8 = _binary.o8 i16 = _binary.i16be i32 = _binary.i32be +__version__ = "0.6" + # # Parser @@ -528,14 +528,14 @@ RAWMODE = { "YCbCr": "YCbCr", } -zigzag_index = ( 0, 1, 5, 6, 14, 15, 27, 28, - 2, 4, 7, 13, 16, 26, 29, 42, - 3, 8, 12, 17, 25, 30, 41, 43, - 9, 11, 18, 24, 31, 40, 44, 53, - 10, 19, 23, 32, 39, 45, 52, 54, - 20, 22, 33, 38, 46, 51, 55, 60, - 21, 34, 37, 47, 50, 56, 59, 61, - 35, 36, 48, 49, 57, 58, 62, 63) +zigzag_index = (0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63) samplings = {(1, 1, 1, 1, 1, 1): 0, (2, 1, 1, 1, 1, 1): 1, diff --git a/PIL/JpegPresets.py b/PIL/JpegPresets.py index 67af9ac9a..66cbde117 100644 --- a/PIL/JpegPresets.py +++ b/PIL/JpegPresets.py @@ -68,7 +68,7 @@ Libjpeg ref.: http://www.jpegcameras.com/libjpeg/libjpeg-3.html presets = { 'web_low': {'subsampling': 2, # "4:1:1" - 'quantization': [ + 'quantization': [ [20, 16, 25, 39, 50, 46, 62, 68, 16, 18, 23, 38, 38, 53, 65, 68, 25, 23, 31, 38, 53, 65, 68, 68, @@ -85,9 +85,9 @@ presets = { 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68] - ]}, + ]}, 'web_medium': {'subsampling': 2, # "4:1:1" - 'quantization': [ + 'quantization': [ [16, 11, 11, 16, 23, 27, 31, 30, 11, 12, 12, 15, 20, 23, 23, 30, 11, 12, 13, 16, 23, 26, 35, 47, @@ -104,10 +104,10 @@ presets = { 26, 26, 30, 39, 48, 63, 64, 64, 38, 35, 46, 53, 64, 64, 64, 64, 48, 43, 53, 64, 64, 64, 64, 64] - ]}, + ]}, 'web_high': {'subsampling': 0, # "4:4:4" - 'quantization': [ - [ 6, 4, 4, 6, 9, 11, 12, 16, + 'quantization': [ + [6, 4, 4, 6, 9, 11, 12, 16, 4, 5, 5, 6, 8, 10, 12, 12, 4, 5, 5, 6, 10, 12, 14, 19, 6, 6, 6, 11, 12, 15, 19, 28, @@ -115,7 +115,7 @@ presets = { 11, 10, 12, 15, 20, 27, 31, 31, 12, 12, 14, 19, 27, 31, 31, 31, 16, 12, 19, 28, 31, 31, 31, 31], - [ 7, 7, 13, 24, 26, 31, 31, 31, + [7, 7, 13, 24, 26, 31, 31, 31, 7, 12, 16, 21, 31, 31, 31, 31, 13, 16, 17, 31, 31, 31, 31, 31, 24, 21, 31, 31, 31, 31, 31, 31, @@ -123,10 +123,10 @@ presets = { 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31] - ]}, + ]}, 'web_very_high': {'subsampling': 0, # "4:4:4" - 'quantization': [ - [ 2, 2, 2, 2, 3, 4, 5, 6, + 'quantization': [ + [2, 2, 2, 2, 3, 4, 5, 6, 2, 2, 2, 2, 3, 4, 5, 6, 2, 2, 2, 2, 4, 5, 7, 9, 2, 2, 2, 4, 5, 7, 9, 12, @@ -134,7 +134,7 @@ presets = { 4, 4, 5, 7, 10, 12, 12, 12, 5, 5, 7, 9, 12, 12, 12, 12, 6, 6, 9, 12, 12, 12, 12, 12], - [ 3, 3, 5, 9, 13, 15, 15, 15, + [3, 3, 5, 9, 13, 15, 15, 15, 3, 4, 6, 11, 14, 12, 12, 12, 5, 6, 9, 14, 12, 12, 12, 12, 9, 11, 14, 12, 12, 12, 12, 12, @@ -142,10 +142,10 @@ presets = { 15, 12, 12, 12, 12, 12, 12, 12, 15, 12, 12, 12, 12, 12, 12, 12, 15, 12, 12, 12, 12, 12, 12, 12] - ]}, + ]}, 'web_maximum': {'subsampling': 0, # "4:4:4" - 'quantization': [ - [ 1, 1, 1, 1, 1, 1, 1, 1, + 'quantization': [ + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 2, @@ -153,7 +153,7 @@ presets = { 1, 1, 1, 1, 2, 2, 3, 3, 1, 1, 1, 2, 2, 3, 3, 3, 1, 1, 2, 2, 3, 3, 3, 3], - [ 1, 1, 1, 2, 2, 3, 3, 3, + [1, 1, 1, 2, 2, 3, 3, 3, 1, 1, 1, 2, 3, 3, 3, 3, 1, 1, 1, 3, 3, 3, 3, 3, 2, 2, 3, 3, 3, 3, 3, 3, @@ -161,9 +161,9 @@ presets = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3] - ]}, + ]}, 'low': {'subsampling': 2, # "4:1:1" - 'quantization': [ + 'quantization': [ [18, 14, 14, 21, 30, 35, 34, 17, 14, 16, 16, 19, 26, 23, 12, 12, 14, 16, 17, 21, 23, 12, 12, 12, @@ -180,9 +180,9 @@ presets = { 20, 12, 12, 12, 12, 12, 12, 12, 17, 12, 12, 12, 12, 12, 12, 12, 17, 12, 12, 12, 12, 12, 12, 12] - ]}, + ]}, 'medium': {'subsampling': 2, # "4:1:1" - 'quantization': [ + 'quantization': [ [12, 8, 8, 12, 17, 21, 24, 17, 8, 9, 9, 11, 15, 19, 12, 12, 8, 9, 10, 12, 19, 12, 12, 12, @@ -199,10 +199,10 @@ presets = { 20, 12, 12, 12, 12, 12, 12, 12, 17, 12, 12, 12, 12, 12, 12, 12, 17, 12, 12, 12, 12, 12, 12, 12] - ]}, + ]}, 'high': {'subsampling': 0, # "4:4:4" - 'quantization': [ - [ 6, 4, 4, 6, 9, 11, 12, 16, + 'quantization': [ + [6, 4, 4, 6, 9, 11, 12, 16, 4, 5, 5, 6, 8, 10, 12, 12, 4, 5, 5, 6, 10, 12, 12, 12, 6, 6, 6, 11, 12, 12, 12, 12, @@ -210,7 +210,7 @@ presets = { 11, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 16, 12, 12, 12, 12, 12, 12, 12], - [ 7, 7, 13, 24, 20, 20, 17, 17, + [7, 7, 13, 24, 20, 20, 17, 17, 7, 12, 16, 14, 14, 12, 12, 12, 13, 16, 14, 14, 12, 12, 12, 12, 24, 14, 14, 12, 12, 12, 12, 12, @@ -218,10 +218,10 @@ presets = { 20, 12, 12, 12, 12, 12, 12, 12, 17, 12, 12, 12, 12, 12, 12, 12, 17, 12, 12, 12, 12, 12, 12, 12] - ]}, + ]}, 'maximum': {'subsampling': 0, # "4:4:4" - 'quantization': [ - [ 2, 2, 2, 2, 3, 4, 5, 6, + 'quantization': [ + [2, 2, 2, 2, 3, 4, 5, 6, 2, 2, 2, 2, 3, 4, 5, 6, 2, 2, 2, 2, 4, 5, 7, 9, 2, 2, 2, 4, 5, 7, 9, 12, @@ -229,7 +229,7 @@ presets = { 4, 4, 5, 7, 10, 12, 12, 12, 5, 5, 7, 9, 12, 12, 12, 12, 6, 6, 9, 12, 12, 12, 12, 12], - [ 3, 3, 5, 9, 13, 15, 15, 15, + [3, 3, 5, 9, 13, 15, 15, 15, 3, 4, 6, 10, 14, 12, 12, 12, 5, 6, 9, 14, 12, 12, 12, 12, 9, 10, 14, 12, 12, 12, 12, 12, @@ -237,5 +237,5 @@ presets = { 15, 12, 12, 12, 12, 12, 12, 12, 15, 12, 12, 12, 12, 12, 12, 12, 15, 12, 12, 12, 12, 12, 12, 12] - ]}, + ]}, } diff --git a/PIL/McIdasImagePlugin.py b/PIL/McIdasImagePlugin.py index c3f255fd2..705fa574f 100644 --- a/PIL/McIdasImagePlugin.py +++ b/PIL/McIdasImagePlugin.py @@ -16,11 +16,11 @@ # See the README file for information on usage and redistribution. # -__version__ = "0.2" - import struct from PIL import Image, ImageFile +__version__ = "0.2" + def _accept(s): return s[:8] == b"\x00\x00\x00\x00\x00\x00\x00\x04" diff --git a/PIL/MicImagePlugin.py b/PIL/MicImagePlugin.py index 053f66c07..3c912442b 100644 --- a/PIL/MicImagePlugin.py +++ b/PIL/MicImagePlugin.py @@ -17,12 +17,11 @@ # -__version__ = "0.1" - - from PIL import Image, TiffImagePlugin from PIL.OleFileIO import MAGIC, OleFileIO +__version__ = "0.1" + # # -------------------------------------------------------------------- diff --git a/PIL/MpegImagePlugin.py b/PIL/MpegImagePlugin.py index 04cf755f3..6671b8691 100644 --- a/PIL/MpegImagePlugin.py +++ b/PIL/MpegImagePlugin.py @@ -13,11 +13,12 @@ # See the README file for information on usage and redistribution. # -__version__ = "0.1" from PIL import Image, ImageFile from PIL._binary import i8 +__version__ = "0.1" + # # Bitstream parser diff --git a/PIL/MpoImagePlugin.py b/PIL/MpoImagePlugin.py index 7eb8f4cf8..1d26021d8 100644 --- a/PIL/MpoImagePlugin.py +++ b/PIL/MpoImagePlugin.py @@ -18,10 +18,10 @@ # See the README file for information on usage and redistribution. # -__version__ = "0.1" - from PIL import Image, JpegImagePlugin +__version__ = "0.1" + def _accept(prefix): return JpegImagePlugin._accept(prefix) diff --git a/PIL/MspImagePlugin.py b/PIL/MspImagePlugin.py index 49dd45b31..85f8e764b 100644 --- a/PIL/MspImagePlugin.py +++ b/PIL/MspImagePlugin.py @@ -17,10 +17,10 @@ # -__version__ = "0.1" - from PIL import Image, ImageFile, _binary +__version__ = "0.1" + # # read MSP files diff --git a/PIL/PalmImagePlugin.py b/PIL/PalmImagePlugin.py index bba1de8bb..8836341df 100644 --- a/PIL/PalmImagePlugin.py +++ b/PIL/PalmImagePlugin.py @@ -7,10 +7,10 @@ # Image plugin for Palm pixmap images (output only). ## -__version__ = "1.0" - from PIL import Image, ImageFile, _binary +__version__ = "1.0" + _Palm8BitColormapValues = ( (255, 255, 255), (255, 204, 255), (255, 153, 255), (255, 102, 255), (255, 51, 255), (255, 0, 255), (255, 255, 204), (255, 204, 204), @@ -30,15 +30,15 @@ _Palm8BitColormapValues = ( (102, 255, 204), (102, 204, 204), (102, 153, 204), (102, 102, 204), (102, 51, 204), (102, 0, 204), (102, 255, 153), (102, 204, 153), (102, 153, 153), (102, 102, 153), (102, 51, 153), (102, 0, 153), - ( 51, 255, 255), ( 51, 204, 255), ( 51, 153, 255), ( 51, 102, 255), - ( 51, 51, 255), ( 51, 0, 255), ( 51, 255, 204), ( 51, 204, 204), - ( 51, 153, 204), ( 51, 102, 204), ( 51, 51, 204), ( 51, 0, 204), - ( 51, 255, 153), ( 51, 204, 153), ( 51, 153, 153), ( 51, 102, 153), - ( 51, 51, 153), ( 51, 0, 153), ( 0, 255, 255), ( 0, 204, 255), - ( 0, 153, 255), ( 0, 102, 255), ( 0, 51, 255), ( 0, 0, 255), - ( 0, 255, 204), ( 0, 204, 204), ( 0, 153, 204), ( 0, 102, 204), - ( 0, 51, 204), ( 0, 0, 204), ( 0, 255, 153), ( 0, 204, 153), - ( 0, 153, 153), ( 0, 102, 153), ( 0, 51, 153), ( 0, 0, 153), + (51, 255, 255), (51, 204, 255), (51, 153, 255), (51, 102, 255), + (51, 51, 255), (51, 0, 255), (51, 255, 204), (51, 204, 204), + (51, 153, 204), (51, 102, 204), (51, 51, 204), (51, 0, 204), + (51, 255, 153), (51, 204, 153), (51, 153, 153), (51, 102, 153), + (51, 51, 153), (51, 0, 153), (0, 255, 255), (0, 204, 255), + (0, 153, 255), (0, 102, 255), (0, 51, 255), (0, 0, 255), + (0, 255, 204), (0, 204, 204), (0, 153, 204), (0, 102, 204), + (0, 51, 204), (0, 0, 204), (0, 255, 153), (0, 204, 153), + (0, 153, 153), (0, 102, 153), (0, 51, 153), (0, 0, 153), (255, 255, 102), (255, 204, 102), (255, 153, 102), (255, 102, 102), (255, 51, 102), (255, 0, 102), (255, 255, 51), (255, 204, 51), (255, 153, 51), (255, 102, 51), (255, 51, 51), (255, 0, 51), @@ -57,25 +57,25 @@ _Palm8BitColormapValues = ( (102, 255, 51), (102, 204, 51), (102, 153, 51), (102, 102, 51), (102, 51, 51), (102, 0, 51), (102, 255, 0), (102, 204, 0), (102, 153, 0), (102, 102, 0), (102, 51, 0), (102, 0, 0), - ( 51, 255, 102), ( 51, 204, 102), ( 51, 153, 102), ( 51, 102, 102), - ( 51, 51, 102), ( 51, 0, 102), ( 51, 255, 51), ( 51, 204, 51), - ( 51, 153, 51), ( 51, 102, 51), ( 51, 51, 51), ( 51, 0, 51), - ( 51, 255, 0), ( 51, 204, 0), ( 51, 153, 0), ( 51, 102, 0), - ( 51, 51, 0), ( 51, 0, 0), ( 0, 255, 102), ( 0, 204, 102), - ( 0, 153, 102), ( 0, 102, 102), ( 0, 51, 102), ( 0, 0, 102), - ( 0, 255, 51), ( 0, 204, 51), ( 0, 153, 51), ( 0, 102, 51), - ( 0, 51, 51), ( 0, 0, 51), ( 0, 255, 0), ( 0, 204, 0), - ( 0, 153, 0), ( 0, 102, 0), ( 0, 51, 0), ( 17, 17, 17), - ( 34, 34, 34), ( 68, 68, 68), ( 85, 85, 85), (119, 119, 119), + (51, 255, 102), (51, 204, 102), (51, 153, 102), (51, 102, 102), + (51, 51, 102), (51, 0, 102), (51, 255, 51), (51, 204, 51), + (51, 153, 51), (51, 102, 51), (51, 51, 51), (51, 0, 51), + (51, 255, 0), (51, 204, 0), (51, 153, 0), (51, 102, 0), + (51, 51, 0), (51, 0, 0), (0, 255, 102), (0, 204, 102), + (0, 153, 102), (0, 102, 102), (0, 51, 102), (0, 0, 102), + (0, 255, 51), (0, 204, 51), (0, 153, 51), (0, 102, 51), + (0, 51, 51), (0, 0, 51), (0, 255, 0), (0, 204, 0), + (0, 153, 0), (0, 102, 0), (0, 51, 0), (17, 17, 17), + (34, 34, 34), (68, 68, 68), (85, 85, 85), (119, 119, 119), (136, 136, 136), (170, 170, 170), (187, 187, 187), (221, 221, 221), (238, 238, 238), (192, 192, 192), (128, 0, 0), (128, 0, 128), - ( 0, 128, 0), ( 0, 128, 128), ( 0, 0, 0), ( 0, 0, 0), - ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), - ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), - ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), - ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), - ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), - ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0)) + (0, 128, 0), (0, 128, 128), (0, 0, 0), (0, 0, 0), + (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), + (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), + (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), + (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), + (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), + (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0)) # so build a prototype image to be used for palette resampling diff --git a/PIL/PcdImagePlugin.py b/PIL/PcdImagePlugin.py index 4481671b1..b53635a99 100644 --- a/PIL/PcdImagePlugin.py +++ b/PIL/PcdImagePlugin.py @@ -15,11 +15,10 @@ # -__version__ = "0.1" - - from PIL import Image, ImageFile, _binary +__version__ = "0.1" + i8 = _binary.i8 diff --git a/PIL/PdfImagePlugin.py b/PIL/PdfImagePlugin.py index 1d8c2ff93..e58e9e666 100644 --- a/PIL/PdfImagePlugin.py +++ b/PIL/PdfImagePlugin.py @@ -20,12 +20,12 @@ # Image plugin for PDF images (output only). ## -__version__ = "0.4" - from PIL import Image, ImageFile from PIL._binary import i8 import io +__version__ = "0.4" + # # -------------------------------------------------------------------- diff --git a/PIL/PixarImagePlugin.py b/PIL/PixarImagePlugin.py index ebf4c8c61..26b872893 100644 --- a/PIL/PixarImagePlugin.py +++ b/PIL/PixarImagePlugin.py @@ -19,10 +19,10 @@ # See the README file for information on usage and redistribution. # -__version__ = "0.1" - from PIL import Image, ImageFile, _binary +__version__ = "0.1" + # # helpers diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index a2bc8f679..92695d493 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -33,14 +33,14 @@ from __future__ import print_function -__version__ = "0.9" - import logging import re import zlib from PIL import Image, ImageFile, ImagePalette, _binary +__version__ = "0.9" + logger = logging.getLogger(__name__) i8 = _binary.i8 diff --git a/PIL/PpmImagePlugin.py b/PIL/PpmImagePlugin.py index d829532e5..87342a229 100644 --- a/PIL/PpmImagePlugin.py +++ b/PIL/PpmImagePlugin.py @@ -15,12 +15,12 @@ # -__version__ = "0.2" - import string from PIL import Image, ImageFile +__version__ = "0.2" + # # -------------------------------------------------------------------- diff --git a/PIL/SgiImagePlugin.py b/PIL/SgiImagePlugin.py index 9b4535ebf..e73cf1601 100644 --- a/PIL/SgiImagePlugin.py +++ b/PIL/SgiImagePlugin.py @@ -18,11 +18,10 @@ # -__version__ = "0.2" - - from PIL import Image, ImageFile, _binary +__version__ = "0.2" + i8 = _binary.i8 i16 = _binary.i16be i32 = _binary.i32be diff --git a/PIL/SunImagePlugin.py b/PIL/SunImagePlugin.py index f6c598a3f..22f27a1c0 100644 --- a/PIL/SunImagePlugin.py +++ b/PIL/SunImagePlugin.py @@ -17,11 +17,10 @@ # -__version__ = "0.3" - - from PIL import Image, ImageFile, ImagePalette, _binary +__version__ = "0.3" + i16 = _binary.i16be i32 = _binary.i32be diff --git a/PIL/TgaImagePlugin.py b/PIL/TgaImagePlugin.py index 75fa1c7e6..8766e3890 100644 --- a/PIL/TgaImagePlugin.py +++ b/PIL/TgaImagePlugin.py @@ -17,10 +17,10 @@ # -__version__ = "0.3" - from PIL import Image, ImageFile, ImagePalette, _binary +__version__ = "0.3" + # # -------------------------------------------------------------------- diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 029690f2f..30f33ab62 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -41,9 +41,6 @@ from __future__ import print_function -__version__ = "1.3.5" -DEBUG = False # Needs to be merged with the new logging approach. - from PIL import Image, ImageFile from PIL import ImagePalette from PIL import _binary @@ -57,6 +54,9 @@ import itertools import os import io +__version__ = "1.3.5" +DEBUG = False # Needs to be merged with the new logging approach. + # Set these to true to force use of libtiff for reading or writing. READ_LIBTIFF = False WRITE_LIBTIFF = False @@ -519,7 +519,7 @@ class ImageFileDirectory(collections.MutableMapping): typ = self.tagtype[tag] if DEBUG: - print ("Tag %s, Type: %s, Value: %s" % (tag, typ, value)) + print("Tag %s, Type: %s, Value: %s" % (tag, typ, value)) if typ == 1: # byte data @@ -654,9 +654,9 @@ class TiffImageFile(ImageFile.ImageFile): self._is_animated = None if DEBUG: - print ("*** TiffImageFile._open ***") - print ("- __first:", self.__first) - print ("- ifh: ", ifh) + print("*** TiffImageFile._open ***") + print("- __first:", self.__first) + print("- ifh: ", ifh) # and load the first frame self._seek(0) @@ -702,7 +702,8 @@ class TiffImageFile(ImageFile.ImageFile): if not self.__next: raise EOFError("no more images in TIFF file") if DEBUG: - print("Seeking to frame %s, on frame %s, __next %s, location: %s" % + print("Seeking to frame %s, on frame %s, " + + "__next %s, location: %s" % (frame, self.__frame, self.__next, self.fp.tell())) # reset python3 buffered io handle in case fp # was passed to libtiff, invalidating the buffer @@ -788,19 +789,19 @@ class TiffImageFile(ImageFile.ImageFile): # that returns an IOError if there's no underlying fp. Easier to # deal with here by reordering. if DEBUG: - print ("have getvalue. just sending in a string from getvalue") + print("have getvalue. just sending in a string from getvalue") n, err = decoder.decode(self.fp.getvalue()) elif hasattr(self.fp, "fileno"): # we've got a actual file on disk, pass in the fp. if DEBUG: - print ("have fileno, calling fileno version of the decoder.") + print("have fileno, calling fileno version of the decoder.") self.fp.seek(0) # 4 bytes, otherwise the trace might error out n, err = decoder.decode(b"fpfp") else: # we have something else. if DEBUG: - print ("don't have fileno or getvalue. just reading") + print("don't have fileno or getvalue. just reading") # UNDONE -- so much for that buffer size thing. n, err = decoder.decode(self.fp.read()) @@ -980,7 +981,7 @@ class TiffImageFile(ImageFile.ImageFile): (0, min(y, ysize), w, min(y+h, ysize)), offsets[i], a)) if DEBUG: - print ("tiles: ", self.tile) + print("tiles: ", self.tile) y = y + h if y >= self.size[1]: x = y = 0 @@ -1165,8 +1166,8 @@ def _save(im, fp, filename): if libtiff: if DEBUG: - print ("Saving using libtiff encoder") - print (ifd.items()) + print("Saving using libtiff encoder") + print(ifd.items()) _fp = 0 if hasattr(fp, "fileno"): try: @@ -1223,7 +1224,7 @@ def _save(im, fp, filename): atts[k] = v if DEBUG: - print (atts) + print(atts) # libtiff always expects the bytes in native order. # we're storing image byte order. So, if the rawmode @@ -1233,7 +1234,7 @@ def _save(im, fp, filename): rawmode = 'I;16N' a = (rawmode, compression, _fp, filename, atts) - # print (im.mode, compression, a, im.encoderconfig) + # print(im.mode, compression, a, im.encoderconfig) e = Image._getencoder(im.mode, 'libtiff', a, im.encoderconfig) e.setimage(im.im, (0, 0)+im.size) while True: diff --git a/PIL/WmfImagePlugin.py b/PIL/WmfImagePlugin.py index bdbbc72f0..3163210a8 100644 --- a/PIL/WmfImagePlugin.py +++ b/PIL/WmfImagePlugin.py @@ -15,10 +15,10 @@ # See the README file for information on usage and redistribution. # -__version__ = "0.2" - from PIL import Image, ImageFile, _binary +__version__ = "0.2" + _handler = None if str != bytes: diff --git a/PIL/XVThumbImagePlugin.py b/PIL/XVThumbImagePlugin.py index 5cf1386fd..9d4b704f6 100644 --- a/PIL/XVThumbImagePlugin.py +++ b/PIL/XVThumbImagePlugin.py @@ -17,10 +17,10 @@ # FIXME: make save work (this requires quantization support) # -__version__ = "0.1" - from PIL import Image, ImageFile, ImagePalette, _binary +__version__ = "0.1" + o8 = _binary.o8 # standard color palette for thumbnails (RGB332) diff --git a/PIL/XbmImagePlugin.py b/PIL/XbmImagePlugin.py index 6916e1888..bca882866 100644 --- a/PIL/XbmImagePlugin.py +++ b/PIL/XbmImagePlugin.py @@ -19,11 +19,11 @@ # See the README file for information on usage and redistribution. # -__version__ = "0.6" - import re from PIL import Image, ImageFile +__version__ = "0.6" + # XBM header xbm_head = re.compile( b"\s*#define[ \t]+.*_width[ \t]+(?P[0-9]+)[\r\n]+" diff --git a/PIL/XpmImagePlugin.py b/PIL/XpmImagePlugin.py index 522302706..556adb8f7 100644 --- a/PIL/XpmImagePlugin.py +++ b/PIL/XpmImagePlugin.py @@ -15,13 +15,12 @@ # -__version__ = "0.2" - - import re from PIL import Image, ImageFile, ImagePalette from PIL._binary import i8, o8 +__version__ = "0.2" + # XPM header xpm_head = re.compile(b"\"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)") diff --git a/Scripts/pilfont.py b/Scripts/pilfont.py index 4425c072c..aa6a34083 100644 --- a/Scripts/pilfont.py +++ b/Scripts/pilfont.py @@ -12,8 +12,6 @@ from __future__ import print_function -VERSION = "0.4" - import glob import sys @@ -21,6 +19,8 @@ import sys from PIL import BdfFontFile from PIL import PcfFontFile +VERSION = "0.4" + if len(sys.argv) <= 1: print("PILFONT", VERSION, "-- PIL font compiler.") print() diff --git a/Tests/bench_get.py b/Tests/bench_get.py index 9c51c0258..51f3a6aa2 100644 --- a/Tests/bench_get.py +++ b/Tests/bench_get.py @@ -1,9 +1,9 @@ -import sys -sys.path.insert(0, ".") - import helper import timeit +import sys +sys.path.insert(0, ".") + def bench(mode): im = helper.hopper(mode) diff --git a/Tests/import_all.py b/Tests/import_all.py index 88a102b64..c5960d8bb 100644 --- a/Tests/import_all.py +++ b/Tests/import_all.py @@ -1,11 +1,12 @@ from __future__ import print_function -import sys -sys.path.insert(0, ".") import glob import os import traceback +import sys +sys.path.insert(0, ".") + for file in glob.glob("PIL/*.py"): module = os.path.basename(file)[:-3] try: diff --git a/Tests/show_icc.py b/Tests/show_icc.py index e062747e7..b39480a4b 100644 --- a/Tests/show_icc.py +++ b/Tests/show_icc.py @@ -1,9 +1,9 @@ -import sys -sys.path.insert(0, ".") - from PIL import Image from PIL import ImageCms +import sys +sys.path.insert(0, ".") + try: filename = sys.argv[1] except IndexError: diff --git a/Tests/show_mcidas.py b/Tests/show_mcidas.py index 1f1c04aa8..f39b6465e 100644 --- a/Tests/show_mcidas.py +++ b/Tests/show_mcidas.py @@ -1,10 +1,11 @@ from __future__ import print_function -import sys -sys.path.insert(0, ".") from PIL import Image from PIL import ImageMath +import sys +sys.path.insert(0, ".") + try: filename = sys.argv[1] except IndexError: diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 08f1f1880..7d0871026 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -88,7 +88,6 @@ class TestFileTiff(PillowTestCase): self.assertRaises(SyntaxError, lambda: TiffImagePlugin.TiffImageFile(invalid_file)) - def test_bad_exif(self): try: Image.open('Tests/images/hopper_bad_exif.jpg')._getexif() diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 803677616..de063f181 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -5,14 +5,14 @@ from PIL import ImageColor from PIL import ImageDraw import os.path +import sys + BLACK = (0, 0, 0) WHITE = (255, 255, 255) GRAY = (190, 190, 190) DEFAULT_MODE = 'RGB' IMAGES_PATH = os.path.join('Tests', 'images', 'imagedraw') -import sys - # Image size W, H = 100, 100 diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index 09587c26c..02c4edb6a 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -94,7 +94,7 @@ endlocal def cp_tk(ver_85, ver_86): - versions = {'ver_85':ver_85, 'ver_86':ver_86} + versions = {'ver_85': ver_85, 'ver_86': ver_86} return r""" mkdir %%INCLIB%%\tcl85\include\X11 copy /Y /B %%BUILD%%\tcl%(ver_85)s\generic\*.h %%INCLIB%%\tcl85\include\ @@ -308,7 +308,7 @@ def add_compiler(compiler): mkdirs() fetch_libs() # extract_binlib() -script = [header(), cp_tk(libs['tk-8.5']['version'],libs['tk-8.6']['version'] )] +script = [header(), cp_tk(libs['tk-8.5']['version'], libs['tk-8.6']['version'])] if 'PYTHON' in os.environ: diff --git a/winbuild/config.py b/winbuild/config.py index d3d2f97d7..cc05a0205 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -2,9 +2,9 @@ import os SF_MIRROR = 'http://iweb.dl.sourceforge.net' -pythons = {#'26': 7, +pythons = { # '26': 7, '27': 7, - #'32': 7, + # '32': 7, '33': 7.1, '34': 7.1} @@ -46,7 +46,7 @@ libs = { 'url': SF_MIRROR+'/project/tcl/Tcl/8.5.18/tk8518-src.zip', 'hash': 'sha1:273f55148777413774aa722ecad25cabda1e31ae', 'dir': '', - 'version':'8.5.18', + 'version': '8.5.18', }, 'tcl-8.6': { 'url': SF_MIRROR+'/project/tcl/Tcl/8.6.4/tcl864-src.zip', @@ -57,7 +57,7 @@ libs = { 'url': SF_MIRROR+'/project/tcl/Tcl/8.6.4/tk864-src.zip', 'hash': 'md5:111d45061a69e7f5250b6ec8ca7c4f35', 'dir': '', - 'version':'8.6.4', + 'version': '8.6.4', }, 'webp': { 'url': 'http://downloads.webmproject.org/releases/webp/libwebp-0.4.3.tar.gz', From d0ad3563ed65042cd5a60f87a5926e96ddea8996 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 25 Aug 2015 16:19:05 +0300 Subject: [PATCH 0480/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index c135798fb..8e7514ff6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Flake8 fixes #1391 + [radarhere] + - Test on Python 3.5 dev and 3.6 nightly #1361 [hugovk] From bbc6f7d6bbd51025ebf4eb1d4cb67e34d03e749f Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 25 Aug 2015 16:27:07 +0300 Subject: [PATCH 0481/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 8e7514ff6..a6568ccce 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- In tutorial of pasting images, add to mask text #1389 + [merriam] + - Flake8 fixes #1391 [radarhere] From 95fa2cdf08dcb38a4cd093c633f4fd2080f73e56 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 28 Aug 2015 18:56:48 +1000 Subject: [PATCH 0482/1037] Health fix --- PIL/TiffImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 30f33ab62..5c35842fc 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -702,7 +702,7 @@ class TiffImageFile(ImageFile.ImageFile): if not self.__next: raise EOFError("no more images in TIFF file") if DEBUG: - print("Seeking to frame %s, on frame %s, " + + print("Seeking to frame %s, on frame %s, " "__next %s, location: %s" % (frame, self.__frame, self.__next, self.fp.tell())) # reset python3 buffered io handle in case fp From a2b6b661d9d2292463969f4fa9f6b9b2c71b5e11 Mon Sep 17 00:00:00 2001 From: Dmitry Yantsen Date: Fri, 28 Aug 2015 14:59:29 +0500 Subject: [PATCH 0483/1037] Added check for flush method existense for file-like object in tiffplugin setup --- PIL/TiffImagePlugin.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 30f33ab62..5a80c8b48 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -935,7 +935,9 @@ class TiffImageFile(ImageFile.ImageFile): # flush the file descriptor, prevents error on pypy 2.4+ # should also eliminate the need for fp.tell for py3 # in _seek - self.fp.flush() + # flush method may not exist for file-like object. + if hasattr(self.fp, 'flush'): + self.fp.flush() except IOError: # io.BytesIO have a fileno, but returns an IOError if # it doesn't use a file descriptor. From 8d6f6fb355ea1265930afcf9ca0d4ffd9e0e2bb8 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 28 Aug 2015 13:00:37 +0300 Subject: [PATCH 0484/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index a6568ccce..4c5185689 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -7,7 +7,7 @@ Changelog (Pillow) - In tutorial of pasting images, add to mask text #1389 [merriam] -- Flake8 fixes #1391 +- Style/health fixes #1391, #1397 [radarhere] - Test on Python 3.5 dev and 3.6 nightly #1361 From 9026b81439251925a6cc3b15cde71e2938dd0aa7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 28 Aug 2015 22:54:17 +1000 Subject: [PATCH 0485/1037] Check that images are L mode in ImageMorph methods --- PIL/ImageMorph.py | 9 +++++++++ Tests/test_imagemorph.py | 10 +++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/PIL/ImageMorph.py b/PIL/ImageMorph.py index 6f92e9e67..44a7e8c04 100644 --- a/PIL/ImageMorph.py +++ b/PIL/ImageMorph.py @@ -198,6 +198,9 @@ class MorphOp(object): if self.lut is None: raise Exception('No operator loaded') + if image.mode != 'L': + raise Exception('Image must be binary, meaning it must use mode L') + return outimage = Image.new(image.mode, image.size, None) count = _imagingmorph.apply( bytes(self.lut), image.im.id, outimage.im.id) @@ -212,6 +215,9 @@ class MorphOp(object): if self.lut is None: raise Exception('No operator loaded') + if image.mode != 'L': + raise Exception('Image must be binary, meaning it must use mode L') + return return _imagingmorph.match(bytes(self.lut), image.im.id) def get_on_pixels(self, image): @@ -220,6 +226,9 @@ class MorphOp(object): Returns a list of tuples of (x,y) coordinates of all matching pixels.""" + if image.mode != 'L': + raise Exception('Image must be binary, meaning it must use mode L') + return return _imagingmorph.get_on_pixels(image.im.id) def load_lut(self, filename): diff --git a/Tests/test_imagemorph.py b/Tests/test_imagemorph.py index bbb3ae190..a3d1dd8b1 100644 --- a/Tests/test_imagemorph.py +++ b/Tests/test_imagemorph.py @@ -1,5 +1,5 @@ # Test the ImageMorphology functionality -from helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase, hopper from PIL import Image from PIL import ImageMorph @@ -168,6 +168,14 @@ class MorphTests(PillowTestCase): self.assertEqual(len(coords), 4) self.assertEqual(tuple(coords), ((2, 2), (4, 2), (2, 4), (4, 4))) + def test_non_binary_images(self): + im = hopper('RGB') + mop = ImageMorph.MorphOp(op_name="erosion8") + + self.assertRaises(Exception, lambda: mop.apply(im)) + self.assertRaises(Exception, lambda: mop.match(im)) + self.assertRaises(Exception, lambda: mop.get_on_pixels(im)) + if __name__ == '__main__': unittest.main() From cc19ca496da4c08f226a78cfd184781eebcbafd9 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 28 Aug 2015 17:25:53 +0300 Subject: [PATCH 0486/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 4c5185689..d579c99cd 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Check that images are L mode in ImageMorph methods #1400 + [radarhere] + - In tutorial of pasting images, add to mask text #1389 [merriam] From a0d38a388443048cdc469e74b6a29b805b872c21 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 29 Aug 2015 02:05:08 +1000 Subject: [PATCH 0487/1037] Fixed infinite loop on truncated file --- PIL/PpmImagePlugin.py | 2 ++ Tests/test_file_ppm.py | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/PIL/PpmImagePlugin.py b/PIL/PpmImagePlugin.py index 87342a229..68073cace 100644 --- a/PIL/PpmImagePlugin.py +++ b/PIL/PpmImagePlugin.py @@ -93,6 +93,8 @@ class PpmImageFile(ImageFile.ImageFile): s = self.fp.read(1) if s not in b_whitespace: break + if s == b"": + raise ValueError("File does not extend beyond magic number") if s != b"#": break s = self.fp.readline() diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 80c2e60da..cda6ec164 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -35,6 +35,14 @@ class TestFilePpm(PillowTestCase): reloaded = Image.open(f) self.assert_image_equal(im, reloaded) + def test_truncated_file(self): + path = self.tempfile('temp.pgm') + f = open(path, 'w') + f.write('P6') + f.close() + + self.assertRaises(ValueError, lambda: Image.open(path)) + if __name__ == '__main__': unittest.main() From 3164b9ee8dce388facc91ff5c0711e6efba043d2 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 28 Aug 2015 22:26:53 +0300 Subject: [PATCH 0488/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index d579c99cd..9ff4f9076 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Fixed infinite loop on truncated file #1401 + [radarhere] + - Check that images are L mode in ImageMorph methods #1400 [radarhere] From e6733c813a6e6f6c11acabe106770d887e02ac18 Mon Sep 17 00:00:00 2001 From: Christoph Gohlke Date: Fri, 28 Aug 2015 23:15:56 -0700 Subject: [PATCH 0489/1037] Disable compiler optimizations for topalette and tobilevel functions for all msvc versions --- libImaging/Convert.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libImaging/Convert.c b/libImaging/Convert.c index 3d9119c7f..757367604 100644 --- a/libImaging/Convert.c +++ b/libImaging/Convert.c @@ -998,7 +998,7 @@ frompalette(Imaging imOut, Imaging imIn, const char *mode) return imOut; } -#if defined(_MSC_VER) && (_MSC_VER == 1600) +#if defined(_MSC_VER) #pragma optimize("", off) #endif static Imaging @@ -1233,7 +1233,7 @@ tobilevel(Imaging imOut, Imaging imIn, int dither) return imOut; } -#if defined(_MSC_VER) && (_MSC_VER == 1600) +#if defined(_MSC_VER) #pragma optimize("", on) #endif From 80b9f7c07e4e5b7ecc5fb64730bdfa567c4d565f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 30 Aug 2015 21:17:06 +1000 Subject: [PATCH 0490/1037] Updated Platform Support for Yosemite [ci skip] --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index e94afa892..56dfc09db 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -240,7 +240,7 @@ current versions of Linux, OS X, and Windows. +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ |**Operating system** |**Supported**|**Tested Python versions** |**Tested Pillow versions** |**Tested processors** | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Mac OS X 10.10 Yosemite |Yes | 2.7,3.3,3.4 | 2.8.1 |x86-64 | +| Mac OS X 10.10 Yosemite |Yes | 2.7,3.3,3.4 | 2.8.1,2.9 |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Mac OS X 10.9 Mavericks |Yes | 2.7,3.4 | 2.6.1 |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ From 3d5d1545d22d79624bd4745049c30cb6a34fe7b2 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 31 Aug 2015 15:18:05 +0300 Subject: [PATCH 0491/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 9ff4f9076..bec1e2de7 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Updated Platform Support for Yosemite #1403 + [radarhere] + - Fixed infinite loop on truncated file #1401 [radarhere] From 63e54260d259e9007f263a2bf76de529fcd6a77c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 1 Sep 2015 19:10:59 +1000 Subject: [PATCH 0492/1037] Updated libtiff to 4.0.5 --- winbuild/config.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winbuild/config.py b/winbuild/config.py index cc05a0205..56039e9ef 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -23,9 +23,9 @@ libs = { 'dir': 'jpeg-9a', }, 'tiff': { - 'url': 'ftp://ftp.remotesensing.org/pub/libtiff/tiff-4.0.4.zip', - 'hash': 'md5:8f538a34156188f9a8dcddb679c65d1e', - 'dir': 'tiff-4.0.4', + 'url': 'ftp://ftp.remotesensing.org/pub/libtiff/tiff-4.0.5.zip', + 'hash': 'md5:3a8feccb0619958281f92e81be88284a', + 'dir': 'tiff-4.0.5', }, 'freetype': { 'url': 'http://download.savannah.gnu.org/releases/freetype/freetype-2.6.tar.gz', From adb531bc7fbd333888b5e8b39aa973fdf2632c72 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 1 Sep 2015 13:09:12 +0300 Subject: [PATCH 0493/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index bec1e2de7..9b53092e2 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Updated libtiff to 4.0.5 #1405 + [radarhere] + - Updated Platform Support for Yosemite #1403 [radarhere] From d21430234e130e5612026e847f84a9a539f62e9c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Sep 2015 23:48:22 +1000 Subject: [PATCH 0494/1037] Improved consistency of checks for flush --- PIL/EpsImagePlugin.py | 6 ++++-- PIL/IcnsImagePlugin.py | 4 +--- PIL/ImageFile.py | 4 +--- PIL/PalmImagePlugin.py | 3 ++- PIL/PdfImagePlugin.py | 3 ++- PIL/PngImagePlugin.py | 4 +--- PIL/TiffImagePlugin.py | 3 +-- 7 files changed, 12 insertions(+), 15 deletions(-) diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py index 34cccf5a7..fb5bf7ffe 100644 --- a/PIL/EpsImagePlugin.py +++ b/PIL/EpsImagePlugin.py @@ -405,13 +405,15 @@ def _save(im, fp, filename, eps=1): fp.write("[%d 0 0 -%d 0 %d]\n" % (im.size[0], im.size[1], im.size[1])) fp.write("{ currentfile buf readhexstring pop } bind\n") fp.write(operator[2] + "\n") - fp.flush() + if hasattr(fp, "flush"): + fp.flush() ImageFile._save(im, base_fp, [("eps", (0, 0)+im.size, 0, None)]) fp.write("\n%%%%EndBinary\n") fp.write("grestore end\n") - fp.flush() + if hasattr(fp, "flush"): + fp.flush() # # -------------------------------------------------------------------- diff --git a/PIL/IcnsImagePlugin.py b/PIL/IcnsImagePlugin.py index 060596b48..a4366e9e7 100644 --- a/PIL/IcnsImagePlugin.py +++ b/PIL/IcnsImagePlugin.py @@ -306,10 +306,8 @@ def _save(im, fp, filename): OS X only. """ - try: + if hasattr(fp, "flush"): fp.flush() - except: - pass # create the temporary set of pngs iconset = tempfile.mkdtemp('.iconset') diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index d892ba584..ad28435e9 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -493,10 +493,8 @@ def _save(im, fp, tile, bufsize=0): if s < 0: raise IOError("encoder error %d when writing image file" % s) e.cleanup() - try: + if hasattr(fp, "flush"): fp.flush() - except: - pass def _safe_read(fp, size): diff --git a/PIL/PalmImagePlugin.py b/PIL/PalmImagePlugin.py index 8836341df..4f415ff7c 100644 --- a/PIL/PalmImagePlugin.py +++ b/PIL/PalmImagePlugin.py @@ -227,7 +227,8 @@ def _save(im, fp, filename, check=0): ImageFile._save( im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, rowbytes, 1))]) - fp.flush() + if hasattr(fp, "flush"): + fp.flush() # diff --git a/PIL/PdfImagePlugin.py b/PIL/PdfImagePlugin.py index e58e9e666..089538d10 100644 --- a/PIL/PdfImagePlugin.py +++ b/PIL/PdfImagePlugin.py @@ -226,7 +226,8 @@ def _save(im, fp, filename): fp.write("%010d 00000 n \n" % x) fp.write("trailer\n<<\n/Size %d\n/Root 1 0 R\n>>\n" % len(xref)) fp.write("startxref\n%d\n%%%%EOF\n" % startxref) - fp.flush() + if hasattr(fp, "flush"): + fp.flush() # # -------------------------------------------------------------------- diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index 92695d493..d6778821b 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -762,10 +762,8 @@ def _save(im, fp, filename, chunk=putchunk, check=0): chunk(fp, b"IEND", b"") - try: + if hasattr(fp, "flush"): fp.flush() - except: - pass # -------------------------------------------------------------------- diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 5a80c8b48..2c9339c32 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -935,8 +935,7 @@ class TiffImageFile(ImageFile.ImageFile): # flush the file descriptor, prevents error on pypy 2.4+ # should also eliminate the need for fp.tell for py3 # in _seek - # flush method may not exist for file-like object. - if hasattr(self.fp, 'flush'): + if hasattr(self.fp, "flush"): self.fp.flush() except IOError: # io.BytesIO have a fileno, but returns an IOError if From 5f2155d38352a9720703c875774554c8bf8f4e32 Mon Sep 17 00:00:00 2001 From: Charles Merriam Date: Thu, 3 Sep 2015 13:26:04 -0700 Subject: [PATCH 0495/1037] Update concepts.rst; clarify 'bands' --- docs/handbook/concepts.rst | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/handbook/concepts.rst b/docs/handbook/concepts.rst index fb97fe098..07b9fce4b 100644 --- a/docs/handbook/concepts.rst +++ b/docs/handbook/concepts.rst @@ -4,15 +4,20 @@ Concepts The Python Imaging Library handles *raster images*; that is, rectangles of pixel data. +.. _concept-bands: + Bands ----- An image can consist of one or more bands of data. The Python Imaging Library allows you to store several bands in a single image, provided they all have the -same dimensions and depth. +same dimensions and depth. For example, a PNG image might have 'R', 'G', 'B', +and 'A' bands for the red, green, blue, and alpha transparency values. Many +operations act on each band separately, e.g., histograms. It is often useful to +think of each pixel as having one value per band. To get the number and names of bands in an image, use the -:py:meth:`~PIL.Image.Image.getbands` method. +:py:meth:`~PIL.Image.Image.getbands` method. .. _concept-modes: From 545154a61f964316937dbc9bb6c72f4284c636f9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 7 Sep 2015 19:24:39 +1000 Subject: [PATCH 0496/1037] Corrected scripts path --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 02baf8c12..c09891c61 100644 --- a/setup.py +++ b/setup.py @@ -751,7 +751,7 @@ setup( ext_modules=[Extension("PIL._imaging", ["_imaging.c"])], include_package_data=True, packages=find_packages(), - scripts=glob.glob("Scripts/pil*.py"), + scripts=glob.glob("Scripts/*.py"), test_suite='nose.collector', keywords=["Imaging", ], license='Standard PIL License', From 8c3f66a9aeee32de3e079cd62397c6e734a43db6 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 8 Sep 2015 12:14:35 +0300 Subject: [PATCH 0497/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 9b53092e2..3b4f36267 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Corrected scripts path #1407 + [radarhere] + - Updated libtiff to 4.0.5 #1405 [radarhere] From b8b3cdc146cdc588b7da1f94ba5ab3a196508659 Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 31 Jul 2015 01:20:51 +0300 Subject: [PATCH 0498/1037] fix truncated images loading --- PIL/ImageFile.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index d892ba584..a3d34652f 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -33,7 +33,7 @@ import io import logging import os import sys -# import traceback +import struct logger = logging.getLogger(__name__) @@ -101,7 +101,8 @@ class ImageFile(Image.Image): except (IndexError, # end of data TypeError, # end of data (ord) KeyError, # unsupported mode - EOFError) as v: # got header but not the first frame + EOFError, # got header but not the first frame + struct.error) as v: logger.exception("%s") raise SyntaxError(v) @@ -204,7 +205,7 @@ class ImageFile(Image.Image): while True: try: s = read(self.decodermaxblock) - except IndexError as ie: # truncated png/gif + except (IndexError, struct.error) as ie: # truncated png/gif if LOAD_TRUNCATED_IMAGES: break else: From b078ad45997175f63dd0548dac8c1bb3f6abfb52 Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 31 Jul 2015 15:48:51 +0300 Subject: [PATCH 0499/1037] raise IOError in all cases when image can't be loaded --- PIL/ImageFile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index a3d34652f..68f9b2b1f 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -209,7 +209,7 @@ class ImageFile(Image.Image): if LOAD_TRUNCATED_IMAGES: break else: - raise IndexError(ie) + raise IOError("image file is truncated") if not s and not d.handles_eof: # truncated jpeg self.tile = [] From 430e9922ebdc4217779371c5867115c42d68c549 Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 31 Jul 2015 15:49:46 +0300 Subject: [PATCH 0500/1037] test for loading truncated image and raising proper exception --- Tests/images/truncated_image.png | Bin 0 -> 81920 bytes Tests/test_imagefile.py | 24 +++++++++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 Tests/images/truncated_image.png diff --git a/Tests/images/truncated_image.png b/Tests/images/truncated_image.png new file mode 100644 index 0000000000000000000000000000000000000000..cb32dc986a9c1a81c0fb9dc27c5366ed9151e173 GIT binary patch literal 81920 zcmV)MK)An&P)FcHf78Bv9RNK7yBHTjG$EC@$tAlA4W+D2_w$ErtDyt`B{uDkUY51Ye)tyZ5Nx7)t2>&BrvS>dkG zjz?B_Id!gg!nqau@jXfR@t@&Ym&O~$!MaO) zeLAJK-XHIe_s2>+DvOlrwYtZ(HoWv4wYd3(8~tH9~p+F44K}6Zv^3>0qTLJz~H(pC2oq_IQWj?_s9DK z@G@F&4Z^aQ{lqdG8h^!jKW6reY3a~^gP!x7Z+Mt;At(!;~% zVbdKh54QU9@u?q%{q=xhpsKesPWy8@m=jy=JdP~~V&3MrIiS0N!I?PD435Z62I3hb z`Qu1j7msu@{w@l?Ki%9~IJ-DCUj$0^zW)Wp?ZlhU4NW%7UstYztB}YqNbg z|0fV8#x65BEXwFwTk(&`VEXNS=kNRD9mEUlV0Hmim#LLXz-f@D9nz92+X@_R{zL!B z0StP_47Uk-QM=4yZ-h~b60IV%qbYYaK?6`UBP$t%^XZXBQ~LOYhxPh{duEx9qVT~p%O_a8 zEx$QJ%Xdn;b|m9_luhskft*pxj$4tDI)`KwVM&K9?=~=)e-Ymw?~nIK^S{au^s1 z{N+<`E2|q=_OcmYtcuPNt=ubI5yR;-bYL(!4ImjLX87I~*TfYS!@0+`z%@J0jfjsN z)>mE>et*1cpG`j4O4h|utP3qwl&k^L^TlZW{-6F^z$&zJ64Z-8Q)ma=g035p?@J_& z+USQ(SN7wB*_WePcsK!X%An)`;0~TnV0Nm_sGDyKZeSqhW`|+tY5|fnwuTK7*iN1x zBwoJVQaU!Nj-yu>*iVV| zb%+Eiv1hha+v~3T;=`usnaq#d{_$xW7yxt&$chkUh6!x83Dq;DH>=0No}FNokjxm) z-$d+2DswDyKAMd%Sm(2Md6F-GcfOIXcuq!v{!DvGY5oqaD{m$j>o&qMUpI)Ho)EhG znP2{`fLMnD3B0-V>K#zwCUPVO{gA+I80Y_cjc6bRUDS zAGXTLn|YAMc79qV6T%?9UT-9WVBHU=0%JMru%k|%aLf#D5|kr+bU_jcfzZweTM#4m z=J7S3GrIB}wJ@A-e-FF8>iPD{Dsw*V8{JRu0GwNG**se>MBKt2Io4OuP8lY4&yG9F zWZS+UhLOJAZh}^u_1bLJ+u$>grYGi3OSJ+-S#IYFaG%L<*NbLZB})>wc1H5~Z%hps z=0K*lq*@S693El*`{VubZWLZ}@$zh59@5=^{!71g_H$MOc!1PesfgC%2IFJ0aX^5P z^?GHsJq*KB-xrl`zuS6Uy3&nYww2X|2BvIFD2uVVkzh*f-}xZBV?`t%j{>kMG&|1X zY$fPWogvw;h4~P<6gJ7D|8;jC9k!Gh_5OIL?D8AkKbv(00RN_M+a$5DikMZ#kw_`)EyN`8r`EM{<=LHg+T$NS@}hGN0Ai`+-|Y$MT?CE%a>rQdpW;sueq znAre`jdCprMnTp}48z81(Aw??d)f|NDTnOQ>wRm-<>N^P{$l1mQtC$V0na87M_=;V zI>Y#Au}(s`Ba)lrseG*6?;82c;l<(Lm;zc0@b?|_?~hwP?7r@x%kFSP9y5Ty^P6Xt zm*BVxunu&}-HPLQ=6nR;&sb$Bv)X=wH@DkiZ0r~;bw%rS83r3Hx^l*|juiH=Za10!=}>Rg;Sr6h*prKFW90zCYgK{{Ds5CE(wrtb&R- zNraJrpx73+*^@TOBOGC(NF76KY%Ne|+rHlpw(g4dm)2uzrM@4Irf}@Cfx-X=16&z+ zdaVa|7)*kc5;g<8c@W~Mn!}3)sVK1olRx;<`-9&XTR_mcPaowg*zfH^PWHMdK)(M5 zbwBx?-$+g7hY46@2?~iu?|HQX{ z12Yym92+BjGj8Wa{aKF0q{0y)x&M**7AdE#P8f!rM!HBvdr`P6T`7HUd-VEwjC{&s zTPP(b5;6(;qKIm#Sq;uJ8Qvm!ygmg%P$&)Owv~7pF#L8Nx;9|nQT=YW)mP*7Y5@uO zHz=ex2)mq+A%d9g$B|C2PwEiqD`bFAu=80atP0C!JMU<-U5M~FP*gVS&Q{hBRyVXv zTJ`IGwA`II4(2BL1U=cHAjVgzpEJiJCGS3acef9MCpiQxL2bkU;jaLW=;-$LS^Q2? zT_eM#^fqy87dP5lGMOaczy5-p0Fq@&$n%1iz*(0*`WrRgT&&647FXr%ei)v%wl2@v zqc>ewO4&Mu{c%OG!#&X86cXE%d2ol{@ zrn`6cy+7{%q447_WLvvWue0vQzx5mO?UEuw*a{o*rUbX(u?5k(NL>Gs$;t^B?5tv3 zR{LfD^xU$bSUxn&STojor z7Uv~|8Eeab{ny*P!~5eM#ad29VxiBxy*+?k0;aSWju40efg~G2Yl>klv#So|^o`)5 z%0qPrtJdCKkWxQ%>+UdZ(-rNl!ZuFwx`#3-YzVPBkm~v6h8pEjpgJBbQi6#dZ}qaD z7u?-jEIQXVH@z)w#SE-2J}~x@ui9gU-D_y1(?TzdiwHs#fN- z2i|7oUjR~Twf@H9#2nS*GwuBSPnj^R?CGhOR)@m=&-prf8^`113GlonJ6mB;5Rgr9 zO`#fBv4&p_;YLtqG2WY=;O?U%#cM$ZGK^A9c#dIYz5yN4aNzyl3sdOoWBdd*PLtD5^(Ky5^zG)K!dDOD<{3;QtM3I+U8GCN$LHe z%6*&qyM{4oFA8r*5bxE|QYWp~o$c6cLCy_a@dH{zs^?VOPgYS(JPB#Jw2Gu`2@57R z3q7|pyZ-A-C@ObFiyTI-_@w38aeIOHT@Zc;t}cOLo@mz=b8UG5|JXNwJ$wXl1^x)r z99LWkSS>ElY+Ytqww<^bSmo{Bc0o#ADMga@qOi(#a0o+zpwLe11X3bTq4eJ*q(WdW zT!m`KVKxZy*mI8rzkU2IiVt1zpRsPG^|!mCupGeO z_-;ZYs0SQLAjmh_Ah|-DRmVAV;!#1l8~2K&IV}>r4^py~x+1MqI&HdjKbQ@}^u^~# zvmlLvKasM7My94Zc0oLJDj8>d*UX$7Xe(jvo~L}J5obCfD>^iP$+;p!}H~5yS%eS_ZPqM>zFo@1<(k^h+9bG z%t~X%?fe=KEm^v9Ao+$hNa^vZm$ZHkQYxFS>jxXm>OF0C7&If96##KU;wXj!>lum* z!7f6sQsFj~fVVlV&;>}v8a1Ng7} z`Z*Ob=F??BHso-|eWP?5hnTXtA2pkx-H$3MzB7e^Y4x-hg?9{=ByDyu*4{JA0)J9d zx}8nCn0gLll9ZgIVFqN!R9%H4avD6wAxvP-5AO{|Pvyl66CR_aqD9rKdUNDve!p}2 z4Lzg=;9k$tZHvLx)NLT@efYiC2%P;_& z6A_un{o-l}A_C9q*yETeACDXbI>yzE=DUq8(%UVu_zu|H=UUUnjL8gDo@EW{Wm~=& zgKTA%hvqia9m>GT;Y{sTWbd{Mu@M+-=+4`g$L;8)N=n;(kdlfHm6W=&FYIqtkzlzc z);s1M@G{Str&kXq?L8Yvu{_-I``ie#jzhhSm z#RqGiXBV@OF=}>O-15uRJ`VW1k|YggNq8>SHRsBMYRQuY3=PjnWA^v)4p*8Lw6jV| zI$uf2)a@BuiXPT$vsG`yF$-j8W03rgA_IY-@DW7lD3ND@)7h%pV&|!BP{~!2cbVJ~ zn+m9rK%NC8FOyVDZ_+fZJLDw4we}U2xHV5EzUsrw+QnR37P{L18EwE_DJxYKxd6Rj zYYecYJXwv(0pBSjLiZz193rkZF9!1gQ5+AsLW%w4-1+=?3;g{-apaxX907%@1rn@8OK^0yS8jZ zup)Q$f|jiNk)QvyA~_{gB)>a27;|uux53&9P|q+L4**Xt_`m`*x1*St3~R7`kdhrL zDXB;gn_{LB%oBC<2{8lZEZE>1D=x(S$y14|A^9k*V*9Q)P*W`>^kpk`VtgSUzuo{W z0ch30mgTj=T6ibDXZ5V^fK=2MR5P=@3j-JTO;hSE(*5W!{-4QHh=5F9)Zbj6@Kc6u5$t$;+j@SKv;JWtj6p3{Pu(m!!H=kfjc!t*z*FgIp-H-gjukBA^HVpD%N!{2qnOQ|4qt}Nq zMI{6dhjo~S+J|X%+$gZ84rWp;x6RurIIZ4eoc#1Oj6XbWmLAqS7!QYUMwPJ%LcMR^ zem(raH@Kyw0b;nJ-opg{aA#qhvG2_BRJF^@<%lIwAJ7Rh@12%k(AyMR_4a|Nb|KO( zrq7pd9WrJx1kk+-eIp6HrCo0?0e_w`?53Mk4q&a$Stw;9Df6lZnJ^@lMH}3fsjk4B zgqApkODdVXXWM=+3QLlnV}-lY_coa2i8Wq;lX3iO(A*}2z~%xl}$)1pwLmU+;^p$2%>IRI|SEkOO8sb#n1Al?Ui`4Hl=#!?}765XlD02z8lF3%X zFzkJB%VD?_xpP*Y9h&oGk+rxf<~N~js`bqiBS&$HgL#ni`6w-2$xD?4P% zI*7tr>EN8DfZ=c)Xe$R^9^K1+(u$LS=uo*q1X?L{D3_LB?LG8W34EI2+yG-Uq04MtF!y96TcnQz_=iUT2eH!jD(O-_52-7AQlKX@ zqc=YDFw<21>-`kHO{p>CVP?0Zf__ifTz@+8q%zsi?=W1XQlyo-omIFDou+~Cq79DdOI1flLzUe?>J zy{vmu95ADq$D}nj=mC=#WU%5NI{^RuU(F1t!?5G9Tq(N$AxnIuDIzF50kI9@HD}U< z*kW?pf^Dhx5IJ;5#vE$r=qAVzUAaGk==P+2YeCpbHznlif+sWR0aFb}`EU$vS)5a0(0%n)(zrpkNl%4+cpWoSQ%*&;7Q<(aA!+IK$ANhQ$)C4x8C_yOVpn zvg@mqja!9Z5KnU%++J5iNEBkkT5C+x!GXe@6f?|JQbOrv9xF$$CfzVc;EAPN>4b!? zrw1OlM(Jn)3@o}-cW*l;=DfM=*%l`=iCIA{n9q!}Ow#2Ac5D5N=~)H60_*9@OnV!o zN!nkK%C0roN`T&gS4i$0P@u#%hh>pKboFN1;f)y8iL-6MaNv`9`&kqoN~a%Q&)&hl*pA8C_5 z14X5{EwTZ(0hNGT$c5>oq1+yk8W1Ifg_^j?f7~QGNwK4po~^>eU?Y7GgPTp)6^&zN zM@h~MUYEuAT@FkNuQP&{nW{jwbWm!@EFB>aZen?$6eBj1i^DbA&^zd+!9bp0VoCQ} zK1greSy+qLtkAufv=vx#dB=k0>PEJ(49pldr$_hozx=D9iwoEr%ualyr6S+4J(?)5 zMuS>qj%doa$lHPIFdH*$WrPiBvl3_ZIz9};<93kLb-PPtr;+XkvvEAe0@w3L@ry!$ zI2C0;Shx=)+{o3*IeWd4>>nFY&>ynBr7U3X3WVRJ>=NBw&A^o!D4<2jtFKTI2ToV< z_O0IvO(HRGG{5G)0gmtF(fyE1z!T0;SHtkEk>1loW#1Y?U`4{M4D<2HeqJ2W#E#AE zJI!q>yqM{+t+p|iOFL1xAOEv;_ngCfvtAdK?cQgdjo)%c2MXqF&L!8{j&OZ#v{ED` z3#>lKdkT!e5(2eOglMHOGg2~+K;}zw;^O?5$#gBkulw6t_s?nRE!1OBqYgvYqQ6}v z^2lxtV3Y`=ztlk^UFjor`#}rLqhyg0rCe%WoS*AlPDJ*c!Y2k6CG8jUbwDjdI}yGI z8%5z>D(h0lepfVN*vxtxB8`+E!@Qlir2&#rw2fd4=Yn!Z0*ZsBI4~X*6&Vqg=z%FQ zR=R22kK={~Hn*h7*@^g?-fmXP`77?&{ag3rcCHyky~L+wP|oJAh34%Z`tBe6n+bmd z6q%9QF`!7r*P2JHChOx~Xm({kFYIZhS(&CA9GyY+jS<80c6wW#z+t`azWDIa>3Pvp zKYaT1bez3X7T_5%Od+{>-m6rDEebW}_~5t?CkaV3;BZK9H{{|-g;~X!3CsO9Kw}B~ zu7Jm>HF+sgin($3oYn$X(;um}(yOxX z$-{+H18?3hlYU{6o|c(HWd*e%j#(+&HY zhWj~=bW4u+x|rHrc4R0rh6B~(4WMgG|i!$WbfNGe4#&m%2T zZliL#$;BG*2T@EYEXR!@oGCoP_KEIdRtL8I&~N*4sPDhhrrR|~2Af4wb3LCD;-cGg z-~$vmOo|1P_ZIRvax@Da2B|)e0fwy46B=@s${c#rldx?t2e(46)nOXXd6y5}&~y0y zJlqQ8lYR>o^rP#Q6iu1-~ zAPLhz)l4vE_bDkMDN)j|2}M{aqL^%`8_9usg9)B>m3wNLJ6?Jx*0fwy58j@9!|qT8 z{d2#1M(1VT2aWUu3bxls%cN;ZrGO!Jjmj(i(I02q36)<}GY0?)=RnJHR5s^)w~7-R z89WSqKXhGpShw!F^3X;LcQ+F9a{@WUMg}8aG{b5zDmO{S+7TA*$yT=;Z08_t``ppb4$|wjv>L%|$9r3*0IzM!-v5Z? z+U9ykVHXHW7V%Y~0lM)zmI{jkl7iOPd+mcF^Dujgfo&V4~xo%OkV0p{u?Xzm?CMpJ9TP)WFux|~Q z?^Xh?Drhdo;7lBhxFETM^y7BBGV8i-rDsw2K;o0Lm^eXnWEWewUsY{_K@K+FB+{2;ae0?rr6^w& zl9a=IBgBd4VFl!`CT}l@iel*0$#*P&E-tMv0Z~aeEgu)KwgYg!M51t>jZ{fDPN4< zSzU1*_38&-K5lI|Sz|)c+)~xCugo9{F+wf!G+>w|>DWi!x;E{wX~1Yn@*3VJ7cy0oi|c|>jDwMZ~4*vXFvPP=YO6333P*cM4^V?u(H8( zAi6lbvXz-hd19$ZHWq>B)I$U@I9}pl;vCQd%EqNJiF6*KYxOKy=$HS&zsj9EIbYSZ3-yRzNH<-iZqEZJ ziuHp5?32Smn9%HG9S}9u6vf2SUVGstmAG{zsOs8I`r_j#3U`N#gV~p#w%fi>D4M!N z*(!-DB0Y1{*AayYAVY^+DPeOOlb*wv1Ac@ahFl~m9wlk=P5I37E*3gg3rTc8Rp$j` zaOJ)oytNdnd0*Y5rQPn!KRyEZ|4KA)fkW)KqGVB`4D;TzM!M+p(!Fz5f_Dx8=48;o zZQv}qU1bzY?79IXAn7M-EQvYptH~{WJ_;etNP=AqQWbZE1;{n9t2# z6Nl=059nHFa^5Qr1tu6z=^a6=0n3_$E4LEvsqP{m9Zw zQCAmStd}hy#fQyi-F@EV)6>)AE~TZ_1;(PhWnU~n`AlX16YLYbdH)*ct5ZYiD-<>CgP~ zIXhKO6wStP^w)KBrkT~t?jvnxiQ$#>;CxsCz~N@?USA;LC4(%|*Pdm7kq?<2L*r?e zv%|Wsl>P4Ot{Ztg3^tfmvD!F?h9=T*;!&2kuyH#Kk}Gswk}t=2q3H-$`GY}F_J$Ff z09m&H#*hP3n4Sw*yO_OGtNYd2>a7)UXkVjyDh~0c9{SGj|4H((!rZMWXsQ+%Ror2` zE2Wb@@7(Wwq}6gL3At6aI(YrtR@)mM6ZUD)) z7Jg{Sk_NgRF0_0m%Nx*93xt_3A_4#OFXaJ<;#5_9(L1aytJBPO(W|~rMJnYmo{I>+ zGb1a`<@AxPiEa|E*##_J#CBe8%gZ&J9oi#Xx5KdQhoWa={JNCQrW-HxgZZL};oJi& z4oCbo)oIAMTo>0TZ+qV5KaLPM8E+h|E8#|AB&-Md6(A4kLLh$#2-ny44AxSFUPza# zMKg0P3ohMQZs%Q=(Sz$dzxOAZ{l=S_EXd+u{^=&tcvtjxrTFHaJj-$yXs&vcJXvZc zFD?LOa5WN^#)Yc`u6Yr z@#IY4`3!n!D2bo{%AQkJt7xr?;lcJQ>E>|YgkgZZ1~W0s-HJjtr$_ed{LB%ROOJK1 z95eglhn+_H^UTNX_VKYFR;E0USA?id5DjW}2z-JBCX_)sC$V=i(G>T>776o!mwXD2 zN}|~WW3FnfGx5o6wN)H?n<6kTSY9=&Tc_o^BXh2xf79Hf-}?K1954l+3Mpt=G^`yo zHb-)oQi`M#!>}o07+{oeE0dAfIl3jQR!)WR3IM(QjUlv^3Z&kXj(YpdSsyoRkJ28@ z9v`=l+dc@nkUPu#WQ<`l_eRBu;0i*L;=;LgpZprb z7=lk`vre~!onXKFl+<)@DY_P@L%lF()p)V{PuvC!MS+34c`K)5?rxgIL0B{lKbtb! zeX<}gbkYXa!Zawf;BAt^5w~&Q+$%WdlD5z7VYRQPG_1OAH&CSYy6d`9eWVo}uamdh zF`T2ZJ(+V-Y#-x*S;gwZl<9&jqwMCz$s@Tmk}{^#OAr=H%%NXf?3kU*THf%fdx`JW zBFxn-^#HfWqjdXI@ak{61PoTKBoPKm%B-&rYq9XY62ovL9ETnO!N7bAv?lE~V>pg8 zdKtroLy&9gtOI(#c-V&vm;HL%X{5h=>`zR=ho%}5PjMPB5OyK>Sb0pBzygZ-movce zpb*C1W3H->a5V5-7-KvVOYo3slVge-9&f)Sk)XNgLyPAq(E>{sdtZeB{(FCPE?<@M zF4>48bJ&m+NuPtz4Z~V_&7Yb+iv9;d!N#(NPyOJt1BMfJrk>G^VQ(;oB?#4GU#CRS10Hh}0a>7dg0Mel2#GKi zYz+0Cnk0C&g7$Jm*5U<#mc_P~z2$uFE$)7;TV?;^Y2R=F!&i0$CfkcS%WYa(SY#OX zv3}+5x?wu!vgTzRVax_x^ynhVf|GqAhP|gubv(gdm5Ns*g2#yA;)C05KVo>WN(z}) z@C7 z5ttdh9DCAUWI_?=vMAfc!6)& zllK+t2{!uusxilgA}ax(q=bq~Ik#3jj6vNUwnBA%9MFPFRNaASsAcf11stvyzQf}3 zur6{7{guC4C19{JVFj0LB8Nff{2jUY(XLsmD(R3)dXHw#Wu487@xwzY&-)C+>eHA=y6=NC6@(aBQ;3l-n}=ac0UZW8trhRo z@H+#tEF^+%ki6Nbr1}Gt=XfWD;g$elX)$SWQ9NJrW>@347La!DC8wsYmTgVv0Q@hL zEdY9;`eN?pp3f-K8hLVOjzhMG zuACNl370s1Y^dK;4rGDtg9eet#*M(B1$W#^ZE&`5XV zc;AnctP&yA`wu%$C**@s=1nk6G2meoqHHLw75fS^tHjkGI1(31d{gz62IV=ti&5J} zNv*G2X6Z%-QQg9$S_?92c`g6#Q;zEr^0j{PfBKi`c+bH^Tt*4pp4oy#G>(orWygea z=-O&83tP|&AZxiSf8O?{xO9U!x`{9O?LqRzYZ7RK51kXvZPw*$A0AZPDSfaXe0u7K z;be28tdc9)*?P%3qijagZ@c4h=%GB!;H$T7Dyxupz^a3h`*2R)hd&EA7bd*tz!6|ulPraSNRnWf@I<>raSp@L!{^7Y80b$|Lc4y{S)`H8K7iOgLV_6*}Gs!VL z41<}%-NH7F=!IvbZ6bcNF`C;PNgUhl81}aJ4y}(5>xa$y8N;^sk=~b7st2{qd)XUd z3Y7j0?;-|(`DinYfzI?#xsiG4rd)H7AqxA1oAN&K8l~R?7!wP1Wm!x&DtM#|Bkzso z*OYElOlr~ZWBP^P{X=<&wP}HbO}X%5q)u~{aSl&b6g7%eR2(BUm9(Os2C5%H5r=GA zTbNMWDEnLGay_?;ub@f@`h0_R*%kR;cimyC4fgohf9kisPR>}^*&wngPgkkb3!Pnx zBB9%(gMe`CcLkbgw%o98S$x#|n&Opq7&p`#UO?5T3lsRt+bvs*Pj@0J>%PC>DCrP@ z!ypK{=*b*G7*Q4{GIznVMCT2=ol22DcLy6E>;TG41>G^=mJH0`CynSm*#kJ7=8Y<0 zhT<{H$*hm-ZWk_G%HeX~4?pmdN}--o+_3g6fh}M-QU)}&5FaC~e#K+rZjcqBQ(m_-orkZzU`ShsMS*3Gkr#n$o_8*$=oeG0n&=qJCGhQNb%EOt4} zf#ne9E=2BT-F;A!$v0G8X}Po6E5%2chS5i0d(NnQ_L&-{sjPp*u#Nvy{IbkeR&Tc* z?5XdIR3xo*aL&?7`|84$kUzsaP`3fYT-<;w%wwoI1BTs|plBe#7<1YYiMbRMkHls? z4;O_bwn~@&aJ=v&UZ>?DsT-#F)H1i$@+B|Su2Jv)!=LTtfbD7lmZhw`(d3G2S& z(kNVc6#;2KcKFX$W|2rx>McVNXK044!0nlzzR zo(S~{3|BRwL5qdgyup&TIW@IRm=)jXEg773A^|&q;S)sS40X9#Dn}UmS4OfV%&1+_ z863lh9S?Yrtt{D*Fw6;>`9kC)mrzXGYGOb8bRCV`S^2tR*yo$sK2@c4UC~OCHk%Q| z>W5XRE__a|m19`vSWVeHDt17GWrrvZoM#v}%H{D)3}XQgQJf8f**WKDzKpXN#&wiC z%lkv$J_pr;u;k(8!;kewgn9kt|MCA!xH!8ULk>{^bRhsKjB>IN3MTGURCE;JI_xtl zD#yNn!fnDZ`jUY(dsz%~hN;??SjLuAHz*pjc5FUuy2BP`tH-D9r^nu8FUzyJdN~r8 z{kkJ(Gk!37(UXdkSOpBt&KAk!Yn5(UR%XoSt9;4q44sUBFdAL~nOY#=b~yvI7Pr;v zcBw(M)Rz*ytb!hGz~}>&bC(rb`FDK|CiE`puDFgtCRPl)mPvA`sO;N&X&5T;14l7s z7QEZZox@!4plWZ|7QGU70IO!^b!Q|`hRJkt8Ubr_~ zocdUpgXD|kTLlhZ^c5I`4VYLEjLa~`3dD`tW|1#9|H38bztdXpTImhs%+eC|m*L92 zxxDFGqP%|2CE(WBeb$skjtYba^4?%SFQ3ja%ado3N-3(Mu$c{Jg3=JF;0b7>y;kNopD)EF7oGS9QWu-Ap>BEP0ac%NGf#r5EtD9TkdO)yiq*%WW@1GLi8D^KH&@(i7JAv| z$=jS-#C@c_og>{o-^sN1k$&nsmC}s^>Pp#cy7BVR`^vP)zhcJ{leDO$ptsN?S`-q@ z8tNRzfL?;}{LnpyF_YdOw6*9w>}(0|8{qh|RRMXC)utBFOzSpcy?97(-r}vR)fLvy z{ty4+Rmsvdzc@F?+CtvgY)F`|TBsz8n0Ldl{L)~DO7Y~7Um$`*%>xC<#VE5$JHE^9GNS4^F!Nm zF|_Vub}X_EJ0IzOY;>jUW=NZLx9Lj1cZQ1?7S&C%@J^s)nKFjOWt}TQre;Ema4oUA zmyN7K_b!fe7T9gWSVm%iF{i9#H3NTA>N+x!7GT*zE>>DdH!WUt>r9)o1J4sDtZzSS zT%nK(hXbKWi^B#Z^cyns5Y^%zIfiW|tD#EzjA3iQFjRMlU2yvX^tJ>aGkT+z8ZjIX z85eugs@{5RZHM8hA0&B^|FT)HJ856!KVUfQ7Wg3eo92qY?C76Ml5=5<2P6ZL&^H2g z@|sx#5zdtwi7+oF-vIuC2$$LP1-Pc=LuwcIzv6-AmBP!XxmLPVKR-HQ`KXzt3o`Ed zDh{bBf>uoARu4@KB|v-d1+9)&k(0gIrh3vR1#{M|n?*~yq4%`{8(!&X;? z0)LUWIvuf>u$q_6ww8QX9<>++`gViONp(|JRfnyYix#ZNP6vJH7 zm}O0>v83R(mL&M_@qOou5Bti7p0m5}hc6$u+hGV84r=v4cq97cdS!&M4>pUi5V}NU zi`%xTr2)^BB$Fc%Id2`O5fD;6g?e1rTH0T;0e1UB_?mld!2{_I9lTacI|Jz3fB%ms zLv}jBj(}{wQ$=$SHOx%j4PdVXFssb5DO4>;*e`I`l8+|y3zUbSX&JE=F)VkJ_AbdE zxz95s_h{Qu&_@60<|JDoTr0saRS_(o*A|r#!<%*I!tgF)_|s$G+im(!$4Yv zh~9>%rHm0oXbLXo!n)keN7$u`s1M*L5;tVrBsOQq!`xZAzSdcnNOifqAbe4LE_JWv z?Fj82-}=t)|H+QBX4(5=@Se$RC7l9W4s281$IP2L3|C4!xkCtO-BY!z(@s5EJx_e$ zvItqT`4}!zC!I*c!!XoyF&UGt*uiUVo!~6Wh$Dx&T7$Q#(qW0q;-OWJPq;4n+NhQ8 z#zE;chvmu3F?J|77Ev=M0BHye5jWK1XkCyFQCMX7u|O$WD9#AN39*x9ZTnyh8Bllu zLolmzwGeJkan?E6evJA%f(>x+)$W2;l_ zXtHlq=~_XqQ%!k2ja0j4(V(i+Bl%o=S+ebg+9+$TJ2qdJ)6k zM`@Tbe7a0N2;VYJG5oOUzStKSKCBrA8yWnxtr)KNqfizLBMDSFqPIc8%Sz^WjbWU> zOn~uBG1R9tuT|0oS1}}U=tBw-N)O5yV45xbQLbC1juK3{Gb?cGYAuinOW*u^f0R~2 z6_?7F*fld!!7L)gFX72D};Z>Mt$dXhfm7;?p zL5GqKX1)lm&WvGibK$}^BuniXquZq6+?z|UES*?q?^>gC*2jmn^gOR0Y|LS~?fVm| z7#l=)vof174bJylMi@6_|A2hxWQgVPK%gBOXu&;$Aw3i0 zVp`Yb9j4U4!zCuNaznrPyMK7jE+U0XD$?W}rM!e;=}c+?QNX?Jpf^{g za(v^=EY6A?kLz<_ z@QBd78~NA1l^CK7XO8oa2Vs>%J;hS>gSB)$0N7&zXaXsPn;iETA}XiBJv%{b!qL{XWE!m;rJA9rIS>A z(eCFk{^#?3KgWdMw|0Yc7AYA;n)yh^xL}$R(VJ_%1BU&{7?z}y?bk&gKW;W%_xX)} z>if}6dN8Z&jhPnVf=~uqY%{EZI-EW;IS@;SjOMsS&ObKLvd}IJR%XQ!=h_6hPPewZqiW+VGZJau=6RPn$|R;@6!Fe+1Ay@Y)#?@yfEoC_X2GN$S~kzN7ApX)pZahA_v3z% z&bi=ahMuCS$QI0v$DmU=MPERGvp2}rC%ZWCVm<(^!iWNCQ~5fI8F99qQo_u1dbt#p zQjVEAm<@I?4%fd)A@wL{l`jy_v%n>N^C&5{6&=E_Z0{ z{j)YIuKXYN6&?EF1RIvoFN;7`-_-+w+ApCiId9-b>4jntfgO?+31P0RC_Ow+b09Jf6u=N^9+B!4QgS?UU6^55flhq@i6_lUaBPU^Z+?=AI%xlY>IIz?t6v@65?YNehz zF--fbNlcCf2KZnGIZ#lC4q`aW%aeGOU7MugQjQN%wED1FKRm39Pucdvm!mj53@7Kc z+zMD*vV_oKGU#q0A1i2dv1C#+FM*Sc&jffVzk)Ix7|&^?G0-a|F)RNunS>UX$d<1% zeESSmTENK}rfBwt1mMVA)OvAJr}IU=rQ##3b5`Z^F-Bk3XQ@&-3Kpj#zE9ETj;e;c z!r=JEALRdzGj2x(5fgM9}{3YOJhja*a`~q|7__h52x6a<(gBPC z>tqE|yJILuE2_4nBsRj&nwhK>)Yg*)&f6HPy6*)fOOqE8j$UUT)1|8ze&$rMg@G0CE!v$)Oi8~1 zrB|hf+F`$VWhE_J2j4*DJr=qi3a(6NcfsSf`6CZvDEB z1^0*p?^n+I)w=Tz!=*?^@yERlD+{llyA@WG)n=7w-k$Q!Tqrb!Xz3G@*4&~t5kqZ? z4GW9I%~b*yjFi#^lUm34xYbT0{qaN9NSoOYK5ZYj{Yn9_l=smRrE&wOU=S#aaZu+C zK&;`-#aiTLyj_aSMk$$o|G7dcn!4j+&OF*oLCp|xFK!oJn$~Jxvj~E9XIV4#lmGSq znovLYCWb6{=vT;oE1V_1J^|cpck?~Na1`RQb55`7Ffw&Nt2gVquAEq=54CS=RB=6l zcdI_!H)W-qqIBa-4HEG2(=3AM0P_e!pBy#`TJG8}H7e{}VI>PnX~3{lFYa4o){YMk zo6l>8e)z%TcH0jj%?hSm@Z$lD1qvg&!udeB7$3(wvAGr23le6b`k3=kUx>uKC%?wH z5Hu2Z<4mQtTVG4jd+~ee%HdGDX0Vq6fKf9CAsQh2Vsflpk_8nv)HZ_~f&dfHrI&Mn zc`MV2uB3n#J=-ihl?vB`n{=J&=dFUcyjRa5@5F#w_M)4pLzT z@XRh8l!KR(Kl3)qK&06QS^-C!4kPBhxc*~gcCNkD5_uhT@%YqRreNmH7l3l=dDDN=&RgMorriqJcD|2h!^wNfdaRO7Gu=RhU%-5q9{ zNbS-obIWOX_Av*gh-BX;Tya{yoOSZF1j3|xv;{ZppvsmeX--tBU(uV`hB>{apK0m^ zu){WaiA|Tn}lIdw>Hudco|1>V-wni8l~IZ{tH1w z0ulq=u^@4Cl1g32k5`QX?^P(XVE*d>fh#<;ge_mE0RB6t02X7IG-0f8=HJ-bjSJ{k z6SY|lrA%j+U&GHKzu;2ZTAg##U9^T_3QS*`uy#_e_hhyvFD;l&cg^J7%)L5fwH=(g zl3XSS7j%mWQskOyKaTTWKFOxuGp4#WVqvphe{F9VJ`4_KqjT1$r@l^-JB|ld#GgiL z;kgBo@b`uP;c|vZt{o&|bGCvDK{Sb&QVM0xz|~$GDpAx&goA$!5^4G5T31lq@|tV9 zIcdREn!1KRn115#{4W8vfuq1zwh43KU+l7?kG(INz9=Z+ISAI7xFYx@jQe#(ox0?A%iDIZrSg--#^J*z6^p&-ESzA-e5&?_;mukqp15N)3_m??2j9<8&m4{krDv(3Ic+5eL0zs=5Kt!+kC0om92yRsz?V>WaQLLU>(P zPVW)Hqha`&xW38~2F4I2LzG40aY3ZEy2G3RgGmflV2&iBt-S-ZOBNzEl^hk;G<@Yx zkS@ZR=KCQpQj%8-q|(I8Jh~`tdHsAs>&K4(EHWMo1mac_`y93nF|1kqtrJ7i4qxJm{x0obORY|65(uB zR<1KQT{lX@k{sFgX}e3@G1$?bq2#4y{}9aoh!R*nsFq3(T$O=IJU7vFzD$5twk`vV z(F053v(*+$$!tC17%nxiXFdm)50J%0H>>NUWphx@w*feS|2deNAvy!!AW_Lp#%W~M z+;xqzFB6?HEbvHSi&x#uIr}j%JC4X<%2r-uo*{+Z7WVWw_UTpMckZ*VM20W)t)R<}?p`%bckw(Vev}g5Yt_?g!Ws;g^XJImk;KI*Jq(BX zhTBQ95%a#B%6|6~)DM&Q(ekL?X7G|!$ST_$NQ`IptK`hh=^}RFR(>}L&(@?c;Fxda zex3r#0z{XH?76ZVu>hIZHw3G3w`JAlBY^)|ZrT(p0`j`zIiDaH9&-{jRBZvUL#Quc z7?sjzfd~#~iLX->=Q8eu; zlZGAKJ`m(xX+mZnNUBXn>br(~=O(>gujM2Cq0I2MKVEHC1zQU+IVR~~iIihLBxkx$ za_4X6E|Zu&C^*!;&4)5Ys8|&ZPEX8uu@Dad#bHmD0hGRaW|wqXxCvN&&jp6sRRvcw zG3(kEbp6=hb_tj&6+z$;`XazcYZ`mtZhOoz+tKPD%xiH7WQJiPqhrW1>>~#ZCrO9q zp_ecWNPeZ;b1I7H2#oT_DU!p|a3IMEP{%^j=+p7yBc+rvXn3>kK5xbSBv3Oy7bO0$KQ2`m2}v9Y;lEX8IHtgTU(r{8-fd%wV`OAtliGk9-bvW?JxTJN( zaFvF~+#N$d9N)|$%>6rQ9f^D!%ubMNClEF^nQ$->*B4^v?YI?}`Qq>L zaUqa6?=}4Q?K*Y_jqb@%aMq;MVYhv5gG`ti?|PT(q~lBGovzp#k6ZZUt zJ%Afar(_!{tDwZroG_9hJR!);%EFwA9)9;J;FvLtn%(>)DTZ@7z%Q{P8c`dM4HftU zdc$opah+p3LXip86*>}g4SAYrz#fC&+lrbF7uUBI@ zi{B8(FUNM6hv<#%NZJ&`Y@7@fP`Gd1G&mM!a85&O?lt%V^AUmjmBc_wxq_q9vnx=v3!tNgGub8 zY7CL$w=VgK9KLKtGreeoks>_mtsRyn0wNhPa}hb@!nPn!PvPawQ?+z;UtK{K*N^T2oSQ)* zab6r`5=$4vFjPTOgUJXTp?Puaf;&qxft{JS`je_O95C!%#xNUs1Fe&JlXmDZhB;7; zOD05$)U$E~S4569BKT_`Hl?d#=9f?X<1Ulsaa6L*Cf?V_q|))AMLV`%R<5%(OWde! z5^r3ct0TkA6oZvxq_Q<6@M`_03Q}^Dv6;&y@po^|*TE#_($SalJyJUymoE`<%^T^u zuO7hEx}gI&C9d}vxhcgtU@7}4c)?s0oCA0BS#%(^)|7~&EDa}z1n^JhRcY9F87wNL zYZE2lT*{3o3zGoIQ}lw@IUCl}aiuzHw)=P~!;07so6RTcNlQw}U?0yKS{1+AE~xBce=V08s*d z9{t9_OPm6pD8i^{mu8nC9IUy-H7#)0i+iU}0NfN55RuEw=2E5tkwu{erNkjX61Xck zY9Ir1mtA1E5`OF|axr4K+k5av+i-GR@WtYc(;)I@?TB7Mj5r!8YKuiEhncV%wltMd zM+T3c(yIDf`*N31dTRzw#45+v6(XDDb3l4N)+(1(IUgfr6H(Sqx$8VJv&b#8i7DB; zc7i42gw>5Th!~<0i03owRpgFZ`4SwC8G}QS;0e z-Z0*h0ZK~Noi>X!Inn5!^$O$wO{z4PQ-muCvZ^zxDcS-Y9khvY*UW-|IdKiMdPJ-Dvs_>mRQzrdXLAxObi~&r z_;Np=#K5kFswoKAe3gd?+RLAuQQW?<1Nd+LV>Uy8yCbBI7GoH;YrrtL+N2|5v#^l7 z6xqOAdq<#=MroThb}kLq<3kX$Ej_2hBS|YcroWx6lD*Cvu z>mWzpE``>or>E^OxuKaQI+u{p%*-LFN`h#t7nA{$iUK_pU&`x}-I%2Y95b7s$gFN* zQBd}-O$awPZ7h?RYlqJyalAQ2E+vtdvqj#;QbOyBNDd;>W%s=-pVicN00VBrIMxN` z0Ny-pq+!W5%FUH(cNoK=OVA}@6y#BiUj#s-&zCi_QM1qj$wM8Dl0R}q2?^eeFUV) zAZfBH1WU74$!VIJZ_Mh<5bb2g1*Podb_StCU_$~umB0-pXFBic5uTm)!hS?K)(hO@V zhCPzTF+7-MlwyDAW(>2(+3TawMG`P4PtEY5z}=)Xv9p#=W32r?B4UZL$#Gmq5-C|R)>#4vUe$kVnTmD0lmy5?*}^*VhAIr9;3j(|jS?S9m9Fvqe}Iw5jNRGm7N^31@Z zvnOtpo9k?o)eCk)O#>qgP!lu6bM{KsM{)Mb`UJ+_Rua|KAS$@qmB7yzb=kxs`r+UH z$8oxFOES!f<=)(E8Ln3nL*6^g(gWVrX_v*ai>jc#*&+(JL@Q;L_8k&M<(Sy!={o`v z!Tu#yDhn2bN$C7{H3>LirBJB2Q))8Ot;;A4AN#?LlJF=En->iZDXjQc$`WQ|qSv_> zo&~q+K;#?rGLs#Uq-ATivr?d^H z+%=0YG$XOGADU+8l`4}LFx^S@isCWczRbETuCU}OK@>QxNqxvrEfQf5<%>}uX0)@~ zSz=3cd}sj^oS|0Z8)78AAkjD&Q5GhEi$VtoDCL<-7kcq)3Fp4zN z%-|L(;1_#N0+I(7l7f~n%%X6zyVMmGmC8)VaH35{nLID_%i?bYrJ9_c*>>Do?%|Xx zq6{Cd#nmyEA2#bTZn&rlvX9&Cr^ns~o1Wc_9W~_9y3IufIoY`QtF|=ZwqDKwQp(F# z0V!OVlP3D~y^5(oTyb5oZzs&NfIuLzFv^IkL4bV-lretw!nRXgs~o>>d=yHRu^0FrLI{)LNKy?2~fDfsU$t5AjKvIBVDS@Fyr&024vl- z!xs13Gifn^o9oN8GdKrk!~oyeDB)n-kVRqOFA2URqH*mq(W-bg5-&MD7aUYP#{t9M zjTo+nAPjpON=hU<5fCFf;YSAQ%MIef z5nsq}ui&2$VK{%k6jeEO%et}3%c_eavEC<#oMX76(cA9E<*kz)tafZ3h8s=@0AE&929R7M%?#$09SB_#d|?C= z%du~Ek? za<_ISqBB&&q?%434To7EqO`I_7lU!TyvWyeOIe*^%nKdBf8!5eBCuEB_sIfgHeFE7 zAF^xZ3}#*QE78ePaZ@!?@gda|F$Gd6*E#axvli&CsFdz_>%r{617>jOK}2~zNClJT zSMU;Rr#aiVOKeQyC=hqMfThj)C~mkPY_tuJ9G)DjzU?||@+RQ6*9@dQv2AIVf$CDc zSVxOfxmuK&%HAe#YdfW8!d>&L(}bFqW7vVpZ#`o;Y;t*0U0#_xzY4JKh{PlrM*zd# zpnGnJ0!F^2u!BVxWiW7)-Lb&*Jy#TFRaQhY-;Ei}vBMF=?kUvU5Ex7;_CY#pl!<56 zQ4+`HY2I%`kzhf@u#a?I8KvRPrt^dEFgyywk5602f^;6kc{VfFON;Z!2(-{J2fX=- z>LrVd(yC0zo3KmXqYC^~Q)X%^w)E`=3{$3b;4G-7Z!H`P`n>2$R$lSki@a41VBS^m z6GMP8oeMi#wY!lth?w&;NHgXM>7qS>!40CYcd+WcG|S7hE2T)swerkt@R-(wkRTBO ziAhjq5tUP1Hp#?d3@e=$!*(XarPCNU{NNZq;P%VMieZ1EG@OrQB$%&-W`VdJ!z@v2 zGQqcwATA}WV9`YuLtC(GdL)iq(InIz`bh?E5s)HH*+?g0Ki zImgXsFA2esw0uRpOj3g!j3(jsy5Q{$M*f242{USlYaVMhgQxyPMRE*RnQSmKs#^mX zMy*;H=W@>TOI^d^QT%ocGrD6^!&3C&VLhs)E73*_kJ9kt)~rr-45$BIE`?zqxjPKU zWn}_2)ouy;7~jEXCrQDwV0}9i`C%26c9J<0Li`!SKCX*nm<7G%Va)i{!Z79;3IGQD z_`u~E%t39UEYr+j1Lu#~492>KAa!vnyOhd#tPqrcjIGFE5R1ugN^XAZ_AEZOjO|M- z8EWL)EnG}ua9Kv;vikz3;D~o~A(t^cL5e0A_Pc+295Z-4O|Y9{*jKgbrCAzg=+4J5 z*C^s6DsVO_|Ey`KoOcW4i=DF20vc_4*~ce4m?UqK4_AXQ3^T4~-_?~2#(ZO4TIT0P zw3P!G@DqCcVa9IFXM4&HoSy_NrI21mV4-QUhsEKzl%sJ|43j9#5lV>?nq95i82pUk zJ&2FaUxM0J+1T!cg-wf zm}8b0!x4f_VM|QWM%!?k$79s@Dyc;=DH0`}98uuH#U)r{0uXajt8%+y*Z{)<@^D3m zE69&p7=Uwme6^gFxdcoE5<3wH#RkTDn`f3XZbLu-R2D}DCx#&msUb~sWO|&-4Cc2X zS3N_W2hNclUuGxAuwNW@!NUeZXtVlEkV}JAHh7lM5evkOcpX_Pd4VR>E7HTm`r~Hp z=s*nTiW=7836$cnT(Af!h_t#-nGp;F}|S*=3(l zR-du$St}HT+mkaVT|jc8V^@)i%JE$Xn_?IocM}reB_{ElgdPcGSPPw#tEKFvR^?<+ zs}GyasFyAkCE6#_dK!AG`z6va3dDqAmWBbkjj_VsWeg*CTg^HOlCRGS$w2&S`ksKI zO!#3`ry3NH1q`zZ@_J(9@-V&ziCbPD132=AqCH9)nqZ z)>cuJ(-+O>uFs~Kd9j63R8)><54Phl`8$iuS@TuXpqD0@m;_?ZAf4CoXVKg-?2od; z@W&GjueQVR<)>}m4+)0r2txPD7={rLj#(+q81^0r&QXn&aaXHSyW-3nqOZN?m!lYF zd464MkcLC^@{+M`>qiYRTSP&vANnu9-{!(4wA&`!0g^(TaXW+Aa!j7e5C94e#w6<~ zG+C~Qn7cNE!!rzL@a8-oWpxmTs{}Dr>u_BHGBL536IcqE*p_Y@&)y;st9XZAf&zwB zR-5&{$Wo~*`oVVjEVsktJPC%uQ43#IWD0YWG0fd~0|A`VGb&&h#Q!XKNl|6(*Xlkh zanHJIDomSAPFMxu+KQ^|20(@80)AV3;R*>lbT#cX0~qFmZVs``wlik156)t6JROMS zfMH}X3o(K$ea#G3re)_qEUufxT>!UU3JXywI?gF*tG!m*fM(6wX;wG4khmd|IKeO= zyUtN6Co_gu@*+#6j`zKN`Si3shhc{93KF+7!?<<|J33Rvdo|c^;x2rR^Mt+VI2~TY z!_K*VIhz{BYb|d$me+}`r>L3Sz##f9+A!6oV{o?E~Okhv)IaPaB-N`9^@TO<7^>r zmwmxP%*cp(>DOaelU+9J?&FB!u3~r?cA>+&e3lk5oWFNG!eA0=cftsYt@AMqYG&_a z)ZzT_r%2_hwXRE*AlY|ImHak2R7I^#PG-PkE)uqYj#@MK;Zcs%(FI?Y1GrgqMrOrk zTd4vRxjDi~kXew*HpM{}Vsg|*KHnvS`4bbm;Yosd7%3{HUXdwaIFctBXD1Xi{PLUz z)ihw(hjB!xaH&>EP43lg%GVwqCKw(~!=u0SMKR3cu#&)ZwhcF67&c&-N+5bRSJzLv z-j{+TRJGQM3T^pZA7U}U>X`QeU;GO4SfRPB>z3N~nVWF>DkM$-jN(7w5im?&c-hSh zFbKPXxXrFRYEFNNS*WnizveL*KnNIS2WKvy)Tmnb>z!lRAM%)`3-$^dr> z!^~YMQ!<^eb*rWlDVG;jjzG5kXe zUvIG_&5^zNexlz*W-x~yYjq_CV@^O8g%eqT{A4|5sV*Aq*f`9rReFgdh6M@t8|#p$ z9mA0$?HKlcFb`|h?b1p=JRD)zY%dMhe3o@bGt0G6%w`sUTa?lqzn4Rov%a>4>dlvi zx#%(-MLHf(*e#UB%*AmtF!JblAnUA^E@{BSx!gx$i+ zv3?pB;m0^*m^Diin_|Uq(Z>(#F?#rT_R}zonWeYeAz~Oj;2=z(A*>e=-5x&RUDj%+ z;;;Bujt$SOpysLZ0GfVAJ}TB`GLaVe>{)DP1*R`MPZhV_38{1=TTWCsT>{Z;4D3uPa_fo8Cm~u znaHJ_5G)WU95^QpH-rw?L|Q(pw_0_0@*=B1jM8w<%J~s|fZ-OWXu$A=9I_(rSR>8% z&MQEK6Q1s%DL6_~Nj!g;0LG!mFkU{(2h6(yj2*zVUhNEKl=)V~;M&tA8O)ag>T;D4 zbE8>6VcNAP$#P4BOE7*%1%fUf;+&v;Rr3G|a|Dl!nm* zL#Lm^59{v3#$%Q|lJwKlPA_dkg>J<#E9z_KWHw+}ENX%Zx)H;SNzA2{_rZITLy(EV z9GfIXL{D6)!{JzpjWBK(j&<)I)bp3-*wLTc16aIF%wSGA*L$_?x} za#_{Mn(ElowNq|sDaUDEcW}ZoGf49tTYTrpD^w(laj1sKT}s-^$@EzuV+M!K7<#!r zmqC|YBalKsKSDPgnvun@&G|O1v*Z}|>+UQacm}2h{#PKRtaud=H*f+3tTqfoC+e`f zQZ}3Jxz3W`r5}c|&e8~C*%ZTdC&aL10i8r;P-jz@q01_0kcrKn1r6?#bPC1(2iluA}M4rbFH1hbdnsyAPuXD!CX$5M>#!! zKos^SN@VG_ZLXN%8p<1u0x*=~WfJQUvs6mNa9qeF=5S%kdo0>1kc2SI=_WW1Rdob? z!pdpPl2kKG`*}4S%)^h{6=IkYz8a8^rQvoAt4$-<`h9eM#f3adBGC^tBqPfyp5s=> zeL>a@G9x#@*n3wp7&GLrDH-b&z<}Xq1~ZEyWmtvGM_`e+sS*-1I4(mS0ML4_L#sauy(8s4nCV<~oY(K>`JO(XPV*X_EynOYGoO`WSVEwQ54B?&RY?g;Hl zX^yguM9U2iB_N;_jn2~h=;3;!Z9jZDqPQPqb6#JNn}Wg3XVgmrJcKUb>r!WonHEB7q%3kYe60W_+|PPa)e+EbJY>xE>ja+ zK1dX9_r{tj3iFp6(iMpXrMO%~Vi=klJ{EJIX6iAFD}cvFWpI7S$GId7A1Gd7^BP-b zK@Dr9MxMPUdz0Am%f7i3>bQPt)+%qVha=~9a7ylNf}vr!u6 zWZ{V6gg;DTmTF?3%j89+G!@TRh$mfl-B|H($6>$tte1Y=TB>3cU>Hn1m%*?aDn496 zBOMPgCol;x46axp3bz)%V;+lzsCF28b`XH^-H=!n!21?pn5=Vr8b)K}1gpUtytQYE z!b%m|4c1p+Gc(w`VG|4&=HJ+4PoJA#XJ1l5+!)Ea4fa#?rExE<<4vnxI>#ekjOb&H z1Nc3FZy;irqm>xLtmE;$>Rn2wQ7JwC?^AZ9-R`qm_Wdvp0fwofh&2TPKq$EypnC}n z$K4u5Va709h2t1og&D(a2Tm8Udr)LCEbXYtB+Tl=0O-Kd*$j?o&*c(!#o|b9RzZW_1*$56UfKqg; zX9I>Wqn9QVFvh!qt9s~h{x-?`EDfK`l(XYuZyxSEHq7kvA8t@DMyqi_f`7WGhPLMSd;*?mw#TaIdG^<4Bkr>gHLq#)5tceGL;+**p&6bAu z0AAT1tE%pbIfmtNegLK>2D4L_STITku^e%rM!3KXZWjWX!A%$zxJdzn13Hkw0gphw zWHHhURUhn^r0j=kF0Hz+_Fh_pu^os@5s2e5=Sah3Y8M2P1yPZuVN^;-4ExlsD`T05 z5yr;{d)oG+zw|JKPej6SODx*vD9yXcr3MT`a;sM7tabnxRXm`z*;NjtpRIg6>1BQuhl zYoT$qBXU(`Eq@$ms&Kx>24ip>EK-qHl0Yww7>;;kfjGr5KrcZJ0tTQP-sXT|RLCk+ z6WzJ@fKNl9K3V$S=yF7-)gywWz{K{D)?cI5zFeHUQ+ORp2 zuI70_N+TpKGR3C1vn%;+MP~fSZ-3uO>KKl3!4<<3nXOJ1Kp)09YcpHdP)UfoN#-X& z6$L?o3^zMSiLJt*n*xW@bT?*jdo_&YXbrj%MnxoX#qjY?!!QI4gG>rMXbW57A|oWn z3VpH~%2rL*P$eQslldQ>&BJA)mmdFLS>kqsA-jO#7N=-XIL}l;$8HWtK8(QU4$jn$ zVTxi*St=N4<}tIkw85ASxOPQ>cn{!1J7W02+GrSF?XSfASH|hbKEP(E!wlMX2FF3hDPx){AOg*s6K5p~MU0U|EgE}AbIDRjQie(nmX+f<=4DP++uwds znFz#YX0}QNV&*WLhgm7jfyf*w$IeHb!aHIZtQcjb2{=?VdTBju*7Exw%)^h{{@^bi zF^o>iQ9LyXFx=t*H3P$Ni7@OJlI4tH`E#}?%(=%J7<>s5GYK>w#+JlD>7p=p0ROGM z1Z*dWJ+!qSb_Un+dP(Dm?--QQ=&;VNb3i@_IB91vhy&p;ctSDoF=+>+t7>L&vpSj? z3`!@m#)C1K1Y(R)Pj)0-h#M~*!$*UIr_%xw2eh*6%YVa+VZKSc2iqfZxJ$3kWL^|7 z>|skq<=D;haaVr0kACSBmuV0!eU)8WBi-UuB?5cPp1$MKR64 zbZT~}uGm$AXofM9SU>(d|KxGotKxIIs~ym9Q54<}Yd7wL6AX)zr7(lRT#Aa=$X2E| zS;pE$VNOOZ{Oqm^xu6SbjaVJcDoQ2yy)=edAkORi2z|*}p7AwBor;gQ>ZK($NW%bT zB6{G!C8gn9NgXkhgIzx6oN&GLW*4?}y!*C4B)04)2b^FSUR11p*yOV&Kog{{ChoHT z<7Gu*)<`qOg>OcCcq~uhSuADE4K6awicb?VniLC1T6JN{%FJmymf(76BfEi`RK}MDb8H1UPgwbMNi?86n zT=#ZW<{^f9*U5~XyPw{1qtzn2A~WK|aIzxr&8OZy_TF!|n_+6RsqVb0)<>MaFn^AZPsWLx25`|M7fW(-AlG~-n`e!1&FGwY%vW{CGFO=FK z)tbSN&C#qkawjeWRv@M-9!kI}IfFSXHc|p>nsJw<)GqDC-IfT$IX=r6me>)-E`_M@ z${3C$ld^)vDTenLXfCP#Ryi9G)T$-dYI1Az#!?PytT{~R$-V=Jk zeIzN&?N5GX|H5R4xYS)4tUi?cDKUnX-;fh(HlFP;Mm?q)%o)Q2ml446jQ98V*&_XR zy@itlhBGz%P+dz^HH@}AwsU|nt)Ob*_(T!{Uds$-46|lM-&8~Lb>XAiSZKCXtt9>6e2yP|v77zUOEBd4OAS_SbEtJT`LT<`t; zeKAeHUwec>4}}W^)+4S5mtYvYxxC3AFIc3jDTf?9%@u*XoAZ>o6W+-r=KU=HT5UQJ z$Gr3p0RO*V!NJ?x>=^#?e!IC|SHQ4Y>m7>ON(OX_VM%Z=jx!j8khyhH4a4>LEKz3& zWBGgrV}BJWWl#vvArQuGCb6L5MkPauee0}*KVlf0np)f0rAMM4l2|YZn*n9#VmO`& ztO;s3s4_6mG$~BG3gVZw#Iy!)Z%fam>z740bVX5<`N4A9_6ml}QW!QdOM$Dt*S*5Y zKC(y)iktw3f3O;^C_q(VoNHnOnBeqf!SFBF+dX>&gC7F7G#DHUN-JPEPzUlp3B$FR zrgGZ^#&84>$g3VkP#ZIY*=Wqr1=uo)*$7|M#9|-{tV<|}0W^qV+ojv&F$?0wE(C&@ zT1-Ig;4?ZWDqIq4j@1iJ2{9aNlm#N_(~RNQkJ^?aN%;M`#%f(}35Fp6JyN*`4hc-M zzgh}ovCGI^m<7DTurFeXvnK@K;^n;I`l2&rvnt&n1isP$V4Tun(z9>)*6sRLieYbS zh~grK*-~3*_s`{>*&N6irfS7(;w)q|Acj?JNh?A07lor8?}W_%dsYjHhO44IOh+)x z?KrhzSjX(r;4b9?z>&ceLYU>tE)s#TiqzHv+YBPpkFO$Y@-EK>))-Ufa zZ@Hsn=<>!@;f{H5FaU%8_bmrStC0B*5G_ezE+Wn5E}f2H9o*06SSBxoVFv(1iOBw^ zKYy7q+(Wg>rx^C85-A*v(jbKk3@%lXgn}wa=t!M9gVnJVCNr3gRxGgD^BiBXJu*xN zZvT=X25Uz^0Ad9cBxrMErhN+W(*4tIkb%`OJ2Ba8fr3~NgI{YO#OZ9_o65HY%pMMH zm|ibyvxiH&4~m`z@mk^X7ObD4(HmmePn{^|C7xb4LBl2#n}kdvl)`94WHSKg0kQ4o z$NPx|t3=+&vIt<7;l1Bx4FB@>R>kl)N@02MxJ6n}bP6zxxX8gap6aD2)hV+-VwjSY zM4e-6`NUxZ_SZ?ws*>3kUvdnCyMnp|6&r0ewPyEl()TdXmemux{#g*$)i4_>SqeuC z>)V6qWT-^iwbtDIP6u6>Au1_%OxBbOccDn?P!K+RIPW`c%p3$OtUPS3z3 zHf~k;=T{RmiCHU(0Gyb>oizN*+`Z&U(y$T?Z~Fklp?=nJ3@1l}-V#BWrEsBg`&SvP zYI3unM79`#5-_O{g!cU~T(V1JB)m$Y(RKKb-Dw>U-TKUh4#pW!!zL1ul`vfQ4Obc+ zMwY}YOHcNa#BA|^mzU|MSrkw09^3D?+ZL3Jx|dGrdy z7FWjRz%Gu?U$kL4M z`T`0^;HGfS`OAEg$~f^zgd3aMj@zF(;+T*tggKKKQifO%3kK=_;TsqmYVLdJN9gnr zMQhP($&D1yH!N;E&lc&byvs4n`9F#nZcP@%&BraGVD0xX+o$_8>r~G__%U0=T2Cm) z7U{YamTLWubf~kir1Q+t2J~SJ>j%VY!6B)=mFLvI`Vao^-+X?ZrYU7GlXxTze;*7R zDs>D4e%bLaz%3BkhgKM@o~t_58O#*sloDoScr8Uo0|Y1vfHBNjG;y#9UB+;-rqTEC zevbul;26Fuq;eo`rC$?a7^qB7DJ=qfC`qb)QoPx(FVp;UnI=hwgRX1H^sI^FRg^|e zb9dejs&#krdEr;_%23dqDJ&(ooEgkV1Cg0@e`d1y{67%%ul&9LYkv97>*c4{*EY2r zWPHuH_m8Au8M1!S9>e$Qwg(J*>IV!*%rF}G%bW3yc}YYRaX1T|5|0sJ7>az9B7GUB zAbg8=jfjDtQ5@v9hXh!LOB@MPRs^w!y9KdlEcp=F9?clW=2&&JIJdq`z!L~YA4ikI z@45=&mzUQicWIENcbB)^c>+SXR^T6q+n%b`avvn_z*xsHOJV+NgU!*LrNCKq>>>W~ zDaG*1MF9Wd|LObq>8H;x3x=B*!`T7qzkFGx9buzJ=>o?NLV?T1=qZL(_-}mRMEAo@ zdYxmnUM_9Y{ROZ6_T}w1%i`;;CvNYaNMZ0(bVw;GxPi$WU9InttUsA4?9Lfnl){YR zAES(!w6sVO!2i3sG%!3h{q*`W|0u^WPvfNF5!xI}Wm)|8-ZP8z2n`MxCP6I9W~#f3 zDc~2nrtsIIfI7#ea6NLhh|S2bx@j6lQFE6CF_AdJqZnu|a064ft|m>=C$)WZ;`txi z3sP)?5Uyibg+OiKZGpjNs-KAd35K_2EpNFvi7$_uPw&?m!)t}h-k<-neMe|WjFvZg zQU-?wYr+1ES0*IqCTKx9pTvm5jiwpHyojOs!}FK)FRuU|rwt5iYWl68KF=naL$m0i z#o^nVz74~PMfyQH3)C=bDfZ+;>@V5zz&yv&gfSe^!~!e-f?#tzch!MY2*ZM33`y)o zbqsU+C3d>fNH#k}aL5?`WDl{t%YryuhuM{E@J#T`y{dq;5p3sOplJzG?=iCKKa%VFY z0=stW;505Vn8i`Pt)nFV@ddUv0FVCwa}0mJOuza3`Er?lja~eDz5VGz;cH;p5ZEnGeYGoVbid;3Q`zUO=JN`VZ!h+5=$V6NzHQKLB=h>o!!F~zkVIV zarRo@tfqZ!OLtvb@Bmjz`NnL{FznGfhPi@&DS7rVh80g6Lt4H7U)ON0`@Ow?gloOM z-+FMra)ZMhmYoG_+{Llh=zWLavM?Fv4i_5x*D%Z^{_z;*o%$F5*8ge{IAnh(%_QH@ zrSXW9hO=3C&-?rJXD1CCn!}-6z;F-p1TpN<0)~OXD8~5vcPv#5vr|+8_UKpF4HJ&@ z^}~=#f|zA`oRuT&^nEcL2N1)NP>(spKIOL5wsh_iXx~)p@*WgV>;`7pt70-iRAy2^ z3XerCVr^<#0;ksmrh6jHUVyjNNxTJ4M}*r+9G|#>eh;{;o1^0%JO#uxFgR>`LdeAe z3=0@nWvTRo1GV%!fAGKfE)5Ltc(MzV{%J`X{ya_XSG)ynorbTsQ!p$D(IS&^T-X>1 z;m_dkWGT$SFzoeX_ORB0aWn!K)Gp31EMr+0#4xfAoQGkq%fU&>k;HA;|5#<(uAbX1 z?Sjj`VV1&#VNOgsp`{WYD$mR|iReO=;#<8my}mBph^O{og}C+pLE@f>_uk=-<$US^ zzQPVh6yz0;Vdmy}OT2}~4?mFu?o<-%&oE><0(f_yv#@fb$9>NEWYG{~5RSBRHB z9mCi<*h@Vj7xIF^F`vP*X{5AzT>aDPA?~b(#mrmFXzO^>P_@%Evp<;lrSPu>0aj$uQB8lrpvTL&kW!%YbQkOfnciYELbzc zb04FqeOU^#_RZYUfZgm$1tSa;Tt0)uFw@y;*Z}^Ue-gPO`#-e9@OYBPKTVTnDf~Gk z4Fh$Byn<0W(q*1D1;9u z5Ht3~7_PSt1`LCfzS_YlnseJHSr88Z_2pqj9F$5S6zlZhEbbM=1+}X1mlC5zss(*a zdmilZx`b=3DNJMENG5)tUwTxiB;QH)cZL0YDUi#RQq$1hKiL#M`SmO~9%Q$^ThVF# z3%~y-CP27VlFYJ)G`j@DzxC5k&Q{`?h&Xeq(486GhVCH16O)&yXjA0%hPYis{6$*n~6;fEqgfWb|lARGb%h~T7nKuOS z0FVW-0aTE}co>#m;!YtIiOa`vGp^QlT*huF=7|*d&iH+C;PeiS3yI%uv#Yp2?I7AY zl_;gHgSN1siOASch|IzuX!Z(Y4=+e{t;11=zF%pIH&3rPB08oxEw14EDE#olyO96NCJvy81_nFm!_sATx%`raSxPR|MYg91+i0@ zp89cn?$lr;7u~=D277^`9GHFISxOBMAV6cAs_y18K0AX0>mAXyN)D9E}c?%a|_{`@+%mj7OCZogia z+8sHDw_}BB*h-o(oI4f-cuIO29mTLXc2vM%BnxvUM@~8GHGvag7)h*3wXl>)NVww| z2Ix|pb`8Uk1VHqusi}<-+ngM#^=%j~#7LJhtWpw^H@Wu%b@G0|slY3owm6cW;_zjf zUSFrzX_EHbTB~c#IKKG<2v)~V+LvR;3zi0mjt1>pV=x$tDr)$2OATO_LTtJvHC(D&>0#*N>hhMFVth{Ti<3Cv4oG3NNN2cX113JP z6ovw8S+@ZxlfvA#Dd13R?}xTloSvjT3a__?#9vlXoK~0Yc}9FNg(mGJpqiQ4KfAds0xZ+fEw(>Ge0SFQI^~N~Y=CjNvbHh}Jb?Sl%>- zM^LopkphOn?kr9hHaW^F4(i_)BapqI!UL5KB(7tae}#J2Bd{UVyEL)TR53(LrNkb_ zaE^-HWk=OT)S8+UVryN_V@z}0BAxCK)H-8}G;f0XMk%Z@nlKpahDb54xh=UQ8;R$K zcAwu$;^k*vN!4oqN~vhBg4sa|Lj$BdGZ-xyq%}CnzpiM{#xMyWWdQ!45*C6*x`yG+ zBNSZ)TT=}irW`f8Mk9^XNC`nuxF0B3Am*;c;5-)mPw^-5&@U3}2 zs4H1S5U=D5MJc=(EC?|(A)(07)bEgLEw34Na6O+qPVc<<*PC`7+Nk|^w_-&^T>a&K zvWPuc11hc_t&L@;J(>C_6a&4JSJidH0aa8%mx70Pe1Mb7T6Ih>`(ClBph5mI3%79~ za@sSm>v^JIk7O63{XTfRjEXA3`oTm-6VY-%fg+!vpKft)lpxXd(Ccelirb9bYHTahZ!uLEe8*!7$y$RbZ)bihLL?jaU zjyC4*s1-wddWCH|$lo%iA$U%U4G4)GPmecVJzbOsH8DA6gsJNmj0xlCOeTS(LtXm_ z=|)YO?vsQ}eo9Kn%v7m<@pKc-o-PQ8E;z{h`u9s`obi-@%0LHVpb2Dg6|4+smN(Cw z3K%-Q0XjAg?BU_CqZ`W9=bo6cHS3{6_`HRw=TGHW#A;HTx!XdNN?X!kSp}%P7b!iR z1?1!YHbY?2t!-|kGF(R-6yiPcEhxeE+Z3_$9Vsz>FXBnQHoF3`g(on$dlu?If8G~ z6B|l;czpwS7cyX>YKY&9`4L~lOFexXPMk&TW9`>ZO$%~XjNG;Gl10-;M=7SrA{~YR zz+nDQo6x3ps&SBrMOjZomwCEMVSnZ)8O7vNHOlPI$IweBpEdfs0u%vR0{(vTlr9*! zb@STJ>>fAq#;mFmIDP9pgCE3FC-q!L53B_pVz-UA?!)=6L`bzxCaVUC!os#)8$q zr9$+sKeS3;f}9S>`kg;FLePWo$NE!#Cw_Fc{q_ z6Ab(r`5gG2V;y|?a`>vQSLGy9HLqvD-DM-co`58WJ8NUVopuN>IncUlEsQq3bMBCoq!(6S3fu{h#dV1AkOCH)I@bPy#KtlQe*GbM&dDSTx>J50L4{UK5F1Ynt!fYppl#NN+6q=xr_ewD)}5&9b#t+iIY?uASrJZOt$Z~Ms zCF4-r+p(=zcbDIB90GocY)plhV<}$>y$ozs?j1j0g4%9iyEGVS`AE2$5 z-2bd8Dyq#MGh#k?g>%%VC=n2Kc5zu%DYAJi5ss>Ae!BjmPW)TqC*@F_rBN0IkL-1v zdvyr&GZH&Zs&EAH3*eoQeBueX`#u{{^ruBElTo&M6Nnb~@R2%HwH*Xk4jD*RD?xE_ z9~&|{lU1t%n|=Ei$=bZLmVU2toX z>3AH0+Vp1qtVV-0Cn?_I16*&Z0ym7)@?H%$9HWUq=$B+&%Wyh06<*I(@vg=`Bl_d? zM?fl_7z?ROZf6yf9%!8X1I7y4%i}wu$ERyG0PopN8wFkBK&}a-fO~|XNJ(nBXe`xd z2f!hs!eGLJ9yr`p-m1_%^7>atH!!BJNx~KPhlR85nb60tNpa8BDYQ3#TJb312|T-G z^4Kg`SZJ>`p`U!7$`mhDBpYH~)D`sm&(R~mOXn8REQy>$86ly`@y8RD0G!1^4^}i4 zRhm9OG$70r1J|>N+vq(4yL(e3?J}~4i~U*kq6F;BsPpb{%fX@sDLhg^F`FCg7CW%Y%;rvBlsDVGHG@*|VR zgEQsh2{h%+3q=XM)HLz&4f$`zBiq*0)5}d%lr%O!5>eq6znWLPt1ewDq#>T&F|c|4 zPKyvgIOTo4@1h4omgDqyXIO$_Ei3jsk{5lklCO6xXrHdlVq*01F{KzwX-m;#jFWQs zE4i-^r^)#hxsez4%blL$NoF0 zYDsqsqO~ha41fPwC~AaGwER(Hnv$6dH>m!Vy^g{_IX>ygHVY8&vM} z8;Z zAGrI$a#&$cnF{WpFC=nv+tnmDJM$(Tm{#W zBPwwAWx>avRK!a~)&Awyye)DGwx)j1qz0W=E&ppFFtmLdefE0u$43iODHaQ(XoFmd zGI+1aa06~ah(aNT3Jo~Lm!IQAhVIN#3b*;^h?t_Cpi3-l(d%t2S;-xIzI8|ia#zsL zl4Mc493OYQK8|@19OwN@YD`rGGh_BL@hFI1oxrOGqycTvh&It6S0qDkj3s@tt8Ylw z<#SJ)vsmrBTZQEosd^D`@>Ml0IQaPDS=)oBD1*2Hpqq8rRQh!iIf?ofm*_Kxyq(>J z?vtbvOUB<zSo4ubz37R~iyAR!kfa<(xHvy;nK@4n0n1&E>Si5SpaWKx^ zLFA?mnfj1}yEfQexm)|u@oz??RGgkVm=R4oX>8Ey{I2dQ)j*jN4U90_g~(Kj{2*?$ zxe^@K!p@!?wzz1bu{F#0sjw+Ig%2KW@w^!p`DIJ*%01I$7}N*YSW3!x;Dq6QvQO2U z)gEPo*WXIz@iyt_%IVeln9s&nN@a8^lsw7}eG#VRr)C$w1#YLMm9ENYENk0NLVaXet+M z7(n_G3EK+YZ^*YPm|G(S~Nh z4v-v-C%FjenygDeQbt%GS^M#;26ft!7D+|_^d&?rhhw^E_KVo<8b~f?RCtT0@Y_$I zF_=9K#WBSC=l5?V>GSl(*#1qdw}zgU%jxOdPUjHWBe=T;8cqZyYfP1@RAtEK|8N<{{G!I$^o;yrYV{i1B$r} zE_BvBCXH3~{^wCY;lG(vJTxdwdd1Rm}Xq|3)=@ z*c6b)$rD`6*!?F!+55jN=~usFYjIr-;DV^454XP5_~BG_gd@WII6Ncx0!XPjIOdc? zIao(1B-imrgBTEqtmrstD?k4)yUh*Lz@|3$E3_wgQ6Z7~s^Q9k2tvKsw7n%AtiAkw zbhd$M=MSWy*_Q(7j!hUI7cTe&3&39HQ5D#(!k`EFD&L<4Djb@otLUVZq?o9>%yG?`M zyL=NnK7U?kv~27a&mo^`-SrTx=CWT*fwREq=rr_&qe=V74RlG~fBK~^e6v`wue;j{ zm{bZ{aBi9-^!+wtJ&^`N|JexZO={SDA%x>KAX3>5-xOvO-BYE+@JO*xRN2YY&;`*o zm1xOY4}dX9pVy8bpj#1udN{!oJ#=C~S(U((j@qW-dwe>Ua8#goCduh5Z}?+(+1cKE zBnOc$Zijm;QBicEP|^w9_07NK@wwXls=oV$JKp&Ew&ZZvUlD&fr^9(H3`_qu6Ex!9 zKMxP4(zJS>@R9PmQ(oT`9lqJV#U)~#&sA<~BdSCNDDd#fSeUu_?;C2Sh+9o%Ea>Sm ziJ=A5yFvT4=D4z^)rA?(+oW@-l+|x$I>O~OQez}5wtDl%6-A2)@xyS5UwlATEz(jd zX8-phR^Ld*39cCYthY+j^^Isqn`<^{%*#vuYc*pli9cI+=^Ov1EF(AGjEe0jozg4C zI}FX_FBk>?%UYCdmC6VTH7)R;msngSOn!)_?RejX1$WxBzRXdiVvp0wWRMsS1rLA6 zegT_EtnC9+N$W7Y!SgxfTD+r<{&Vmz)Qon6B^+UL4%LqMtYd|3REVpv3C&tfugO-=4|R*Yj<07qU+tX+=|IS6B0DJ(@q)gQqTVwR=ROC>X z!ULBmG!1ET#y>*>n1kz(gMI88$g2ZC)G2*Aw)PlYavC5PHL!#gGBxJoh4I$|VUPMl!8`|~g`TuZ`ZB7uzyefx>zkz^2_~`MUx8CGCF#n&rLo?4MhJI$!+6*&zRk-#7+xq?X`*!Av>S zaHUDHyd7K}N5*M9TZ420YI8C_dOO(Vr|v@5q_!}`$tKSkFSfStQI47iAAXKG1!mY7`>*VXafMV)R`ttQePR3Q}jlJic5vsa;}V0d%qx3OEg$eS@QSJ&Plg=2Xr9^s0%g^3lu44BuYjMemN{YR4}iLW zj^r7m{!=s7fTQ&VE+k>B!dKUv6K9rR7EG~mcED!qTN$Je3sNCw^Y~*LEf%TYO~@fm zfSSN2up3Hb8a?o0T?DnRw9jo12B!*G$-UC?D+;LUv?3hpLMUAhkKn@rjq6jJapbI;ZTcb~KK zz5fmOiNLTh6xwQSOxgi`o|hI#4D_%Znhe8zpQjPumQmR^6yqdUs=QYP8XKjC&&OiHU_vs{&8wYQ=#Mq@bkB8%xrO(YO#VjR;`C&YSfW|`?PTQ@=beLUlT5f{#ffZ^z^KKk49`wGOpo9RX=e?{NwXJ zo3NnJLS8%2X9wlY)V8au;veh$T5gazZnmzBrE)5?-+?cvVupp&@D2eU_kd&#ESV~L z^ZiMpvZU|bpxZH;S>CsT=X{$EpSssN8!r{mWWiK+r(9^*8+HGBdKWus*N`oTP$uC* zblf%%8*hmrdY(Fa&GfGI*ecIOGcR#hvbwdJXm@N`+BfPhkne9r(@~EU_5ET0*Y?X4 z4a!C*NeqF}imb+C559n?RKc49G5Yz2$#+76-(6Ge=}QPq;P^m9@#uWxr?r(??*XM7g_E{IML1ILw3zKomyJ~p}by6&{Ui@C=|5i~%77qVoc=-uUeN?A(P^OMc-@#j-4 zYr<-z$$&z3GHRMiV7$>S0g9W6CL(ldq{U8YmCauv^+kgl1ziS3=@3?v`P-EjFL_aq+Ja`MIzXtunE(k zzE(Vl#o@re{sdOl2aaYV1Eph>0P^X@cfpk%-vvMZWXst&IX{vo%yc6+MOa%Ns< zOrMDL#iT527c;X;D@Faktuif1{eH~{O!@cw1{P-~sRQyH8;x&n`aO#{m z+!%Nupd(4p$WlIEG1LzOUcFU!`Q9u&+|UjCQV|J~R|rQ&yRyYVpL~c&aT>`0%M3TX z#p{V#gRcI)E>tyAMK0V811z9=2w7ekk3Fb_R;E0#CNE)xEdR^4gq22hFyD5Mf zV13=gSWpQ_eaBi+73~lhzU#s#F7q4k@gK-zglzgmly&GZWTl{>2j;d$pAB>$3-% z2!MJlV-Urcgc|niTCjbtN9NQ~+Me7CC->_l5dC+nY`7R6*UukvAUW0WEU>$r3a|~D zq4zuF>wOY9fT{dq%4t8KX5kG(xx;S&HI}ZI{laAI73G}@hZh}seDZ}gk^7IgSQIQj zVdr}cFr(D@+8!{b502?!0il2V5FFpR=+u@u*%7h_=a9+s?x^! z!s(Khb5j*-^p&r(e8E%3lQ`?*)v1*jErEhFuG7Liy8ND?>$K+dzmpGy|9xwD6c$Lo z+a_$yi}P_E(D})Eg8o)N(BtwQb8c$cCfZFNcqcQM{)j$(m})oVuY@-azv3#iV0BOV`CvYuocMoW2h&RL+OTzOY6Wd12X-1Fb}7v%K#YpF-J;0(mb3O|SdIm%;kEs7?O!oEfTI)F~w- z0NcnPeDB2%XI|jsoU*7XlPt;! z&2u$Bvv7hF49OaqwZg`^lW9^+7=bq?de1{m|G-Kc+!KB#rBs4J$XCe@dg*vmVYz@u|j6p7*%zzaD4{3t*yy?hMr?_ zWs0;HvPf37BmUaO@+{NAq@v2gi(FLS_}Dj&W9wvG6ET|=VvHfBdY5E+&U2xNLcE~H z^q;U^Fmez&A8k0H%S&xVh4A@=gH#sJ+m-6Sb z>_DF`5=tJnsl4dY^4fhRXWHhi9Awf&JBjS_w}q*W~DEzZs#)7iNSmmuKH zK6C7#MIvpp!7j;n6l+q%9Y~XMJ_`rE{bIW4XXr}<&xij+v*W#;Ok**giwB}^D>F(p zc8%g)zLXezD?5hDnC5NlS)`5>S0V#l)-2oMs1fNsb-&3o%(&|wkSI28lp{70CA)1X z6xDI#$wQla*enzk^>O8ic)N{25NU}=t-^Nhn8E7j%o$3CmKl5mmZM2&`dC*hmn#-{)XY0;}Qk1BQI&Xrw9L3aB zZng&{%;c@;Y<670@iLlRZv77=kbUDeThU6w-$e1cd=s+21TVQ643~yOGt@d%Hr*}N zfMaGN##j;cX1x0qXb(u4ya5_AqKH5VMV;dkZxuHR5kFd`UV4jTmgS^C&{os`INdk_2-3wQmbD7ATIcbUOV8}IofB%L;*vd~O;pe9Ropy3O zR`E=vk#E1lKX6v^(}O)aq7-vi3s#{L2nugi%b>Bbu=jy z!xgJ+&%{&-1%0nZ62}X<*Kqse(;zj;)CH_0)e*Fp28F21499(Yz{^&uuj3J`k6uqQ z0!22%Ih4d(@L0ZuwcwhQd}aHd8x%6^07#~S!E593?*jCp4aL8KmsnXt;RoQxPYsTh z7P@LSmG5bzte-NiB27dV1tEQQgg^$`WGIrzKv+IVMo zqMmmIu%n|HHq*Z6;}}*#b>;CJH1WaG?bva*5m1uI92bdyfQ#-oJC5$ZbONxnjZM|Z zk8jb({>{ewWcplryp6hC8#ob;E7b|+Fh4xAKai}v4O#kBDs)-REcrNC!7yB3RuzN; zTKT5@Q~EE4Ut)>j3AjB0>ezsLf5O-=u-m`1aNk~h5Y%ejX%KFNWqGaR{QwieTuZrB z6_?spIgzE(cCVC*u~$EuGtG5zLt@YH7B0K@XV+2!3|1encRWy z6#NGeM{rv}+`%`$PiO^+xYx|B)W;+sA%7eL5-JH9>QtJVNk~K6tZhpSbf*ya-u97M ze_G>%xb=!}^5!rBj7syB%gJOUXscY4vYFS^I0^bIvC+S1BOID6VrZeQI~G5jigDiy zEtbe$?|nw#y8IX0{C+dFWbHmlF?$Qy-J&xAE;8z9p;tp3q%R#MstpndKnWzAJbXUJ zgS`W^*boAwKL;H@ z8HWA>Ce?2j&@i&-ONBFfjc+(-+t8D4 z#hKggrSe2zKR86ipXwWBkpku_!Xnn%A_*;yTtsPDwz+HN%*zisc?rF8G>iIL*J;=8 zk$P$Iozr?R5SVB|3K~))2gWA2CD1C5>KCHIV4Y%!1M`8$jM1Qi(s=Yn1d6Ie5NVlS z)b-*^5#@QB)wg^pfX}iJ1(xo@J`QwbJv@>_B-<^Kv=_CYiBf1{LR+as3yEO2NqDr2 zrFGN#<5#b3c^cpDMw&61ZwSO5L2Z~Q`ns(uvOu$Apkr-x`2qE(3j;&9Pf!X)$kI!C zjK0`2e}c&O&Vkpwd>P=|tw$5_lYb80Hfcy8%t?HTs0-5(3LBs|%d&_L`9u<61?|Gg z=*Avw%bn%lcn)f>mF+^b^xo%hmaeH-S`P-IsAuE)#rezcWIq#+j|}#3WfGs)k$v|k zO#LuAMh4$)4^Y<2h(e_`cJb_2&KndgYV6|a?WVbcLpG1zG}Cmly}obBq~HBhx0u~J zpLR6k9^lwhv-;+X_KBQ=Mo>!a>e2^yk8HMOU3o>q&vrS!9ri}wIKMe0#sUg_tlBhu z;PlywatPGnQaMGkMAe_Lh;az*@G#@}LkKkhKh2D8_?$W&S1gC7Ixp|%Np<^Q{eO{) zLlFPf3RBO_d(=2&0W~*=qQFIrfoyQ@DLx6~4LirGS)U>uhzz1Khm) z>OjI9>Z5NY3+V>*!fLiZoUm`XWdW-=XC|Ku3ieSIreH*$(fiS3I^}s`kZ@K4tnrA% zVg}9oeRM4dgcgcQwk&f?H788Z*dvcSNC9KTzk-X8Ht9fy4&bX?96)wxzaK!oD1$cS zuD^j>Av)--=I`;!QOzm$ct-%fAO2g4eTaPnL)t+1R~!*vBHHUr6sS8erd%xk_+^d+00 zPN1+dzxRtAXNK{T;fyq%OU~BW0~ZNU4Tj4VDnRqOr32ul;>X@xRP^}tnLtwb#+gBf zMOLwvTuv1Dj9s#~sSLJhz3{z&IeLC&_HMCjP4diH$CP0G26!oF&xenAcx4CIBxCNU ztR%tbk!bg?rRcGfd@Rz&_^OeS;5@Go1114wT|G^f=~4W=8N+t;qDnNob$@eA+i0!P zBRk8ZXOeauuk~l-%pLs6h$VhwX!c45L?$-18q2B)QpDoHd|a2I_JORJ)K59zWP7KlU z*Cfe45I5?ZoD7iri?ieDSyt#Y0V{(@TrwV_EEh8>7Vx-12yROC*qq(XxTqS(FJxzWK^VBDTk;fI1Gp-8;Uw@jF6B zt@A4+sZObbwQ3xeCnhAVP9bej0*7cup`er&%%otUtW3iWe8fjnQVGyN43|oKU&CVK zM;ZxG*i*-brw72WHDM{K{0+Q9Q((R0?{m;$X)emSj`lm`?3e6kGiB1P zE_lj^vDJ|v0*Z7{TI_Tbxe@nKz;%@X6-J&OBgf|BwVypkOZmc8=D${tw(^V3`*y~} zU}d4i+6Xg+H(f2W-974IssZ^b1$m?&&OjjL@ZbFqdp^6I(jttU3}r9C2o%`Q2>ZhcME+$ge*^-P(E1;F-Yo0tQ%SuH*!HS&&Z<-a1dHYz zQHaZc6<7*T0OBfa=+wb~JcE?YGY*Gyj~dyT8Z9=^4tcM9Kc>CrXzaJB#Y)+RUrJFZ zt<30Ax@mHRg%&O-)y699(T=!Ui1_c{?0$!=k;3c(p20F*XrAU3m7Zq4r9p15$3UM+ z44!%kuuLjOpbP|m*by@-&Aympx^k?O$Vt^q3ecd_106oPTX;k&e<{^YW@q2n@@SFvX4EfA z85}%xZlClwv>{Cbe>@n-`yKSe4J?ZGhLq=ul3MEsOhgz`q+H^N<&fT zzVbL#4H|?Gid8MH(OfHI;3?CQEFiaG0w~h{{-AN{E}X_5{%;@HmOIxX#OTWKrdya# zZ0SyE+Pjn+I=C5+?(VPs_ zY>Zm0AZzxIv14|xP_A};v8~8dV{PQ7<;jU>7xnBW)AELO|2z$K`a{Rtet5uR0R9^T z5(&;6S2pC&*V5`(gj3y-_(AFq9?+4P(IrzN$2cGd6}ZF4pbUKnW`HJqR0a7BIg~tR zj41SvJZO9V>5SvW5_qYhIaK<5%D^a?9*j`7T zIA>nxmbO;*qKeC-g@Eb)qo9Xh523n` z)u@>p<$=-oYtaG;@p+>AO6H_{Zk7q-Fd+JsBJ#a?l9{K^)+i&RmJNx>CN<*RTt-`l zg9waR`0rCO^<`lSrxkgk-Uy~d`e{3nFL)U@?|$l>qej@MtjmoisGcr7a?Z^CyA8PX}O0x{!Bk*imh2!Yy zdEW7RZY%z~5Vs4ca^DyHaHAxYJpW^TXC>?891YknvphXl#Igg{yW|Z!@^c(=o&Xh# z1a-dB>GQC@CFc7~xaAQFfSTR%9-}aj_#r%ok-#bz2TR`q>l#D{x7pg@mpwTI zSHFc1RUO6%OLIkO(68ezj=%uPeha9Q|2jAgb3OsU#%c3lhRH42Yg*E++Ja%30B+I1 zJ00EoL^dxt_2O^qC;YFrr5Dber#CoNe@FMYeaI4Y{m6v9(QT|aQTM&e&0hVcL-wJC zQ8q;ezS8C2+JUFgX3Z)z!%_92M?VvlBVy$Q$3Rqo?eCQxShZ<)fKR4vH6m#@569@oP(={gD&{O|Xe z(ZsRehxf(b(6x%B?U55tAL3rSgeRQads`cGX?opiDfk^Z+DR82%VrY3p|t za6(GVj3tQ<@O)z9el%CHUP7iwoU?=WcsF#qh~mWS)qfl@JvNTumojainfl@69I0=A z*HE)RJLi|Iji&!ww__-&+bmHyrtP&uZCQqJzrbduvx4cOyo_O)>5~ggpQI9%YMGEI zt7}DdUD1mDD|tAe1^GLZB=jM=mkcxkUzYebva?52)k(WU;3I^heR_~;9ymC<4ccL1 zU#35R^jc$QG;!}Ru;yj~iY2Su_3j3zjs{f2@nE8_!|7t{1aEouZsm!WRI5&9mjmXj zj}eMecPOU*I_?#u;or>3ztA~w!Z-2Qn;<{)f>zEjrA&Fz#xD#I(7A@p!{^Gb_gOxq zTXTQ;NY`IcJ}uWRIM(ve)xD~vNVbznSdx7B=O5aKU&*4K`7`iQJ^UnCP1e96on!W?Oz#k6A3m++Ne?`!+@E!btkW`4w$MhdLyxZo@-KC2MuH1cD}Aw+C!O0OxMRCZwh^@(twZ zeh+9^ZW?mcwfl*ZEKC96jqm;QaiOt`()YY1lBDkUH+B@>Zt>XBLq7og5)Y_{Wr{*C^a^`n3*qOG1FFJ=BmCCKm1SOU*Quaf`Lk6Y2^H@6S2v%cJow6igE=~*z}zmP!v9L7JX zCPE9Fq2~bbF$&nM8i3%qt~tWR5oqr$VYws; zM2CR`leJT-SXyBS=3Ks6%J+930}LL8d#nB5#1A_X@mR)2n1w|2!8&Zh>dl*+icE&T z8;)X!m8KsZXpzp-hfUtP;KRijD0{k{S(?94yUr|2wG^RlHLfEGNSdJDZSL<%h|At$ zFvH2~^Xyyn<4|?#bhD(TxPGD=%m$6Hb7`KeoOq^Kf|P*Z&fbe34s?txrptubx5-xY z@#Nb^P!RQM33k6PKJ)_IwF~igPCyK9d`Hc^U+m_YLBB{ROAY&I-V}N*yS~Osp3=4v zKnM~-zMaNVHwN2>JBkY~Ip^8@JF06V-nt>xFWB{xoF^8^F=B3+&0M+S_fADElQskSwG$>H(e`FBR4(9pwItiWLdCDUiv zhqBanvVo*T`I31M_*ghGe8%bj^_hmVC{FV1X2B)Lus{u1-l{@exIT31+PsZl)q4vkApR~e9?H@5;XB@ zL2tiDv_PGf>B+>*?VF5JQ8v+EMk>YIk0~x~;5dAT_JCyVEWd_i!101_L-cPTpsw;V z`i--(TS?Jd|JV@?>2P8rKaU{fBybWaueiFwJXqcnWq4(H)1r8TAxbBp8pPjmo>6_?czKK8w41w z0$ixj0WAiY#V;1iI6ic=)?GArQXX&<(9j~AR-OU3s}aTd*Y+P}haYFcpc$S9J3{Pr zv6_N_ZZ$94zq#p@_Q@t0DxvqAuwovQmn|4+pk^$So|cZF73M=CX#LR}-8Zp$|MvgP zRAl*nu9sL^ZL=))$)?u~=~`#ZeMvRChriB46Rcrwo?H}Kk+c*U4*SIW6=VwQSIP!| zE$xTIB$0DGqyjJ1a#15Jtd)$ar%A}5Od^6LSn5Mq%+v|8c zeW_vTajn$F4{AXwp}o9WIJOBgIv)P*3q_c``O`ei z9BE{ZM9$+ke7Qhy!Cwc%60Miyjh8?DKl<9{g4Z0W--YFBK3lE8kK=sn3R;&uyIWlq z^<0d?Ah)FNCNvM_uTutp_PfOI!)=d^a{eT&dlhPkC_{xkO1TYYZr6c&)7ZPf6A81a z=7@qktBD}@N=eLdBh)6NQedxpWY4gF5&MRN3Q;5DeROeh=u=BH;f z%0xd_siDc#Nfmp}jtv=kCNj4SFlBuQILiob%wVh!cJ1syQxEe6vNnau^2TF>B+!+-Tw(}W zO{`2Q#_ucc#6q!~i5T`a@T?G*1^NxBhXa(=iKcKQ%TCLN*2G2U$XYXCj{78A)%V&f zhK~SeYZ}z^q3FohT9|np6(!$hPZ=4gzvJ(@?*aOxfj(H1ukqw~#Sz#h0r23sp0g5s zky`Y#e_w~$FYyb8jeL3jqM(}R-yfRXbXlAhWGbG|qwi$ixL;5*vb5o`C=)Ke zD9v_QT`E_GId}bUHm*~iP@5Y$mSNS&ol$?h2L=52L zsU9WBZ$d#(RPE|Lb9o7D6b{;H` z?5IffkC6^|34SeOGn!8m%%!F~2`ZY6sf29SYi?TYSb08-Ww*&wSG)1A;S!-2NfzuD zI@ZG?zjk%zRGmy!R!n%BFcvH^KPG8NE5aZ>7f>ur-~NAw*UUx9eWx$vo|};`x{2;P2i2NvT3PfT}C%q%P5is9-_H9&S$6)Wf#}(1wd4@L#8*!f*i$ z`D5O`z5^TEa-bDi`?tDM?;32E=*fHv>JXt(Yh@UAPH@is#$3lHHy~X?Cf2Pi#gY8$ zXfO|PI+~VjKj_~e2nV<%$iJN~DRv#JKL$$!lOF{5&-zePkp5I(YT*iflH`mlPXjc5 z^FWUC;XBU6Sm2(QFrzuej-t7L3w2uX@cYpHf>cFo{Yt z+-0zp*8}a`e*OCz6KbvViQKV{P*LT29yD%JYDgGN^InM2doq4xRHtC{V+L-@1B#lD zh9{fm7IA*c!+y@n_zXaTYhH9H#5(Sse-Q8rzo{f}BcE}tTb?)L5EEz7C2752Ws0A} zP^iDP)%H9+XU8}S!>Q36EkpRLMNLoDGTLcIZLpS%pf!B? zXKv9nE|od9d0)(GtE939Sh|>_{%FDOV!I+%95Cd_QH{j5l^09Rmm73@iVGJCMsMbV z`2faE>ba=$zhz$yVeqYuzpk+;xH9B)>Df4hSm@6%i5;;V?=MLhV3@|c<15nH5q^CB z{Y?gl<-}irBc5!!s_Nt5KF{l_xrd|$U-Pw8(zmV1OsmCu1{`p|8CJ6R&zCLMC+E8L zJvPI}PKF7h-o9WL+m!4WgA%dUWi{oolYwDba0#0^(@#z91ojn*=sa>WuZCB~jHc|u zYPhq)QD#bF)NWj7N77Gu!z?w0%x`beMzn2q_Ez@0pqSsED5eYCxSDN=5nRzZs0(sr zZZE1(E**8qazTC!Oa--uqo_1g0hJ8;Q)r;H_HVhI$dUt~UK+disgJ}H<&nf$P(e-; zo$8n9mCj$p(!rHB75m;Ux+|X<`X-+*3d{Q`92Wn3`j6ZT8(;veYtw@X!W5!nonS!q zQE{Xpjl7i`NiF=2siC!|tSatV6^9*Y4$Hsc($2HEl?GS>XvU3C+!5ab3spOQ|K>Ow zJBY6Y02aZAFEV{@XI36IAISTcOHXz$RbRB$#6)|z69Uk7q8=NwTtHHZr#YxpS{{J0 zj_K070+c&7dk9VlRA(Wa5~p{3Lj$sh&GPP-?I&P;phozoqP%)0Amjt1UTXvPe^S0E z>nqew_0&uiE+{a2{O}ANdDnRBH9Cy9(MJZ`r8KsT_z|E~i1`#t)sd4y%t8Qkx;x_T z|;%boY;P};hB6j7@Y}YkE&5Gx?*fS)vckiGn&JqRHAbEsCE4>&U>$kwgiae zZ!KsJHo}kI<4;9bRv-j81moYxN282L2DqPB4nQ9L#YHq~8XsHn0(RF-d;!7Mj};Fc zTxT6RTNX@};ooC|dupdzy&7bD&^5n%2yyUt^%^9eHatVGd+R0>BqVH7$C|*vef1aV z-P+KZy{4z{J~78~;i=NQh`!Qd0%NcHpE5JLho?3p&a$&M^X|=%# z)(fu>v5rK&LM7KsRPD@$_C5(sZ0%W*zLzK$juyT|ahjZP;LgPE&5?XON?uJBu$x0p zgxx|9B`ez-8dv#V6{7avj8GkJV;k~#k1Q4*NkikBPamd@hN-gF@Y-o#rB~HX6|OkA zWw!Z6nm23zs2B8QE%}V{c{9Z-99bKFW4Jh^73~9L_x|o3WnUN%ja`pf@*D$Tlwe0= zr1JUg!yUPWR_L^H_`hm_QcJdKse69SKWP*`HT2mFe{>kq&8Q(V5py5H-lt2QEl{5Y z_4qR<-66=()k8mxZ!i8P4wgPseXn1`U&3woX%s?!bn6Ic7M44K7((D_tx-Valt)4a zb%B0pFr&iK^U-`tAcqNl(^fgRJv@gPq$5@N8<>>pE({Lnm$}z6o|SZQogH&65+g*3#5y^DiR(=6I$a)Wp-`vO+Ljf1Pri34E6#H<_smy zC?Quwt6C%wuWia+#(rdU?7` zYU{{M+Us>SFRzA3`B+KKzTsQnqAoY38}7D!ZySLB7G#~ABZwKX?Eq`VEfJZ-ghtQA zdm^6dHhq@PlGZFHN$pYYTNC%JcorAP?m<+XfMF%`N=j5$(>QrIZ|wSz=Fyw>gBu>7 zQCrA)=IVW5-GKKqyYXWSMQNND%-!CbzC+K#S-Xr* zW^lYvT(D@Dw~@gLOv*Xnv4j3#F3ZnBUkNI~7L5#MTeP?9hG_371Yvm-t^*9Fcj&Q< z^NLd&$79q{IICf~Ezg_Xu`lN3#jU$F*YTzFv3!~~FmFEHdBuqlaT(F6-ZX&gjIA@f zP{O$tG%@HY^};zft`r>4)r+%H;)+{HDuECJw{C@ z?KgM!B3~kF!88)~W(Efm3m^;(jt?!Kp)Gv+dJTJBYC{hrV_U(H?Sn%R4@>RAczA~^ zw!^5-V6eO#WUxohXD~olWd;MBI04yP-~+AdU=ZN)YQAKjke})d_EG0DIKJz6h%XVL zyQb$ZqGq))H%T{pbFW+UYd5gBw2S4rX@TxXEY7BOqb#>mt$Ewp*I=Ci{0C$a&Fwa9 z0_KJ5UjhLU(oAB8Qk}#qw;in4>GD>S+7=5inDo$`c>Y(wFnEQ%RrLyUQd2Y+#Y-z@ z5Crq6$A<=hQoULTOcu}DWk_~(u-g=>1$jjEDwE5mwH+cdFBEN&`8`JV|G`}=i~#WqlT zTuRy$hgOK$kz-3~QnC^!y{85p47w%|G-Yu1;h)1`Zy#r{H<7`WMhZm+Lk}X-!DVMK zly*)`57;5d0}y&=v><~$dsvzt?Pnu|YRS!QS~G^)CR=X~p>mVDLD0=s+$3pdUQVi( z*yv_aWPV7}-cEOE+DzOFfCCK%?dm9IH0lV3vv+lV$aE3I`EHfPuIQadP9-#yqz@7| z?YOwLMC`XXg^UQaR$h1ztFv;`? zBUZ!t1Wr{O>Ti(Pl|6DP9CL-M3}z|JEgw>Yi_~)Cm=rr2EBJ{chVP@mrZ#PwcwX7n z3dwUc5@6?|Y{=r(K>g)yC9zCZALKzPaUPFdv5{hgq7upvWl`D6(oB$N1<5NlUe+9Zqv4X~YZm)gs;DcuFS+wfBTaVv{) zwE@s`sHr_7X$@i3^4!OD19#Q-#dp2FgRWNmFK^~!rgqPQ6~KQpfCQ6w1>`(m6Pv+s zsqdV*#DZ9YsX+~Mx-o+~?VZ!6sroy7t=u@X)}qI9KKbHev^Kz3F8K zYzVM3xdquk0_G5dBNOwuTw*X}BBet7ID;dFnZbhYnUPn-U~scS3%??RBSPc#$)KIu z(QH_b7o4WHI&>Gv*-g6yEic^th@tX1>jYamQB&)ixagZ(SIcHk^}AiTl8tJ5d}Fdu zkknS z(QA}_pLH>`7A|xbCJZZwRgPgjhgxjhpgq~7dzPpLm5KfzIy0zU2CUFl_1BQvgiE_rj{d0Or$)!x&^{MSol^(aoYIGiFf96lq z^!c*3M4Z|Q42GuEi*fAjeM>C9_5shH1Tn+};!wmR$l^%bdk;kywXN~7M|5!H6uX3h zyGjDGI5e20a%z08G5Dws=1#bgiQHwX$Y4KJ1EHIuM3DR%vC*WDOrfc{-tvOZ_ceZM z<8JSs+JPhUqNY_dxGcu{teyv}59k`Arl{miNoKS&hUXs}rA=>&(08`sU-)akpU9IM zj5(B=X&gT(#&Cm6onVwM+NEI!7McPnEYS8<3|k9owckuJ9`?HWiU%OLh_t}z%~Dw5 ziEpf=IJ^svFfY$1{-sl>?o1;RRer_kcW>R=#Fh`=7UQNR@`I;bA2*zA7^l2V1dCt>U;)s*(u$l6&Wm8 zl4vdr!rBD~?}+=nvb#3oE`D5hIB>i4Pg}(D{Jd;P_YGMjZ9&Txxh}mtA4sh(R(=lX zer%{2x}i#f)LLUw@~0 zI`ar@a5&7IOA&~Vc0+4>49*MP>Bd?VE>94E+*=tYI+UQS0u$`aNNhfSV1yMQdO)!q z1%qpoaP47tYcE4V{rU5;6ftC*UrK2eCN2Aa;!JCT!|>h;u^7nZD!wGKzy2F$a9zq! zAzRcAk|&f-(Yjjs-or2)sgC0S;2T877Y4gdiO}^@4c$2H$=KW7e`9Y8 zfD3Bi>ttdDv6A=_jP04u(rn@RO6lifILB|Hk=7k>xOUp~=v|58wGs0~`h`NFR!5^U zh1a>vfIJO=Xs44L2A0>sjR_f`j6Ll56p7!yc`PDi_(DgLGg!fZX*Kxx_;LHxQQtfx zsc4&1N9g5K;p=1(=_17C7Htu;banvwE#yeWP`xIKfTSG;Cm5?3T$d@-t~@S$pTSC4 zohAwew5%2p)_|U~2oF-p-0)&EAZKWm4=`zN8^mRDf>;9KFo2^qO1LiUrl$XZZis=i z1$f(k!s6>?eLX#vQU1-$(XJqFc82N zKdW;<6sp`?IrRE*O9m^Zb?|)#M{gNNSH!PxNW%I5LtQyoSG5cYRJEb7${D<@ldS57 zZW9|YXt+n@dyM4TPv4-%uSh>rBWD)NKm;iVW#)<`9%NM||ffm@zsI?^x z>pt5gPNd=IF}M(Ab)|OF$}z0Cf}4r~XrbCp;=yCVODfG_!z3(RDoLy}GU%j&(XoFn z?}SMiIB~{=AXsX8vz`^jVzX4yk~3Jhlo=<6>q`6R5l!PO5b6+(CLC+6u?Jd8@dTig zYB^pdhUi+=OW``}c(-aHisYyl87x}>s6dRl0%6o3u9q}; zwKo#Cy)fP5&6lN|2!TiK5y$tv#sdXjanwT=B@AzY91fY+cRE08u6Zb?CX`QTvxe|> zC3)*fwH&$wt1EQlMwHO?DUXA?HVprC2Vm3;u1Spj5e%2DzySVDVOXW%8p9WTqzksz zqHw`8p!DtvQ?Z#2+Ce;Aw@L;JOE%M3?lM?PgQ*~7(PPz}BkZB*1z`gKfyZ&sR7?eR z&0yVJLS^k3`ENg5GH&g0j?1=;qY5;xB6bfZaH|SkC!tvwtFWTbQ02Dddh%7h!Qd@j zbk5-XsWva5tQ`Cd2H!OeGB)^s0FEo=g*wItbl+Z$x02|r9mMU3ibRefkNSflhP*jz z{ihLN01^-^Qtk?J^fvb(enM2JdEE#U0)G+3C1lK~hvikAT2@yn){#ZaQM-1Q%?$STcD(j*rUO7_Ak>zk0x5%I z>3$;boyMKV!Qb$s20K+U=0aT@R~uG7oC*+Y%1ro}$kSUu*bkk3p*WPaO?yjdIWpG0ybZ^s#c|e!%e{o}k_biz{sAR?qXoz-wz!IYO zPyZ!RL8BxmwpfLY1Y5YGd9G=V^5C|C;pc0lQ5>$EA&QA2Z7P^b45tpw1_K6*O%FHY z-)@B*yu;uc!?hC+TL6lzLz0zMiMzUeA;yR7FZnYuny;^iTZ_%C6oo5PBpg!PV{1P~ zSqS&u0)lorn8pX=aTsx1RhS%&`)kZbtfmm<0uHF3X)P1Wt7-=88Qm|>fSSR`F4o!_ zoHN+ew>jvuZV5f^VC_MSu~0#4?rPMvCmxb3)?d(`%5gEU#- z=$bikr++oF$phyz6}dTkf{xlI!c-%`g{(?~3M&UXcpmE!1KI!`pcy z4fvX59yNv|Pz6_VY3*XT#ye00BWnY@IEu!0<%| zYmtXM_fo7#1yUKtR^#v^QyLhN7)v)5f0B5Vg1}YR==8(Nop9Ri<;T!bcN&MM%o6O; zR88eDj^Usgrg6?>nM?3ZfLA0G=V-{~;93`*T7OXin15AN(msQ0MYN`x0)*EH;6X<> zGrYPUYYMnvhT2&|?bZ`4cj&egH3>m8UnO|lXW5^YHzZWWsXRAg?||+(C=eC~-40zt z3|tm>TA#-3x>@(#lSj7@ZmCILvfN-b>-q^ijB^ez4?#FaLr&8jx}XBa&i1p0ut+=KcBii2DlL!qOtf>CZG*N5lcx{;_M#RMzm zs&C5?8do3=T%>!VAO^q3xV7b0uQiVwa8(XQa$mD$43^Y{U&Ua(vS{Q~@P~o)u)_<} z+6czw{JW#_P`Y;F_NI8s8^Zw*DK~<+RJ8K2g56d71M+K;SHXc7FJ_v$SWDlL50o{x z*1zV6yuHkjq0iQ=$3VsNqG+ZKg;rm(fw{40+V$Dr7L`%)M*wr#hB0!Q?$Z{9Kzt3g z*g59DUK-dC)N69==aB{~z!sRUNDt+5&0sb)W65DQT>eepM~}a!g1WW`*P?Kp5Tq7G zq}G^>od+bTI4vcL)w$aHvLy8Y-d>Nl;X!Rw(-iaDWw0WI+0Z(SJBhn+eE%|y0A7i8 zBB<)3+oP8VH`rR(uMAG0%K(-O4Aud6EB$2i@V-X6 zCWj7v9m5ra%MUPL0k#;dGcXS<2Gs7~hPAkf-&QhxTo;97ONpoeaenkQJCF+90n&v4 zfQgA&{c3#Z7~vR&ir;#)s^qYV`oGn<&-L6FAJe|DmX;e& zXdyVb+04ETzbz`LFEhBP+Mb=*AMb83?nw5jYu3poW5IV=VI}1aRdi-6^T~==hIm|m zdAEYW8PsbhDiwlrLkOV5QB)LuhkL=m?O}LjB<`GK=funKP;R3Dfa{ani3UcF5B4D_ z)L9EwG>D0(@;4-vx_P%$&-IZ)2&U7r>%+~&{)Z&rmvU_wl{uQ0G!4y}`_8LYHHK9`I~YRk>7ssMDC6AvVxWemb*Xk}En z(<%IBy)@Hd5C%u{3WIBXSi80Ez2y-pETRSKjf1kwSmMLAylRne64F0JQ3<$3(2J2MBhahm{Ipl~KL9kBzYb}bdojyiv z@nO3{aG9@A7tQnBb=3Wrne`W~GI^Z)aGhgB7Ui_W0y~Y1R2z$OQMsL~4JlhgDf^hg z$}a;HzgUD@h!Wl0Si^`Q8^6rz)R<(dXH=f|rtPH(*nN;SH!|^bV=V)nxvC6cffWkh zOZX_(G*k!?-Y|$uQ}h9mm4Ywwr@~_j%ViX;7CLIaRbvfdP=IbN z4=l^{8|$`c>Bbts`y~Ea40FL#_0qL8oNqJ%+rCx!I)<^4kwIQpGPri-LSYZyR7d|D z>W77bT7ytWY;h}m-2OlY2Td|%=6dxg(O@E90TLvs%mM&aKT}8jN#h|In|?n$nZZia zHo{rDaNH$hCILn$qVxP~pck}o&ikM)*A{M5vOf^<{|b2c4DmdRFE^ZwkG^hm(7v5xVeRz^06jF(VTY z$@Q+di%-(0-)*`c7Df98EA7*k8ww20`eYcU^E8g}G_FmCDHF1(z+0EAsLT~W3gDY! zW{<=u`>+N0TvWQ8*s^Bg4);yBUdONkux4Q|%AED$tvcj3fRA+7~H0tAO0T{0fON1BXNX{%2=3|>#L zXJdJPzj*?s#c!*)21Bf=HE=~>aqu(5>yp7z0D-coFqi{K@z@Z2k8v^E5!G-V4XlPc z!+{2J`pP&Ei2vZf0UR4J&V7l)l?miY7y&k;dcl$<%NF*={h=p)^oexn9;QMVBpDZR zsmZ|mhT$wm`JT?kGePtzg$otM0^Pp&sf90e|EBOCRLuXm>Ne7{Tq%R`w*YXx*fUA2 z9k>#AoDXd&Pf+oBAH%+3AH&y0VfLVK)w+gZUBfPeYcX9gTr-%10_G(}m83IOx`+4G z49+zNC{SEoQIIAexv$b_CH3i8p$J2)3Wnn-(E&~O>pE)BMGdPN9L;S8>w0)N4}a?0 zh;F*A2&N%7Pnsrf@;Fta(OBrbb_C91SIiqeByeIdB#{$hcy_{>>y!l!4+Nqr*+tta z*Rn95D|J+^XD0uX$H0Ir2{ouv)kxO7<*Sq)tC0ngkHKIU)2SXmyw_9=02ci70}AZ2 za$mc7cRmb_(n!zdr=K%iC|Er=!4Mp3w z5bsm2#xM4ATLOje))PfxZu%loSYc73ww@^xXg|RuESqx%S20kS7BU!MkB-9XO#GMI z^NY?oq@+R3V69gu`$NQI4a(8Nq(GYB=mk}3kqEVX?Lke>g_^-^Zp0kuY;=_Ob{jaJ z>4h^zbW8l#Ta&jaJboAq+}3r~KD0Q=n(%u4WPus)Zqq~Rb!e6S7@d+gMx%c6W*9P6 zM2|_3c0hc>#B2~ZtEt?YGB_rwp2}bzt+>(iXG)G{@OnqaV)f#=y+EDBeW;QEhU1tQ zu6H+WCZM9zT!Sh{*x&S0n~HC5QE%_uMS@glSEYzv&+)zx%%%ePI*jd#uT~9sTvbii zdTDIc|EqCt_5B0B#43E9!8zFTUTibCE?)#uM;C{CToQg-i?cb+h^D5qRPv)byETIq z?Fw+1DT9Ro8i`IAm~vgYNA4=fx!7Wq#@na~F*0CU5N4ObO0=56Tq8JLGWMvF(@XB9 zyjEz+RwgB%p88{h=U1<}hM1SJo@~E+fs3{lZplNWb1m`*Nf45pT<#Wj)|e}CyS%Ov zw*(EoYU1-Z9yrx`S%F-Q=%5i&jOFez_3Umfbp~)5_nk!!!fG3Ohfpks0%Rf?P(T2# zO#q^r;R4<2OyQystlWh_Bol%Q01J`64#V|E)ELHwC@e!4cz!B|b(3DG&&M$MchNh# z<~9;^$}vh2Uz=5~13iwxlh(th`(VVwHi_3tFcZ zd#}hdu5$eOXotDM*aJ*O2eIdnr5&h!8p4P5~AWMm%l;?7TD$tE^-T$0IoTh5~v`0xnN%)MxDpQkUJU`c;2F^#F77z3@+Eo>c88b zSt&(pS+?k`f{am@PBJF?5d1N`n6(m>`2}A$MPWG`$?1eWX7HkG3iG1SRJ@@1n!>g$ zfid@|VMH|b-*2yn?^H8b2)QE!O9x@&4zW%<&$l4f($zY+ikhHd%3&8+2?E-Ztj*|E zG}Nw4-+vpw11kRLMrjL>bH$*szrVacFOWmjz-g+koTN zhJAV8lLpKviu!^;t?{owPy&`=qXGR;>Y0 zNoIArjswi_;#d1@00Dvu!FAm$j-;* z_qAr#BRfEeX&E7@?7wt1cH!0-9YpDW*63SH_>s70#d2>;zHVTCD3N2lO(FNzMEn=f zscz}IBHG&(z#+Q;#rGHf_MbX{cOp=)r;d82Oq^pxnd?@g|eJ>IjV7?$osU*BY zZ-c?*p@1##=sdVqcd6CUVuvajEHE}0bhtK@T3)&szw7K?XE3|!T%QjatWs~FSWDEY zqEf-h$W9fDMP7sr7<&QZ!7s}I-1IsCGV^w-(|^|~jLCsC^j-@tqy@1zx5SKI zJuc`lICqKSxCj>ZWC)1j1ijH5x~#+B1=5iAnb8|V{)#d zx+M5l3BfgwMT*tqYNq9`3Ib#;p{E3kKIxMWvJhhgB4d)Js;0{Z8JlfYbMr z{7uA+D7Oi=9?@jSupB>_3@n>)Qq)p*Q;CD!dKvhK8p=)a#r+s58)xRUI3%1CA*xBrM1nv+gxr6??kOFS;NVjL*w3#Sjv0pQDs!j+hWNybYRIic z_l?fmx8`YU<`KWs+gG&qpb%LC$fmE40Scl`3(%TEZ7!c_&qpYDoO#ocxWEO5#g70ZqVQ>my1vaFSdVb-rj{tlv zABB$b<$7u5W+9mFi^6Pf=FY;lC=BeCaM))sJHC#=eE50>t2S0v3+-+t#xvy641l7L zFn;k`-YHVKii>h^&0wMxI-ZKfL6cNTx4zoa`ZwS(kO*VVlRQK*&%o6=A$|dt=vYM! zh-t#ac$lm;gcv$(w5^M_&BIE)B98j(Iz-$-*CSBJGjU6q(zVFWo;DlFr`(!chs0Yh zx!Iab!NC2wEA{_z6L1*3KJGZF7N0&a=_M3dS-=@El#A^bH*b&A(O3^Ou-wkb^)cAd zkbR2)W*AE#nYHU#D5QZfcu}8^x0f`ZUOxa9^i@CM*Gh9n5`A@Xth7(a*Bc4#yD%9U1;8qs1J;Au)vgdsalu| zUmUoqSJVn#gL55g&-x1$f|DDX9U)khMg(vvHP+n2RSf4WypCbEurCc85zQ$n1aLr)-*l++iv6yZ5-lF5q(-<5~$xn-J zhRUyMmRd6oDoJ{;3LV5eWONt+MSnmJSfiU>k3WqXiht_$B96)3!+cEEpd7Z@p)vNX z$if=7y&oEdgFr3&Lx7LUwx=C;R!hhw+N}L^ zrLV9p2k3rSFtYl~HG`KxYbcy!PzCUQJtOVIRaEEO?1H0K&ERxGR&X}YXoy`5!%*(5 zh*S*M(p?=FyvJb0Ypsixri@fGI5+#&3|8zFR#@;MW2qVZ2YP*AdWv>jh%<@ORk3`X z!Lm-E;oJR48^$LCUK7%t$Ytjeh za;=Eo7J_rJGKa6Bc{K zur@jC`cm`;2is^NAx@R?vGVvlt6K05b-_Ul_c1Z@49FV1G0YI$<38*-D)%wH6l^Z6j8~ah*h#hU#Nkq(fNFx<2jj1t#N#I< z-Ssx+RMi3?kDIvmna|1qdcQL{CT`vkbEoQnbd?iJe6DICSa~eZ3R7fqcEa+C!T6~n zS`*$Jo<8%hfB%+$ zAh2EfzssYGob=G9w@zaZ7ImDHGUSp9NGlCOV+;}Q)mW$A z*QaM4r}cc9Q*G{BJ=q1UHxvl4n0@MM*H$$E0sKDf-jv80ic7y@j@bxWG~8#!2pacI zatZ)K7vL(IBR9gD`GOccXE1UktM8JudCeq3aOD~OQp?=>n%Wyn*ebKI;4pZz*h`)q zE&>1jA2naElekt&ZwbWqB@C|Ljp65G?WeU$aBJnBwHzD^HW-WqgWL9qGO3SV+Z%3K z>}nZ<`Sb)`G$1P_gEcE8=(3APb9OZ~jmF%lNRSRy_O2>01@Qf@kHrU^bk+y#pp@nw zw@0{zQ=px-T4$V38_VEATVhEenfi!JEOL3I`nB;$qiN(gbi#~eRn}8u4z@`_U&KRg zOjKPiX16UMx%W3dLl~oIfiV<{VdbpG(*;wWB?p)ACEDD+W}{@bDNlUCI~!s@f{&-- z{XQ1=zCdBiteK1s;u}Zc|F-CE5=^$#oGJy3a|G86j<)P^=55Lln;yh*cVXx?vcFbZ4buxMr{dNf~_IB;2ZW)-{8b zPT2pr!H3HXuH|6V!NGMDJHC#=+5})2ShM5d8yeqqUag7FKB|ZYa!o)u(2{2&^*T9M z&K^v=$O?MC_;^H!hlIWik_2(vsf>Z@YENkxT5BxQO1WB0c?<~+Hz@~x=Gk}5LOsWE zbSfkF)R7YqTKnLJ|15#XYR^VLoWU#vTpocH{IZ3GdR!55V}nZHSb9y|uG$_Yp$5Di z=Yi>{XV4shL><^597BGNkU1O%%Jv3?Z9T-?aKy@Q1cO>dU~S`ugyT&}TYF~lIb*QA zOv#=fLYd4K1is5E)@R`Du-9>+%%E!Co^}8WK=w#nzwXjF8KR!RUtjc&g-n1^BEzKT!b_5m zF{w;JYaNNu7cg*^m~{?vxZ7lE4{c@&M(?AyPVxO2&uEKBRNxkYwiuF*$W1@JHWA2v z`R^bUW8L6s7Y87HS%+Aq-lgUs&SL@$KIT~C1OkZ7IUKMR7~+K`zJK&2A@4svzQ2F8 zP#{l&uC?o5wFG!OS<9n404yK|zPaMaUTz=FD=;qibAR~{|0wZy-G{jrhH7*_-#<&qRb#jo zg=-9N1lrrN#?vWvXc5wiff@pwk4GM z9;m|BF(3;2EVag1Ho%TyaLOq$ptI_g>AmG~O*452 zb6|#la#vi>Lc(79skz*&WsM!2S`r`NNubfZlbXuv)o}hOcpJoGCTcchH6~Rv+tSmf zE78^$pd}_VH9+vBC+RxswycxJGAh;s9EP`UCb5@>erq+N)#o-Md;xHc;p-OSd~96D z@HT^M!B7>vr6^q6hHF_Vw;-1c)f0k=U5P)gL+pG!nJV2x0V02)MS+TWhXQ-;X}OF($p$uhbIq)oU)M?x zGB}_ZW?yv+Oyfttry(2t7}mmRO$;q1q8Tp_LO57^ zW8nwvblo}Nxj1D(3oX&tLbt4^NtN(`@=N95*fSVRwpa{iq%TQCB|PP5ZyPB~^o|~2 zwEYJ{nYt_|&eAQtrP?eTd|xBDai4XhcFwES)}w*zlAx`B;Qdg%>uR$gV7oEyNDCeV zdZo5jq^6MflB#Rd%`0=49IgD0?i z2;hR6%Asu-W4~bFK7+4gSec*<-W7#wIe4GJ*D?InZdrWZl#_LMrLf?m&?;kFgUueX z=%t40sOT;>jQ?E}5)4y_|7fcJA!)+E+->>dNf$j`9~W3vV!`;dYkC>gwO5`MlYT!(4Z zaLtHMIovM_@bx5lY;6r7jYS&?e<3KhWE0QHF8S$6QJSVp)}5Qy%CiaH6J;4 zn+ZVFjG$mUF^e@5M*?kk#vUs^X66y>M0sfBk?b2qatkpz?}sGixp{N>yD;=hCstaw zg_~@>8hRj{Sr7gm(b&G@GOA!dhN%HuOF1=zYno~o{&43kY(PkdHQ82H-c=}EXK;<- zuVC=z6@pT2jam-IqNU7{EhB(pYf+^F5AEL;OtlfTGuMtGKiaFEUNW?gov)1Wb)Dpk zcQn1^TmJDOHb2T}=>&Qij2M6kT*b+1uAO|L`4C=(W&wLv^(enw$U?3bg{!!;R{UT4 zr;rD`^EstymYJd*w1)v*@-Ti(0S8Typnh81bFsE!Jd>uaC%cGrU9B-T8a?F6;QI>$ zsMRN5br;o#*?RXu61Wr_(LsqGDDg%r%%oU~Uf<=1akUFq6Gb%Z{$@4tTc-N0pxauL z4ONw-A}07JIilMNV+0-b=u{7l7<4;^{{MYcv+#bk3M=8g=a+AV4+K z!^~dbt09R}#Wxb|vdxg4&xePa;)ekqv5DPb6w@?mgl|Z_IJ?BSc@L|z$HLh=6Y`B4 zem{d@hFno`E|{;dB>}=oXo1d>nRSE_;nfZ&t^rP5O|HRlOs_gvl2>0K5VdbV8?Ctw z7c5lNRw0u9M;HMndb0xcDGbg@yn*4})Roa^33fIatOwU1G##d7@P<8jkHPsT zZbg*=C2MSfcV%*LBGEFihdA|TFz+sU%eVu8^IYA9nC_;1p*Kyt`Pz!TD5|PxB40!O zmb-99H+=DKu@3u~Z15*D5ZH9=vcRR3j3AF&@@4f{x-tWY(4^4o?bKqqj2VV6_c23k zBWt6XW=da0R~5Rr`dErb3xs0u<2G^o+5TXC{k!i|=3K^_jsh)Qs$il-9TsHRtdMV0 zWp0aq0Osr{KHl6RMRlQ|h%WORP(NMszvKknTnJWKSKE#4BBM{v zU}!OQNZO3(kV;0ts|~5wtfA|%e!TZjOEm9Ed)oBzr;gq?7=HWyVF24WA=vQAs^nk*g%({WCaxATUF7AVB~>A2 z{t9oI!O4o=!vG#o2$W2I{_6suM|8Lec1DHF#-rJ{>K=cpZX&wu#eIjvv| z-7~ln9IOJzH2VieJpjT_Zh5AflhMXf{g>=qjbS*;=omd*9UunxcT?)Y(mys9h{yx6 z`_;8zZCMR3V-n&P)q!p^DOY11eJq+!7IIjPok4J4*E?cL^)8Ob35QGbjLb%qX3|OJ zu1ht8p(ra;WGI7`V|5W+VFBFTi~4hVHn3t6Xv3B_s$~*%LwdTJx{Kwv0aVUpF~Y!* zA6SXm+OR~t-);~VtZgOuK<_WFpW}W1@&4;?KW?;8;Lj_7?Q$JoY*>0X9lr3K9G1%9 z=gYzDuGP_3qJ*{IE;0B8R*JkjO~7={l!EF{9{!-4Iv5TEX+*3>?eQV7x-g*ulb&1V z*4)7}()&4QJ+I@~CK3fLSi-8zUj|LKLUtyi7?8?!2o$5NO=PzhTo+j~kpQ?hCb(w6 z&oq)dhDMKC@cR^qhcl584pQD@LD?t)q<&dYz(i~HL=}xY?n13#^NZf&o@p0wb0$N8 z62rUW7SWA0cQI)U5ya_#=Y+7s?zNhcOLIuMYld(~^0X*(i~@NDgGC5_mLgi)W$>*^ zlnTYj2jb7;$>Hq_KM3J5@|ga7gN$v>mXh2q+?`NE=fC)%`H_? z)`7ZE2Ggnv7@HHK6RR-p0AQ6MJ4f9wlU3`cfJ(0RZf9!}=^K{K1Vua=mY0-ivp6gw zQ3S$33=?H`K-n?iD|)Y-+V1L5ZEOg^!ZX6yjg>IdE86v>AOW!(6<&uq|3J44^fFT)P4k2SNsK%D4fth9y?DXDet-Y%`+F1x_v^!h zyZ-#||J%<6A=}3=*PepmM4P+u2I}Kkf#Hh`R&_LBC4)_RREeTIL_*@cIuXhwA;=7((u()3M4Xn!>1pYTU*dF-);IQknX+i!X?a!ZwAHOQ*7B16DhIEM zR$GKxK(7Ix6s=+)WwX2S!!42o9B+lmBTzn&e z@X!bgsDF&{us;K^KzCp^Jl#GfYM0D}kvIyV0zc$qKN(RDXbH~@0L%3j;ZROo02FR9 zZ>V_K6Tb@gAwW~tLTg5VH6L*t#|&vhsZPTMZ>-FRE6>xIM?IB7DwjO$sIm$xNkZxE zkIatEE~+@%EEnxfVzB=mX71bk*%Jko9nwN>dO}RK(2TVd*H=k|U^(3Y9kVe%tJVoL&wQ9zPb16t1}w5WAgtsl@C=s> zs6EX6dq>5gSJg*xBsdaz83-Cg4~Eb#Vm^_zw_q(3O1bw;<41kIBqNu^y&hR?tgIS@ zbIz1FD||LGl6nh7ji8u4BK@K*PB!?r$>(oRWqpFvMM=P8BAynev+4t zOwyFCm7@l!<4W89pP6zP%l-;q%|~*B|ev@T9ni*v(=L|Lg!pg~&RoFM!LDNC{$- z;FI0^0BqD|IFeZi8#DbV#u6>e*@aAL3mZrag41Ud7E5>k)=2?=;Z;x&H&Pe>pI2rm%C0|uc) z0+fWj01^*GK}Zq7L9uZh{E_u~y*oQQU)^0@oLi@VI=c1qnM-GPYPb5Nx9{(s zuir6va2a$!k3kCYdQ^+kKY*Yvj^ZUI0q$-LI6O@C>+-7k@uug@mY7MKXO!y3FqsT1 z?czE7z0bCpPIWs+$0x@pCr3xK`Ft@9;~4M#5&2LKtR-*_Zm;fS93yPdPpgV(mzQi* z-JG=3Gn+fx)15PCHa0f&vQK8SgQJs!qvN9!1>%L_MA4YfHoP~kL|Mbul`>O5*+qQ~ zSq4$TD_>uMBB3WXLVYhbPf}~zLREWO06YU_Wvt8<@gh(cvdC#(ISg#c)~P87qQ&gl z5R`4A>4($|w@k2^B$NpmK)iy@1g9AGCGv>oDDEV-NaH4XtI&OZ;+hb=HU?wY+KOnj zai>Bslf|-Un31bDB7>Dz!56zihxCw3GGz~EogcT_pELB%y3&_2X05j3SrBm$ri7Nr zv~JiE#WL1#zK*7eFRc0pgfpbKVu^_2bNq3=;Lqb>e1y{#lHpV}3BBtxQO>|%E=2-K z6Am5V5rl;5dzUf973ZqkgS|wQc^L0Ph>=hZ?)xgy851U0jL}9r*_do7?rv|LIlD7G zyR~(8ds_iWYd-KJ55DXE%kz2n&2NAA<*V2B_789D?O)%!wSW6ik$5)m{CerxbIl!K za1c37hex|55i?zxM89d8&CT}g*7U-;vllO%d)GTJ|JG-o**`e`{Ig$qbMMw0H}`Mc z(tB1FK%RM61D0ZVFlB+Y%kmEp87E0*72^<#XXv*h{1(g$dys$S0S*db*uOlu+9)~m z0&%wBudv#}8|zfvr~vKUZJ}@)a9s`iI=a?b}oMlN15cC}@{HgP0oVmz*6&VeA2m!F|BDPzl)04WP0 z6m>E6V=2bDfaJvtL?P+;tViMNK`E%6dr&nGab=Ma0qnRzOJvP%0|w?@Z4q^uGT+ z5B%mYJ@dfjdtrH!=U#mI0c1U!!t>95^#^b09UmOe=0@UE>V(JH<^z$Z*0W-sWCYi~ zEsL`mPC<`s@MbYW80~T6F!wsx3bZsUONXvc@A{*lsV2cUnIgT0me4pHvLa8TBm}%+ zCx>yE5aCqYCurba>CiWd$pX{oQ>bxE?#jpj!iAW*=RR zIp)Nmi(7f2b`6AFZzIwEjkZzEoVK~l8maloybEIfVj&nc^4=#u%~)6$E~U;Y$xA6< zQ4O|Tf`=H#no78$?A#_eV_d|v!aSfYE8H4K( zNbzeGuF4um$4CpwiM+)Y3EeYnj4slFXY>`-GH*?%=g)0lI=}nSdmeo3%8xzu;U8}s zrwvLe0f(f=tm1cm?@#{g`IoL;-@AQuq#8~P+_Xp?gL$eRw-7i*<3B=fuo|K4$UbY0 z!=tKAqAK@`yXPLf|Lwo=>8Gze^zI%vYlUxo`}r^a-9KHue)IOx@!SkfeBDxUydi0f z47HdvDrr*SplI}IS#<8JeYLB9BL_?e^e zb(jvZ8DxJANjn2Ev_dWA1$tFLL_aVNKJ<+g_hTl2;@m!r_u?u(XtQ$(Al-nynqZy) zcs)xnw-?LDzK^9|co8No8}2T9QuWh&BX&OjuU6iv{%@q~dKGz90Yx*5(wrt0Az1GF z$)AZZ(oHnDb+?+7KQOD}C_11WD%T9#m9-qL<47J{Hj91r=2L)z|vb{A`srSB1_kQxJk3RY6 zBbV>p%>zB(dwJ}?eDgnl|BHY7%GK-pw-3}BtVh%cKI>_eSc2qX)U!LVyK6!#qt*aI zj?+UPU|WnbsYyw#-P_a6i@UoYe&k1g?Nd*w8F=&ydg0}(fBL1b|L<#W?A<<809H5* zL4pT%Kol<8*`dL{3HK;ngN(yFhEJO~aH!xU;`tovVc*H`-BS&hPIK-*VIkm#`FZhf z*0W8nmi<3s*ougjxEBNo>KUM!v4n+BYG6cl&G5;0DH6>kBmPGTjLWnG7aduGbtj0r zL81;zoRyp3Bxm+opug}U8BSac>g-e^L7oE?ty8&M)9`?`C>~-LTHX1n z@n%Y*kTacT^u7P`rO$rq;~)RXPvCUnqZDYQ@7+B+X^{&kIAL7b17ewl8g-;ev?`)$ z^doHtMoi01E8=0DS^(@OQmRNZSgKn6y-)Rb-!#BWm;3d8w=~W_gSB$EnvKw3J1Np6XXtILS(_7phSR$OJne9L zJOD3O1|70t)A#6Uvihms#V9Fa-{iB683ycnaYH^3aWjFw}`t0c2fpN4dnhHe2H5shd zl!oIC;pw;}mV^1!5lr+X1QY!CLQ@GqRgA*@ayTdnpBEbpuI8+QHk12$x0z#W%~K5Gl4yrZw%Ht{}@=QEZc0dB&(u0#O>;oHPd9;zkpwLmK4q?|Dv{->Hj? zmglE4)OB3YlB>6HEV_VCK2|!(?R&|58uLYw*$Iek4zK~}ug(st(l9D$BM@);j!>|6>YjNRV)|%cFds-YyexsiA_doqS301Qw>|?Z z%wTR?4ds0~Z5{*vQ}*XhUD$o*iANv5@-U%qMHT<&U;esA zBI%E=w`-@&oaFA82!pMLToIGEWxrT_EQ|ZhqkPn8i6m}1-9+VqA>L6NJ3N`6%(|W@Mj-n3Mrv|A{ueZsviZ>CRZ$~_ zepqWNrPrsgs-KcK>dEhGhXhrLDx;Hl6qCU4XPaoDGz9WA2FUJg0EH#ncHNmhSk}m! z6bius%gz5grGGQH`<~s|QrY*X9(m}ufBBPY0A>Id5rq}PQ8_-D{lQ;;`5WK*&edx- z_6`nDW|^6zkWYaD?BvhZ>;YAw3mw*?YiB@7fh|8`=irEjdg25ts^Gh`y>;KE^N(M7 z|7V_ltZkYSjO2T-UHih{{Ns0CeEHgqn`%YMnl`rxxHRlc5+uwFctCH z*!pp*zgI$4$doB5qCsSZ8aJN^Klu#YP@Q|D3}dnX7RZ~Llu=J5Q`3KW5UKS`T_r0x z`$~djlUxy#60T!1nW)s;q@<=9jp)@$eZccU(f91f#Q{Bz2rd-J=KCIxFom&aeNSl# znJn)tnLlGk9mqv$`HN!i6>K%GpCs4Ba8lwB3J`VgB&#XWQDT~<8tsWxF_kWU;Ed8fBvOcj!tG9ZF^(y_N{}% zoBOwKY8ddr@v-WO`VKOUhspWig=9<3N=MunvNl@T*uHxN9(J>+jA{RIT#U5x3$?WzMAY#m1Ar&Hv$Os~- z4!{zeO`>ontYgYk3Dh*f^-JjMCL9|SO>l%Mq=MN1p|PPV0(sM<(QQ$TTv(1tcZq*% zKW|{Lx6crHW>cddwc(_?q?t0!bshNfC3!D{(GZhO8&5`Dm*O8B{4dz8hXH^CRJvO* zdwPt2*6S9fJYGUK3$( z{2>wncuHxq(Q3%=J5<#B&?CS5>%XXMPj7jTD(QaVum9ntSFbAQ9xD7Q6=Rq6uCpBf zK07&C%x1a9BtFv%BVCpJAu5^`BH)#}%NcGffX;7T?ouR>i zCX+U)PX7M4U;M;JKlrOZ|I-Y>|MTkWfAm*hQ2@Sn{pL}Y`28K1Q(3o5F9Cz87XCfPBvQ&3C}@*x2ymK*-u8Et$ihT3m{T9P-BEzrv#;3(QzOBYR22fJlCmtN#ky^MXb3esDS z+N`)f@f+l?@}{3nh9aAMiyloM`1#NfyJwYZg9Tr$**`e?voC$^ zxfi~#>egHPhck=1L3p=*wv)D{;cn?0lF;ieiUkZ`-Qu3xFxWb6U6Z6BQ*l@u9y_?RR#--?%f)jS^pdhygDe&XcVhO#4k zF+~k$W`G6q8aA(H{j=;No$7?U;rbzy5o2zQh>Yqo&l znAfY+#reaiDlGNV$j6Cj-aIcUDm+X8sBVXkXMQO|xftsdn&m}gMdBfiXDAXDI1Hv2 zfFRCXUV%0SPl~{^Rp_UFy2ut_a2;V8;^V7kHIhMc*MY#K%b%cL)V-1#w)mLqt(H=e zlG5zSkc#FY-oV5EB&CL?ML%M6EZvdFrl_v8pIap zSWYv&>^vk6x`13~gVNGi!a{pIVl0ym@;#nzsOf5lCu_c-#-)n9ksQp==Dq+9*U>T8 z>X5u+ifX-p;p4SH*4Iqu3U_Q^pGs%J-W`nM|P*%dGfM@Pajs$S~ z7M_w5C1g!oHuNTKTIpLCi{+`Hwhp5~FF3CVV43}89J0&3El>+asTv?zk-}sHtFU9e zP^A=B$sU#U$55-~pJI%`rw1QLVXS`T@ddCQrfirnn2wbsu}_vK4EHKdf`)x$t@Vtv z{B`jf4;hthY%>-PbvT0lxmXl*|KqtAj*dhu$Eq!2sNUE08 z8Oh0FF(gG^vI|`-(MIA|Eb{Ua<8gf8-TmfzLNGxfZ}Ki(0Lk5|3p76EELthkzyL1Y zR3P3__|)sqN@5JI^YF(Ju=T}VP3f6>1pBEPDHQr7sU6Y+N;42fE|&8luLowHd{18C za3Q?Qtx?+cbYnw(taMBD*oTKXU~lZr)(3s4qOyUc3mhTa7ph%4M;`&&CcS-*;_hQW z0wysAJFukq1{fi$mE6$X#bDN#@p3mX*gBj<%)^Pugqs3cNzaRI4iU74L!=}LYt))1 zAK;2%0WWa^JbHV9rzWbX0|xK26d=rHS+V9V-LDplPF2t5;cRN)G4be7WqI6S|SmRv;gJZ1#R zv{^G^z_qh?5z3$#qVm{V$#%L%3;)DD-6BD`h6mV?%0nW6W{H^{M%`2*fzw4sAR z2+)~0wizV6oV4YG!O+{6mR@fib>sS2F(fW9 zIwrYHMb=4y04&SixIHILQvWYqx69JG*S&rUo##bBMl&a)1BVc@C@SosT(#ks}miyVLY4*;mO80}5*xJ=8w zGsh$hv4CH)?vW}3hL}Q-#n6l;n-!s_cbV8Ea#MdBOY>54jH747tS{u}8CqTK$y-lI zdBL+tJtiyW{A(A5EE*NCG;YwH`X0p>+#FXbdiCVL#V6+;yD!t0@Dk!VFMuAOG?urtf)z8)^&Ra z>fO93ZMwKS&LY4(T?{)O$AL3S!nONeSR`P1lODp8OeRT_In^-s;Xk6p1Y+6f71i3vKeKly%kxpsCwZpMF(qsIL;JErQ|wgI5h zSTUI-E9Cu7**B_bayi_nJ}-$4+~&lOKA%jww*Q%hBU>eWz#CTkE^UT(xld%*_go`a97`Qe2p{_8^*Ol2bhl zC8$1Z3eXLYV!7z;Y3OEWkAXRsdBa8gtkTHsr$%=}n$K^m8$?oz(AC}TAPMOgO(Zf1 z=mypLe3t1o^9XIg$?~0Pg7dxPrkdPHe#p5Df!cu0?qZ>>4R>U>M8QQB^?cQ4P9epP zV33ai%haAYBU^I&h5anhno(vG{NL@RE`~(D%_tECWRpzQoFs_?>wHi(mwpLow;Z=c z_%+xJ6=kUVLESvjjB47DqVCb`Sl=^y{D>!Bs1I=?jWd12Q*tEtO4nN32a_;Ps=YnW z44UL;EaQNsqK;lu6n1B;7>3adiNf6dV+>|!jAQp*-xZM~N|4r&&NAlHPeedLDe5p2 z)M_Ys%@W{ryDTEdgjGrAZSjA^y$O^Y z$9W%)?wOsPJ?{fpoCgVDabE-h0zAZ16iJh!D9JLF$dY5mj_vq4$%`H5<>mi>$$Lqh z_>6x)(1N49Alq$u$aNRbpTfFelYB5n{TfyK4=IXgQ?=l50ZH#OZ~_Y`IVy-e@U z^i&^J)!kFS`mWyx1XQE52i7-WA=YEn^gSM`J-||5U(;Au5AioKHKidK>p){Hd1of& zfKLd{Yg|*6xdeMuMG|(P1iZbHv{H?dm>LhX_(ELD*-VNEJu)yhBiKoe{)jf#_CJpy zwwT)8Bmu~?U}YR{So|eipbl1{%4D)Si3U_xL-nM39MilPfCJQp9>FUUwIeP962Bf$ zfQ%NT6}Lw^ALt2NOkqE~K$F$@!}Y**s7g>vsx&W{o^eboTIqC}uEtcFKv1&+0km{C zOX{I@^hg{cNKH*Ot~OE{V=o+zej$i#DxF0bkj~Ny3wHu{BuUMv#{TM_pd1wdeJ7{V zV-r)8sT7hOPXoRKL0ud{M2K5eh3e2RJ@}$ubRt6cna4PzCT(~}8^E%lZ!V-FP1mn; z8r#jW(Eu%Wh;jhTdpx5uq-@NOh<|Zj_M64xA5zd}#y^&o!xT=IHi?38UTo{ELzXf& z#6=t5u^Pt9dL{3*iSX*0LRacX2tgo$0)>JDb0O4XsOCyje*ma%& zb+$GG66$J`#CSCgY?vOON`a1JV$$^>9p|$e{fv;r*BNpZAO=WKm#l7RsBLPfZ>+0D zu+yUMPB>lHqaBF^jsn`@Y@?H@6K5};y?AAGd=m7Q6BDVi6abOnhYVC6V=-$rr9wXF z*r~XC6tq;=*Cs)B-Be%KP+PNh`Qin0I*{uZ>(~m+1A#Rv?H?Zd;PA(T!=oc(6XS$c z(=-D-6y(Cfz98eD%|icLLBzyR8W#t{-%0e8_7aGnf~j(>3Y z6jsJ3r|3PEnk4KZ5k0H!bCglXB_O9HiRf%=V|_~0>zj4Ll*{w~uCitt0#eun>Tp4)roxS}-(2h*xQ#0{kh6P&PTuk85 z^jdYEhFTn(T)uG5qV9IA)Ty}+Fo32tQ^5Z8<$;mIr!T-32+C4;)T4xET9{f;m!v6H zMzNH#)LH>5LQHn@%16-8Rfsi{bAnZ@K8RGC1H@QtQg|`u{`M)wi`Xp8h2D_nK z#Vh780C5+kcaqD7Q`fNL7RHH`Bf#}&(U(?y%cE>GiH?O+6slFbYfH6Rt`kG`Y zhybLJnLmpcL>N^!VEC;MKH78e=#~CKGz>3buy1r$ew7I&?*eaH8XMZ18yC-?^U$4J z7It?5Y?Rj3MJw^n$n8MHyL-zzoc!R4voE~02L#KBkt)O<&zhXdd6`*RW`?ntSdE)i z6#-sVLv3wyV|{yb)6xaAAGza}xm|5!{!s!Nsms+1T~IMExrc7wbhh{E58rtI!j(Q? z6-so*U#k!t!EZndO}!EO2(GdEy5@#DBu>6{&9WUEdH|408P1P+kqh z`@sGa=LSc{hDRqxl{8Ayu(qX&M7Nq91kKSWNTCX!tM_2Hm|c4H$^AFC|DC#EtxmBqvadVz7Ap=r|@i zV^eALiO#S46cWQEFjxkGde)3Ta<9m3%#1=cV78<(Ha0Xh>ByB7j=DAyhXc43? zQuQl0m-a{2HM_8G#+CXXFi0wge8%X5BFU&FL7v^v7>g-wb5#|3g)C?kF3cjYW9^Xa zx3u)TA?{#O#x?k5kk=4@BAGX0pXi3smhd=P%uSr+vfF}p630iQY=qV~`r1moq4WC$ zbtwzR1cHi$Hlmz+UbJYerBBzOrP|+^WQ?9C5bYY#0J%g9({?(MFlyp*G9t?`1L?G^ z4PM23g)F8jRAt{E79|BygDEkrEL>gCVOS(AXY))=b!$`O{MnuVN;AQ=FRT-+y|nRFe16P!K2FI0VYNx@2=heS1sO z;(1+9e&)`b7j*k;H|vGI7i$0T$j3i?V-IS@c{Z(F`uY2|11W=C(7jjx{2yLEapuCc zp%J8FH0Caotq4FB2T(!YZEb)&+PrL0_fwDGr#XiBszwv(P&rZby}WBLK&x+X1d{Ri zII2oBVEG<+HSdWx)Fx-QwQO6r{I@@MFJ#c-1Xi!@IrNj=2O#T19%yc?d*t>F+t)4U z9r%LqieCSlm)^l4sCr_G1aK7MIzZ^5n_FMs(bCY>+Vsz!xEHfJ-4}6l!=EA~nESh( zyH8)d3a!I1R4J*c6h?5JP&y)cLx4=%Mg#3wsi9?hhElxoa=>RK;4Jrk=5BccAMKUl ztmh~IN7NND@&q!8DIIzuuD#!AoFV@Kts zR5^}Z2s6$Z{1M)=e?y1AWM0>AJ@G($bAzd_<@Adt4?0bq;bm0FTqG+| zL_)Cwhj)PCN5)X8oM~k2$9GHsrzX=n>u8iMTMX(^o^nAKOMg5Of0fUs!B$hKW{2c) zq6#84=viX;BEyy{^T<~*G{n;t^ERT)Zow5hb5 zWPD}BCROStD3DBy&{7q_fUE;G6kKZlck{(4gmoYXmE`4Vf+^OWU;hd5iyTv6*zWFVeQ?Ld2e)qolA%(| zWsjH;%xQ1_gI{~-hdyS-WT$hw$Hmvndhizgra(cAlW zfAq2P*cjJ+f&gblSv%>oEGwV*OTuo*x3I`Pc5C&yVH?$8R6ZEBEhxqm``vL?q8NwJ zOlO#xP9#dyb-RKZ+RzpV|Kd>XeL``?L&#RS@yRie?~gye+2^?uIiZn@(0Xzqcf=r? zY8>Yo=H9A$;@Xpxei|~8*n~!7=twJ{65~0AFr>O<($v9z@VdlhS+iKdQ~2O=23}ig zJlSFUJjJnx`${LHa*mOqtl}3?W&oxsB-A7tCXY?4&J2#y2+PJ+rjGEr)UYlt3^^G1 zc$2iv3x_OmnK{f(@=Oan^-2yyCJR^~(Y;SCF{?U`7pX7K_Fn01YsNlSFtEz0eubgq z(j1xmab~x-Y+kecH=p=)OH%`%-&7}oP%6NS_q=1%>ei-)Z~y2uY?}#Sffy{Bt!XtA zq#dn+vbSgHg5UkpLl8Z&yRkI;UUu-q^6Blc3SZ*^(f$y!=9zQ=K;L;??O%Fu$By+q zh291FXjUR##rr&d_pK1AUw(I=h61n~P>{CAhpUlOx!T3CfDvqxwW~zHqfK8E-w%6RiC^27D)MTe0WT6m`ve=eC{G) zQ^K+=F>leFk`lx1HI)&?AbPE5^ggO87JL|SLdh;?g7sbeB~+YPPKE#Bt`wqjU5zu0 z`o>d2VUSeHqgZ;J%GhqqWos!xuLC{p=;Rc@K2pNgxNF*NRYS;hoK#*+C9Ul4X5CyQ z1H0In<_C~^(a2JXIn?h2Pck}U1T&8Ij>9`cDqtfLGTU*|zBP)u!3L=^(Lu#D9kN`? zG!lagCR9yfh&5B9=Yg#C#SR)B9Byl1Z1qh|yl@_xK=@W(aQ(X|^ffP3Ad1>via^k1 zgMp!|Qe9yUM_?*uPRSiK$qcA8k2{iyPo+o4C%*appWM23g>C4xV=xp@7q9fekyQus zyQFa?(X+FyWy7kQzxvtxU;suf5^E*ON~F>2h4XEtmsAJnCKq#+a-g`_(J*T{BFXy1r$jEjs zXE?BACbSX+l|ce%QCXMAS;?cBf*;QqJ2mtGiZo9oZkZ02o2F>>JYj0UidvkIzToBT zSD;auZ(dKk1$A=FaIS;XRD?p7fH#*9jkT2GiOu-OYpzJ4?58zy3OpJ!uxqG)jkdW8 zyaI7-jjq|tt0*{~t=ln7bG{5maeL$obD~CSqxk|xnDjhrtgtmvoG05P9Q3V^J7-sA z?9^+#%7wI(-kA!RUY!bMihkqo>-n9YqNWB7n~t9TWE4tDQX!Flr{}O6l6C+l{+$G}8h5rdec}G?OXt$5B#ad|1MWYE zPn}Pt)7T0x<+V32!UV$X!24s0ZISc%{CdR1$OR=l)C_lRUJdk>D&;_{jv@4FU>L&$ zpmBCv^NK}t18ok>4{lqx3%PCfgivSV_S$9!YvX8~BUdCDMDiBK}*s!7d(`#iZ@2G%z}Z7X`rG^kg>A zaN)2R$P`dsC=I>w$O)vsqhjM4<@s=W7Dy`KLjai5SITbJk?~FkneoBLUdB=SU>ut3^u~2J?Y- z!BlPyMmEUEC$66t9#-hn6tgQQ{H}KV*dVrQr`EZh?NHlFP=WXHd;5=IFhw#X&5exd(ALXiFP z!*_rD$)yRz;$UPT<_-X`5r6ebS{m!`xpf`Ty2SJX@85mpT{v%F9UQ(oFaqaljk0gH zAA){$<3HRh504<(GXit3 z4UG-qutfWl+HF-1*)?c&%R>8iRYYPV$E6 z=g!uK&eq1&OXjU#zQ~EVq=+ZJ=ay9mPJV)BaLz3rF|6a59r9#pnfM!>V=B~#()W4Y zGI`8#6)Tymcp^O|dFK4ov-k+c6}gVfnwTCG%!hQ7MtJRL5~DfbLj| z=Ef{YGer8qA>_uC?4RS)a`~9LeB@+?k+5i;t=ZM_7;Mqjut5Bj&ZH346^q-&XFF?JUH^9OUF=1s9*&C3AIaw6C2Q>;mN3mL`o!ZAwlQ}7`o z+`#WRZ1?%ft5dmU1aX?|xjyeuW1sLVhXpRijKss{US%=WwdMInh;RxKv3-4XK$6@{ zx{{!7z#TNkLnEUY;1FM1n*?CNd{SMn^u}A(EMIxkLRl4v6cCtu;qtYCkue~jqO8h- zr2+$`%1=n)H}J29?^SbC{q{|(w{KV#>gxoY`@yTb-al~c>Hx$lI#f|wYFYzgWx6sj zc=1aAx!$W!K73ctk_8g{a63MK|BfqtL*tXEpBTBc5Ue7cDb^n71)s0wi|3(ZQHs!W zm;3(qg*Pwv4Ir|1ER~`-1i}f3hVkC3{m`<0bn@KZqo<#I=uX5g^GR&@%PuzBNw}$= zg0CLDZ3EVIrSkpN_Yc0a_h|pn=x9-Z6}ka_jB^*S!p-=|=?j3XC+^uAs#}0W+#{p8^o_&#?nF;Cg#OHQ$>D{M zFf#*x__s^*V?ZxmAzuiI0D3%fn7nD^HH@^HH&de-l^Ke9jd*dx>S=(*r&{E1#hFuT@g8IN*Sl~f%#NW&J}61cr9PjkQyCa zWc-o}jFkpN4VjmS7vnX)8|J3yxKAUk$~1X0eUWIVC%8K#d70g?BDE*dP@o%}oi*hV z@&N!f7o&_J<#V2x4dizy~DH;N%5b7E&vvi0}!_THk#3)u%rH;EKib0v1!euUo!k zL3iiS=omzKoB=|xzK1T5k1Wu&_ikIidF4$3gKGgWPMqs~`p2(B;X5=u3djN96H)W2 z8e)o`8s476;}c_(Q-G7-`Qn3f+FJ_8T0vMbf4NCaMC$9laQ|(sEGbGm?%%xl*8XE> zt_}>3OiTjyNSe>mhT474T7?5UcI+p^*`mju}p+EGPv#&#-ey7a@KVXONI7 zkCo~CrT){C`6+F&NxWwyU`7z?>PJUA%DDk*R>MunieKJp($6_z53grB?x_W44=#65 z&Ycoa9LX;xw^bf=W#xO~Rq3=QG1$PW4p{)`xK38PR${nEXCVYbm<{Du!HJ;tMG!6! zT8XV$W)9IHN5-mzLh+jxc7tuh_&pWXyKCR!xAqYF0?RSllPi^;6!P-p}xDL%>-Eq-i*I}@y$@4s>s z27rFd0ieH6(HO!i~*~u`^tc4(L!4c zvey^CRE`iFi31jim|dLIb)_t%bQ9IICleBF7$NnFkm7JwE7<~VfvrWJq9o-2!L^0| z22I_9dGuIItUg>rav^FGSH|V&S^8&q4KQh&d4eBs^_p=6Bds_|2q-D#?3)MlW-$^d zYcxYB<85&+7B2N<8q+wQPJ@v`{xUGY<%}Gr+d{sy&{ArQ4`8qjR}9iix=MR&k_?41 zOwLC7WHvEV$p4HDT&chDhA>;ff|gKoMLGjJ>F)0>yJw}E^v6k!T8z`AxF%~;9f)jj8J;$ zz~|5DfFl+5l%gk;-Y>qn=iH@h*9IZ{juT)3aQJ~yJ306cJSIO}Dk5KxuYdPtVDR>cZtU%Eyr{TR+gqo+Ok7H{AD(VOQk^+8~T2%ZMiD;0L$r_y8X zW!!~U02bWt&el+ec3jH8e`R;?wL$0%&sw&Z%S`M-A=@;JyQ5RH- z(mIZb6r2eL5~J}r&;iACqVlCxiH5;9iIJ9CtnSd!Ho%yRznD%xg{}ArXjPIJeFUoU zHVMx{U&m#p!2FhB-lc3PRdJjd_nSoCY>p3LHJ(X|UG7L)Xz zcH?rEj<}5B-B4YrepkgQo`MYFMt~NcFe;*VI-?D}#krx3HIALFP@p?V|9V?NYp%0% z#9>C0em#2UykyD_0y))HRsa%Xt=t-+1!YHRgUZ5J*EMl%L3*X8$8^Y!Sf7dX{g2+)RM=}T!i|21Y)Ol@-d^1374cXG2^8? zPbCW)!P#~lICkfz)o4CB9%hwy-`aZ&vhTpq81y6BH%{+U=#_fV@PO&`RcW*V{#kH( z-*e>Dj&;jqcr>)rD;Cba+&36^@p5=*3{6UJkxA`3YTtpjlL&;gY}Cn!bO5j8(0~Rf z3|L;gykOIetXBT+dE}L20QZIw(Bub9RH-XTj?K_3IBf7^j1kqab1Q3|O3Xy`2B|F_Mh_)FV5Xi?Y8L9?%uvE_Me_wmI7OkBUPEEZao6086SuDB#C zMPBdj|GDBJfu0C{zdcJ9hKAWd-UR^Tchyi+n{V-eeG2;{33pvZrdX?To4vRH=wtV6 zAK+_M<^?LhP34n3!%r5b~=PU;{5 z!amFOrHT&~f-4}|i>yXKrXpl=6&abzLILxn2ktU;tgTPh>}x)apsb4-VZWrfphFC=Wng0(yy7>^NkGyF&# z26IPP1UPAFHf@wfCJhBUta-&W-P{ew)grRAoH8=Tl`m*WwgcDTi*9+4X)K_>)pn%d zd02r_idV*Oqtz!Y%+z$uDc=HLKDdGf%@yEjNrYgHw=3y>Xv%zWE1w)8&5La}GFi5L zlR#SurFQ+cy$ezZwkzrEee?qJ8_jr(Uk(Cr=PL6ChQcJAdOkjXN&6e;nDD>E^aMKqnIO>N z5F86Z9M)D_1Td(!i43o2$-H?Tt#Tp)$UXJR6+|Ax(M-E2@m7f+jKNCDrt|W`&I-Il zuLBb!Br@u3X+XVN%)(N-od-{z8yp>j4jwZMEkuN9W;n$g0dOpi+)6DCb)jdwcy$nl zWQe}0^bALLDRAS&&k&Sx2z20QFZCfrSc>Ugt&I?oHQ{1bjcb;tw|odKhucaFMp9rV zzXw8GS`=q0Nomz~R`O04PAoJUT%JhN7KJCN??h zFdo^j5n(?U2KX5{JAx@vpSmp2atb|ANvK`zo%^u$X&+1wi?PlyF7cI|PDi5&7 zyh{w06-$3&vDh%Uav@lr-wwxiDK%UdZBv@_T4jq@-X8o*gVG3ih&+R#L`cAf$L_t& zNZaMmP3uFXUl@TeS<~pHInLv80$|93lHdtgcjm%nAWn+RUPkL1rAqWzLl0#kFb{4@ zND}<;9h*yE7I%h$vZ}kI1uc;jV1QD$#&HETADUUs2@YG3~K&0BM;-UE> zmC0;o+FY6da_{l8l!%wo{FCCj1ZMzV+P}T6v0;8!o4gC?MZa=|Q{by-6n3s}?4~pG zV^#rQxX^vjqogu0uog2Cd+iV_guD`#V7YDu_1FyV(koBB%d6;iPo!tFoKwquyiKo5 k4H2D9AU1u~-c>X$jZrfo@?uJ4oCczPz>&BsbO!==8Q^IWvj6}9 literal 0 HcmV?d00001 diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index fbd10d47b..857df1441 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -79,12 +79,11 @@ class TestImageFile(PillowTestCase): self.assertEqual((48, 48), p.image.size) def test_safeblock(self): - - im1 = hopper() - if "zip_encoder" not in codecs: self.skipTest("PNG (zlib) encoder not available") + im1 = hopper() + try: ImageFile.SAFEBLOCK = 1 im2 = fromstring(tostring(im1, "PNG")) @@ -96,6 +95,25 @@ class TestImageFile(PillowTestCase): def test_raise_ioerror(self): self.assertRaises(IOError, lambda: ImageFile.raise_ioerror(1)) + def test_truncated_with_errors(self): + if "zip_encoder" not in codecs: + self.skipTest("PNG (zlib) encoder not available") + + im = Image.open("Tests/images/truncated_image.png") + with self.assertRaises(IOError): + im.load() + + def test_truncated_without_errors(self): + if "zip_encoder" not in codecs: + self.skipTest("PNG (zlib) encoder not available") + + im = Image.open("Tests/images/truncated_image.png") + + ImageFile.LOAD_TRUNCATED_IMAGES = True + try: + im.load() + finally: + ImageFile.LOAD_TRUNCATED_IMAGES = False if __name__ == '__main__': unittest.main() From 1d236f02621f26da8724be131934e44f9f1598b4 Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 31 Jul 2015 17:43:36 +0300 Subject: [PATCH 0501/1037] unused var --- PIL/ImageFile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index 68f9b2b1f..597e41783 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -205,7 +205,7 @@ class ImageFile(Image.Image): while True: try: s = read(self.decodermaxblock) - except (IndexError, struct.error) as ie: # truncated png/gif + except (IndexError, struct.error): # truncated png/gif if LOAD_TRUNCATED_IMAGES: break else: From 5309c8e21eab3db40424b37459cda511cd559b9e Mon Sep 17 00:00:00 2001 From: homm Date: Tue, 8 Sep 2015 18:00:00 +0300 Subject: [PATCH 0502/1037] Skip ImageFont_bitmap test if _imagingft C module is not installed --- Tests/test_imagefont_bitmap.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Tests/test_imagefont_bitmap.py b/Tests/test_imagefont_bitmap.py index a9d745b22..51da499b8 100644 --- a/Tests/test_imagefont_bitmap.py +++ b/Tests/test_imagefont_bitmap.py @@ -2,6 +2,14 @@ from helper import unittest, PillowTestCase from PIL import Image, ImageFont, ImageDraw +image_font_installed = True +try: + ImageFont.core.getfont +except ImportError: + image_font_installed = False + + +@unittest.skipIf(not image_font_installed, "image font not installed") class TestImageFontBitmap(PillowTestCase): def test_similar(self): text = 'EmbeddedBitmap' From 0b19a86087abdf7521aa042c40f67433d1876d8c Mon Sep 17 00:00:00 2001 From: homm Date: Mon, 6 Jul 2015 19:05:08 +0300 Subject: [PATCH 0503/1037] skip any number extraneous chars at the end of chunks --- PIL/JpegImagePlugin.py | 4 ++-- Tests/images/junk_jpeg_header.jpg | Bin 107466 -> 107470 bytes 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 95a057754..fee1f672e 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -288,7 +288,7 @@ class JpegImageFile(ImageFile.ImageFile): s = self.fp.read(1) - if i8(s[0]) != 255: + if i8(s) != 255: raise SyntaxError("not a JPEG file") # Create attributes @@ -311,7 +311,7 @@ class JpegImageFile(ImageFile.ImageFile): i = i16(s) else: # Skip non-0xFF junk - s = b"\xff" + s = self.fp.read(1) continue if i in MARKER: diff --git a/Tests/images/junk_jpeg_header.jpg b/Tests/images/junk_jpeg_header.jpg index 564eb3199da25fc050a53940b9c67743896a7e59..68819f243e57afb9a2a1474755419d40f3f881f1 100644 GIT binary patch delta 21 dcmX?gobB9kwh4i(8L2rr`HewagBU9o0|07d2_pai delta 17 ZcmX?iobA+cwh4iZT#aE{!x$?T0{}&>2OR(a From ab5e812f4d938d35f68e13f313a3f46c6a8bc7a0 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 9 Sep 2015 16:54:47 +0100 Subject: [PATCH 0504/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 3b4f36267..b1a383f84 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,12 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Skip ImageFont_bitmap test if _imagingft C module is not installed + [homm] + +- Add param documentation to ImagePalette + [bwrsandman] + - Corrected scripts path #1407 [radarhere] From 3889ae5d6f1ff85819d2ed57523d96bf214825ee Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 9 Sep 2015 16:58:41 +0100 Subject: [PATCH 0505/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index b1a383f84..0ac74a017 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Disable compiler optimizations for topalette and tobilevel functions for all msvc versions, fixes #1357 + [cgohlke] + - Skip ImageFont_bitmap test if _imagingft C module is not installed [homm] From 772d470f30683af27bb6dc7cc7f8bf9f4560d585 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 9 Sep 2015 21:07:03 +0100 Subject: [PATCH 0506/1037] Single threaded build for pypy3, refactor. Workaround for #1176 --- mp_compile.py | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/mp_compile.py b/mp_compile.py index a930f4245..229f9993d 100644 --- a/mp_compile.py +++ b/mp_compile.py @@ -51,15 +51,33 @@ def _mp_compile(self, sources, output_dir=None, macros=None, # Return *all* object filenames, not just the ones we just built. return objects -# explicitly don't enable if environment says 1 processor -if MAX_PROCS != 1 and not sys.platform.startswith('win'): - try: - # bug, only enable if we can make a Pool. see issue #790 and - # http://stackoverflow.com/questions/6033599/oserror-38-errno-38-with-multiprocessing - pool = Pool(2) - CCompiler.compile = _mp_compile - except Exception as msg: - print("Exception installing mp_compile, proceeding without: %s" % msg) -else: - print("Single threaded build, not installing mp_compile: %s processes" % - MAX_PROCS) + +def install(): + + fl_pypy3 = hasattr(sys, 'pypy_version_info') and sys.version_info > (3,0) + fl_win = sys.platform.startswith('win') + + if fl_pypy3: + # see https://github.com/travis-ci/travis-ci/issues/3587 + print("Single threaded build for pypy3") + return + + if fl_win: + #windows barfs on multiprocessing installs + print("Single threaded build for windows") + return + + if MAX_PROCS != 1: + # explicitly don't enable if environment says 1 processor + try: + # bug, only enable if we can make a Pool. see issue #790 and + # http://stackoverflow.com/questions/6033599/oserror-38-errno-38-with-multiprocessing + pool = Pool(2) + CCompiler.compile = _mp_compile + except Exception as msg: + print("Exception installing mp_compile, proceeding without: %s" % msg) + else: + print("Single threaded build, not installing mp_compile: %s processes" % + MAX_PROCS) + +install() From 71a88d4b689e1b01f8d64a54bb102cc5a6eff528 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 9 Sep 2015 21:46:07 +0100 Subject: [PATCH 0507/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 0ac74a017..426153029 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,13 +4,16 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Improved handling of getink color #1387 + [radarhere] + - Disable compiler optimizations for topalette and tobilevel functions for all msvc versions, fixes #1357 [cgohlke] -- Skip ImageFont_bitmap test if _imagingft C module is not installed +- Skip ImageFont_bitmap test if _imagingft C module is not installed #1409 [homm] -- Add param documentation to ImagePalette +- Add param documentation to ImagePalette #1381 [bwrsandman] - Corrected scripts path #1407 From 6b3666c3d343a544b94b6f89293d3812c4c92de1 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 9 Sep 2015 22:09:07 +0100 Subject: [PATCH 0508/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 426153029..b289a177d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Add Solaris/SmartOS include and library directories #1356 + [njones11] + - Improved handling of getink color #1387 [radarhere] From 525f47a64f8ff603d5f824f1bbe773e0ec8ee884 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 10 Sep 2015 11:20:50 +0100 Subject: [PATCH 0509/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index b289a177d..00b62fb91 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,7 +3,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ - +- Documentation update for concepts: bands + [merriam] + - Add Solaris/SmartOS include and library directories #1356 [njones11] From c614846fb73e21f6f5cf8338d64749fba4068385 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 10 Sep 2015 04:19:25 -0700 Subject: [PATCH 0510/1037] Extend the fix to the other exif seek --- PIL/JpegImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index da8c6922d..b1c25db1d 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -433,7 +433,7 @@ def _getexif(self): # get gpsinfo extension try: file.seek(exif[0x8825]) - except KeyError: + except (KeyError, TypeError): pass else: info = TiffImagePlugin.ImageFileDirectory(head) From 0da8bfed6270b9f33241f705d9b0c933630c8361 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 10 Sep 2015 12:19:44 +0100 Subject: [PATCH 0511/1037] Updated Changes.rst [ci skip] --- CHANGES.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 00b62fb91..efcf6f985 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,10 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ + +- Fix loading of truncated images with LOAD_TRUNCATED_IMAGES enabled #1366 + [homm] + - Documentation update for concepts: bands [merriam] From 587698051fa92d4ea416c835cdf1037c1bfdccc1 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 10 Sep 2015 04:36:07 -0700 Subject: [PATCH 0512/1037] comments for future understanding --- PIL/JpegImagePlugin.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index b1c25db1d..38e4a5f87 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -422,6 +422,9 @@ def _getexif(self): exif[key] = _fixup(value) # get exif extension try: + # exif field 0x8769 is an offset pointer to the location + # of the nested embedded exif ifd. + # It should be a long, but may be corrupted. file.seek(exif[0x8769]) except (KeyError, TypeError): pass @@ -432,7 +435,10 @@ def _getexif(self): exif[key] = _fixup(value) # get gpsinfo extension try: - file.seek(exif[0x8825]) + # exif field 0x8825 is an offset pointer to the location + # of the nested embedded gps exif ifd. + # It should be a long, but may be corrupted. + file.seek(exif[0x8825]) except (KeyError, TypeError): pass else: From 07d95c38611fb0f13946671b2f69f933f825347f Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 10 Sep 2015 05:21:21 -0700 Subject: [PATCH 0513/1037] Added test for typeerror test for gpsexif --- Tests/images/exif_gps_typeerror.jpg | Bin 0 -> 52510 bytes Tests/test_file_jpeg.py | 5 +++++ 2 files changed, 5 insertions(+) create mode 100644 Tests/images/exif_gps_typeerror.jpg diff --git a/Tests/images/exif_gps_typeerror.jpg b/Tests/images/exif_gps_typeerror.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0b1f060438aef10d8f0d4cc67628ca75572793db GIT binary patch literal 52510 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 literal 0 HcmV?d00001 diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 371b7037b..e6da7bb8b 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -168,6 +168,11 @@ class TestFileJpeg(PillowTestCase): def test_exif_typeerror(self): im = Image.open('Tests/images/exif_typeerror.jpg') + # Should not raise a TypeError + im._getexif() + + def test_exif_gps_typeerror(self): + im = Image.open('Tests/images/exif_gps_typeerror.jpg') # Should not raise a TypeError im._getexif() From c8e570b08e7ab2008b3e672375619e2241e1c559 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 10 Sep 2015 17:08:17 +0300 Subject: [PATCH 0514/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index efcf6f985..e92d80375 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Single threaded build for pypy3, refactor #1413 + [wiredfool] + - Fix loading of truncated images with LOAD_TRUNCATED_IMAGES enabled #1366 [homm] From 9cbbab2da56e6b06389eb37e299ee7510e6d5274 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 10 Sep 2015 07:03:24 -0700 Subject: [PATCH 0515/1037] Dedup code in image.open --- PIL/Image.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index a58644f00..bc9fc65d0 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -2299,19 +2299,7 @@ def open(fp, mode="r"): preinit() - for i in ID: - try: - factory, accept = OPEN[i] - if not accept or accept(prefix): - fp.seek(0) - im = factory(fp, filename) - _decompression_bomb_check(im.size) - return im - except (SyntaxError, IndexError, TypeError, struct.error): - logger.debug("", exc_info=True) - - if init(): - + def _open_core(fp, filename, prefix): for i in ID: try: factory, accept = OPEN[i] @@ -2322,11 +2310,20 @@ def open(fp, mode="r"): return im except (SyntaxError, IndexError, TypeError, struct.error): logger.debug("", exc_info=True) + return None + + im = _open_core(fp, filename, prefix) + + if im is None: + if init(): + im = _open_core(fp, filename, prefix) + + if im: + return im raise IOError("cannot identify image file %r" % (filename if filename else fp)) - # # Image processing. From 4f705c96367616c0517d9e19d011983827e211ba Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 10 Sep 2015 15:40:28 +0100 Subject: [PATCH 0516/1037] Update CHANGES.rst --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index e92d80375..e02493b5a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Skip any number extraneous chars at the end of JPEG chunks #1337 + [homm] + - Single threaded build for pypy3, refactor #1413 [wiredfool] From 42b5a85cb4c2c702e0dfa33c466a5c3d2764263b Mon Sep 17 00:00:00 2001 From: Bogdan Kubala Date: Sat, 27 Jun 2015 18:35:36 +0200 Subject: [PATCH 0517/1037] Fix for UnicodeDecodeError in TiffImagePlugin Fix for UnicodeDecodeError: ascii codec cannot decode byte while saving a TIFF image Problem occured while saving TIFF images that contain non-ascii characters in metadata Manually merged with master by wiredfool --- PIL/TiffImagePlugin.py | 2 ++ Tests/test_file_tiff_metadata.py | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 5c35842fc..456991898 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -550,6 +550,8 @@ class ImageFileDirectory(collections.MutableMapping): # contains a 7-bit ASCII code; the last byte must be # NUL (binary zero). Also, I don't think this was well # exercised before. + if sys.version_info[0] == 2: + value = value.decode('ascii','replace') data = value = b"" + value.encode('ascii', 'replace') + b"\0" else: # integer data diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index dfc16682b..f71db0924 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -15,7 +15,8 @@ class TestFileTiffMetadata(PillowTestCase): img = hopper() - textdata = "This is some arbitrary metadata for a text field" + basetextdata = "This is some arbitrary metadata for a text field" + textdata = basetextdata + " \xff" floatdata = 12.345 doubledata = 67.89 @@ -35,8 +36,8 @@ class TestFileTiffMetadata(PillowTestCase): loaded = Image.open(f) - self.assertEqual(loaded.tag[50838], (len(textdata),)) - self.assertEqual(loaded.tag[50839], textdata) + self.assertEqual(loaded.tag[50838], (len(basetextdata + " ?"),)) + self.assertEqual(loaded.tag[50839], basetextdata + " ?") self.assertAlmostEqual(loaded.tag[tag_ids['RollAngle']][0], floatdata, places=5) self.assertAlmostEqual(loaded.tag[tag_ids['YawAngle']][0], doubledata) From 5a675d63b37ba1dc46cc97d209a73aaabc19f081 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 10 Sep 2015 19:07:04 +0300 Subject: [PATCH 0518/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index e02493b5a..ff3313d00 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Dedup code in image.open #1415 + [wiredfool] + - Skip any number extraneous chars at the end of JPEG chunks #1337 [homm] From 79dd6ac3602f161770508584ac777dcf4380db46 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 10 Sep 2015 19:08:34 +0300 Subject: [PATCH 0519/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index ff3313d00..7dbb807ad 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Fix for UnicodeDecodeError in TiffImagePlugin #1416 + [bogdan199, wirdfool] + - Dedup code in image.open #1415 [wiredfool] From eecb1ffcad47957b2a90ae6eb714672799a99858 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 10 Sep 2015 19:12:25 +0300 Subject: [PATCH 0520/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 7dbb807ad..c427cab5a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Catch TypeError in _getexif #1414 + [radarhere, wiredfool] + - Fix for UnicodeDecodeError in TiffImagePlugin #1416 [bogdan199, wirdfool] From 105e96350575f8fed2c046f1a80da993fbe0d984 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 11 Sep 2015 19:28:19 +1000 Subject: [PATCH 0521/1037] Flake8 fixes --- PIL/Image.py | 3 ++- PIL/ImagePalette.py | 2 +- PIL/TiffImagePlugin.py | 2 +- mp_compile.py | 15 ++++++++------- setup.py | 2 +- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index bc9fc65d0..0f11ad7a9 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -2313,7 +2313,7 @@ def open(fp, mode="r"): return None im = _open_core(fp, filename, prefix) - + if im is None: if init(): im = _open_core(fp, filename, prefix) @@ -2327,6 +2327,7 @@ def open(fp, mode="r"): # # Image processing. + def alpha_composite(im1, im2): """ Alpha composite im2 over im1. diff --git a/PIL/ImagePalette.py b/PIL/ImagePalette.py index b7a0008a3..598b9b27e 100644 --- a/PIL/ImagePalette.py +++ b/PIL/ImagePalette.py @@ -28,7 +28,7 @@ class ImagePalette(object): :param mode: The mode to use for the Palette. See: :ref:`concept-modes`. Defaults to "RGB" :param palette: An optional palette. If given, it must be a bytearray, - an array or a list of ints between 0-255 and of length ``size`` + an array or a list of ints between 0-255 and of length ``size`` times the number of colors in ``mode``. The list must be aligned by channel (All R values must be contiguous in the list before G and B values.) Defaults to 0 through 255 per channel. diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 456991898..b0e7c9639 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -551,7 +551,7 @@ class ImageFileDirectory(collections.MutableMapping): # NUL (binary zero). Also, I don't think this was well # exercised before. if sys.version_info[0] == 2: - value = value.decode('ascii','replace') + value = value.decode('ascii', 'replace') data = value = b"" + value.encode('ascii', 'replace') + b"\0" else: # integer data diff --git a/mp_compile.py b/mp_compile.py index 229f9993d..078f62476 100644 --- a/mp_compile.py +++ b/mp_compile.py @@ -54,19 +54,19 @@ def _mp_compile(self, sources, output_dir=None, macros=None, def install(): - fl_pypy3 = hasattr(sys, 'pypy_version_info') and sys.version_info > (3,0) + fl_pypy3 = hasattr(sys, 'pypy_version_info') and sys.version_info > (3, 0) fl_win = sys.platform.startswith('win') - + if fl_pypy3: # see https://github.com/travis-ci/travis-ci/issues/3587 print("Single threaded build for pypy3") return - + if fl_win: #windows barfs on multiprocessing installs print("Single threaded build for windows") return - + if MAX_PROCS != 1: # explicitly don't enable if environment says 1 processor try: @@ -75,9 +75,10 @@ def install(): pool = Pool(2) CCompiler.compile = _mp_compile except Exception as msg: - print("Exception installing mp_compile, proceeding without: %s" % msg) + print("Exception installing mp_compile, proceeding without:" + "%s" % msg) else: - print("Single threaded build, not installing mp_compile: %s processes" % - MAX_PROCS) + print("Single threaded build, not installing mp_compile:" + "%s processes" % MAX_PROCS) install() diff --git a/setup.py b/setup.py index adeae9f56..bf8ec94a7 100644 --- a/setup.py +++ b/setup.py @@ -308,7 +308,7 @@ class pil_build_ext(build_ext): elif sys.platform.startswith("sunos5"): _add_directory(library_dirs, "/opt/local/lib") _add_directory(include_dirs, "/opt/local/include") - + # FIXME: check /opt/stuff directories here? # locate tkinter libraries From 21459e9ab43340ba8a8d519de64fe0f60c79ea29 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 11 Sep 2015 14:57:29 +0300 Subject: [PATCH 0522/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index c427cab5a..1f220e7dd 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -58,7 +58,7 @@ Changelog (Pillow) - In tutorial of pasting images, add to mask text #1389 [merriam] -- Style/health fixes #1391, #1397 +- Style/health fixes #1391, #1397, #1417 [radarhere] - Test on Python 3.5 dev and 3.6 nightly #1361 From 64e733f5521f0816ea438750ed18ca2daca515d6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 11 Sep 2015 23:54:57 +1000 Subject: [PATCH 0523/1037] Style changes --- PIL/TiffImagePlugin.py | 16 ++++++---------- mp_compile.py | 6 ++---- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index b0e7c9639..4d16ced48 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -428,8 +428,7 @@ class ImageFileDirectory(collections.MutableMapping): ifd = fp.read(12) if len(ifd) != 12: - warnings.warn("Possibly corrupt EXIF data. " - "Expecting to read 12 bytes but only got %d." + warnings.warn("Possibly corrupt EXIF data. Expecting to read 12 bytes but only got %d." % (len(ifd))) continue @@ -467,9 +466,8 @@ class ImageFileDirectory(collections.MutableMapping): data = ifd[8:8+size] if len(data) != size: - warnings.warn("Possibly corrupt EXIF data. " - "Expecting to read %d bytes but only got %d. " - "Skipping tag %s" % (size, len(data), tag)) + warnings.warn("Possibly corrupt EXIF data. Expecting to read %d bytes but only got %d. Skipping tag %s" + % (size, len(data), tag)) continue self.tagdata[tag] = data @@ -484,8 +482,7 @@ class ImageFileDirectory(collections.MutableMapping): ifd = fp.read(4) if len(ifd) != 4: - warnings.warn("Possibly corrupt EXIF data. " - "Expecting to read 4 bytes but only got %d." + warnings.warn("Possibly corrupt EXIF data. Expecting to read 4 bytes but only got %d." % (len(ifd))) return @@ -704,9 +701,8 @@ class TiffImageFile(ImageFile.ImageFile): if not self.__next: raise EOFError("no more images in TIFF file") if DEBUG: - print("Seeking to frame %s, on frame %s, " - "__next %s, location: %s" % - (frame, self.__frame, self.__next, self.fp.tell())) + print("Seeking to frame %s, on frame %s, __next %s, location: %s" + % (frame, self.__frame, self.__next, self.fp.tell())) # reset python3 buffered io handle in case fp # was passed to libtiff, invalidating the buffer self.fp.tell() diff --git a/mp_compile.py b/mp_compile.py index 078f62476..93ddbe99b 100644 --- a/mp_compile.py +++ b/mp_compile.py @@ -75,10 +75,8 @@ def install(): pool = Pool(2) CCompiler.compile = _mp_compile except Exception as msg: - print("Exception installing mp_compile, proceeding without:" - "%s" % msg) + print("Exception installing mp_compile, proceeding without: %s" % msg) else: - print("Single threaded build, not installing mp_compile:" - "%s processes" % MAX_PROCS) + print("Single threaded build, not installing mp_compile: %s processes" % MAX_PROCS) install() From aba7a340360f2a9bfc9204513f1924aa441748ad Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sun, 28 Dec 2014 21:47:16 +0100 Subject: [PATCH 0524/1037] Fix setting of TIFF ExtraSamples tag. - force cast ExtraSamples to a list. - fix calls to ImagingLibTiffSetField to include array length. --- PIL/TiffImagePlugin.py | 2 ++ encode.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index b0e7c9639..f9f208af4 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -1183,6 +1183,8 @@ def _save(im, fp, filename): atts = {} # bits per sample is a single short in the tiff directory, not a list. atts[BITSPERSAMPLE] = bits[0] + if EXTRASAMPLES in ifd: + atts[EXTRASAMPLES] = list(ifd[EXTRASAMPLES]) # Merge the ones that we have with (optional) more bits from # the original file, e.g x,y resolution so that we can # save(load('')) == original file. diff --git a/encode.c b/encode.c index 6bdb8c71a..0f66e230b 100644 --- a/encode.c +++ b/encode.c @@ -770,7 +770,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) } status = ImagingLibTiffSetField(&encoder->state, (ttag_t) PyInt_AsLong(key), - intav); + len, intav); free(intav); } } else { @@ -782,7 +782,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) } status = ImagingLibTiffSetField(&encoder->state, (ttag_t) PyInt_AsLong(key), - floatav); + len, floatav); free(floatav); } } From 974bcc074be9eb791b9c66fe278f6638ad5e3e06 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Mon, 29 Dec 2014 16:48:01 +0100 Subject: [PATCH 0525/1037] Major rewrite of TIFF ImageFileDirectory. Do not represent scalar tags as 1-element tuples. Keep tag type and count information in TiffTags.TAGS. Normalize data in ImageFileDirectory.__setitem__: wrap and unwrap tuples as needed, convert rationals to floats. (To ensure consistency, make the "tags" attribute private.) Interpret byte data as a series of integers rather than a bytearray (which should only map to the "undefined" type). On Python3, if a str is assigned to an "undefined" tag, encode it as ASCII. Note that a large number of tags have been removed from TiffTags.TAGS because I do not have time to figure out the type and count of each of them. They should be restored before this gets merged in. This obviously breaks backwards compatibility in a lot of ways... --- PIL/TiffImagePlugin.py | 786 +++++++++++++------------------ PIL/TiffTags.py | 373 +++++---------- Tests/test_file_libtiff.py | 6 +- Tests/test_file_tiff.py | 48 +- Tests/test_file_tiff_metadata.py | 40 +- encode.c | 33 +- 6 files changed, 493 insertions(+), 793 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index f9f208af4..99f29865f 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -39,20 +39,23 @@ # See the README file for information on usage and redistribution. # -from __future__ import print_function +from __future__ import division, print_function from PIL import Image, ImageFile from PIL import ImagePalette from PIL import _binary -from PIL._util import isStringType -import warnings -import array -import sys import collections -import itertools -import os +from fractions import Fraction import io +import itertools +from numbers import Number +import os +import struct +import sys +import warnings + +from .TiffTags import TAGS, TYPES, TagInfo __version__ = "1.3.5" DEBUG = False # Needs to be merged with the new logging approach. @@ -67,25 +70,10 @@ MM = b"MM" # big-endian (Motorola style) i8 = _binary.i8 o8 = _binary.o8 -if sys.byteorder == "little": - native_prefix = II -else: - native_prefix = MM - # # -------------------------------------------------------------------- # Read TIFF files -il16 = _binary.i16le -il32 = _binary.i32le -ol16 = _binary.o16le -ol32 = _binary.o32le - -ib16 = _binary.i16be -ib32 = _binary.i32be -ob16 = _binary.o16be -ob32 = _binary.o32be - # a few tag names, just to make the code below a bit more readable IMAGEWIDTH = 256 IMAGELENGTH = 257 @@ -145,74 +133,74 @@ COMPRESSION_INFO_REV = dict([(v, k) for (k, v) in COMPRESSION_INFO.items()]) OPEN_INFO = { # (ByteOrder, PhotoInterpretation, SampleFormat, FillOrder, BitsPerSample, # ExtraSamples) => mode, rawmode - (II, 0, 1, 1, (1,), ()): ("1", "1;I"), - (II, 0, 1, 2, (1,), ()): ("1", "1;IR"), - (II, 0, 1, 1, (8,), ()): ("L", "L;I"), - (II, 0, 1, 2, (8,), ()): ("L", "L;IR"), - (II, 0, 3, 1, (32,), ()): ("F", "F;32F"), - (II, 1, 1, 1, (1,), ()): ("1", "1"), - (II, 1, 1, 1, (4,), ()): ("L", "L;4"), - (II, 1, 1, 2, (1,), ()): ("1", "1;R"), - (II, 1, 1, 1, (8,), ()): ("L", "L"), - (II, 1, 1, 1, (8, 8), (2,)): ("LA", "LA"), - (II, 1, 1, 2, (8,), ()): ("L", "L;R"), - (II, 1, 1, 1, (12,), ()): ("I;16", "I;12"), - (II, 1, 1, 1, (16,), ()): ("I;16", "I;16"), - (II, 1, 2, 1, (16,), ()): ("I;16S", "I;16S"), - (II, 1, 1, 1, (32,), ()): ("I", "I;32N"), - (II, 1, 2, 1, (32,), ()): ("I", "I;32S"), - (II, 1, 3, 1, (32,), ()): ("F", "F;32F"), - (II, 2, 1, 1, (8, 8, 8), ()): ("RGB", "RGB"), - (II, 2, 1, 2, (8, 8, 8), ()): ("RGB", "RGB;R"), - (II, 2, 1, 1, (8, 8, 8, 8), ()): ("RGBA", "RGBA"), # missing ExtraSamples - (II, 2, 1, 1, (8, 8, 8, 8), (0,)): ("RGBX", "RGBX"), - (II, 2, 1, 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), - (II, 2, 1, 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), - (II, 2, 1, 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 - (II, 3, 1, 1, (1,), ()): ("P", "P;1"), - (II, 3, 1, 2, (1,), ()): ("P", "P;1R"), - (II, 3, 1, 1, (2,), ()): ("P", "P;2"), - (II, 3, 1, 2, (2,), ()): ("P", "P;2R"), - (II, 3, 1, 1, (4,), ()): ("P", "P;4"), - (II, 3, 1, 2, (4,), ()): ("P", "P;4R"), - (II, 3, 1, 1, (8,), ()): ("P", "P"), - (II, 3, 1, 1, (8, 8), (2,)): ("PA", "PA"), - (II, 3, 1, 2, (8,), ()): ("P", "P;R"), - (II, 5, 1, 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"), - (II, 6, 1, 1, (8, 8, 8), ()): ("YCbCr", "YCbCr"), - (II, 8, 1, 1, (8, 8, 8), ()): ("LAB", "LAB"), + (II, 0, (1,), 1, (1,), ()): ("1", "1;I"), + (II, 0, (1,), 2, (1,), ()): ("1", "1;IR"), + (II, 0, (1,), 1, (8,), ()): ("L", "L;I"), + (II, 0, (1,), 2, (8,), ()): ("L", "L;IR"), + (II, 0, (3,), 1, (32,), ()): ("F", "F;32F"), + (II, 1, (1,), 1, (1,), ()): ("1", "1"), + (II, 1, (1,), 1, (4,), ()): ("L", "L;4"), + (II, 1, (1,), 2, (1,), ()): ("1", "1;R"), + (II, 1, (1,), 1, (8,), ()): ("L", "L"), + (II, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"), + (II, 1, (1,), 2, (8,), ()): ("L", "L;R"), + (II, 1, (1,), 1, (12,), ()): ("I;16", "I;12"), + (II, 1, (1,), 1, (16,), ()): ("I;16", "I;16"), + (II, 1, (2,), 1, (16,), ()): ("I;16S", "I;16S"), + (II, 1, (1,), 1, (32,), ()): ("I", "I;32N"), + (II, 1, (2,), 1, (32,), ()): ("I", "I;32S"), + (II, 1, (3,), 1, (32,), ()): ("F", "F;32F"), + (II, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"), + (II, 2, (1,), 2, (8, 8, 8), ()): ("RGB", "RGB;R"), + (II, 2, (1,), 1, (8, 8, 8, 8), ()): ("RGBA", "RGBA"), # missing ExtraSamples + (II, 2, (1,), 1, (8, 8, 8, 8), (0,)): ("RGBX", "RGBX"), + (II, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), + (II, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), + (II, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 + (II, 3, (1,), 1, (1,), ()): ("P", "P;1"), + (II, 3, (1,), 2, (1,), ()): ("P", "P;1R"), + (II, 3, (1,), 1, (2,), ()): ("P", "P;2"), + (II, 3, (1,), 2, (2,), ()): ("P", "P;2R"), + (II, 3, (1,), 1, (4,), ()): ("P", "P;4"), + (II, 3, (1,), 2, (4,), ()): ("P", "P;4R"), + (II, 3, (1,), 1, (8,), ()): ("P", "P"), + (II, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"), + (II, 3, (1,), 2, (8,), ()): ("P", "P;R"), + (II, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"), + (II, 6, (1,), 1, (8, 8, 8), ()): ("YCbCr", "YCbCr"), + (II, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"), - (MM, 0, 1, 1, (1,), ()): ("1", "1;I"), - (MM, 0, 1, 2, (1,), ()): ("1", "1;IR"), - (MM, 0, 1, 1, (8,), ()): ("L", "L;I"), - (MM, 0, 1, 2, (8,), ()): ("L", "L;IR"), - (MM, 1, 1, 1, (1,), ()): ("1", "1"), - (MM, 1, 1, 2, (1,), ()): ("1", "1;R"), - (MM, 1, 1, 1, (8,), ()): ("L", "L"), - (MM, 1, 1, 1, (8, 8), (2,)): ("LA", "LA"), - (MM, 1, 1, 2, (8,), ()): ("L", "L;R"), - (MM, 1, 1, 1, (16,), ()): ("I;16B", "I;16B"), - (MM, 1, 2, 1, (16,), ()): ("I;16BS", "I;16BS"), - (MM, 1, 2, 1, (32,), ()): ("I;32BS", "I;32BS"), - (MM, 1, 3, 1, (32,), ()): ("F", "F;32BF"), - (MM, 2, 1, 1, (8, 8, 8), ()): ("RGB", "RGB"), - (MM, 2, 1, 2, (8, 8, 8), ()): ("RGB", "RGB;R"), - (MM, 2, 1, 1, (8, 8, 8, 8), (0,)): ("RGBX", "RGBX"), - (MM, 2, 1, 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), - (MM, 2, 1, 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), - (MM, 2, 1, 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 - (MM, 3, 1, 1, (1,), ()): ("P", "P;1"), - (MM, 3, 1, 2, (1,), ()): ("P", "P;1R"), - (MM, 3, 1, 1, (2,), ()): ("P", "P;2"), - (MM, 3, 1, 2, (2,), ()): ("P", "P;2R"), - (MM, 3, 1, 1, (4,), ()): ("P", "P;4"), - (MM, 3, 1, 2, (4,), ()): ("P", "P;4R"), - (MM, 3, 1, 1, (8,), ()): ("P", "P"), - (MM, 3, 1, 1, (8, 8), (2,)): ("PA", "PA"), - (MM, 3, 1, 2, (8,), ()): ("P", "P;R"), - (MM, 5, 1, 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"), - (MM, 6, 1, 1, (8, 8, 8), ()): ("YCbCr", "YCbCr"), - (MM, 8, 1, 1, (8, 8, 8), ()): ("LAB", "LAB"), + (MM, 0, (1,), 1, (1,), ()): ("1", "1;I"), + (MM, 0, (1,), 2, (1,), ()): ("1", "1;IR"), + (MM, 0, (1,), 1, (8,), ()): ("L", "L;I"), + (MM, 0, (1,), 2, (8,), ()): ("L", "L;IR"), + (MM, 1, (1,), 1, (1,), ()): ("1", "1"), + (MM, 1, (1,), 2, (1,), ()): ("1", "1;R"), + (MM, 1, (1,), 1, (8,), ()): ("L", "L"), + (MM, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"), + (MM, 1, (1,), 2, (8,), ()): ("L", "L;R"), + (MM, 1, (1,), 1, (16,), ()): ("I;16B", "I;16B"), + (MM, 1, (2,), 1, (16,), ()): ("I;16BS", "I;16BS"), + (MM, 1, (2,), 1, (32,), ()): ("I;32BS", "I;32BS"), + (MM, 1, (3,), 1, (32,), ()): ("F", "F;32BF"), + (MM, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"), + (MM, 2, (1,), 2, (8, 8, 8), ()): ("RGB", "RGB;R"), + (MM, 2, (1,), 1, (8, 8, 8, 8), (0,)): ("RGBX", "RGBX"), + (MM, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), + (MM, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), + (MM, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 + (MM, 3, (1,), 1, (1,), ()): ("P", "P;1"), + (MM, 3, (1,), 2, (1,), ()): ("P", "P;1R"), + (MM, 3, (1,), 1, (2,), ()): ("P", "P;2"), + (MM, 3, (1,), 2, (2,), ()): ("P", "P;2R"), + (MM, 3, (1,), 1, (4,), ()): ("P", "P;4"), + (MM, 3, (1,), 2, (4,), ()): ("P", "P;4R"), + (MM, 3, (1,), 1, (8,), ()): ("P", "P"), + (MM, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"), + (MM, 3, (1,), 2, (8,), ()): ("P", "P;R"), + (MM, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"), + (MM, 6, (1,), 1, (8, 8, 8), ()): ("YCbCr", "YCbCr"), + (MM, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"), } @@ -223,248 +211,263 @@ def _accept(prefix): return prefix[:4] in PREFIXES +def _limit_rational(val, max_val): + inv = abs(val) > 1 + f = Fraction.from_float(1 / val if inv else val).limit_denominator(max_val) + n_d = (f.numerator, f.denominator) + return n_d[::-1] if inv else n_d + ## # Wrapper for TIFF IFDs. -class ImageFileDirectory(collections.MutableMapping): - """ This class represents a TIFF tag directory. To speed things - up, we don't decode tags unless they're asked for. +_load_dispatch = {} +_write_dispatch = {} + +class ImageFileDirectory(collections.MutableMapping): + """This class represents a TIFF tag directory. To speed things up, we + don't decode tags unless they're asked for. + + Exposes a dictionary interface of the tags in the directory - Exposes a dictionary interface of the tags in the directory ImageFileDirectory[key] = value value = ImageFileDirectory[key] - Also contains a dictionary of tag types as read from the tiff - image file, 'ImageFileDirectory.tagtype' + Also contains a dictionary of tag types as read from the tiff image file, + 'ImageFileDirectory.tagtype' - - Data Structures: + Data Structures: 'public' * self.tagtype = {} Key: numerical tiff tag number Value: integer corresponding to the data type from `TiffTags.TYPES` 'internal' - * self.tags = {} Key: numerical tiff tag number - Value: Decoded data, Generally a tuple. - * If set from __setval__ -- always a tuple - * Numeric types -- always a tuple - * String type -- not a tuple, returned as string - * Undefined data -- not a tuple, returned as bytes - * Byte -- not a tuple, returned as byte. - * self.tagdata = {} Key: numerical tiff tag number - Value: undecoded byte string from file + * self._tags = {} Key: numerical tiff tag number + Value: decoded data, as tuple for multiple values + * self._tagdata = {} Key: numerical tiff tag number + Value: undecoded byte string from file - - Tags will be found in either self.tags or self.tagdata, but - not both. The union of the two should contain all the tags - from the Tiff image file. External classes shouldn't - reference these unless they're really sure what they're doing. - """ + Tags will be found in either self._tags or self._tagdata, but not + both. The union of the two should contain all the tags from the Tiff + image file. External classes shouldn't reference these unless they're + really sure what they're doing. + """ def __init__(self, prefix=II): """ - :prefix: 'II'|'MM' tiff endianness + :prefix: "II"|"MM" tiff endianness """ - self.prefix = prefix[:2] - if self.prefix == MM: - self.i16, self.i32 = ib16, ib32 - self.o16, self.o32 = ob16, ob32 - elif self.prefix == II: - self.i16, self.i32 = il16, il32 - self.o16, self.o32 = ol16, ol32 + self._prefix = prefix + if prefix == MM: + self._endian = ">" + elif prefix == II: + self._endian = "<" else: - raise SyntaxError("not a TIFF IFD") + raise ValueError("not a TIFF IFD") self.reset() + prefix = property(lambda self: self._prefix) + offset = property(lambda self: self._offset) + + @property + def offset(self): + return self._offset + def reset(self): - #: Tags is an incomplete dictionary of the tags of the image. - #: For a complete dictionary, use the as_dict method. - self.tags = {} - self.tagdata = {} + self._tags = {} + self._tagdata = {} self.tagtype = {} # added 2008-06-05 by Florian Hoech - self.next = None - self.offset = None + self._next = None + self._offset = None def __str__(self): - return str(self.as_dict()) + return str(dict(self)) def as_dict(self): """Return a dictionary of the image's tags.""" - return dict(self.items()) + # FIXME Deprecate: use dict(self) + return dict(self) def named(self): """ Returns the complete tag dictionary, with named tags where possible. """ - from PIL import TiffTags - result = {} - for tag_code, value in self.items(): - tag_name = TiffTags.TAGS.get(tag_code, tag_code) - result[tag_name] = value - return result - - # dictionary API + return {TAGS.get(code, TagInfo()).name: value + for code, value in self.items()} def __len__(self): - return len(self.tagdata) + len(self.tags) + return len(self._tagdata) + len(self._tags) def __getitem__(self, tag): try: - return self.tags[tag] - except KeyError: - data = self.tagdata[tag] # unpack on the fly - type = self.tagtype[tag] - size, handler = self.load_dispatch[type] - self.tags[tag] = data = handler(self, data) - del self.tagdata[tag] - return data - - def getscalar(self, tag, default=None): - try: - value = self[tag] - if len(value) != 1: - if tag == SAMPLEFORMAT: - # work around broken (?) matrox library - # (from Ted Wright, via Bob Klimek) - raise KeyError # use default - raise ValueError("not a scalar") - return value[0] - except KeyError: - if default is None: - raise - return default + return self._tags[tag] + except KeyError: # unpack on the fly + data = self._tagdata[tag] + typ = self.tagtype[tag] + size, handler = self._load_dispatch[typ] + self[tag] = handler(self, data) # check type + del self._tagdata[tag] + return self[tag] def __contains__(self, tag): - return tag in self.tags or tag in self.tagdata + return tag in self._tags or tag in self._tagdata if bytes is str: def has_key(self, tag): return tag in self def __setitem__(self, tag, value): - # tags are tuples for integers - # tags are not tuples for byte, string, and undefined data. - # see load_* - if not isinstance(value, tuple): - value = (value,) - self.tags[tag] = value + basetypes = (Number, bytes, str) + if bytes is str: + basetypes += unicode, + + info = TAGS.get(tag, TagInfo()) + values = [value] if isinstance(value, basetypes) else value + + if tag not in self.tagtype: + try: + self.tagtype[tag] = info.type + except KeyError: + self.tagtype[tag] = 7 + if all(isinstance(v, int) for v in values): + if all(v < 2 ** 16 for v in values): + self.tagtype[tag] = 3 + else: + self.tagtype[tag] = 4 + elif all(isinstance(v, float) for v in values): + self.tagtype[tag] = 12 + else: + if bytes is str: + # Never treat data as binary by default on Python 2. + self.tagtype[tag] = 2 + else: + if all(isinstance(v, str) for v in values): + self.tagtype[tag] = 2 + + if self.tagtype[tag] == 7 and bytes is not str: + values = [value.encode("ascii") if isinstance(value, str) else value + for value in values] + values = tuple(info.cvt_enum(value) for value in values) + if info.length == 1: + self._tags[tag], = values + else: + self._tags[tag] = values def __delitem__(self, tag): - self.tags.pop(tag, self.tagdata.pop(tag, None)) + self._tags.pop(tag, None) + self._tagdata.pop(tag, None) def __iter__(self): - return itertools.chain(self.tags.__iter__(), self.tagdata.__iter__()) + return itertools.chain(list(self._tags), list(self._tagdata)) - def items(self): - keys = list(self.__iter__()) - values = [self[key] for key in keys] - return zip(keys, values) + def unpack(self, fmt, data): + return struct.unpack(self._endian + fmt, data) - # load primitives + def pack(self, fmt, *values): + return struct.pack(self._endian + fmt, *values) - load_dispatch = {} + def _register_loader(idx, size): + def decorator(func): + from PIL.TiffTags import TYPES + if func.__name__.startswith("load_"): + TYPES[idx] = func.__name__[5:].replace("_", " ") + _load_dispatch[idx] = size, func + return func + return decorator - def load_byte(self, data): - return data - load_dispatch[1] = (1, load_byte) + def _register_writer(idx): + def decorator(func): + _write_dispatch[idx] = func + return func + return decorator + def _register_basic(idx_fmt_name): + from PIL.TiffTags import TYPES + idx, fmt, name = idx_fmt_name + TYPES[idx] = name + size = struct.calcsize("=" + fmt) + _load_dispatch[idx] = size, lambda self, data: ( + self.unpack("{}{}".format(len(data) // size, fmt), data)) + _write_dispatch[idx] = lambda self, *values: ( + b"".join(self.pack(fmt, value) for value in values)) + + list(map(_register_basic, + [(1, "B", "byte"), (3, "H", "short"), (4, "L", "long"), + (6, "b", "signed byte"), (8, "h", "signed short"), + (9, "l", "signed long"), (11, "f", "float"), (12, "d", "double")])) + + @_register_loader(2, 1) def load_string(self, data): - if data[-1:] == b'\0': + if data.endswith(b"\0"): data = data[:-1] - return data.decode('latin-1', 'replace') - load_dispatch[2] = (1, load_string) + return data.decode("latin-1", "replace") - def load_short(self, data): - l = [] - for i in range(0, len(data), 2): - l.append(self.i16(data, i)) - return tuple(l) - load_dispatch[3] = (2, load_short) - - def load_long(self, data): - l = [] - for i in range(0, len(data), 4): - l.append(self.i32(data, i)) - return tuple(l) - load_dispatch[4] = (4, load_long) + @_register_writer(2) + def write_string(self, value): + # remerge of https://github.com/python-pillow/Pillow/pull/1416 + if sys.version_info[0] == 2: + value = value.decode('ascii', 'replace') + return b"" + value.encode('ascii', 'replace') + b"\0" + @_register_loader(5, 8) def load_rational(self, data): - l = [] - for i in range(0, len(data), 8): - l.append((self.i32(data, i), self.i32(data, i+4))) - return tuple(l) - load_dispatch[5] = (8, load_rational) + vals = self.unpack("{}L".format(len(data) // 4), data) + return tuple(num / denom for num, denom in zip(vals[::2], vals[1::2])) - def load_float(self, data): - a = array.array("f", data) - if self.prefix != native_prefix: - a.byteswap() - return tuple(a) - load_dispatch[11] = (4, load_float) - - def load_double(self, data): - a = array.array("d", data) - if self.prefix != native_prefix: - a.byteswap() - return tuple(a) - load_dispatch[12] = (8, load_double) + @_register_writer(5) + def write_rational(self, *values): + return b"".join(self.pack("2L", *_limit_rational(frac, 2 ** 31)) + for frac in values) + @_register_loader(7, 1) def load_undefined(self, data): - # Untyped data return data - load_dispatch[7] = (1, load_undefined) + + @_register_writer(7) + def write_undefined(self, value): + return value + + @_register_loader(10, 8) + def load_signed_rational(self, data): + vals = self.unpack("{}l".format(len(data) // 4), data) + return tuple(num / denom for num, denom in zip(vals[::2], vals[1::2])) + + @_register_writer(10) + def write_signed_rational(self, *values): + return b"".join(self.pack("2L", *_limit_rational(frac, 2 ** 30)) + for frac in values) def load(self, fp): - # load tag dictionary self.reset() - self.offset = fp.tell() - - i16 = self.i16 - i32 = self.i32 - - for i in range(i16(fp.read(2))): - - ifd = fp.read(12) - if len(ifd) != 12: - warnings.warn("Possibly corrupt EXIF data. " - "Expecting to read 12 bytes but only got %d." - % (len(ifd))) - continue - - tag, typ = i16(ifd), i16(ifd, 2) + self._offset = fp.tell() + for i in range(self.unpack("H", fp.read(2))[0]): + tag, typ, count, data = self.unpack("HHL4s", fp.read(12)) if DEBUG: - from PIL import TiffTags - tagname = TiffTags.TAGS.get(tag, "unknown") - typname = TiffTags.TYPES.get(typ, "unknown") - print("tag: %s (%d)" % (tagname, tag), end=' ') - print("- type: %s (%d)" % (typname, typ), end=' ') + tagname = TAGS.get(tag, TagInfo()).name + typname = TYPES.get(typ, "unknown") + print("tag: %s (%d) - type: %s (%d)" % + (tagname, tag, typname, typ), end=" ") try: - dispatch = self.load_dispatch[typ] + unit_size, handler = self._load_dispatch[typ] except KeyError: if DEBUG: print("- unsupported type", typ) continue # ignore unsupported type - - size, handler = dispatch - - size = size * i32(ifd, 4) - - # Get and expand tag value + size = count * unit_size if size > 4: here = fp.tell() + offset, = self.unpack("L", data) if DEBUG: - print("Tag Location: %s" % here) - fp.seek(i32(ifd, 8)) - if DEBUG: - print("Data Location: %s" % fp.tell()) + print("Tag Location: %s - Data Location: %s" % + (here, offset), end=" ") + fp.seek(offset) data = ImageFile._safe_read(fp, size) fp.seek(here) else: - data = ifd[8:8+size] + data = data[:size] if len(data) != size: warnings.warn("Possibly corrupt EXIF data. " @@ -472,160 +475,89 @@ class ImageFileDirectory(collections.MutableMapping): "Skipping tag %s" % (size, len(data), tag)) continue - self.tagdata[tag] = data + self._tagdata[tag] = data self.tagtype[tag] = typ if DEBUG: - if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, - ICCPROFILE, XMP): + if size > 32: print("- value: " % size) else: print("- value:", self[tag]) - ifd = fp.read(4) - if len(ifd) != 4: - warnings.warn("Possibly corrupt EXIF data. " - "Expecting to read 4 bytes but only got %d." - % (len(ifd))) - return - - self.next = i32(ifd) - - # save primitives + self.next, = self.unpack("L", fp.read(4)) def save(self, fp): - o16 = self.o16 - o32 = self.o32 - - fp.write(o16(len(self.tags))) - - # always write in ascending tag order - tags = sorted(self.tags.items()) - - directory = [] - append = directory.append - - offset = fp.tell() + len(self.tags) * 12 + 4 + # FIXME What about tagdata? + fp.write(self.pack("H", len(self._tags))) + entries = [] + offset = fp.tell() + len(self._tags) * 12 + 4 stripoffsets = None # pass 1: convert tags to binary format - for tag, value in tags: - - typ = None - - if tag in self.tagtype: - typ = self.tagtype[tag] - + # always write tags in ascending order + for tag, value in sorted(self._tags.items()): + if tag == STRIPOFFSETS: + stripoffsets = len(entries) + typ = self.tagtype.get(tag) if DEBUG: print("Tag %s, Type: %s, Value: %s" % (tag, typ, value)) - - if typ == 1: - # byte data - if isinstance(value, tuple): - data = value = value[-1] - else: - data = value - elif typ == 7: - # untyped data - data = value = b"".join(value) - elif typ in (11, 12): - # float value - tmap = {11: 'f', 12: 'd'} - if not isinstance(value, tuple): - value = (value,) - a = array.array(tmap[typ], value) - if self.prefix != native_prefix: - a.byteswap() - data = a.tostring() - elif isStringType(value[0]): - # string data - if isinstance(value, tuple): - value = value[-1] - typ = 2 - # was b'\0'.join(str), which led to \x00a\x00b sorts - # of strings which I don't see in in the wild tiffs - # and doesn't match the tiff spec: 8-bit byte that - # contains a 7-bit ASCII code; the last byte must be - # NUL (binary zero). Also, I don't think this was well - # exercised before. - if sys.version_info[0] == 2: - value = value.decode('ascii', 'replace') - data = value = b"" + value.encode('ascii', 'replace') + b"\0" - else: - # integer data - if tag == STRIPOFFSETS: - stripoffsets = len(directory) - typ = 4 # to avoid catch-22 - elif tag in (X_RESOLUTION, Y_RESOLUTION) or typ == 5: - # identify rational data fields - typ = 5 - if isinstance(value[0], tuple): - # long name for flatten - value = tuple(itertools.chain.from_iterable(value)) - elif not typ: - typ = 3 - for v in value: - if v >= 65536: - typ = 4 - if typ == 3: - data = b"".join(map(o16, value)) - else: - data = b"".join(map(o32, value)) - + values = value if isinstance(value, tuple) else (value,) + data = self._write_dispatch[typ](self, *values) if DEBUG: - from PIL import TiffTags - tagname = TiffTags.TAGS.get(tag, "unknown") - typname = TiffTags.TYPES.get(typ, "unknown") - print("save: %s (%d)" % (tagname, tag), end=' ') - print("- type: %s (%d)" % (typname, typ), end=' ') - if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, - ICCPROFILE, XMP): - size = len(data) - print("- value: " % size) + tagname = TAGS.get(tag, TagInfo()).name + typname = TYPES.get(typ, "unknown") + print("save: %s (%d) - type: %s (%d)" % + (tagname, tag, typname, typ), end=" ") + if len(data) >= 16: + print("- value: " % len(data)) else: - print("- value:", value) + print("- value:", values) - # figure out if data fits into the directory - if len(data) == 4: - append((tag, typ, len(value), data, b"")) - elif len(data) < 4: - append((tag, typ, len(value), data + (4-len(data))*b"\0", b"")) + # count is sum of lengths for string and arbitrary data + count = len(data) if typ in [2, 7] else len(values) + # figure out if data fits into the entry + if len(data) <= 4: + entries.append((tag, typ, count, data.ljust(4, b"\0"), b"")) else: - count = len(value) - if typ == 5: - count = count // 2 # adjust for rational data field - - append((tag, typ, count, o32(offset), data)) - offset += len(data) - if offset & 1: - offset += 1 # word padding + entries.append((tag, typ, count, self.pack("L", offset), data)) + offset += (len(data) + 1) // 2 * 2 # pad to word # update strip offset data to point beyond auxiliary data if stripoffsets is not None: - tag, typ, count, value, data = directory[stripoffsets] - assert not data, "multistrip support not yet implemented" - value = o32(self.i32(value) + offset) - directory[stripoffsets] = tag, typ, count, value, data + tag, typ, count, value, data = entries[stripoffsets] + if data: + raise NotImplementedError( + "multistrip support not yet implemented") + value = self.pack("L", self.unpack("L", value)[0] + offset) + entries[stripoffsets] = tag, typ, count, value, data - # pass 2: write directory to file - for tag, typ, count, value, data in directory: + # pass 2: write entries to file + for tag, typ, count, value, data in entries: if DEBUG > 1: print(tag, typ, count, repr(value), repr(data)) - fp.write(o16(tag) + o16(typ) + o32(count) + value) + fp.write(self.pack("HHL4s", tag, typ, count, value)) # -- overwrite here for multi-page -- - fp.write(b"\0\0\0\0") # end of directory + fp.write(b"\0\0\0\0") # end of entries # pass 3: write auxiliary data to file - for tag, typ, count, value, data in directory: + for tag, typ, count, value, data in entries: fp.write(data) if len(data) & 1: fp.write(b"\0") return offset +ImageFileDirectory._load_dispatch = _load_dispatch +ImageFileDirectory._write_dispatch = _write_dispatch +for idx, name in TYPES.items(): + name = name.replace(" ", "_") + setattr(ImageFileDirectory, "load_" + name, _load_dispatch[idx][1]) + setattr(ImageFileDirectory, "write_" + name, _write_dispatch[idx]) +del _load_dispatch, _write_dispatch, idx, name + ## # Image plugin for TIFF files. @@ -648,7 +580,7 @@ class TiffImageFile(ImageFile.ImageFile): self.tag = self.ifd = ImageFileDirectory(ifh[:2]) # setup frame pointers - self.__first = self.__next = self.ifd.i32(ifh, 4) + self.__first, = self.__next, = self.ifd.unpack("L", ifh[4:]) self.__frame = -1 self.__fp = self.fp self._frame_pos = [] @@ -739,7 +671,8 @@ class TiffImageFile(ImageFile.ImageFile): args = rawmode, "" if JPEGTABLES in self.tag: # Hack to handle abbreviated JPEG headers - self.tile_prefix = self.tag[JPEGTABLES] + # FIXME This will fail with more than one value + self.tile_prefix, = self.tag[JPEGTABLES] elif compression == "packbits": args = rawmode elif compression == "tiff_lzw": @@ -828,17 +761,15 @@ class TiffImageFile(ImageFile.ImageFile): if 0xBC01 in self.tag: raise IOError("Windows Media Photo files not yet supported") - getscalar = self.tag.getscalar - # extract relevant tags - self._compression = COMPRESSION_INFO[getscalar(COMPRESSION, 1)] - self._planar_configuration = getscalar(PLANAR_CONFIGURATION, 1) + self._compression = COMPRESSION_INFO[self.tag.get(COMPRESSION, 1)] + self._planar_configuration = self.tag.get(PLANAR_CONFIGURATION, 1) # photometric is a required tag, but not everyone is reading # the specification - photo = getscalar(PHOTOMETRIC_INTERPRETATION, 0) + photo = self.tag.get(PHOTOMETRIC_INTERPRETATION, 0) - fillorder = getscalar(FILLORDER, 1) + fillorder = self.tag.get(FILLORDER, 1) if DEBUG: print("*** Summary ***") @@ -848,14 +779,14 @@ class TiffImageFile(ImageFile.ImageFile): print("- fill_order:", fillorder) # size - xsize = getscalar(IMAGEWIDTH) - ysize = getscalar(IMAGELENGTH) + xsize = self.tag.get(IMAGEWIDTH) + ysize = self.tag.get(IMAGELENGTH) self.size = xsize, ysize if DEBUG: print("- size:", self.size) - format = getscalar(SAMPLEFORMAT, 1) + format = self.tag.get(SAMPLEFORMAT, (1,)) # mode: check photometric interpretation and bits per pixel key = ( @@ -878,8 +809,8 @@ class TiffImageFile(ImageFile.ImageFile): self.info["compression"] = self._compression - xres = getscalar(X_RESOLUTION, (1, 1)) - yres = getscalar(Y_RESOLUTION, (1, 1)) + xres = self.tag.get(X_RESOLUTION, (1, 1)) + yres = self.tag.get(Y_RESOLUTION, (1, 1)) if xres and not isinstance(xres, tuple): xres = (xres, 1.) @@ -888,7 +819,7 @@ class TiffImageFile(ImageFile.ImageFile): if xres and yres: xres = xres[0] / (xres[1] or 1) yres = yres[0] / (yres[1] or 1) - resunit = getscalar(RESOLUTION_UNIT, 1) + resunit = self.tag.get(RESOLUTION_UNIT, 1) if resunit == 2: # dots per inch self.info["dpi"] = xres, yres elif resunit == 3: # dots per centimeter. convert to dpi @@ -902,7 +833,7 @@ class TiffImageFile(ImageFile.ImageFile): if STRIPOFFSETS in self.tag: # striped image offsets = self.tag[STRIPOFFSETS] - h = getscalar(ROWSPERSTRIP, ysize) + h = self.tag.get(ROWSPERSTRIP, ysize) w = self.size[0] if READ_LIBTIFF or self._compression in ["tiff_ccitt", "group3", "group4", "tiff_jpeg", @@ -991,8 +922,8 @@ class TiffImageFile(ImageFile.ImageFile): a = None elif TILEOFFSETS in self.tag: # tiled image - w = getscalar(322) - h = getscalar(323) + w = self.tag.get(322) + h = self.tag.get(323) a = None for o in self.tag[TILEOFFSETS]: if not a: @@ -1053,17 +984,6 @@ SAVE_INFO = { } -def _cvt_res(value): - # convert value to TIFF rational number -- (numerator, denominator) - if isinstance(value, collections.Sequence): - assert(len(value) % 2 == 0) - return value - if isinstance(value, int): - return (value, 1) - value = float(value) - return (int(value * 65536), 65536) - - def _save(im, fp, filename): try: @@ -1085,7 +1005,7 @@ def _save(im, fp, filename): if not libtiff and fp.tell() == 0: # tiff header (write via IFD to get everything right) # PIL always starts the first IFD at offset 8 - fp.write(ifd.prefix + ifd.o16(42) + ifd.o32(8)) + fp.write(ifd.prefix + ifd.pack("HL", 42, 8)) ifd[IMAGEWIDTH] = im.size[0] ifd[IMAGELENGTH] = im.size[1] @@ -1093,9 +1013,8 @@ def _save(im, fp, filename): # write any arbitrary tags passed in as an ImageFileDirectory info = im.encoderinfo.get("tiffinfo", {}) if DEBUG: - print("Tiffinfo Keys: %s" % info.keys) - keys = list(info.keys()) - for key in keys: + print("Tiffinfo Keys: %s" % list(info)) + for key in info: ifd[key] = info.get(key) try: ifd.tagtype[key] = info.tagtype[key] @@ -1117,31 +1036,29 @@ def _save(im, fp, filename): if "icc_profile" in im.info: ifd[ICCPROFILE] = im.info["icc_profile"] - for key, name, cvt in [ - (IMAGEDESCRIPTION, "description", lambda x: x), - (X_RESOLUTION, "resolution", _cvt_res), - (Y_RESOLUTION, "resolution", _cvt_res), - (X_RESOLUTION, "x_resolution", _cvt_res), - (Y_RESOLUTION, "y_resolution", _cvt_res), - (RESOLUTION_UNIT, "resolution_unit", - lambda x: {"inch": 2, "cm": 3, "centimeter": 3}.get(x, 1)), - (SOFTWARE, "software", lambda x: x), - (DATE_TIME, "date_time", lambda x: x), - (ARTIST, "artist", lambda x: x), - (COPYRIGHT, "copyright", lambda x: x)]: + for key, name in [(IMAGEDESCRIPTION, "description"), + (X_RESOLUTION, "resolution"), + (Y_RESOLUTION, "resolution"), + (X_RESOLUTION, "x_resolution"), + (Y_RESOLUTION, "y_resolution"), + (RESOLUTION_UNIT, "resolution_unit"), + (SOFTWARE, "software"), + (DATE_TIME, "date_time"), + (ARTIST, "artist"), + (COPYRIGHT, "copyright")]: name_with_spaces = name.replace("_", " ") if "_" in name and name_with_spaces in im.encoderinfo: warnings.warn("%r is deprecated; use %r instead" % (name_with_spaces, name), DeprecationWarning) - ifd[key] = cvt(im.encoderinfo[name.replace("_", " ")]) + ifd[key] = im.encoderinfo[name.replace("_", " ")] if name in im.encoderinfo: - ifd[key] = cvt(im.encoderinfo[name]) + ifd[key] = im.encoderinfo[name] dpi = im.encoderinfo.get("dpi") if dpi: ifd[RESOLUTION_UNIT] = 2 - ifd[X_RESOLUTION] = _cvt_res(dpi[0]) - ifd[Y_RESOLUTION] = _cvt_res(dpi[1]) + ifd[X_RESOLUTION] = dpi[0] + ifd[Y_RESOLUTION] = dpi[1] if bits != (1,): ifd[BITSPERSAMPLE] = bits @@ -1169,7 +1086,7 @@ def _save(im, fp, filename): if libtiff: if DEBUG: print("Saving using libtiff encoder") - print(ifd.items()) + print("Items: %s" % sorted(ifd.items())) _fp = 0 if hasattr(fp, "fileno"): try: @@ -1183,52 +1100,19 @@ def _save(im, fp, filename): atts = {} # bits per sample is a single short in the tiff directory, not a list. atts[BITSPERSAMPLE] = bits[0] - if EXTRASAMPLES in ifd: - atts[EXTRASAMPLES] = list(ifd[EXTRASAMPLES]) # Merge the ones that we have with (optional) more bits from # the original file, e.g x,y resolution so that we can # save(load('')) == original file. for k, v in itertools.chain(ifd.items(), getattr(im, 'ifd', {}).items()): if k not in atts and k not in blocklist: - if type(v[0]) == tuple and len(v) > 1: - # A tuple of more than one rational tuples - # flatten to floats, - # following tiffcp.c->cpTag->TIFF_RATIONAL - atts[k] = [float(elt[0])/float(elt[1]) for elt in v] - continue - if type(v[0]) == tuple and len(v) == 1: - # A tuple of one rational tuples - # flatten to floats, - # following tiffcp.c->cpTag->TIFF_RATIONAL - atts[k] = float(v[0][0])/float(v[0][1]) - continue - if (type(v) == tuple and - (len(v) > 2 or - (len(v) == 2 and v[1] == 0))): - # List of ints? - # Avoid divide by zero in next if-clause - if type(v[0]) in (int, float): - atts[k] = list(v) - continue - if type(v) == tuple and len(v) == 2: - # one rational tuple - # flatten to float, - # following tiffcp.c->cpTag->TIFF_RATIONAL - atts[k] = float(v[0])/float(v[1]) - continue - if type(v) == tuple and len(v) == 1: - v = v[0] - # drop through - if isStringType(v): - atts[k] = bytes(v.encode('ascii', 'replace')) + b"\0" - continue + if isinstance(v, unicode if bytes is str else str): + atts[k] = v.encode('ascii', 'replace') + b"\0" else: - # int or similar atts[k] = v if DEBUG: - print(atts) + print("Converted items: %s" % sorted(atts.items())) # libtiff always expects the bytes in native order. # we're storing image byte order. So, if the rawmode diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py index d15aa7ebe..422891aeb 100644 --- a/PIL/TiffTags.py +++ b/PIL/TiffTags.py @@ -17,291 +17,132 @@ # well-known TIFF tags. ## +from collections import namedtuple + +class TagInfo(namedtuple("_TagInfo", "value name type length enum")): + __slots__ = [] + + def __new__(cls, value=None, name="unknown", type=4, length=0, enum=None): + return super(TagInfo, cls).__new__( + cls, value, name, type, length, enum or {}) + + def cvt_enum(self, value): + return self.enum.get(value, value) + ## -# Map tag numbers (or tag number, tag value tuples) to tag names. +# Map tag numbers to tag info. TAGS = { - 254: "NewSubfileType", - 255: "SubfileType", - 256: "ImageWidth", - 257: "ImageLength", - 258: "BitsPerSample", + 254: ("NewSubfileType", 4, 1), + 255: ("SubfileType", 3, 1), + 256: ("ImageWidth", 4, 1), + 257: ("ImageLength", 4, 1), + 258: ("BitsPerSample", 3, 0), + 259: ("Compression", 3, 1, + {"Uncompressed": 1, "CCITT 1d": 2, "Group 3 Fax": 3, "Group 4 Fax": 4, + "LZW": 5, "JPEG": 6, "PackBits": 32773}), - 259: "Compression", - (259, 1): "Uncompressed", - (259, 2): "CCITT 1d", - (259, 3): "Group 3 Fax", - (259, 4): "Group 4 Fax", - (259, 5): "LZW", - (259, 6): "JPEG", - (259, 32773): "PackBits", + 262: ("PhotometricInterpretation", 3, 1, + {"WhiteIsZero": 0, "BlackIsZero": 1, "RGB": 2, "RBG Palette": 3, + "Transparency Mask": 4, "CMYK": 5, "YCbCr": 6, "CieLAB": 8, + "CFA": 32803, # TIFF/EP, Adobe DNG + "LinearRaw": 32892}), # Adobe DNG + 263: ("Thresholding", 3, 1), + 264: ("CellWidth", 3, 1), + 265: ("CellHeight", 3, 1), + 266: ("FillOrder", 3, 1), + 269: ("DocumentName", 2, 1), - 262: "PhotometricInterpretation", - (262, 0): "WhiteIsZero", - (262, 1): "BlackIsZero", - (262, 2): "RGB", - (262, 3): "RGB Palette", - (262, 4): "Transparency Mask", - (262, 5): "CMYK", - (262, 6): "YCbCr", - (262, 8): "CieLAB", - (262, 32803): "CFA", # TIFF/EP, Adobe DNG - (262, 32892): "LinearRaw", # Adobe DNG + 270: ("ImageDescription", 2, 1), + 271: ("Make", 2, 1), + 272: ("Model", 2, 1), + 273: ("StripOffsets", 4, 0), + 274: ("Orientation", 3, 1), + 277: ("SamplesPerPixel", 3, 1), + 278: ("RowsPerStrip", 4, 1), + 279: ("StripByteCounts", 4, 0), - 263: "Thresholding", - 264: "CellWidth", - 265: "CellHeight", - 266: "FillOrder", - 269: "DocumentName", + 280: ("MinSampleValue", 4, 0), + 281: ("MaxSampleValue", 3, 0), + 282: ("XResolution", 5, 1), + 283: ("YResolution", 5, 1), + 284: ("PlanarConfiguration", 3, 1, {"Contigous": 1, "Separate": 2}), + 285: ("PageName", 2, 1), + 286: ("XPosition", 5, 1), + 287: ("YPosition", 5, 1), + 288: ("FreeOffsets", 4, 1), + 289: ("FreeByteCounts", 4, 1), - 270: "ImageDescription", - 271: "Make", - 272: "Model", - 273: "StripOffsets", - 274: "Orientation", - 277: "SamplesPerPixel", - 278: "RowsPerStrip", - 279: "StripByteCounts", + 290: ("GrayResponseUnit", 3, 1), + 291: ("GrayResponseCurve", 3, 0), + 292: ("T4Options", 4, 1), + 293: ("T6Options", 4, 1), + 296: ("ResolutionUnit", 3, 1, {"inch": 1, "cm": 2}), + 297: ("PageNumber", 3, 2), - 280: "MinSampleValue", - 281: "MaxSampleValue", - 282: "XResolution", - 283: "YResolution", - 284: "PlanarConfiguration", - (284, 1): "Contigous", - (284, 2): "Separate", + 301: ("TransferFunction", 3, 0), + 305: ("Software", 2, 1), + 306: ("DateTime", 2, 1), - 285: "PageName", - 286: "XPosition", - 287: "YPosition", - 288: "FreeOffsets", - 289: "FreeByteCounts", + 315: ("Artist", 2, 1), + 316: ("HostComputer", 2, 1), + 317: ("Predictor", 3, 1), + 318: ("WhitePoint", 5, 2), + 319: ("PrimaryChromaticies", 3, 6), - 290: "GrayResponseUnit", - 291: "GrayResponseCurve", - 292: "T4Options", - 293: "T6Options", - 296: "ResolutionUnit", - 297: "PageNumber", + 320: ("ColorMap", 3, 0), + 321: ("HalftoneHints", 3, 2), + 322: ("TileWidth", 4, 1), + 323: ("TileLength", 4, 1), + 324: ("TileOffsets", 4, 0), + 325: ("TileByteCounts", 4, 0), - 301: "TransferFunction", - 305: "Software", - 306: "DateTime", + 332: ("InkSet", 3, 1), + 333: ("InkNames", 2, 1), + 334: ("NumberOfInks", 3, 1), + 336: ("DotRange", 3, 0), + 337: ("TargetPrinter", 2, 1), + 338: ("ExtraSamples", 1, 0), + 339: ("SampleFormat", 3, 0), - 315: "Artist", - 316: "HostComputer", - 317: "Predictor", - 318: "WhitePoint", - 319: "PrimaryChromaticies", - - 320: "ColorMap", - 321: "HalftoneHints", - 322: "TileWidth", - 323: "TileLength", - 324: "TileOffsets", - 325: "TileByteCounts", - - 332: "InkSet", - 333: "InkNames", - 334: "NumberOfInks", - 336: "DotRange", - 337: "TargetPrinter", - 338: "ExtraSamples", - 339: "SampleFormat", - - 340: "SMinSampleValue", - 341: "SMaxSampleValue", - 342: "TransferRange", - - 347: "JPEGTables", + 340: ("SMinSampleValue", 12, 0), + 341: ("SMaxSampleValue", 12, 0), + 342: ("TransferRange", 3, 6), # obsolete JPEG tags - 512: "JPEGProc", - 513: "JPEGInterchangeFormat", - 514: "JPEGInterchangeFormatLength", - 515: "JPEGRestartInterval", - 517: "JPEGLosslessPredictors", - 518: "JPEGPointTransforms", - 519: "JPEGQTables", - 520: "JPEGDCTables", - 521: "JPEGACTables", + 512: ("JPEGProc", 3, 1), + 513: ("JPEGInterchangeFormat", 4, 1), + 514: ("JPEGInterchangeFormatLength", 4, 1), + 515: ("JPEGRestartInterval", 3, 1), + 517: ("JPEGLosslessPredictors", 3, 0), + 518: ("JPEGPointTransforms", 3, 0), + 519: ("JPEGQTables", 4, 0), + 520: ("JPEGDCTables", 4, 0), + 521: ("JPEGACTables", 4, 0), - 529: "YCbCrCoefficients", - 530: "YCbCrSubSampling", - 531: "YCbCrPositioning", - 532: "ReferenceBlackWhite", + 529: ("YCbCrCoefficients", 5, 3), + 530: ("YCbCrSubSampling", 3, 2), + 531: ("YCbCrPositioning", 3, 1), + 532: ("ReferenceBlackWhite", 4, 0), - # XMP - 700: "XMP", + 33432: ("Copyright", 2, 1), - 33432: "Copyright", - - # various extensions (should check specs for "official" names) - 33723: "IptcNaaInfo", - 34377: "PhotoshopInfo", - - # Exif IFD - 34665: "ExifIFD", - - # ICC Profile - 34675: "ICCProfile", - - # Additional Exif Info - 33434: "ExposureTime", - 33437: "FNumber", - 34850: "ExposureProgram", - 34852: "SpectralSensitivity", - 34853: "GPSInfoIFD", - 34855: "ISOSpeedRatings", - 34856: "OECF", - 34864: "SensitivityType", - 34865: "StandardOutputSensitivity", - 34866: "RecommendedExposureIndex", - 34867: "ISOSpeed", - 34868: "ISOSpeedLatitudeyyy", - 34869: "ISOSpeedLatitudezzz", - 36864: "ExifVersion", - 36867: "DateTimeOriginal", - 36868: "DateTImeDigitized", - 37121: "ComponentsConfiguration", - 37122: "CompressedBitsPerPixel", - 37377: "ShutterSpeedValue", - 37378: "ApertureValue", - 37379: "BrightnessValue", - 37380: "ExposureBiasValue", - 37381: "MaxApertureValue", - 37382: "SubjectDistance", - 37383: "MeteringMode", - 37384: "LightSource", - 37385: "Flash", - 37386: "FocalLength", - 37396: "SubjectArea", - 37500: "MakerNote", - 37510: "UserComment", - 37520: "SubSec", - 37521: "SubSecTimeOriginal", - 37522: "SubsecTimeDigitized", - 40960: "FlashPixVersion", - 40961: "ColorSpace", - 40962: "PixelXDimension", - 40963: "PixelYDimension", - 40964: "RelatedSoundFile", - 40965: "InteroperabilityIFD", - 41483: "FlashEnergy", - 41484: "SpatialFrequencyResponse", - 41486: "FocalPlaneXResolution", - 41487: "FocalPlaneYResolution", - 41488: "FocalPlaneResolutionUnit", - 41492: "SubjectLocation", - 41493: "ExposureIndex", - 41495: "SensingMethod", - 41728: "FileSource", - 41729: "SceneType", - 41730: "CFAPattern", - 41985: "CustomRendered", - 41986: "ExposureMode", - 41987: "WhiteBalance", - 41988: "DigitalZoomRatio", - 41989: "FocalLengthIn35mmFilm", - 41990: "SceneCaptureType", - 41991: "GainControl", - 41992: "Contrast", - 41993: "Saturation", - 41994: "Sharpness", - 41995: "DeviceSettingDescription", - 41996: "SubjectDistanceRange", - 42016: "ImageUniqueID", - 42032: "CameraOwnerName", - 42033: "BodySerialNumber", - 42034: "LensSpecification", - 42035: "LensMake", - 42036: "LensModel", - 42037: "LensSerialNumber", - 42240: "Gamma", - - # MP Info - 45056: "MPFVersion", - 45057: "NumberOfImages", - 45058: "MPEntry", - 45059: "ImageUIDList", - 45060: "TotalFrames", - 45313: "MPIndividualNum", - 45569: "PanOrientation", - 45570: "PanOverlap_H", - 45571: "PanOverlap_V", - 45572: "BaseViewpointNum", - 45573: "ConvergenceAngle", - 45574: "BaselineLength", - 45575: "VerticalDivergence", - 45576: "AxisDistance_X", - 45577: "AxisDistance_Y", - 45578: "AxisDistance_Z", - 45579: "YawAngle", - 45580: "PitchAngle", - 45581: "RollAngle", - - # Adobe DNG - 50706: "DNGVersion", - 50707: "DNGBackwardVersion", - 50708: "UniqueCameraModel", - 50709: "LocalizedCameraModel", - 50710: "CFAPlaneColor", - 50711: "CFALayout", - 50712: "LinearizationTable", - 50713: "BlackLevelRepeatDim", - 50714: "BlackLevel", - 50715: "BlackLevelDeltaH", - 50716: "BlackLevelDeltaV", - 50717: "WhiteLevel", - 50718: "DefaultScale", - 50719: "DefaultCropOrigin", - 50720: "DefaultCropSize", - 50778: "CalibrationIlluminant1", - 50779: "CalibrationIlluminant2", - 50721: "ColorMatrix1", - 50722: "ColorMatrix2", - 50723: "CameraCalibration1", - 50724: "CameraCalibration2", - 50725: "ReductionMatrix1", - 50726: "ReductionMatrix2", - 50727: "AnalogBalance", - 50728: "AsShotNeutral", - 50729: "AsShotWhiteXY", - 50730: "BaselineExposure", - 50731: "BaselineNoise", - 50732: "BaselineSharpness", - 50733: "BayerGreenSplit", - 50734: "LinearResponseLimit", - 50735: "CameraSerialNumber", - 50736: "LensInfo", - 50737: "ChromaBlurRadius", - 50738: "AntiAliasStrength", - 50740: "DNGPrivateData", - 50741: "MakerNoteSafety", - 50780: "BestQualityScale", - - # ImageJ - 50838: "ImageJMetaDataByteCounts", # private tag registered with Adobe - 50839: "ImageJMetaData", # private tag registered with Adobe + # FIXME add more tags here + 50741: ("MakerNoteSafety", 3, 1, {0: "Unsafe", 1: "Safe"}), + 50780: ("BestQualityScale", 5, 1), + # private tags registered with Adobe + 50838: ("ImageJMetaDataByteCounts", 4, 1), + 50839: ("ImageJMetaData", 7, 1) } + +for k, v in TAGS.items(): + TAGS[k] = TagInfo(k, *v) +del k, v + + ## -# Map type numbers to type names. +# Map type numbers to type names -- defined in ImageFileDirectory. -TYPES = { - - 1: "byte", - 2: "ascii", - 3: "short", - 4: "long", - 5: "rational", - 6: "signed byte", - 7: "undefined", - 8: "signed short", - 9: "signed long", - 10: "signed rational", - 11: "float", - 12: "double", - -} +TYPES = {} diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 8d5b383a9..703ba3015 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -142,9 +142,8 @@ class TestFileLibTiff(LibTiffTestCase): for tag, value in reloaded.items(): if tag not in ignored: if tag.endswith('Resolution'): - val = original[tag] self.assert_almost_equal( - val[0][0]/val[0][1], value[0][0]/value[0][1], + original[tag], value, msg="%s didn't roundtrip" % tag) else: self.assertEqual( @@ -153,9 +152,8 @@ class TestFileLibTiff(LibTiffTestCase): for tag, value in original.items(): if tag not in ignored: if tag.endswith('Resolution'): - val = reloaded[tag] self.assert_almost_equal( - val[0][0]/val[0][1], value[0][0]/value[0][1], + original[tag], value, msg="%s didn't roundtrip" % tag) else: self.assertEqual( diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 7d0871026..147507f54 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -74,11 +74,9 @@ class TestFileTiff(PillowTestCase): from PIL.TiffImagePlugin import X_RESOLUTION, Y_RESOLUTION filename = "Tests/images/pil168.tif" im = Image.open(filename) - assert isinstance(im.tag.tags[X_RESOLUTION][0], tuple) - assert isinstance(im.tag.tags[Y_RESOLUTION][0], tuple) # Try to read a file where X,Y_RESOLUTION are ints - im.tag.tags[X_RESOLUTION] = (72,) - im.tag.tags[Y_RESOLUTION] = (72,) + im.tag[X_RESOLUTION] = (72,) + im.tag[Y_RESOLUTION] = (72,) im._setup() self.assertEqual(im.info['dpi'], (72., 72.)) @@ -228,10 +226,9 @@ class TestFileTiff(PillowTestCase): self.assertIsInstance(ret, dict) self.assertEqual( - ret, {256: (55,), 257: (43,), 258: (8, 8, 8, 8), 259: (1,), - 262: (2,), 296: (2,), 273: (8,), 338: (1,), 277: (4,), - 279: (9460,), 282: ((720000, 10000),), - 283: ((720000, 10000),), 284: (1,)}) + ret, {256: 55, 257: 43, 258: (8, 8, 8, 8), 259: 1, 262: 2, 296: 2, + 273: (8,), 338: (1,), 277: 4, 279: (9460,), + 282: 72.0, 283: 72.0, 284: 1}) def test__delitem__(self): # Arrange @@ -255,7 +252,7 @@ class TestFileTiff(PillowTestCase): ret = ifd.load_byte(data) # Assert - self.assertEqual(ret, b"abc") + self.assertEqual(ret, (97, 98, 99)) def test_load_string(self): # Arrange @@ -310,38 +307,27 @@ class TestFileTiff(PillowTestCase): # Act / Assert self.assertRaises(EOFError, lambda: im.seek(1)) - def test__cvt_res_int(self): + def test__limit_rational_int(self): # Arrange - from PIL.TiffImagePlugin import _cvt_res + from PIL.TiffImagePlugin import _limit_rational value = 34 # Act - ret = _cvt_res(value) + ret = _limit_rational(value, 65536) # Assert self.assertEqual(ret, (34, 1)) - def test__cvt_res_float(self): + def test__limit_rational_float(self): # Arrange - from PIL.TiffImagePlugin import _cvt_res + from PIL.TiffImagePlugin import _limit_rational value = 22.3 # Act - ret = _cvt_res(value) + ret = _limit_rational(value, 65536) # Assert - self.assertEqual(ret, (1461452, 65536)) - - def test__cvt_res_sequence(self): - # Arrange - from PIL.TiffImagePlugin import _cvt_res - value = [0, 1] - - # Act - ret = _cvt_res(value) - - # Assert - self.assertEqual(ret, [0, 1]) + self.assertEqual(ret, (223, 10)) def test_4bit(self): # Arrange @@ -388,8 +374,8 @@ class TestFileTiff(PillowTestCase): # Assert from PIL.TiffImagePlugin import X_RESOLUTION, Y_RESOLUTION im = Image.open(filename) - self.assertEqual(im.tag.tags[X_RESOLUTION][0][0], 72) - self.assertEqual(im.tag.tags[Y_RESOLUTION][0][0], 36) + self.assertEqual(im.tag[X_RESOLUTION], 72) + self.assertEqual(im.tag[Y_RESOLUTION], 36) def test_deprecation_warning_with_spaces(self): # Arrange: use spaces @@ -405,8 +391,8 @@ class TestFileTiff(PillowTestCase): # Assert from PIL.TiffImagePlugin import X_RESOLUTION, Y_RESOLUTION im = Image.open(filename) - self.assertEqual(im.tag.tags[X_RESOLUTION][0][0], 36) - self.assertEqual(im.tag.tags[Y_RESOLUTION][0][0], 72) + self.assertEqual(im.tag[X_RESOLUTION], 36) + self.assertEqual(im.tag[Y_RESOLUTION], 72) if __name__ == '__main__': diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index f71db0924..51317aba3 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -1,8 +1,10 @@ +from __future__ import division + from helper import unittest, PillowTestCase, hopper from PIL import Image, TiffImagePlugin, TiffTags -tag_ids = dict(zip(TiffTags.TAGS.values(), TiffTags.TAGS.keys())) +tag_ids = {info.name: info.value for info in TiffTags.TAGS.values()} class TestFileTiffMetadata(PillowTestCase): @@ -19,7 +21,6 @@ class TestFileTiffMetadata(PillowTestCase): textdata = basetextdata + " \xff" floatdata = 12.345 doubledata = 67.89 - info = TiffImagePlugin.ImageFileDirectory() info[tag_ids['ImageJMetaDataByteCounts']] = len(textdata) @@ -45,32 +46,25 @@ class TestFileTiffMetadata(PillowTestCase): def test_read_metadata(self): img = Image.open('Tests/images/hopper_g4.tif') - known = {'YResolution': ((4294967295, 113653537),), - 'PlanarConfiguration': (1,), + known = {'YResolution': 4294967295 / 113653537, + 'PlanarConfiguration': 1, 'BitsPerSample': (1,), - 'ImageLength': (128,), - 'Compression': (4,), - 'FillOrder': (1,), - 'RowsPerStrip': (128,), - 'ResolutionUnit': (3,), - 'PhotometricInterpretation': (0,), + 'ImageLength': 128, + 'Compression': 4, + 'FillOrder': 1, + 'RowsPerStrip': 128, + 'ResolutionUnit': 3, + 'PhotometricInterpretation': 0, 'PageNumber': (0, 1), - 'XResolution': ((4294967295, 113653537),), - 'ImageWidth': (128,), - 'Orientation': (1,), + 'XResolution': 4294967295 / 113653537, + 'ImageWidth': 128, + 'Orientation': 1, 'StripByteCounts': (1968,), - 'SamplesPerPixel': (1,), - 'StripOffsets': (8,), + 'SamplesPerPixel': 1, + 'StripOffsets': (8,) } - # self.assertEqual is equivalent, - # but less helpful in telling what's wrong. - named = img.tag.named() - for tag, value in named.items(): - self.assertEqual(known[tag], value) - - for tag, value in known.items(): - self.assertEqual(value, named[tag]) + self.assertEqual(known, img.tag.named()) def test_write_metadata(self): """ Test metadata writing through the python code """ diff --git a/encode.c b/encode.c index 0f66e230b..244f5ca0d 100644 --- a/encode.c +++ b/encode.c @@ -721,7 +721,6 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) pos = 0; } - TRACE(("new tiff encoder %s fp: %d, filename: %s \n", compname, fp, filename)); encoder = PyImaging_EncoderNew(sizeof(TIFFSTATE)); @@ -737,11 +736,9 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) return NULL; } - // While fails on 64 bit machines, complains that pos is an int instead of a Py_ssize_t - // while (PyDict_Next(dir, &pos, &key, &value)) { - for (pos=0;posstate, (ttag_t) PyInt_AsLong(key), PyInt_AsLong(value)); - } else if(PyBytes_Check(value)) { + } else if (PyFloat_Check(value)) { + TRACE(("Setting from Float: %d, %f \n", (int)PyInt_AsLong(key),PyFloat_AsDouble(value))); + status = ImagingLibTiffSetField(&encoder->state, + (ttag_t) PyInt_AsLong(key), + (float)PyFloat_AsDouble(value)); + } else if (PyBytes_Check(value)) { TRACE(("Setting from Bytes: %d, %s \n", (int)PyInt_AsLong(key),PyBytes_AsString(value))); status = ImagingLibTiffSetField(&encoder->state, (ttag_t) PyInt_AsLong(key), PyBytes_AsString(value)); - } else if(PyList_Check(value)) { + } else if (PyTuple_Check(value)) { int len,i; float *floatav; int *intav; - TRACE(("Setting from List: %d \n", (int)PyInt_AsLong(key))); - len = (int)PyList_Size(value); + TRACE(("Setting from Tuple: %d \n", (int)PyInt_AsLong(key))); + len = (int)PyTuple_Size(value); if (len) { - if (PyInt_Check(PyList_GetItem(value,0))) { + if (PyInt_Check(PyTuple_GetItem(value,0))) { TRACE((" %d elements, setting as ints \n", len)); intav = malloc(sizeof(int)*len); if (intav) { for (i=0;istate, (ttag_t) PyInt_AsLong(key), @@ -778,7 +780,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) floatav = malloc(sizeof(float)*len); if (floatav) { for (i=0;istate, (ttag_t) PyInt_AsLong(key), @@ -787,11 +789,6 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) } } } - } else if (PyFloat_Check(value)) { - TRACE(("Setting from Float: %d, %f \n", (int)PyInt_AsLong(key),PyFloat_AsDouble(value))); - status = ImagingLibTiffSetField(&encoder->state, - (ttag_t) PyInt_AsLong(key), - (float)PyFloat_AsDouble(value)); } else { TRACE(("Unhandled type for key %d : %s \n", (int)PyInt_AsLong(key), From 56a3f0f2ab83630e0df6f1a09628a8a78278b2bd Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 30 Dec 2014 11:57:45 +0100 Subject: [PATCH 0526/1037] Fix EXIF support. --- PIL/JpegImagePlugin.py | 22 +++--------- PIL/TiffImagePlugin.py | 79 ++++++++++++++++++++++-------------------- PIL/TiffTags.py | 2 +- 3 files changed, 47 insertions(+), 56 deletions(-) diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index ffc14d2b6..9e2b35728 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -394,13 +394,6 @@ class JpegImageFile(ImageFile.ImageFile): return _getmp(self) -def _fixup(value): - # Helper function for _getexif() and _getmp() - if len(value) == 1: - return value[0] - return value - - def _getexif(self): # Extract EXIF information. This method is highly experimental, # and is likely to be replaced with something better in a future @@ -414,12 +407,10 @@ def _getexif(self): return None file = io.BytesIO(data[6:]) head = file.read(8) - exif = {} # process dictionary info = TiffImagePlugin.ImageFileDirectory(head) info.load(file) - for key, value in info.items(): - exif[key] = _fixup(value) + exif = dict(info) # get exif extension try: # exif field 0x8769 is an offset pointer to the location @@ -431,8 +422,7 @@ def _getexif(self): else: info = TiffImagePlugin.ImageFileDirectory(head) info.load(file) - for key, value in info.items(): - exif[key] = _fixup(value) + exif.update(info) # get gpsinfo extension try: # exif field 0x8825 is an offset pointer to the location @@ -444,9 +434,7 @@ def _getexif(self): else: info = TiffImagePlugin.ImageFileDirectory(head) info.load(file) - exif[0x8825] = gps = {} - for key, value in info.items(): - gps[key] = _fixup(value) + exif[0x8825] = dict(info) return exif @@ -464,12 +452,10 @@ def _getmp(self): file_contents = io.BytesIO(data) head = file_contents.read(8) endianness = '>' if head[:4] == b'\x4d\x4d\x00\x2a' else '<' - mp = {} # process dictionary info = TiffImagePlugin.ImageFileDirectory(head) info.load(file_contents) - for key, value in info.items(): - mp[key] = _fixup(value) + mp = dict(info) # it's an error not to have a number of images try: quant = mp[0xB001] diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 99f29865f..15597847e 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -253,18 +253,28 @@ class ImageFileDirectory(collections.MutableMapping): really sure what they're doing. """ - def __init__(self, prefix=II): + def __init__(self, ifh=b"II\052\0\0\0\0\0", prefix=None): + """Initialize an ImageFileDirectory. + + To construct an ImageFileDirectory from a real file, pass the 8-byte + magic header to the constructor. To only set the endianness, pass it + as the 'prefix' keyword argument. + + :ifh: One of the accepted magic headers (cf. PREFIXES); also sets + endianness. + :prefix: Override the endianness of the file. """ - :prefix: "II"|"MM" tiff endianness - """ - self._prefix = prefix - if prefix == MM: + if ifh[:4] not in PREFIXES: + raise SyntaxError("not a TIFF file (header %r not valid)" % ifh) + self._prefix = prefix if prefix is not None else ifh[:2] + if self._prefix == MM: self._endian = ">" - elif prefix == II: + elif self._prefix == II: self._endian = "<" else: - raise ValueError("not a TIFF IFD") + raise SyntaxError("not a TIFF IFD") self.reset() + self.next, = self._unpack("L", ifh[4:]) prefix = property(lambda self: self._prefix) offset = property(lambda self: self._offset) @@ -360,10 +370,10 @@ class ImageFileDirectory(collections.MutableMapping): def __iter__(self): return itertools.chain(list(self._tags), list(self._tagdata)) - def unpack(self, fmt, data): + def _unpack(self, fmt, data): return struct.unpack(self._endian + fmt, data) - def pack(self, fmt, *values): + def _pack(self, fmt, *values): return struct.pack(self._endian + fmt, *values) def _register_loader(idx, size): @@ -387,9 +397,9 @@ class ImageFileDirectory(collections.MutableMapping): TYPES[idx] = name size = struct.calcsize("=" + fmt) _load_dispatch[idx] = size, lambda self, data: ( - self.unpack("{}{}".format(len(data) // size, fmt), data)) + self._unpack("{}{}".format(len(data) // size, fmt), data)) _write_dispatch[idx] = lambda self, *values: ( - b"".join(self.pack(fmt, value) for value in values)) + b"".join(self._pack(fmt, value) for value in values)) list(map(_register_basic, [(1, "B", "byte"), (3, "H", "short"), (4, "L", "long"), @@ -411,12 +421,12 @@ class ImageFileDirectory(collections.MutableMapping): @_register_loader(5, 8) def load_rational(self, data): - vals = self.unpack("{}L".format(len(data) // 4), data) + vals = self._unpack("{}L".format(len(data) // 4), data) return tuple(num / denom for num, denom in zip(vals[::2], vals[1::2])) @_register_writer(5) def write_rational(self, *values): - return b"".join(self.pack("2L", *_limit_rational(frac, 2 ** 31)) + return b"".join(self._pack("2L", *_limit_rational(frac, 2 ** 31)) for frac in values) @_register_loader(7, 1) @@ -429,12 +439,12 @@ class ImageFileDirectory(collections.MutableMapping): @_register_loader(10, 8) def load_signed_rational(self, data): - vals = self.unpack("{}l".format(len(data) // 4), data) + vals = self._unpack("{}l".format(len(data) // 4), data) return tuple(num / denom for num, denom in zip(vals[::2], vals[1::2])) @_register_writer(10) def write_signed_rational(self, *values): - return b"".join(self.pack("2L", *_limit_rational(frac, 2 ** 30)) + return b"".join(self._pack("2L", *_limit_rational(frac, 2 ** 30)) for frac in values) def load(self, fp): @@ -442,9 +452,9 @@ class ImageFileDirectory(collections.MutableMapping): self.reset() self._offset = fp.tell() - for i in range(self.unpack("H", fp.read(2))[0]): - tag, typ, count, data = self.unpack("HHL4s", fp.read(12)) - if DEBUG: + for i in range(self._unpack("H", fp.read(2))[0]): + tag, typ, count, data = self._unpack("HHL4s", fp.read(12)) + if Image.DEBUG: tagname = TAGS.get(tag, TagInfo()).name typname = TYPES.get(typ, "unknown") print("tag: %s (%d) - type: %s (%d)" % @@ -459,8 +469,8 @@ class ImageFileDirectory(collections.MutableMapping): size = count * unit_size if size > 4: here = fp.tell() - offset, = self.unpack("L", data) - if DEBUG: + offset, = self._unpack("L", data) + if Image.DEBUG: print("Tag Location: %s - Data Location: %s" % (here, offset), end=" ") fp.seek(offset) @@ -484,12 +494,16 @@ class ImageFileDirectory(collections.MutableMapping): else: print("- value:", self[tag]) - self.next, = self.unpack("L", fp.read(4)) + self.next, = self._unpack("L", fp.read(4)) def save(self, fp): + if fp.tell() == 0: # skip TIFF header on subsequent pages + # tiff header -- PIL always starts the first IFD at offset 8 + fp.write(self._prefix + self._pack("HL", 42, 8)) + # FIXME What about tagdata? - fp.write(self.pack("H", len(self._tags))) + fp.write(self._pack("H", len(self._tags))) entries = [] offset = fp.tell() + len(self._tags) * 12 + 4 @@ -521,7 +535,7 @@ class ImageFileDirectory(collections.MutableMapping): if len(data) <= 4: entries.append((tag, typ, count, data.ljust(4, b"\0"), b"")) else: - entries.append((tag, typ, count, self.pack("L", offset), data)) + entries.append((tag, typ, count, self._pack("L", offset), data)) offset += (len(data) + 1) // 2 * 2 # pad to word # update strip offset data to point beyond auxiliary data @@ -530,14 +544,14 @@ class ImageFileDirectory(collections.MutableMapping): if data: raise NotImplementedError( "multistrip support not yet implemented") - value = self.pack("L", self.unpack("L", value)[0] + offset) + value = self._pack("L", self._unpack("L", value)[0] + offset) entries[stripoffsets] = tag, typ, count, value, data # pass 2: write entries to file for tag, typ, count, value, data in entries: if DEBUG > 1: print(tag, typ, count, repr(value), repr(data)) - fp.write(self.pack("HHL4s", tag, typ, count, value)) + fp.write(self._pack("HHL4s", tag, typ, count, value)) # -- overwrite here for multi-page -- fp.write(b"\0\0\0\0") # end of entries @@ -573,14 +587,11 @@ class TiffImageFile(ImageFile.ImageFile): # Header ifh = self.fp.read(8) - if ifh[:4] not in PREFIXES: - raise SyntaxError("not a TIFF file") - # image file directory (tag dictionary) - self.tag = self.ifd = ImageFileDirectory(ifh[:2]) + self.tag = self.ifd = ImageFileDirectory(ifh) # setup frame pointers - self.__first, = self.__next, = self.ifd.unpack("L", ifh[4:]) + self.__first = self.__next = self.ifd.next self.__frame = -1 self.__fp = self.fp self._frame_pos = [] @@ -991,7 +1002,7 @@ def _save(im, fp, filename): except KeyError: raise IOError("cannot write mode %s as TIFF" % im.mode) - ifd = ImageFileDirectory(prefix) + ifd = ImageFileDirectory(prefix=prefix) compression = im.encoderinfo.get('compression', im.info.get('compression', 'raw')) @@ -1001,12 +1012,6 @@ def _save(im, fp, filename): # required for color libtiff images ifd[PLANAR_CONFIGURATION] = getattr(im, '_planar_configuration', 1) - # -- multi-page -- skip TIFF header on subsequent pages - if not libtiff and fp.tell() == 0: - # tiff header (write via IFD to get everything right) - # PIL always starts the first IFD at offset 8 - fp.write(ifd.prefix + ifd.pack("HL", 42, 8)) - ifd[IMAGEWIDTH] = im.size[0] ifd[IMAGELENGTH] = im.size[1] diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py index 422891aeb..f667b9143 100644 --- a/PIL/TiffTags.py +++ b/PIL/TiffTags.py @@ -129,9 +129,9 @@ TAGS = { 33432: ("Copyright", 2, 1), # FIXME add more tags here + 34665: ("ExifIFD", 3, 1), 50741: ("MakerNoteSafety", 3, 1, {0: "Unsafe", 1: "Safe"}), 50780: ("BestQualityScale", 5, 1), - # private tags registered with Adobe 50838: ("ImageJMetaDataByteCounts", 4, 1), 50839: ("ImageJMetaData", 7, 1) } From d5b46dce960dc3a92cf40f14e01a92e52c867462 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 13 Jan 2015 23:07:41 -0800 Subject: [PATCH 0527/1037] Fix MPO support, and Python2.6 support. --- PIL/JpegImagePlugin.py | 9 +++++---- PIL/TiffImagePlugin.py | 14 +++++--------- PIL/TiffTags.py | 7 +++++++ Tests/test_file_tiff_metadata.py | 2 +- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 9e2b35728..8484638cb 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -36,7 +36,7 @@ import array import struct import io import warnings -from struct import unpack +from struct import unpack_from from PIL import Image, ImageFile, TiffImagePlugin, _binary from PIL.JpegPresets import presets from PIL._util import isStringType @@ -462,11 +462,12 @@ def _getmp(self): except KeyError: raise SyntaxError("malformed MP Index (no number of images)") # get MP entries + mpentries = [] try: - mpentries = [] + rawmpentries = mp[0xB002] for entrynum in range(0, quant): - rawmpentry = mp[0xB002][entrynum * 16:(entrynum + 1) * 16] - unpackedentry = unpack('{0}LLLHH'.format(endianness), rawmpentry) + unpackedentry = unpack_from( + '{0}LLLHH'.format(endianness), rawmpentries, entrynum * 16) labels = ('Attribute', 'Size', 'DataOffset', 'EntryNo1', 'EntryNo2') mpentry = dict(zip(labels, unpackedentry)) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 15597847e..8c6cf60a9 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -279,10 +279,6 @@ class ImageFileDirectory(collections.MutableMapping): prefix = property(lambda self: self._prefix) offset = property(lambda self: self._offset) - @property - def offset(self): - return self._offset - def reset(self): self._tags = {} self._tagdata = {} @@ -302,8 +298,8 @@ class ImageFileDirectory(collections.MutableMapping): """ Returns the complete tag dictionary, with named tags where possible. """ - return {TAGS.get(code, TagInfo()).name: value - for code, value in self.items()} + return dict((TAGS.get(code, TagInfo()).name, value) + for code, value in self.items()) def __len__(self): return len(self._tagdata) + len(self._tags) @@ -397,7 +393,7 @@ class ImageFileDirectory(collections.MutableMapping): TYPES[idx] = name size = struct.calcsize("=" + fmt) _load_dispatch[idx] = size, lambda self, data: ( - self._unpack("{}{}".format(len(data) // size, fmt), data)) + self._unpack("{0}{1}".format(len(data) // size, fmt), data)) _write_dispatch[idx] = lambda self, *values: ( b"".join(self._pack(fmt, value) for value in values)) @@ -421,7 +417,7 @@ class ImageFileDirectory(collections.MutableMapping): @_register_loader(5, 8) def load_rational(self, data): - vals = self._unpack("{}L".format(len(data) // 4), data) + vals = self._unpack("{0}L".format(len(data) // 4), data) return tuple(num / denom for num, denom in zip(vals[::2], vals[1::2])) @_register_writer(5) @@ -439,7 +435,7 @@ class ImageFileDirectory(collections.MutableMapping): @_register_loader(10, 8) def load_signed_rational(self, data): - vals = self._unpack("{}l".format(len(data) // 4), data) + vals = self._unpack("{0}l".format(len(data) // 4), data) return tuple(num / denom for num, denom in zip(vals[::2], vals[1::2])) @_register_writer(10) diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py index f667b9143..5ae5d2a93 100644 --- a/PIL/TiffTags.py +++ b/PIL/TiffTags.py @@ -130,6 +130,13 @@ TAGS = { # FIXME add more tags here 34665: ("ExifIFD", 3, 1), + + 45056: ("MPFVersion", 7, 1), + 45057: ("NumberOfImages", 4, 1), + 45058: ("MPEntry", 7, 1), + 45059: ("ImageUIDList", 7, 0), + 45060: ("TotalFrames", 4, 1), + 50741: ("MakerNoteSafety", 3, 1, {0: "Unsafe", 1: "Safe"}), 50780: ("BestQualityScale", 5, 1), 50838: ("ImageJMetaDataByteCounts", 4, 1), diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 51317aba3..78129fc58 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -4,7 +4,7 @@ from helper import unittest, PillowTestCase, hopper from PIL import Image, TiffImagePlugin, TiffTags -tag_ids = {info.name: info.value for info in TiffTags.TAGS.values()} +tag_ids = dict((info.name, info.value) for info in TiffTags.TAGS.values()) class TestFileTiffMetadata(PillowTestCase): From 38f7e23495bb7e739c755b09831dee6653d95595 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Wed, 25 Feb 2015 19:12:34 -0800 Subject: [PATCH 0528/1037] Include tests of #1113. --- PIL/TiffTags.py | 15 +++++++++++++++ Tests/test_file_tiff_metadata.py | 3 +-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py index 5ae5d2a93..6cc173332 100644 --- a/PIL/TiffTags.py +++ b/PIL/TiffTags.py @@ -131,11 +131,26 @@ TAGS = { # FIXME add more tags here 34665: ("ExifIFD", 3, 1), + # MPInfo 45056: ("MPFVersion", 7, 1), 45057: ("NumberOfImages", 4, 1), 45058: ("MPEntry", 7, 1), 45059: ("ImageUIDList", 7, 0), 45060: ("TotalFrames", 4, 1), + 45313: ("MPIndividualNum", 4, 1), + 45569: ("PanOrientation", 4, 1), + 45570: ("PanOverlap_H", 5, 1), + 45571: ("PanOverlap_V", 5, 1), + 45572: ("BaseViewpointNum", 4, 1), + 45573: ("ConvergenceAngle", 10, 1), + 45574: ("BaselineLength", 5, 1), + 45575: ("VerticalDivergence", 10, 1), + 45576: ("AxisDistance_X", 10, 1), + 45577: ("AxisDistance_Y", 10, 1), + 45578: ("AxisDistance_Z", 10, 1), + 45579: ("YawAngle", 10, 1), + 45580: ("PitchAngle", 10, 1), + 45581: ("RollAngle", 10, 1), 50741: ("MakerNoteSafety", 3, 1, {0: "Unsafe", 1: "Safe"}), 50780: ("BestQualityScale", 5, 1), diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 78129fc58..9c7a451e3 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -27,7 +27,6 @@ class TestFileTiffMetadata(PillowTestCase): info[tag_ids['ImageJMetaData']] = textdata info[tag_ids['RollAngle']] = floatdata info.tagtype[tag_ids['RollAngle']] = 11 - info[tag_ids['YawAngle']] = doubledata info.tagtype[tag_ids['YawAngle']] = 12 @@ -41,7 +40,7 @@ class TestFileTiffMetadata(PillowTestCase): self.assertEqual(loaded.tag[50839], basetextdata + " ?") self.assertAlmostEqual(loaded.tag[tag_ids['RollAngle']][0], floatdata, places=5) - self.assertAlmostEqual(loaded.tag[tag_ids['YawAngle']][0], doubledata) + self.assertAlmostEqual(loaded.tag[tag_ids['YawAngle']], doubledata) def test_read_metadata(self): img = Image.open('Tests/images/hopper_g4.tif') From 93abbd0caa035d2ff178cbef0720cd7dfcef20b0 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sat, 28 Feb 2015 19:44:38 -0800 Subject: [PATCH 0529/1037] Restore legacy TIFF API. To have the old API that always returns tuples, and fractions as pairs, set the `legacy_api` attribute of the IFD to True. This should alleviate concerns about backwards compatibility. --- PIL/TiffImagePlugin.py | 52 ++++++++---- Tests/helper.py | 5 -- Tests/test_file_gimpgradient.py | 2 +- Tests/test_file_libtiff.py | 52 +++++------- Tests/test_file_tiff.py | 136 ++++++++++--------------------- Tests/test_file_tiff_metadata.py | 78 ++++++++++++------ 6 files changed, 157 insertions(+), 168 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 8c6cf60a9..24a401331 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -247,10 +247,8 @@ class ImageFileDirectory(collections.MutableMapping): * self._tagdata = {} Key: numerical tiff tag number Value: undecoded byte string from file - Tags will be found in either self._tags or self._tagdata, but not - both. The union of the two should contain all the tags from the Tiff - image file. External classes shouldn't reference these unless they're - really sure what they're doing. + Tags will be found in the private attributes self._tagdata, and in + self._tags once decoded. """ def __init__(self, ifh=b"II\052\0\0\0\0\0", prefix=None): @@ -275,9 +273,17 @@ class ImageFileDirectory(collections.MutableMapping): raise SyntaxError("not a TIFF IFD") self.reset() self.next, = self._unpack("L", ifh[4:]) + self._legacy_api = False prefix = property(lambda self: self._prefix) offset = property(lambda self: self._offset) + legacy_api = property(lambda self: self._legacy_api) + + @legacy_api.setter + def legacy_api(self, value): + if value != self._legacy_api: + self._tags.clear() + self._legacy_api = value def reset(self): self._tags = {} @@ -302,18 +308,18 @@ class ImageFileDirectory(collections.MutableMapping): for code, value in self.items()) def __len__(self): - return len(self._tagdata) + len(self._tags) + return len(set(self._tagdata) | set(self._tags)) def __getitem__(self, tag): - try: - return self._tags[tag] - except KeyError: # unpack on the fly + if tag not in self._tags: # unpack on the fly data = self._tagdata[tag] typ = self.tagtype[tag] size, handler = self._load_dispatch[typ] self[tag] = handler(self, data) # check type - del self._tagdata[tag] - return self[tag] + val = self._tags[tag] + if self.legacy_api and not isinstance(val, (tuple, bytes)): + val = val, + return val def __contains__(self, tag): return tag in self._tags or tag in self._tagdata @@ -355,6 +361,8 @@ class ImageFileDirectory(collections.MutableMapping): for value in values] values = tuple(info.cvt_enum(value) for value in values) if info.length == 1: + if self.legacy_api and self.tagtype[tag] in [5, 10]: + values = values, self._tags[tag], = values else: self._tags[tag] = values @@ -364,7 +372,7 @@ class ImageFileDirectory(collections.MutableMapping): self._tagdata.pop(tag, None) def __iter__(self): - return itertools.chain(list(self._tags), list(self._tagdata)) + return iter(set(self._tagdata) | set(self._tags)) def _unpack(self, fmt, data): return struct.unpack(self._endian + fmt, data) @@ -398,10 +406,19 @@ class ImageFileDirectory(collections.MutableMapping): b"".join(self._pack(fmt, value) for value in values)) list(map(_register_basic, - [(1, "B", "byte"), (3, "H", "short"), (4, "L", "long"), + [(3, "H", "short"), (4, "L", "long"), (6, "b", "signed byte"), (8, "h", "signed short"), (9, "l", "signed long"), (11, "f", "float"), (12, "d", "double")])) + @_register_loader(1, 1) # Basic type, except for the legacy API. + def load_byte(self, data): + return (data if self.legacy_api else + tuple(map(ord, data) if bytes is str else data)) + + @_register_writer(1) # Basic type, except for the legacy API. + def write_byte(self, data): + return data + @_register_loader(2, 1) def load_string(self, data): if data.endswith(b"\0"): @@ -418,7 +435,9 @@ class ImageFileDirectory(collections.MutableMapping): @_register_loader(5, 8) def load_rational(self, data): vals = self._unpack("{0}L".format(len(data) // 4), data) - return tuple(num / denom for num, denom in zip(vals[::2], vals[1::2])) + combine = lambda a, b: (a, b) if self.legacy_api else a / b + return tuple(combine(num, denom) + for num, denom in zip(vals[::2], vals[1::2])) @_register_writer(5) def write_rational(self, *values): @@ -436,7 +455,9 @@ class ImageFileDirectory(collections.MutableMapping): @_register_loader(10, 8) def load_signed_rational(self, data): vals = self._unpack("{0}l".format(len(data) // 4), data) - return tuple(num / denom for num, denom in zip(vals[::2], vals[1::2])) + combine = lambda a, b: (a, b) if self.legacy_api else a / b + return tuple(combine(num, denom) + for num, denom in zip(vals[::2], vals[1::2])) @_register_writer(10) def write_signed_rational(self, *values): @@ -1026,11 +1047,14 @@ def _save(im, fp, filename): # inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com if hasattr(im, 'tag'): # preserve tags from original TIFF image file + orig_api = im.tag.legacy_api + im.tag.legacy_api = False for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP): if key in im.tag: ifd[key] = im.tag[key] ifd.tagtype[key] = im.tag.tagtype.get(key, None) + im.tag.legacy_api = orig_api # preserve ICC profile (should also work when saving other formats # which support profiles as TIFF) -- 2008-06-06 Florian Hoech diff --git a/Tests/helper.py b/Tests/helper.py index 83d86b5d9..abb2fbd6d 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -43,11 +43,6 @@ class PillowTestCase(unittest.TestCase): else: print("=== orphaned temp file: %s" % path) - def assert_almost_equal(self, a, b, msg=None, eps=1e-6): - self.assertLess( - abs(a-b), eps, - msg or "got %r, expected %r" % (a, b)) - def assert_deep_equal(self, a, b, msg=None): try: self.assertEqual( diff --git a/Tests/test_file_gimpgradient.py b/Tests/test_file_gimpgradient.py index c54dca7c1..1653fb304 100644 --- a/Tests/test_file_gimpgradient.py +++ b/Tests/test_file_gimpgradient.py @@ -80,7 +80,7 @@ class TestImage(PillowTestCase): ret = GimpGradientFile.sphere_increasing(middle, pos) # Assert - self.assert_almost_equal(ret, 0.9682458365518543) + self.assertAlmostEqual(ret, 0.9682458365518543) def test_sphere_decreasing(self): # Arrange diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 703ba3015..2d0f2b041 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -2,7 +2,11 @@ from __future__ import print_function from helper import unittest, PillowTestCase, hopper, py3 import io +<<<<<<< HEAD import logging +======= +import itertools +>>>>>>> Restore legacy TIFF API. import os from PIL import Image, TiffImagePlugin @@ -123,41 +127,27 @@ class TestFileLibTiff(LibTiffTestCase): def test_write_metadata(self): """ Test metadata writing through libtiff """ - img = Image.open('Tests/images/hopper_g4.tif') - f = self.tempfile('temp.tiff') + for legacy_api in [False, True]: + img = Image.open('Tests/images/hopper_g4.tif') + img.tag.legacy_api = legacy_api + f = self.tempfile('temp.tiff') - img.save(f, tiffinfo=img.tag) + img.save(f, tiffinfo=img.tag) + original = img.tag.named() - loaded = Image.open(f) + # PhotometricInterpretation is set from SAVE_INFO, + # not the original image. + ignored = ['StripByteCounts', 'RowsPerStrip', 'PageNumber', + 'PhotometricInterpretation'] - original = img.tag.named() - reloaded = loaded.tag.named() + loaded = Image.open(f) + loaded.tag.legacy_api = legacy_api + reloaded = loaded.tag.named() - # PhotometricInterpretation is set from SAVE_INFO, - # not the original image. - ignored = [ - 'StripByteCounts', 'RowsPerStrip', - 'PageNumber', 'PhotometricInterpretation'] - - for tag, value in reloaded.items(): - if tag not in ignored: - if tag.endswith('Resolution'): - self.assert_almost_equal( - original[tag], value, - msg="%s didn't roundtrip" % tag) - else: - self.assertEqual( - original[tag], value, "%s didn't roundtrip" % tag) - - for tag, value in original.items(): - if tag not in ignored: - if tag.endswith('Resolution'): - self.assert_almost_equal( - original[tag], value, - msg="%s didn't roundtrip" % tag) - else: - self.assertEqual( - value, reloaded[tag], "%s didn't roundtrip" % tag) + for tag, value in itertools.chain(reloaded.items(), original.items()): + if tag not in ignored: + val = original[tag] + self.assertEqual(val, value, msg="%s didn't roundtrip" % tag) def test_g3_compression(self): i = Image.open('Tests/images/hopper_g4_500.tif') diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 147507f54..4e3331058 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -73,12 +73,18 @@ class TestFileTiff(PillowTestCase): def test_xyres_tiff(self): from PIL.TiffImagePlugin import X_RESOLUTION, Y_RESOLUTION filename = "Tests/images/pil168.tif" - im = Image.open(filename) - # Try to read a file where X,Y_RESOLUTION are ints - im.tag[X_RESOLUTION] = (72,) - im.tag[Y_RESOLUTION] = (72,) - im._setup() - self.assertEqual(im.info['dpi'], (72., 72.)) + for legacy_api in [False, True]: + im = Image.open(filename) + im.tag.legacy_api = legacy_api + if legacy_api: + assert isinstance(im.tag[X_RESOLUTION][0], tuple) + assert isinstance(im.tag[Y_RESOLUTION][0], tuple) + # Try to read a file where X,Y_RESOLUTION are ints + im.tag[X_RESOLUTION] = (72,) + im.tag[Y_RESOLUTION] = (72,) + im.tag.legacy_api = False # _setup assumes the new API. + im._setup() + self.assertEqual(im.info['dpi'], (72., 72.)) def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" @@ -204,7 +210,6 @@ class TestFileTiff(PillowTestCase): self.assertEqual(im.convert('RGB').getpixel((0, 0)), (0, 0, 255)) def test___str__(self): - # Arrange filename = "Tests/images/pil136.tiff" im = Image.open(filename) @@ -217,127 +222,81 @@ class TestFileTiff(PillowTestCase): def test_as_dict(self): # Arrange filename = "Tests/images/pil136.tiff" - im = Image.open(filename) - - # Act - ret = im.ifd.as_dict() - - # Assert - self.assertIsInstance(ret, dict) - - self.assertEqual( - ret, {256: 55, 257: 43, 258: (8, 8, 8, 8), 259: 1, 262: 2, 296: 2, - 273: (8,), 338: (1,), 277: 4, 279: (9460,), - 282: 72.0, 283: 72.0, 284: 1}) + for legacy_api in [False, True]: + im = Image.open(filename) + im.tag.legacy_api = legacy_api + self.assertEqual( + im.tag.as_dict(), + {256: (55,), 257: (43,), 258: (8, 8, 8, 8), 259: (1,), + 262: (2,), 296: (2,), 273: (8,), 338: (1,), 277: (4,), + 279: (9460,), 282: ((720000, 10000),), + 283: ((720000, 10000),), 284: (1,)} if legacy_api else + {256: 55, 257: 43, 258: (8, 8, 8, 8), 259: 1, + 262: 2, 296: 2, 273: (8,), 338: (1,), 277: 4, + 279: (9460,), 282: 72.0, 283: 72.0, 284: 1}) def test__delitem__(self): - # Arrange filename = "Tests/images/pil136.tiff" im = Image.open(filename) len_before = len(im.ifd.as_dict()) - - # Act del im.ifd[256] - - # Assert len_after = len(im.ifd.as_dict()) self.assertEqual(len_before, len_after + 1) def test_load_byte(self): - # Arrange - ifd = TiffImagePlugin.ImageFileDirectory() - data = b"abc" - - # Act - ret = ifd.load_byte(data) - - # Assert - self.assertEqual(ret, (97, 98, 99)) + for legacy_api in [False, True]: + ifd = TiffImagePlugin.ImageFileDirectory() + ifd.legacy_api = legacy_api + data = b"abc" + ret = ifd.load_byte(data) + self.assertEqual(ret, b"abc" if legacy_api else (97, 98, 99)) def test_load_string(self): - # Arrange ifd = TiffImagePlugin.ImageFileDirectory() data = b"abc\0" - - # Act ret = ifd.load_string(data) - - # Assert self.assertEqual(ret, "abc") def test_load_float(self): - # Arrange ifd = TiffImagePlugin.ImageFileDirectory() data = b"abcdabcd" - - # Act ret = ifd.load_float(data) - - # Assert self.assertEqual(ret, (1.6777999408082104e+22, 1.6777999408082104e+22)) def test_load_double(self): - # Arrange ifd = TiffImagePlugin.ImageFileDirectory() data = b"abcdefghabcdefgh" - - # Act ret = ifd.load_double(data) - - # Assert self.assertEqual(ret, (8.540883223036124e+194, 8.540883223036124e+194)) def test_seek(self): - # Arrange filename = "Tests/images/pil136.tiff" im = Image.open(filename) - - # Act im.seek(-1) - - # Assert self.assertEqual(im.tell(), 0) def test_seek_eof(self): - # Arrange filename = "Tests/images/pil136.tiff" im = Image.open(filename) self.assertEqual(im.tell(), 0) - - # Act / Assert self.assertRaises(EOFError, lambda: im.seek(1)) def test__limit_rational_int(self): - # Arrange from PIL.TiffImagePlugin import _limit_rational value = 34 - - # Act ret = _limit_rational(value, 65536) - - # Assert self.assertEqual(ret, (34, 1)) def test__limit_rational_float(self): - # Arrange from PIL.TiffImagePlugin import _limit_rational value = 22.3 - - # Act ret = _limit_rational(value, 65536) - - # Assert self.assertEqual(ret, (223, 10)) def test_4bit(self): - # Arrange test_file = "Tests/images/hopper_gray_4bpp.tif" original = hopper("L") - - # Act im = Image.open(test_file) - - # Assert self.assertEqual(im.size, (128, 128)) self.assertEqual(im.mode, "L") self.assert_image_similar(im, original, 7.3) @@ -347,52 +306,45 @@ class TestFileTiff(PillowTestCase): # Test TIFF with tag 297 (Page Number) having value of 0 0. # The first number is the current page number. # The second is the total number of pages, zero means not available. - - # Arrange outfile = self.tempfile("temp.tif") - # Created by printing a page in Chrome to PDF, then: # /usr/bin/gs -q -sDEVICE=tiffg3 -sOutputFile=total-pages-zero.tif # -dNOPAUSE /tmp/test.pdf -c quit infile = "Tests/images/total-pages-zero.tif" im = Image.open(infile) - - # Act / Assert # Should not divide by zero im.save(outfile) def test_with_underscores(self): - # Arrange: use underscores kwargs = {'resolution_unit': 'inch', 'x_resolution': 72, 'y_resolution': 36} filename = self.tempfile("temp.tif") - - # Act hopper("RGB").save(filename, **kwargs) - - # Assert from PIL.TiffImagePlugin import X_RESOLUTION, Y_RESOLUTION - im = Image.open(filename) - self.assertEqual(im.tag[X_RESOLUTION], 72) - self.assertEqual(im.tag[Y_RESOLUTION], 36) + for legacy_api in [False, True]: + im = Image.open(filename) + im.tag.legacy_api = legacy_api + self.assertEqual(im.tag[X_RESOLUTION][0][0] if legacy_api + else im.tag[X_RESOLUTION], 72) + self.assertEqual(im.tag[Y_RESOLUTION][0][0] if legacy_api + else im.tag[Y_RESOLUTION], 36) def test_deprecation_warning_with_spaces(self): - # Arrange: use spaces kwargs = {'resolution unit': 'inch', 'x resolution': 36, 'y resolution': 72} filename = self.tempfile("temp.tif") - - # Act self.assert_warning(DeprecationWarning, lambda: hopper("RGB").save(filename, **kwargs)) - - # Assert from PIL.TiffImagePlugin import X_RESOLUTION, Y_RESOLUTION - im = Image.open(filename) - self.assertEqual(im.tag[X_RESOLUTION], 36) - self.assertEqual(im.tag[Y_RESOLUTION], 72) + for legacy_api in [False, True]: + im = Image.open(filename) + im.tag.legacy_api = legacy_api + self.assertEqual(im.tag[X_RESOLUTION][0][0] if legacy_api + else im.tag[X_RESOLUTION], 36) + self.assertEqual(im.tag[Y_RESOLUTION][0][0] if legacy_api + else im.tag[Y_RESOLUTION], 72) if __name__ == '__main__': diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 9c7a451e3..df1934482 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -34,36 +34,64 @@ class TestFileTiffMetadata(PillowTestCase): img.save(f, tiffinfo=info) - loaded = Image.open(f) + for legacy_api in [False, True]: + loaded = Image.open(f) + loaded.tag.legacy_api = legacy_api + + self.assertEqual(loaded.tag[50838], + (len(basetextdata + " ?"),) if legacy_api else len(basetextdata + " ?")) + self.assertEqual(loaded.tag[50839], basetextdata + " ?") + loaded_float = loaded.tag[tag_ids['RollAngle']] + if legacy_api: + loaded_float = loaded_float[0] + self.assertAlmostEqual(loaded_float, floatdata, places=5) + loaded_double = loaded.tag[tag_ids['YawAngle']] + if legacy_api: + loaded_double = loaded_double[0] + self.assertAlmostEqual(loaded_double, doubledata) - self.assertEqual(loaded.tag[50838], (len(basetextdata + " ?"),)) - self.assertEqual(loaded.tag[50839], basetextdata + " ?") - self.assertAlmostEqual(loaded.tag[tag_ids['RollAngle']][0], floatdata, - places=5) - self.assertAlmostEqual(loaded.tag[tag_ids['YawAngle']], doubledata) def test_read_metadata(self): - img = Image.open('Tests/images/hopper_g4.tif') + for legacy_api in [False, True]: + img = Image.open('Tests/images/hopper_g4.tif') + img.tag.legacy_api = legacy_api - known = {'YResolution': 4294967295 / 113653537, - 'PlanarConfiguration': 1, - 'BitsPerSample': (1,), - 'ImageLength': 128, - 'Compression': 4, - 'FillOrder': 1, - 'RowsPerStrip': 128, - 'ResolutionUnit': 3, - 'PhotometricInterpretation': 0, - 'PageNumber': (0, 1), - 'XResolution': 4294967295 / 113653537, - 'ImageWidth': 128, - 'Orientation': 1, - 'StripByteCounts': (1968,), - 'SamplesPerPixel': 1, - 'StripOffsets': (8,) - } + known = {'YResolution': ((4294967295, 113653537),), + 'PlanarConfiguration': (1,), + 'BitsPerSample': (1,), + 'ImageLength': (128,), + 'Compression': (4,), + 'FillOrder': (1,), + 'RowsPerStrip': (128,), + 'ResolutionUnit': (3,), + 'PhotometricInterpretation': (0,), + 'PageNumber': (0, 1), + 'XResolution': ((4294967295, 113653537),), + 'ImageWidth': (128,), + 'Orientation': (1,), + 'StripByteCounts': (1968,), + 'SamplesPerPixel': (1,), + 'StripOffsets': (8,) + } if legacy_api else { + 'YResolution': 4294967295 / 113653537, + 'PlanarConfiguration': 1, + 'BitsPerSample': (1,), + 'ImageLength': 128, + 'Compression': 4, + 'FillOrder': 1, + 'RowsPerStrip': 128, + 'ResolutionUnit': 3, + 'PhotometricInterpretation': 0, + 'PageNumber': (0, 1), + 'XResolution': 4294967295 / 113653537, + 'ImageWidth': 128, + 'Orientation': 1, + 'StripByteCounts': (1968,), + 'SamplesPerPixel': 1, + 'StripOffsets': (8,) + } - self.assertEqual(known, img.tag.named()) + self.assertEqual(known, img.tag.named()) def test_write_metadata(self): """ Test metadata writing through the python code """ From 6309bfe92110be8b0cd0b588ce36ac13cd027c5c Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 16 Jun 2015 14:16:56 -0700 Subject: [PATCH 0530/1037] Support too long ExtraSamples. Some programs generate SamplesPerPixel entries in ExtraSamples instead of SamplesPerPixel-3, cf. #1227. This is a stopgap measure to support them. One could also decide to add generic code to always support having SamplesPerPixel entries (by dropping the first 3). --- PIL/TiffImagePlugin.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 24a401331..2fa9388eb 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -157,6 +157,7 @@ OPEN_INFO = { (II, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), (II, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), (II, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 + (II, 2, (1, 1, 1, 1), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBA"), # OSX Grab (II, 3, (1,), 1, (1,), ()): ("P", "P;1"), (II, 3, (1,), 2, (1,), ()): ("P", "P;1R"), (II, 3, (1,), 1, (2,), ()): ("P", "P;2"), @@ -185,10 +186,12 @@ OPEN_INFO = { (MM, 1, (3,), 1, (32,), ()): ("F", "F;32BF"), (MM, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"), (MM, 2, (1,), 2, (8, 8, 8), ()): ("RGB", "RGB;R"), + (MM, 2, (1,), 1, (8, 8, 8, 8), ()): ("RGBA", "RGBA"), # missing ExtraSamples (MM, 2, (1,), 1, (8, 8, 8, 8), (0,)): ("RGBX", "RGBX"), (MM, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), (MM, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), (MM, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 + (MM, 2, (1, 1, 1, 1), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBA"), # OSX Grab (MM, 3, (1,), 1, (1,), ()): ("P", "P;1"), (MM, 3, (1,), 2, (1,), ()): ("P", "P;1R"), (MM, 3, (1,), 1, (2,), ()): ("P", "P;2"), @@ -705,9 +708,9 @@ class TiffImageFile(ImageFile.ImageFile): args = rawmode elif compression == "tiff_lzw": args = rawmode - if 317 in self.tag: + if PREDICTOR in self.tag: # Section 14: Differencing Predictor - self.decoderconfig = (self.tag[PREDICTOR][0],) + self.decoderconfig = (self.tag[PREDICTOR],) if ICCPROFILE in self.tag: self.info['icc_profile'] = self.tag[ICCPROFILE] From 1b9b3749ca23cf723229d23535b327f28600138e Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 16 Jun 2015 14:27:02 -0700 Subject: [PATCH 0531/1037] Reorder OPEN_INFO to cover both endiannesses. By interleaving little and big-endian entries we make sure entries exist for both cases. Some additional entries created when the big-endian was missing. I am not sure of what entry to create for the big-endian, 4-bit case (what is the order of the two entries within the byte?). --- PIL/TiffImagePlugin.py | 68 +++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 2fa9388eb..40165f101 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -134,77 +134,77 @@ OPEN_INFO = { # (ByteOrder, PhotoInterpretation, SampleFormat, FillOrder, BitsPerSample, # ExtraSamples) => mode, rawmode (II, 0, (1,), 1, (1,), ()): ("1", "1;I"), + (MM, 0, (1,), 1, (1,), ()): ("1", "1;I"), (II, 0, (1,), 2, (1,), ()): ("1", "1;IR"), + (MM, 0, (1,), 2, (1,), ()): ("1", "1;IR"), (II, 0, (1,), 1, (8,), ()): ("L", "L;I"), + (MM, 0, (1,), 1, (8,), ()): ("L", "L;I"), (II, 0, (1,), 2, (8,), ()): ("L", "L;IR"), + (MM, 0, (1,), 2, (8,), ()): ("L", "L;IR"), (II, 0, (3,), 1, (32,), ()): ("F", "F;32F"), + (MM, 0, (3,), 1, (32,), ()): ("F", "F;32BF"), (II, 1, (1,), 1, (1,), ()): ("1", "1"), + (MM, 1, (1,), 1, (1,), ()): ("1", "1"), (II, 1, (1,), 1, (4,), ()): ("L", "L;4"), + # ? (II, 1, (1,), 2, (1,), ()): ("1", "1;R"), + (MM, 1, (1,), 2, (1,), ()): ("1", "1;R"), (II, 1, (1,), 1, (8,), ()): ("L", "L"), + (MM, 1, (1,), 1, (8,), ()): ("L", "L"), (II, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"), + (MM, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"), (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, 1, (1,), 1, (16,), ()): ("I;16", "I;16"), + (MM, 1, (1,), 1, (16,), ()): ("I;16B", "I;16B"), (II, 1, (2,), 1, (16,), ()): ("I;16S", "I;16S"), + (MM, 1, (2,), 1, (16,), ()): ("I;16BS", "I;16BS"), (II, 1, (1,), 1, (32,), ()): ("I", "I;32N"), (II, 1, (2,), 1, (32,), ()): ("I", "I;32S"), - (II, 1, (3,), 1, (32,), ()): ("F", "F;32F"), - (II, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"), - (II, 2, (1,), 2, (8, 8, 8), ()): ("RGB", "RGB;R"), - (II, 2, (1,), 1, (8, 8, 8, 8), ()): ("RGBA", "RGBA"), # missing ExtraSamples - (II, 2, (1,), 1, (8, 8, 8, 8), (0,)): ("RGBX", "RGBX"), - (II, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), - (II, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), - (II, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 - (II, 2, (1, 1, 1, 1), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBA"), # OSX Grab - (II, 3, (1,), 1, (1,), ()): ("P", "P;1"), - (II, 3, (1,), 2, (1,), ()): ("P", "P;1R"), - (II, 3, (1,), 1, (2,), ()): ("P", "P;2"), - (II, 3, (1,), 2, (2,), ()): ("P", "P;2R"), - (II, 3, (1,), 1, (4,), ()): ("P", "P;4"), - (II, 3, (1,), 2, (4,), ()): ("P", "P;4R"), - (II, 3, (1,), 1, (8,), ()): ("P", "P"), - (II, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"), - (II, 3, (1,), 2, (8,), ()): ("P", "P;R"), - (II, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"), - (II, 6, (1,), 1, (8, 8, 8), ()): ("YCbCr", "YCbCr"), - (II, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"), - - (MM, 0, (1,), 1, (1,), ()): ("1", "1;I"), - (MM, 0, (1,), 2, (1,), ()): ("1", "1;IR"), - (MM, 0, (1,), 1, (8,), ()): ("L", "L;I"), - (MM, 0, (1,), 2, (8,), ()): ("L", "L;IR"), - (MM, 1, (1,), 1, (1,), ()): ("1", "1"), - (MM, 1, (1,), 2, (1,), ()): ("1", "1;R"), - (MM, 1, (1,), 1, (8,), ()): ("L", "L"), - (MM, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"), - (MM, 1, (1,), 2, (8,), ()): ("L", "L;R"), - (MM, 1, (1,), 1, (16,), ()): ("I;16B", "I;16B"), - (MM, 1, (2,), 1, (16,), ()): ("I;16BS", "I;16BS"), (MM, 1, (2,), 1, (32,), ()): ("I;32BS", "I;32BS"), + (II, 1, (3,), 1, (32,), ()): ("F", "F;32F"), (MM, 1, (3,), 1, (32,), ()): ("F", "F;32BF"), + (II, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"), (MM, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"), + (II, 2, (1,), 2, (8, 8, 8), ()): ("RGB", "RGB;R"), (MM, 2, (1,), 2, (8, 8, 8), ()): ("RGB", "RGB;R"), + (II, 2, (1,), 1, (8, 8, 8, 8), ()): ("RGBA", "RGBA"), # missing ExtraSamples (MM, 2, (1,), 1, (8, 8, 8, 8), ()): ("RGBA", "RGBA"), # missing ExtraSamples + (II, 2, (1,), 1, (8, 8, 8, 8), (0,)): ("RGBX", "RGBX"), (MM, 2, (1,), 1, (8, 8, 8, 8), (0,)): ("RGBX", "RGBX"), + (II, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), (MM, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), + (II, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), (MM, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), + (II, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 (MM, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 + (II, 2, (1, 1, 1, 1), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBA"), # OSX Grab (MM, 2, (1, 1, 1, 1), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBA"), # OSX Grab + (II, 3, (1,), 1, (1,), ()): ("P", "P;1"), (MM, 3, (1,), 1, (1,), ()): ("P", "P;1"), + (II, 3, (1,), 2, (1,), ()): ("P", "P;1R"), (MM, 3, (1,), 2, (1,), ()): ("P", "P;1R"), + (II, 3, (1,), 1, (2,), ()): ("P", "P;2"), (MM, 3, (1,), 1, (2,), ()): ("P", "P;2"), + (II, 3, (1,), 2, (2,), ()): ("P", "P;2R"), (MM, 3, (1,), 2, (2,), ()): ("P", "P;2R"), + (II, 3, (1,), 1, (4,), ()): ("P", "P;4"), (MM, 3, (1,), 1, (4,), ()): ("P", "P;4"), + (II, 3, (1,), 2, (4,), ()): ("P", "P;4R"), (MM, 3, (1,), 2, (4,), ()): ("P", "P;4R"), + (II, 3, (1,), 1, (8,), ()): ("P", "P"), (MM, 3, (1,), 1, (8,), ()): ("P", "P"), + (II, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"), (MM, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"), + (II, 3, (1,), 2, (8,), ()): ("P", "P;R"), (MM, 3, (1,), 2, (8,), ()): ("P", "P;R"), + (II, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"), (MM, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"), + (II, 6, (1,), 1, (8, 8, 8), ()): ("YCbCr", "YCbCr"), (MM, 6, (1,), 1, (8, 8, 8), ()): ("YCbCr", "YCbCr"), + (II, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"), (MM, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"), - } PREFIXES = [b"MM\000\052", b"II\052\000", b"II\xBC\000"] From ba7b8d1708079a7026d640f1f7eb06a91d24ef93 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Mon, 29 Jun 2015 15:37:58 -0700 Subject: [PATCH 0532/1037] Fail on invalid EXIF, reverting ed2cca1. --- Tests/test_file_tiff.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 4e3331058..c2c7c0eec 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -93,16 +93,12 @@ class TestFileTiff(PillowTestCase): lambda: TiffImagePlugin.TiffImageFile(invalid_file)) def test_bad_exif(self): - try: - Image.open('Tests/images/hopper_bad_exif.jpg')._getexif() - except struct.error: - self.fail( - "Bad EXIF data passed incorrect values to _binary unpack") + image = Image.open('Tests/images/hopper_bad_exif.jpg') + self.assertRaises(Exception, image._getexif) def test_save_unsupported_mode(self): im = hopper("HSV") outfile = self.tempfile("temp.tif") - self.assertRaises(IOError, lambda: im.save(outfile)) def test_little_endian(self): From c113e8f7cdbfb66763273f49145598d56aa36cd4 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 30 Jun 2015 18:29:29 -0700 Subject: [PATCH 0533/1037] libtiff's rational precision is limited to C floats. --- Tests/test_file_libtiff.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 2d0f2b041..13823d28f 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -1,6 +1,7 @@ from __future__ import print_function from helper import unittest, PillowTestCase, hopper, py3 +from ctypes import c_float import io <<<<<<< HEAD import logging @@ -144,10 +145,23 @@ class TestFileLibTiff(LibTiffTestCase): loaded.tag.legacy_api = legacy_api reloaded = loaded.tag.named() - for tag, value in itertools.chain(reloaded.items(), original.items()): + for tag, value in itertools.chain(reloaded.items(), + original.items()): if tag not in ignored: val = original[tag] - self.assertEqual(val, value, msg="%s didn't roundtrip" % tag) + if tag.endswith('Resolution'): + if legacy_api: + self.assertEqual( + c_float(val[0][0] / val[0][1]).value, + c_float(value[0][0] / value[0][1]).value, + msg="%s didn't roundtrip" % tag) + else: + self.assertEqual( + c_float(val).value, c_float(value).value, + msg="%s didn't roundtrip" % tag) + else: + self.assertEqual( + val, value, msg="%s didn't roundtrip" % tag) def test_g3_compression(self): i = Image.open('Tests/images/hopper_g4_500.tif') From 0c942911d99dea2ac9c736ae95b292286e937f21 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 10 Sep 2015 08:33:14 -0700 Subject: [PATCH 0534/1037] fixed the rebase --- PIL/TiffImagePlugin.py | 4 ++-- Tests/test_file_libtiff.py | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 40165f101..b64c9e9bc 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -474,7 +474,7 @@ class ImageFileDirectory(collections.MutableMapping): for i in range(self._unpack("H", fp.read(2))[0]): tag, typ, count, data = self._unpack("HHL4s", fp.read(12)) - if Image.DEBUG: + if DEBUG: tagname = TAGS.get(tag, TagInfo()).name typname = TYPES.get(typ, "unknown") print("tag: %s (%d) - type: %s (%d)" % @@ -490,7 +490,7 @@ class ImageFileDirectory(collections.MutableMapping): if size > 4: here = fp.tell() offset, = self._unpack("L", data) - if Image.DEBUG: + if DEBUG: print("Tag Location: %s - Data Location: %s" % (here, offset), end=" ") fp.seek(offset) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 13823d28f..33d77f558 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -3,11 +3,8 @@ from helper import unittest, PillowTestCase, hopper, py3 from ctypes import c_float import io -<<<<<<< HEAD import logging -======= import itertools ->>>>>>> Restore legacy TIFF API. import os from PIL import Image, TiffImagePlugin From 9bb4c516299a92c3d2c10cc4b94dd4d6ec84829e Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 10 Sep 2015 08:49:37 -0700 Subject: [PATCH 0535/1037] module level default api level --- PIL/TiffImagePlugin.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index b64c9e9bc..64efb4d9b 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -63,6 +63,7 @@ DEBUG = False # Needs to be merged with the new logging approach. # Set these to true to force use of libtiff for reading or writing. READ_LIBTIFF = False WRITE_LIBTIFF = False +IFD_LEGACY_API = True II = b"II" # little-endian (Intel style) MM = b"MM" # big-endian (Motorola style) @@ -276,7 +277,7 @@ class ImageFileDirectory(collections.MutableMapping): raise SyntaxError("not a TIFF IFD") self.reset() self.next, = self._unpack("L", ifh[4:]) - self._legacy_api = False + self._legacy_api = IFD_LEGACY_API prefix = property(lambda self: self._prefix) offset = property(lambda self: self._offset) From 47a963c2a44130bf95f03ca3a0e2c41218ff2c3c Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 11 Sep 2015 10:09:14 -0700 Subject: [PATCH 0536/1037] Legacy/versioned interface --- PIL/JpegImagePlugin.py | 8 +-- PIL/TiffImagePlugin.py | 114 ++++++++++++++++++++++++---------------- Tests/test_file_tiff.py | 96 +++++++++++++++++++-------------- 3 files changed, 130 insertions(+), 88 deletions(-) diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 8484638cb..3d01c5fc1 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -408,7 +408,7 @@ def _getexif(self): file = io.BytesIO(data[6:]) head = file.read(8) # process dictionary - info = TiffImagePlugin.ImageFileDirectory(head) + info = TiffImagePlugin.ImageFileDirectory_v2(head) info.load(file) exif = dict(info) # get exif extension @@ -420,7 +420,7 @@ def _getexif(self): except (KeyError, TypeError): pass else: - info = TiffImagePlugin.ImageFileDirectory(head) + info = TiffImagePlugin.ImageFileDirectory_v2(head) info.load(file) exif.update(info) # get gpsinfo extension @@ -432,7 +432,7 @@ def _getexif(self): except (KeyError, TypeError): pass else: - info = TiffImagePlugin.ImageFileDirectory(head) + info = TiffImagePlugin.ImageFileDirectory_v2(head) info.load(file) exif[0x8825] = dict(info) return exif @@ -453,7 +453,7 @@ def _getmp(self): head = file_contents.read(8) endianness = '>' if head[:4] == b'\x4d\x4d\x00\x2a' else '<' # process dictionary - info = TiffImagePlugin.ImageFileDirectory(head) + info = TiffImagePlugin.ImageFileDirectory_v2(head) info.load(file_contents) mp = dict(info) # it's an error not to have a number of images diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 64efb4d9b..de2381b34 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -227,7 +227,7 @@ def _limit_rational(val, max_val): _load_dispatch = {} _write_dispatch = {} -class ImageFileDirectory(collections.MutableMapping): +class ImageFileDirectory_v2(collections.MutableMapping): """This class represents a TIFF tag directory. To speed things up, we don't decode tags unless they're asked for. @@ -277,8 +277,8 @@ class ImageFileDirectory(collections.MutableMapping): raise SyntaxError("not a TIFF IFD") self.reset() self.next, = self._unpack("L", ifh[4:]) - self._legacy_api = IFD_LEGACY_API - + self._legacy_api = False + prefix = property(lambda self: self._prefix) offset = property(lambda self: self._offset) legacy_api = property(lambda self: self._legacy_api) @@ -585,14 +585,33 @@ class ImageFileDirectory(collections.MutableMapping): return offset -ImageFileDirectory._load_dispatch = _load_dispatch -ImageFileDirectory._write_dispatch = _write_dispatch +ImageFileDirectory_v2._load_dispatch = _load_dispatch +ImageFileDirectory_v2._write_dispatch = _write_dispatch for idx, name in TYPES.items(): name = name.replace(" ", "_") - setattr(ImageFileDirectory, "load_" + name, _load_dispatch[idx][1]) - setattr(ImageFileDirectory, "write_" + name, _write_dispatch[idx]) + setattr(ImageFileDirectory_v2, "load_" + name, _load_dispatch[idx][1]) + setattr(ImageFileDirectory_v2, "write_" + name, _write_dispatch[idx]) del _load_dispatch, _write_dispatch, idx, name +#Legacy ImageFileDirectory support. +class ImageFileDirectory_v1(ImageFileDirectory_v2): + def __init__(self, *args, **kwargs): + ImageFileDirectory_v2.__init__(self, *args, **kwargs) + self.legacy_api=True + #insert deprecation warning here. + + tags = property(lambda self: self._tags) + tagdata = property(lambda self: self._tagdata) + + @classmethod + def from_v2(cls, original): + ifd = cls(prefix=original.prefix) + ifd._tagdata = original._tagdata + ifd.tagtype = original.tagtype + return ifd + +# undone -- switch this pointer when IFD_LEGACY_API == False +ImageFileDirectory = ImageFileDirectory_v1 ## # Image plugin for TIFF files. @@ -609,10 +628,13 @@ class TiffImageFile(ImageFile.ImageFile): ifh = self.fp.read(8) # image file directory (tag dictionary) - self.tag = self.ifd = ImageFileDirectory(ifh) + self.tag_v2 = ImageFileDirectory_v2(ifh) + + # legacy tag/ifd entries will be filled in later + self.tag = self.ifd = None # setup frame pointers - self.__first = self.__next = self.ifd.next + self.__first = self.__next = self.tag_v2.next self.__frame = -1 self.__fp = self.fp self._frame_pos = [] @@ -678,11 +700,13 @@ class TiffImageFile(ImageFile.ImageFile): self._frame_pos.append(self.__next) if DEBUG: print("Loading tags, location: %s" % self.fp.tell()) - self.tag.load(self.fp) - self.__next = self.tag.next + self.tag_v2.load(self.fp) + self.__next = self.tag_v2.next self.__frame += 1 self.fp.seek(self._frame_pos[frame]) - self.tag.load(self.fp) + self.tag_v2.load(self.fp) + # fill the legacy tag/ifd entries + self.tag = self.ifd = ImageFileDirectory_v1.from_v2(self.tag_v2) self.__frame = frame self._setup() @@ -701,20 +725,20 @@ class TiffImageFile(ImageFile.ImageFile): args = (rawmode, 0, 1) elif compression == "jpeg": args = rawmode, "" - if JPEGTABLES in self.tag: + if JPEGTABLES in self.tag_v2: # Hack to handle abbreviated JPEG headers # FIXME This will fail with more than one value - self.tile_prefix, = self.tag[JPEGTABLES] + self.tile_prefix, = self.tag_v2[JPEGTABLES] elif compression == "packbits": args = rawmode elif compression == "tiff_lzw": args = rawmode - if PREDICTOR in self.tag: + if PREDICTOR in self.tag_v2: # Section 14: Differencing Predictor - self.decoderconfig = (self.tag[PREDICTOR],) + self.decoderconfig = (self.tag_v2[PREDICTOR],) - if ICCPROFILE in self.tag: - self.info['icc_profile'] = self.tag[ICCPROFILE] + if ICCPROFILE in self.tag_v2: + self.info['icc_profile'] = self.tag_v2[ICCPROFILE] return args @@ -737,7 +761,7 @@ class TiffImageFile(ImageFile.ImageFile): # (self._compression, (extents tuple), # 0, (rawmode, self._compression, fp)) extents = self.tile[0][1] - args = self.tile[0][3] + (self.ifd.offset,) + args = self.tile[0][3] + (self.tag_v2.offset,) decoder = Image._getdecoder(self.mode, 'libtiff', args, self.decoderconfig) try: @@ -790,18 +814,18 @@ class TiffImageFile(ImageFile.ImageFile): def _setup(self): "Setup this image object based on current tags" - if 0xBC01 in self.tag: + if 0xBC01 in self.tag_v2: raise IOError("Windows Media Photo files not yet supported") # extract relevant tags - self._compression = COMPRESSION_INFO[self.tag.get(COMPRESSION, 1)] - self._planar_configuration = self.tag.get(PLANAR_CONFIGURATION, 1) + self._compression = COMPRESSION_INFO[self.tag_v2.get(COMPRESSION, 1)] + self._planar_configuration = self.tag_v2.get(PLANAR_CONFIGURATION, 1) # photometric is a required tag, but not everyone is reading # the specification - photo = self.tag.get(PHOTOMETRIC_INTERPRETATION, 0) + photo = self.tag_v2.get(PHOTOMETRIC_INTERPRETATION, 0) - fillorder = self.tag.get(FILLORDER, 1) + fillorder = self.tag_v2.get(FILLORDER, 1) if DEBUG: print("*** Summary ***") @@ -811,20 +835,20 @@ class TiffImageFile(ImageFile.ImageFile): print("- fill_order:", fillorder) # size - xsize = self.tag.get(IMAGEWIDTH) - ysize = self.tag.get(IMAGELENGTH) + xsize = self.tag_v2.get(IMAGEWIDTH) + ysize = self.tag_v2.get(IMAGELENGTH) self.size = xsize, ysize if DEBUG: print("- size:", self.size) - format = self.tag.get(SAMPLEFORMAT, (1,)) + format = self.tag_v2.get(SAMPLEFORMAT, (1,)) # mode: check photometric interpretation and bits per pixel key = ( - self.tag.prefix, photo, format, fillorder, - self.tag.get(BITSPERSAMPLE, (1,)), - self.tag.get(EXTRASAMPLES, ()) + self.tag_v2.prefix, photo, format, fillorder, + self.tag_v2.get(BITSPERSAMPLE, (1,)), + self.tag_v2.get(EXTRASAMPLES, ()) ) if DEBUG: print("format key:", key) @@ -841,8 +865,8 @@ class TiffImageFile(ImageFile.ImageFile): self.info["compression"] = self._compression - xres = self.tag.get(X_RESOLUTION, (1, 1)) - yres = self.tag.get(Y_RESOLUTION, (1, 1)) + xres = self.tag_v2.get(X_RESOLUTION, (1, 1)) + yres = self.tag_v2.get(Y_RESOLUTION, (1, 1)) if xres and not isinstance(xres, tuple): xres = (xres, 1.) @@ -851,7 +875,7 @@ class TiffImageFile(ImageFile.ImageFile): if xres and yres: xres = xres[0] / (xres[1] or 1) yres = yres[0] / (yres[1] or 1) - resunit = self.tag.get(RESOLUTION_UNIT, 1) + resunit = self.tag_v2.get(RESOLUTION_UNIT, 1) if resunit == 2: # dots per inch self.info["dpi"] = xres, yres elif resunit == 3: # dots per centimeter. convert to dpi @@ -862,10 +886,10 @@ class TiffImageFile(ImageFile.ImageFile): # build tile descriptors x = y = l = 0 self.tile = [] - if STRIPOFFSETS in self.tag: + if STRIPOFFSETS in self.tag_v2: # striped image - offsets = self.tag[STRIPOFFSETS] - h = self.tag.get(ROWSPERSTRIP, ysize) + offsets = self.tag_v2[STRIPOFFSETS] + h = self.tag_v2.get(ROWSPERSTRIP, ysize) w = self.size[0] if READ_LIBTIFF or self._compression in ["tiff_ccitt", "group3", "group4", "tiff_jpeg", @@ -912,9 +936,9 @@ class TiffImageFile(ImageFile.ImageFile): # https://github.com/python-pillow/Pillow/issues/279 if fillorder == 2: key = ( - self.tag.prefix, photo, format, 1, - self.tag.get(BITSPERSAMPLE, (1,)), - self.tag.get(EXTRASAMPLES, ()) + self.tag_v2.prefix, photo, format, 1, + self.tag_v2.get(BITSPERSAMPLE, (1,)), + self.tag_v2.get(EXTRASAMPLES, ()) ) if DEBUG: print("format key:", key) @@ -952,12 +976,12 @@ class TiffImageFile(ImageFile.ImageFile): x = y = 0 l += 1 a = None - elif TILEOFFSETS in self.tag: + elif TILEOFFSETS in self.tag_v2: # tiled image - w = self.tag.get(322) - h = self.tag.get(323) + w = self.tag_v2.get(322) + h = self.tag_v2.get(323) a = None - for o in self.tag[TILEOFFSETS]: + for o in self.tag_v2[TILEOFFSETS]: if not a: a = self._decoder(rawmode, l) # FIXME: this doesn't work if the image size @@ -981,7 +1005,7 @@ class TiffImageFile(ImageFile.ImageFile): # fixup palette descriptor if self.mode == "P": - palette = [o8(b // 256) for b in self.tag[COLORMAP]] + palette = [o8(b // 256) for b in self.tag_v2[COLORMAP]] self.palette = ImagePalette.raw("RGB;L", b"".join(palette)) # # -------------------------------------------------------------------- @@ -1023,7 +1047,7 @@ def _save(im, fp, filename): except KeyError: raise IOError("cannot write mode %s as TIFF" % im.mode) - ifd = ImageFileDirectory(prefix=prefix) + ifd = ImageFileDirectory_v2(prefix=prefix) compression = im.encoderinfo.get('compression', im.info.get('compression', 'raw')) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index c2c7c0eec..f5df2c301 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -73,18 +73,24 @@ class TestFileTiff(PillowTestCase): def test_xyres_tiff(self): from PIL.TiffImagePlugin import X_RESOLUTION, Y_RESOLUTION filename = "Tests/images/pil168.tif" - for legacy_api in [False, True]: - im = Image.open(filename) - im.tag.legacy_api = legacy_api - if legacy_api: - assert isinstance(im.tag[X_RESOLUTION][0], tuple) - assert isinstance(im.tag[Y_RESOLUTION][0], tuple) - # Try to read a file where X,Y_RESOLUTION are ints - im.tag[X_RESOLUTION] = (72,) - im.tag[Y_RESOLUTION] = (72,) - im.tag.legacy_api = False # _setup assumes the new API. - im._setup() - self.assertEqual(im.info['dpi'], (72., 72.)) + im = Image.open(filename) + + #legacy api + self.assert_(isinstance(im.tag[X_RESOLUTION][0], tuple)) + self.assert_(isinstance(im.tag[Y_RESOLUTION][0], tuple)) + + #v2 api + self.assert_(isinstance(im.tag_v2[X_RESOLUTION], float)) + self.assert_(isinstance(im.tag_v2[Y_RESOLUTION], float)) + + self.assertEqual(im.info['dpi'], (72., 72.)) + + def xtest_int_resolution(self): + # Try to read a file where X,Y_RESOLUTION are ints + im.tag[X_RESOLUTION] = (72,) + im.tag[Y_RESOLUTION] = (72,) + im._setup() + self.assertEqual(im.info['dpi'], (72., 72.)) def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" @@ -94,6 +100,7 @@ class TestFileTiff(PillowTestCase): def test_bad_exif(self): image = Image.open('Tests/images/hopper_bad_exif.jpg') + image._getexif() self.assertRaises(Exception, image._getexif) def test_save_unsupported_mode(self): @@ -218,18 +225,21 @@ class TestFileTiff(PillowTestCase): def test_as_dict(self): # Arrange filename = "Tests/images/pil136.tiff" - for legacy_api in [False, True]: - im = Image.open(filename) - im.tag.legacy_api = legacy_api - self.assertEqual( + im = Image.open(filename) + # v2 interface + self.assertEqual( + im.tag_v2.as_dict(), + {256: 55, 257: 43, 258: (8, 8, 8, 8), 259: 1, + 262: 2, 296: 2, 273: (8,), 338: (1,), 277: 4, + 279: (9460,), 282: 72.0, 283: 72.0, 284: 1}) + + # legacy interface + self.assertEqual( im.tag.as_dict(), {256: (55,), 257: (43,), 258: (8, 8, 8, 8), 259: (1,), 262: (2,), 296: (2,), 273: (8,), 338: (1,), 277: (4,), 279: (9460,), 282: ((720000, 10000),), - 283: ((720000, 10000),), 284: (1,)} if legacy_api else - {256: 55, 257: 43, 258: (8, 8, 8, 8), 259: 1, - 262: 2, 296: 2, 273: (8,), 338: (1,), 277: 4, - 279: (9460,), 282: 72.0, 283: 72.0, 284: 1}) + 283: ((720000, 10000),), 284: (1,)}) def test__delitem__(self): filename = "Tests/images/pil136.tiff" @@ -241,26 +251,26 @@ class TestFileTiff(PillowTestCase): def test_load_byte(self): for legacy_api in [False, True]: - ifd = TiffImagePlugin.ImageFileDirectory() + ifd = TiffImagePlugin.ImageFileDirectory_v2() ifd.legacy_api = legacy_api data = b"abc" ret = ifd.load_byte(data) self.assertEqual(ret, b"abc" if legacy_api else (97, 98, 99)) def test_load_string(self): - ifd = TiffImagePlugin.ImageFileDirectory() + ifd = TiffImagePlugin.ImageFileDirectory_v2() data = b"abc\0" ret = ifd.load_string(data) self.assertEqual(ret, "abc") def test_load_float(self): - ifd = TiffImagePlugin.ImageFileDirectory() + ifd = TiffImagePlugin.ImageFileDirectory_v2() data = b"abcdabcd" ret = ifd.load_float(data) self.assertEqual(ret, (1.6777999408082104e+22, 1.6777999408082104e+22)) def test_load_double(self): - ifd = TiffImagePlugin.ImageFileDirectory() + ifd = TiffImagePlugin.ImageFileDirectory_v2() data = b"abcdefghabcdefgh" ret = ifd.load_double(data) self.assertEqual(ret, (8.540883223036124e+194, 8.540883223036124e+194)) @@ -297,7 +307,10 @@ class TestFileTiff(PillowTestCase): self.assertEqual(im.mode, "L") self.assert_image_similar(im, original, 7.3) - def test_page_number_x_0(self): +### +# UNDONE +### Segfaulting + def xtest_page_number_x_0(self): # Issue 973 # Test TIFF with tag 297 (Page Number) having value of 0 0. # The first number is the current page number. @@ -318,13 +331,15 @@ class TestFileTiff(PillowTestCase): filename = self.tempfile("temp.tif") hopper("RGB").save(filename, **kwargs) from PIL.TiffImagePlugin import X_RESOLUTION, Y_RESOLUTION - for legacy_api in [False, True]: - im = Image.open(filename) - im.tag.legacy_api = legacy_api - self.assertEqual(im.tag[X_RESOLUTION][0][0] if legacy_api - else im.tag[X_RESOLUTION], 72) - self.assertEqual(im.tag[Y_RESOLUTION][0][0] if legacy_api - else im.tag[Y_RESOLUTION], 36) + im = Image.open(filename) + + # legacy interface + self.assertEqual(im.tag[X_RESOLUTION][0][0], 72) + self.assertEqual(im.tag[Y_RESOLUTION][0][0], 36) + + # v2 interface + self.assertEqual(im.tag_v2[X_RESOLUTION], 72) + self.assertEqual(im.tag_v2[Y_RESOLUTION], 36) def test_deprecation_warning_with_spaces(self): kwargs = {'resolution unit': 'inch', @@ -334,13 +349,16 @@ class TestFileTiff(PillowTestCase): self.assert_warning(DeprecationWarning, lambda: hopper("RGB").save(filename, **kwargs)) from PIL.TiffImagePlugin import X_RESOLUTION, Y_RESOLUTION - for legacy_api in [False, True]: - im = Image.open(filename) - im.tag.legacy_api = legacy_api - self.assertEqual(im.tag[X_RESOLUTION][0][0] if legacy_api - else im.tag[X_RESOLUTION], 36) - self.assertEqual(im.tag[Y_RESOLUTION][0][0] if legacy_api - else im.tag[Y_RESOLUTION], 72) + + im = Image.open(filename) + + # legacy interface + self.assertEqual(im.tag[X_RESOLUTION][0][0], 36) + self.assertEqual(im.tag[Y_RESOLUTION][0][0], 72) + + # v2 interface + self.assertEqual(im.tag_v2[X_RESOLUTION], 36) + self.assertEqual(im.tag_v2[Y_RESOLUTION], 72) if __name__ == '__main__': From 5f9fff02155957287154787a9d1d235845108cea Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 11 Sep 2015 10:45:10 -0700 Subject: [PATCH 0537/1037] Restoring bad exif handling --- PIL/TiffImagePlugin.py | 90 +++++++++++++++++++++++------------------ Tests/test_file_tiff.py | 8 ++-- 2 files changed, 56 insertions(+), 42 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index de2381b34..4638acd8f 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -468,55 +468,67 @@ class ImageFileDirectory_v2(collections.MutableMapping): return b"".join(self._pack("2L", *_limit_rational(frac, 2 ** 30)) for frac in values) + def _ensure_read(self, fp, size): + ret = fp.read(size) + if len(ret) != size: + raise IOError("Corrupt EXIF data. " + + "Expecting to read %d bytes but only got %d. " % + (size, len(ret))) + return ret + def load(self, fp): self.reset() self._offset = fp.tell() - for i in range(self._unpack("H", fp.read(2))[0]): - tag, typ, count, data = self._unpack("HHL4s", fp.read(12)) - if DEBUG: - tagname = TAGS.get(tag, TagInfo()).name - typname = TYPES.get(typ, "unknown") - print("tag: %s (%d) - type: %s (%d)" % - (tagname, tag, typname, typ), end=" ") - - try: - unit_size, handler = self._load_dispatch[typ] - except KeyError: + try: + for i in range(self._unpack("H", self._ensure_read(fp,2))[0]): + tag, typ, count, data = self._unpack("HHL4s", self._ensure_read(fp,12)) if DEBUG: - print("- unsupported type", typ) - continue # ignore unsupported type - size = count * unit_size - if size > 4: - here = fp.tell() - offset, = self._unpack("L", data) - if DEBUG: - print("Tag Location: %s - Data Location: %s" % - (here, offset), end=" ") - fp.seek(offset) - data = ImageFile._safe_read(fp, size) - fp.seek(here) - else: - data = data[:size] + tagname = TAGS.get(tag, TagInfo()).name + typname = TYPES.get(typ, "unknown") + print("tag: %s (%d) - type: %s (%d)" % + (tagname, tag, typname, typ), end=" ") - if len(data) != size: - warnings.warn("Possibly corrupt EXIF data. " - "Expecting to read %d bytes but only got %d. " - "Skipping tag %s" % (size, len(data), tag)) - continue - - self._tagdata[tag] = data - self.tagtype[tag] = typ - - if DEBUG: - if size > 32: - print("- value: " % size) + try: + unit_size, handler = self._load_dispatch[typ] + except KeyError: + if DEBUG: + print("- unsupported type", typ) + continue # ignore unsupported type + size = count * unit_size + if size > 4: + here = fp.tell() + offset, = self._unpack("L", data) + if DEBUG: + print("Tag Location: %s - Data Location: %s" % + (here, offset), end=" ") + fp.seek(offset) + data = ImageFile._safe_read(fp, size) + fp.seek(here) else: - print("- value:", self[tag]) + data = data[:size] - self.next, = self._unpack("L", fp.read(4)) + if len(data) != size: + warnings.warn("Possibly corrupt EXIF data. " + "Expecting to read %d bytes but only got %d. " + "Skipping tag %s" % (size, len(data), tag)) + continue + self._tagdata[tag] = data + self.tagtype[tag] = typ + + if DEBUG: + if size > 32: + print("- value: " % size) + else: + print("- value:", self[tag]) + + self.next, = self._unpack("L", self._ensure_read(fp,4)) + except IOError as msg: + warnings.warn(str(msg)) + return + def save(self, fp): if fp.tell() == 0: # skip TIFF header on subsequent pages diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index f5df2c301..7ec6d9e60 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -99,9 +99,11 @@ class TestFileTiff(PillowTestCase): lambda: TiffImagePlugin.TiffImageFile(invalid_file)) def test_bad_exif(self): - image = Image.open('Tests/images/hopper_bad_exif.jpg') - image._getexif() - self.assertRaises(Exception, image._getexif) + try: + Image.open('Tests/images/hopper_bad_exif.jpg')._getexif() + except struct.error: + self.fail( + "Bad EXIF data passed incorrect values to _binary unpack") def test_save_unsupported_mode(self): im = hopper("HSV") From 426c9d8fc2b9407f4df8fb8ce27acca96333a4ca Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 11 Sep 2015 14:24:35 -0700 Subject: [PATCH 0538/1037] test failing update --- Tests/test_file_tiff_metadata.py | 99 +++++++++++++++----------------- 1 file changed, 47 insertions(+), 52 deletions(-) diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index df1934482..449c67517 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -34,64 +34,59 @@ class TestFileTiffMetadata(PillowTestCase): img.save(f, tiffinfo=info) - for legacy_api in [False, True]: - loaded = Image.open(f) - loaded.tag.legacy_api = legacy_api + loaded = Image.open(f) - self.assertEqual(loaded.tag[50838], - (len(basetextdata + " ?"),) if legacy_api else len(basetextdata + " ?")) - self.assertEqual(loaded.tag[50839], basetextdata + " ?") - loaded_float = loaded.tag[tag_ids['RollAngle']] - if legacy_api: - loaded_float = loaded_float[0] - self.assertAlmostEqual(loaded_float, floatdata, places=5) - loaded_double = loaded.tag[tag_ids['YawAngle']] - if legacy_api: - loaded_double = loaded_double[0] - self.assertAlmostEqual(loaded_double, doubledata) + self.assertEqual(loaded.tag[50838], (len(basetextdata + " ?"),)) + self.assertEqual(loaded.tag_v2[50838], len(basetextdata + " ?")) + + self.assertEqual(loaded.tag[50839], basetextdata + " ?") + self.assertEqual(loaded.tag_v2[50839], basetextdata + " ?") + + + loaded_float = loaded.tag[tag_ids['RollAngle']][0] + self.assertAlmostEqual(loaded_float, floatdata, places=5) + loaded_double = loaded.tag[tag_ids['YawAngle']][0] + self.assertAlmostEqual(loaded_double, doubledata) def test_read_metadata(self): - for legacy_api in [False, True]: - img = Image.open('Tests/images/hopper_g4.tif') - img.tag.legacy_api = legacy_api + img = Image.open('Tests/images/hopper_g4.tif') - known = {'YResolution': ((4294967295, 113653537),), - 'PlanarConfiguration': (1,), - 'BitsPerSample': (1,), - 'ImageLength': (128,), - 'Compression': (4,), - 'FillOrder': (1,), - 'RowsPerStrip': (128,), - 'ResolutionUnit': (3,), - 'PhotometricInterpretation': (0,), - 'PageNumber': (0, 1), - 'XResolution': ((4294967295, 113653537),), - 'ImageWidth': (128,), - 'Orientation': (1,), - 'StripByteCounts': (1968,), - 'SamplesPerPixel': (1,), - 'StripOffsets': (8,) - } if legacy_api else { - 'YResolution': 4294967295 / 113653537, - 'PlanarConfiguration': 1, - 'BitsPerSample': (1,), - 'ImageLength': 128, - 'Compression': 4, - 'FillOrder': 1, - 'RowsPerStrip': 128, - 'ResolutionUnit': 3, - 'PhotometricInterpretation': 0, - 'PageNumber': (0, 1), - 'XResolution': 4294967295 / 113653537, - 'ImageWidth': 128, - 'Orientation': 1, - 'StripByteCounts': (1968,), - 'SamplesPerPixel': 1, - 'StripOffsets': (8,) - } + self.assertEqual({'YResolution': 4294967295 / 113653537, + 'PlanarConfiguration': 1, + 'BitsPerSample': (1,), + 'ImageLength': 128, + 'Compression': 4, + 'FillOrder': 1, + 'RowsPerStrip': 128, + 'ResolutionUnit': 3, + 'PhotometricInterpretation': 0, + 'PageNumber': (0, 1), + 'XResolution': 4294967295 / 113653537, + 'ImageWidth': 128, + 'Orientation': 1, + 'StripByteCounts': (1968,), + 'SamplesPerPixel': 1, + 'StripOffsets': (8,) + }, img.tag_v2.named()) - self.assertEqual(known, img.tag.named()) + self.assertEqual({'YResolution': ((4294967295, 113653537),), + 'PlanarConfiguration': (1,), + 'BitsPerSample': (1,), + 'ImageLength': (128,), + 'Compression': (4,), + 'FillOrder': (1,), + 'RowsPerStrip': (128,), + 'ResolutionUnit': (3,), + 'PhotometricInterpretation': (0,), + 'PageNumber': (0, 1), + 'XResolution': ((4294967295, 113653537),), + 'ImageWidth': (128,), + 'Orientation': (1,), + 'StripByteCounts': (1968,), + 'SamplesPerPixel': (1,), + 'StripOffsets': (8,) + }, img.tag.named()) def test_write_metadata(self): """ Test metadata writing through the python code """ From b1fdff4034d3f2c33ba534ed10d8fd966fd32993 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 11 Sep 2015 23:44:23 +0100 Subject: [PATCH 0539/1037] In a twisty maze of bytes, text and arbitrary metadata, py2 and py3. New IFD is putting textdata in type7 metadata and returning bytes, old one put it in type 2 string and returned a string. This may be an issue --- PIL/TiffImagePlugin.py | 2 +- Tests/test_file_tiff_metadata.py | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 4638acd8f..243524e0f 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -361,7 +361,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): self.tagtype[tag] = 2 if self.tagtype[tag] == 7 and bytes is not str: - values = [value.encode("ascii") if isinstance(value, str) else value + values = [value.encode("ascii",'replace') if isinstance(value, str) else value for value in values] values = tuple(info.cvt_enum(value) for value in values) if info.length == 1: diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 449c67517..0a7425d7b 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -18,29 +18,32 @@ class TestFileTiffMetadata(PillowTestCase): img = hopper() basetextdata = "This is some arbitrary metadata for a text field" - textdata = basetextdata + " \xff" + textdata = basetextdata + " \xff" + reloaded_textdata = basetextdata.encode('ascii') + b" ?" floatdata = 12.345 doubledata = 67.89 info = TiffImagePlugin.ImageFileDirectory() - info[tag_ids['ImageJMetaDataByteCounts']] = len(textdata) + info[tag_ids['ImageJMetaDataByteCounts']] = len(reloaded_textdata) info[tag_ids['ImageJMetaData']] = textdata info[tag_ids['RollAngle']] = floatdata info.tagtype[tag_ids['RollAngle']] = 11 info[tag_ids['YawAngle']] = doubledata info.tagtype[tag_ids['YawAngle']] = 12 + print(info.tagtype) + f = self.tempfile("temp.tif") img.save(f, tiffinfo=info) loaded = Image.open(f) - self.assertEqual(loaded.tag[50838], (len(basetextdata + " ?"),)) - self.assertEqual(loaded.tag_v2[50838], len(basetextdata + " ?")) + self.assertEqual(loaded.tag[50838], (len(reloaded_textdata),)) + self.assertEqual(loaded.tag_v2[50838], len(reloaded_textdata)) - self.assertEqual(loaded.tag[50839], basetextdata + " ?") - self.assertEqual(loaded.tag_v2[50839], basetextdata + " ?") + self.assertEqual(loaded.tag[50839], reloaded_textdata) + self.assertEqual(loaded.tag_v2[50839], reloaded_textdata) loaded_float = loaded.tag[tag_ids['RollAngle']][0] From 0c35194167846698b3f0173dc09b57aba27a327f Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sat, 12 Sep 2015 10:11:10 +0100 Subject: [PATCH 0540/1037] rewrite of #1416 working --- Tests/test_file_tiff_metadata.py | 36 +++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 0a7425d7b..ed968ef00 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -17,34 +17,50 @@ class TestFileTiffMetadata(PillowTestCase): img = hopper() + # Behaviour change: + # Pre ifd rewrite, ImageJMetaData was being written as a string(2), + # Post ifd rewrite, it's defined as arbitrary bytes(7). It should + # roundtrip with the actual bytes, rather than stripped text + # of the premerge tests. + # + # For text items, we still have to decode('ascii','replace') because + # the tiff file format can't take 8 bit bytes in that field. + basetextdata = "This is some arbitrary metadata for a text field" - textdata = basetextdata + " \xff" - reloaded_textdata = basetextdata.encode('ascii') + b" ?" + bindata = basetextdata.encode('ascii') + b" \xff" + textdata = basetextdata + " " + chr(255) + reloaded_textdata = basetextdata + " ?" floatdata = 12.345 doubledata = 67.89 info = TiffImagePlugin.ImageFileDirectory() - info[tag_ids['ImageJMetaDataByteCounts']] = len(reloaded_textdata) - info[tag_ids['ImageJMetaData']] = textdata + ImageJMetaData = tag_ids['ImageJMetaData'] + ImageJMetaDataByteCounts = tag_ids['ImageJMetaDataByteCounts'] + ImageDescription = tag_ids['ImageDescription'] + + info[ImageJMetaDataByteCounts] = len(bindata) + info[ImageJMetaData] = bindata info[tag_ids['RollAngle']] = floatdata info.tagtype[tag_ids['RollAngle']] = 11 info[tag_ids['YawAngle']] = doubledata info.tagtype[tag_ids['YawAngle']] = 12 - print(info.tagtype) - + info[ImageDescription] = textdata + f = self.tempfile("temp.tif") img.save(f, tiffinfo=info) loaded = Image.open(f) - self.assertEqual(loaded.tag[50838], (len(reloaded_textdata),)) - self.assertEqual(loaded.tag_v2[50838], len(reloaded_textdata)) + self.assertEqual(loaded.tag[ImageJMetaDataByteCounts], (len(bindata),)) + self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], len(bindata)) - self.assertEqual(loaded.tag[50839], reloaded_textdata) - self.assertEqual(loaded.tag_v2[50839], reloaded_textdata) + self.assertEqual(loaded.tag[ImageJMetaData], bindata) + self.assertEqual(loaded.tag_v2[ImageJMetaData], bindata) + self.assertEqual(loaded.tag[ImageDescription], (reloaded_textdata,)) + self.assertEqual(loaded.tag_v2[ImageDescription], reloaded_textdata) loaded_float = loaded.tag[tag_ids['RollAngle']][0] self.assertAlmostEqual(loaded_float, floatdata, places=5) From a183fc4030f262be9d9e10216f111aaf132d03b4 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 12 Sep 2015 17:31:38 +0300 Subject: [PATCH 0541/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 1f220e7dd..0bb8bbc13 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -58,7 +58,7 @@ Changelog (Pillow) - In tutorial of pasting images, add to mask text #1389 [merriam] -- Style/health fixes #1391, #1397, #1417 +- Style/health fixes #1391, #1397, #1417, #1418 [radarhere] - Test on Python 3.5 dev and 3.6 nightly #1361 From 156972874da19642d92b9b328bb9c7d8782f02af Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 13 Sep 2015 10:53:47 +0100 Subject: [PATCH 0542/1037] missed a set of _v2 versioning --- PIL/TiffImagePlugin.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 243524e0f..f8fbae224 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -1085,16 +1085,13 @@ def _save(im, fp, filename): # additions written by Greg Couch, gregc@cgl.ucsf.edu # inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com - if hasattr(im, 'tag'): + if hasattr(im, 'tag_v2'): # preserve tags from original TIFF image file - orig_api = im.tag.legacy_api - im.tag.legacy_api = False for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP): - if key in im.tag: - ifd[key] = im.tag[key] - ifd.tagtype[key] = im.tag.tagtype.get(key, None) - im.tag.legacy_api = orig_api + if key in im.tag_v2: + ifd[key] = im.tag_v2[key] + ifd.tagtype[key] = im.tag_v2.tagtype.get(key, None) # preserve ICC profile (should also work when saving other formats # which support profiles as TIFF) -- 2008-06-06 Florian Hoech From a9396ab4120f68759a1eda00f078d5d5ba00ac4f Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 13 Sep 2015 11:07:43 +0100 Subject: [PATCH 0543/1037] convert load_* functions to pure functions with no state --- PIL/TiffImagePlugin.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index f8fbae224..248fab15f 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -319,7 +319,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): data = self._tagdata[tag] typ = self.tagtype[tag] size, handler = self._load_dispatch[typ] - self[tag] = handler(self, data) # check type + self[tag] = handler(self, self.legacy_api, data) # check type val = self._tags[tag] if self.legacy_api and not isinstance(val, (tuple, bytes)): val = val, @@ -404,7 +404,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): idx, fmt, name = idx_fmt_name TYPES[idx] = name size = struct.calcsize("=" + fmt) - _load_dispatch[idx] = size, lambda self, data: ( + _load_dispatch[idx] = size, lambda self, legacy_api, data: ( self._unpack("{0}{1}".format(len(data) // size, fmt), data)) _write_dispatch[idx] = lambda self, *values: ( b"".join(self._pack(fmt, value) for value in values)) @@ -415,8 +415,8 @@ class ImageFileDirectory_v2(collections.MutableMapping): (9, "l", "signed long"), (11, "f", "float"), (12, "d", "double")])) @_register_loader(1, 1) # Basic type, except for the legacy API. - def load_byte(self, data): - return (data if self.legacy_api else + def load_byte(self, legacy_api, data): + return (data if legacy_api else tuple(map(ord, data) if bytes is str else data)) @_register_writer(1) # Basic type, except for the legacy API. @@ -424,7 +424,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): return data @_register_loader(2, 1) - def load_string(self, data): + def load_string(self, legacy_api, data): if data.endswith(b"\0"): data = data[:-1] return data.decode("latin-1", "replace") @@ -437,9 +437,9 @@ class ImageFileDirectory_v2(collections.MutableMapping): return b"" + value.encode('ascii', 'replace') + b"\0" @_register_loader(5, 8) - def load_rational(self, data): + def load_rational(self, legacy_api, data): vals = self._unpack("{0}L".format(len(data) // 4), data) - combine = lambda a, b: (a, b) if self.legacy_api else a / b + combine = lambda a, b: (a, b) if legacy_api else a / b return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2])) @@ -449,7 +449,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): for frac in values) @_register_loader(7, 1) - def load_undefined(self, data): + def load_undefined(self, legacy_api, data): return data @_register_writer(7) @@ -457,9 +457,9 @@ class ImageFileDirectory_v2(collections.MutableMapping): return value @_register_loader(10, 8) - def load_signed_rational(self, data): + def load_signed_rational(self, legacy_api, data): vals = self._unpack("{0}l".format(len(data) // 4), data) - combine = lambda a, b: (a, b) if self.legacy_api else a / b + combine = lambda a, b: (a, b) if legacy_api else a / b return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2])) From e1236d702d859f2360827823e6904d5c205a2ef0 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 13 Sep 2015 14:01:01 +0100 Subject: [PATCH 0544/1037] v1/v2 tag storage in IFD, legacy_api as a parameter to _saveitem, save both _tags_v* when saving as legacy api --- PIL/TiffImagePlugin.py | 112 ++++++++++++++++++++++++++++++++--------- 1 file changed, 88 insertions(+), 24 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 248fab15f..e1738cee9 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -244,15 +244,26 @@ class ImageFileDirectory_v2(collections.MutableMapping): * self.tagtype = {} Key: numerical tiff tag number Value: integer corresponding to the data type from `TiffTags.TYPES` - + """ + """ + Documentation: + 'internal' - * self._tags = {} Key: numerical tiff tag number - Value: decoded data, as tuple for multiple values + * self._tags_v2 = {} Key: numerical tiff tag number + Value: decoded data, as tuple for multiple values * self._tagdata = {} Key: numerical tiff tag number Value: undecoded byte string from file - + * self._tags_v1 = {} Key: numerical tiff tag number + Value: decoded data in the v1 format + Tags will be found in the private attributes self._tagdata, and in - self._tags once decoded. + self._tags_v2 once decoded. + + If legacy_api is true, then decoded tags will be populated into both + _tags_v1 and _tags_v2. _Tags_v2 will be used if this IFD is used in + the TIFF save routine. + + Tags will be read from tags_v1 if legacy_api == true. """ def __init__(self, ifh=b"II\052\0\0\0\0\0", prefix=None): @@ -278,19 +289,18 @@ class ImageFileDirectory_v2(collections.MutableMapping): self.reset() self.next, = self._unpack("L", ifh[4:]) self._legacy_api = False - + prefix = property(lambda self: self._prefix) offset = property(lambda self: self._offset) legacy_api = property(lambda self: self._legacy_api) @legacy_api.setter def legacy_api(self, value): - if value != self._legacy_api: - self._tags.clear() - self._legacy_api = value + raise Exception("Not allowing setting of legacy api") def reset(self): - self._tags = {} + self._tags_v1 = {} # will remain empty if legacy_api is false + self._tags_v2 = {} # main tag storage self._tagdata = {} self.tagtype = {} # added 2008-06-05 by Florian Hoech self._next = None @@ -312,27 +322,30 @@ class ImageFileDirectory_v2(collections.MutableMapping): for code, value in self.items()) def __len__(self): - return len(set(self._tagdata) | set(self._tags)) + return len(set(self._tagdata) | set(self._tags_v2)) def __getitem__(self, tag): - if tag not in self._tags: # unpack on the fly + if tag not in self._tags_v2: # unpack on the fly data = self._tagdata[tag] typ = self.tagtype[tag] size, handler = self._load_dispatch[typ] self[tag] = handler(self, self.legacy_api, data) # check type - val = self._tags[tag] + val = self._tags_v2[tag] if self.legacy_api and not isinstance(val, (tuple, bytes)): val = val, return val def __contains__(self, tag): - return tag in self._tags or tag in self._tagdata + return tag in self._tags_v2 or tag in self._tagdata if bytes is str: def has_key(self, tag): return tag in self def __setitem__(self, tag, value): + self._setitem(tag, value, self.legacy_api) + + def _setitem(self, tag, value, legacy_api): basetypes = (Number, bytes, str) if bytes is str: basetypes += unicode, @@ -363,20 +376,25 @@ class ImageFileDirectory_v2(collections.MutableMapping): if self.tagtype[tag] == 7 and bytes is not str: values = [value.encode("ascii",'replace') if isinstance(value, str) else value for value in values] + values = tuple(info.cvt_enum(value) for value in values) + + dest = self._tags_v1 if legacy_api else self._tags_v2 + if info.length == 1: - if self.legacy_api and self.tagtype[tag] in [5, 10]: + if legacy_api and self.tagtype[tag] in [5, 10]: values = values, - self._tags[tag], = values + dest[tag], = values else: - self._tags[tag] = values + dest[tag] = values def __delitem__(self, tag): - self._tags.pop(tag, None) + self._tags_v2.pop(tag, None) + self._tags_v1.pop(tag, None) self._tagdata.pop(tag, None) def __iter__(self): - return iter(set(self._tagdata) | set(self._tags)) + return iter(set(self._tagdata) | set(self._tags_v2)) def _unpack(self, fmt, data): return struct.unpack(self._endian + fmt, data) @@ -536,15 +554,15 @@ class ImageFileDirectory_v2(collections.MutableMapping): fp.write(self._prefix + self._pack("HL", 42, 8)) # FIXME What about tagdata? - fp.write(self._pack("H", len(self._tags))) + fp.write(self._pack("H", len(self._tags_v2))) entries = [] - offset = fp.tell() + len(self._tags) * 12 + 4 + offset = fp.tell() + len(self._tags_v2) * 12 + 4 stripoffsets = None # pass 1: convert tags to binary format # always write tags in ascending order - for tag, value in sorted(self._tags.items()): + for tag, value in sorted(self._tags_v2.items()): if tag == STRIPOFFSETS: stripoffsets = len(entries) typ = self.tagtype.get(tag) @@ -609,18 +627,62 @@ del _load_dispatch, _write_dispatch, idx, name class ImageFileDirectory_v1(ImageFileDirectory_v2): def __init__(self, *args, **kwargs): ImageFileDirectory_v2.__init__(self, *args, **kwargs) - self.legacy_api=True + self._legacy_api=True #insert deprecation warning here. - tags = property(lambda self: self._tags) + tags = property(lambda self: self._tags_v1) tagdata = property(lambda self: self._tagdata) @classmethod def from_v2(cls, original): + """ returns: ImageFileDirectory_v1 + + Returns an ImageFileDirectory_v1 instance with the same + data as is contained in the original ImageFileDirectory_v2 + instance """ + ifd = cls(prefix=original.prefix) ifd._tagdata = original._tagdata ifd.tagtype = original.tagtype return ifd + + def to_v2(self): + """ returns: ImageFileDirectory_v2 + + Returns an ImageFileDirectory_v2 instance with the same + data as is contained in this ImageFileDirectory_v1 instance """ + + ifd = ImageFileDirectory_v2(prefix=self.prefix) + ifd._tagdata = dict(self._tagdata) + ifd.tagtype = dict(self.tagtype) + ifd._tags_v2 = dict(self._tags_v2) + return ifd + + def __contains__(self, tag): + return tag in self._tags_v1 or tag in self._tagdata + + def __len__(self): + return len(set(self._tagdata) | set(self._tags_v1)) + + def __iter__(self): + return iter(set(self._tagdata) | set(self._tags_v1)) + + def __setitem__(self, tag, value): + for legacy_api in (False,True): + self._setitem(tag, value, legacy_api) + + def __getitem__(self, tag): + if tag not in self._tags_v1: # unpack on the fly + data = self._tagdata[tag] + typ = self.tagtype[tag] + size, handler = self._load_dispatch[typ] + for legacy in (False, True): + self._setitem(tag, handler(self, legacy, data), legacy) + val = self._tags_v1[tag] + if not isinstance(val, (tuple, bytes)): + val = val, + return val + # undone -- switch this pointer when IFD_LEGACY_API == False ImageFileDirectory = ImageFileDirectory_v1 @@ -1076,6 +1138,8 @@ def _save(im, fp, filename): info = im.encoderinfo.get("tiffinfo", {}) if DEBUG: print("Tiffinfo Keys: %s" % list(info)) + if isinstance(info, ImageFileDirectory_v1): + info = info.to_v2() for key in info: ifd[key] = info.get(key) try: From 26366798686049656f68509ac6fbc93d49cd4324 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 13 Sep 2015 14:08:49 +0100 Subject: [PATCH 0545/1037] updating tests for legacy_api api change --- Tests/test_file_libtiff.py | 13 +++++++++---- Tests/test_file_tiff.py | 9 ++++----- Tests/test_file_tiff_metadata.py | 6 +++--- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 33d77f558..5d4016555 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -127,11 +127,14 @@ class TestFileLibTiff(LibTiffTestCase): """ Test metadata writing through libtiff """ for legacy_api in [False, True]: img = Image.open('Tests/images/hopper_g4.tif') - img.tag.legacy_api = legacy_api f = self.tempfile('temp.tiff') img.save(f, tiffinfo=img.tag) - original = img.tag.named() + + if legacy_api: + original = img.tag.named() + else: + original = img.tag_v2.named() # PhotometricInterpretation is set from SAVE_INFO, # not the original image. @@ -139,8 +142,10 @@ class TestFileLibTiff(LibTiffTestCase): 'PhotometricInterpretation'] loaded = Image.open(f) - loaded.tag.legacy_api = legacy_api - reloaded = loaded.tag.named() + if legacy_api: + reloaded = loaded.tag.named() + else: + reloaded = loaded.tag_v2.named() for tag, value in itertools.chain(reloaded.items(), original.items()): diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 7ec6d9e60..2d787e66d 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -254,27 +254,26 @@ class TestFileTiff(PillowTestCase): def test_load_byte(self): for legacy_api in [False, True]: ifd = TiffImagePlugin.ImageFileDirectory_v2() - ifd.legacy_api = legacy_api data = b"abc" - ret = ifd.load_byte(data) + ret = ifd.load_byte(legacy_api, data) self.assertEqual(ret, b"abc" if legacy_api else (97, 98, 99)) def test_load_string(self): ifd = TiffImagePlugin.ImageFileDirectory_v2() data = b"abc\0" - ret = ifd.load_string(data) + ret = ifd.load_string(False, data) self.assertEqual(ret, "abc") def test_load_float(self): ifd = TiffImagePlugin.ImageFileDirectory_v2() data = b"abcdabcd" - ret = ifd.load_float(data) + ret = ifd.load_float(False, data) self.assertEqual(ret, (1.6777999408082104e+22, 1.6777999408082104e+22)) def test_load_double(self): ifd = TiffImagePlugin.ImageFileDirectory_v2() data = b"abcdefghabcdefgh" - ret = ifd.load_double(data) + ret = ifd.load_double(False, data) self.assertEqual(ret, (8.540883223036124e+194, 8.540883223036124e+194)) def test_seek(self): diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index ed968ef00..05aa98bb5 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -17,7 +17,7 @@ class TestFileTiffMetadata(PillowTestCase): img = hopper() - # Behaviour change: + # Behaviour change: re #1416 # Pre ifd rewrite, ImageJMetaData was being written as a string(2), # Post ifd rewrite, it's defined as arbitrary bytes(7). It should # roundtrip with the actual bytes, rather than stripped text @@ -116,8 +116,8 @@ class TestFileTiffMetadata(PillowTestCase): loaded = Image.open(f) - original = img.tag.named() - reloaded = loaded.tag.named() + original = img.tag_v2.named() + reloaded = loaded.tag_v2.named() ignored = [ 'StripByteCounts', 'RowsPerStrip', 'PageNumber', 'StripOffsets'] From bb75b2d6adec0c3dbf9a6239d49a0d46d9947ac1 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 13 Sep 2015 14:09:42 +0100 Subject: [PATCH 0546/1037] Added doc comment --- PIL/TiffTags.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py index 6cc173332..3e7aa3613 100644 --- a/PIL/TiffTags.py +++ b/PIL/TiffTags.py @@ -31,7 +31,9 @@ class TagInfo(namedtuple("_TagInfo", "value name type length enum")): ## # Map tag numbers to tag info. - +# +# id: (Name, Type, Length, enum_values) +# TAGS = { 254: ("NewSubfileType", 4, 1), From ca24a441006b1c613617b4035848bfcd23177518 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 13 Sep 2015 14:16:12 +0100 Subject: [PATCH 0547/1037] Rewrap intelligently --- PIL/TiffImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index e1738cee9..0044c567e 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -1123,8 +1123,8 @@ def _save(im, fp, filename): ifd = ImageFileDirectory_v2(prefix=prefix) - compression = im.encoderinfo.get('compression', im.info.get('compression', - 'raw')) + compression = im.encoderinfo.get('compression', + im.info.get('compression', 'raw')) libtiff = WRITE_LIBTIFF or compression != 'raw' From fce11e0b7b48339e88f552654b7d96457f97868a Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 13 Sep 2015 14:31:49 +0100 Subject: [PATCH 0548/1037] Revert "Style changes" --- PIL/TiffImagePlugin.py | 16 ++++++++++------ mp_compile.py | 6 ++++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 4d16ced48..b0e7c9639 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -428,7 +428,8 @@ class ImageFileDirectory(collections.MutableMapping): ifd = fp.read(12) if len(ifd) != 12: - warnings.warn("Possibly corrupt EXIF data. Expecting to read 12 bytes but only got %d." + warnings.warn("Possibly corrupt EXIF data. " + "Expecting to read 12 bytes but only got %d." % (len(ifd))) continue @@ -466,8 +467,9 @@ class ImageFileDirectory(collections.MutableMapping): data = ifd[8:8+size] if len(data) != size: - warnings.warn("Possibly corrupt EXIF data. Expecting to read %d bytes but only got %d. Skipping tag %s" - % (size, len(data), tag)) + warnings.warn("Possibly corrupt EXIF data. " + "Expecting to read %d bytes but only got %d. " + "Skipping tag %s" % (size, len(data), tag)) continue self.tagdata[tag] = data @@ -482,7 +484,8 @@ class ImageFileDirectory(collections.MutableMapping): ifd = fp.read(4) if len(ifd) != 4: - warnings.warn("Possibly corrupt EXIF data. Expecting to read 4 bytes but only got %d." + warnings.warn("Possibly corrupt EXIF data. " + "Expecting to read 4 bytes but only got %d." % (len(ifd))) return @@ -701,8 +704,9 @@ class TiffImageFile(ImageFile.ImageFile): if not self.__next: raise EOFError("no more images in TIFF file") if DEBUG: - print("Seeking to frame %s, on frame %s, __next %s, location: %s" - % (frame, self.__frame, self.__next, self.fp.tell())) + print("Seeking to frame %s, on frame %s, " + "__next %s, location: %s" % + (frame, self.__frame, self.__next, self.fp.tell())) # reset python3 buffered io handle in case fp # was passed to libtiff, invalidating the buffer self.fp.tell() diff --git a/mp_compile.py b/mp_compile.py index 93ddbe99b..078f62476 100644 --- a/mp_compile.py +++ b/mp_compile.py @@ -75,8 +75,10 @@ def install(): pool = Pool(2) CCompiler.compile = _mp_compile except Exception as msg: - print("Exception installing mp_compile, proceeding without: %s" % msg) + print("Exception installing mp_compile, proceeding without:" + "%s" % msg) else: - print("Single threaded build, not installing mp_compile: %s processes" % MAX_PROCS) + print("Single threaded build, not installing mp_compile:" + "%s processes" % MAX_PROCS) install() From f3ab9b9f812402c0ac32bdd6135a093425435e22 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 13 Sep 2015 14:38:51 +0100 Subject: [PATCH 0549/1037] temp removing segfaulting test on travis --- Tests/test_file_libtiff.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 5d4016555..0a64b0f8b 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -198,7 +198,7 @@ class TestFileLibTiff(LibTiffTestCase): # UNDONE - libtiff defaults to writing in native endian, so # on big endian, we'll get back mode = 'I;16B' here. - def test_big_endian(self): + def xtest_big_endian(self): im = Image.open('Tests/images/16bit.MM.deflate.tif') self.assertEqual(im.getpixel((0, 0)), 480) From 4596df45c18d3f5fe4088bfbf8bf496406a24f8d Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 13 Sep 2015 15:15:13 +0100 Subject: [PATCH 0550/1037] Versioned interface for TiffTags --- PIL/TiffImagePlugin.py | 10 +++++----- PIL/TiffTags.py | 20 ++++++++++++++------ Tests/test_file_tiff_metadata.py | 2 +- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 0044c567e..648aa68f0 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -55,7 +55,7 @@ import struct import sys import warnings -from .TiffTags import TAGS, TYPES, TagInfo +from .TiffTags import TAGS_V2, TYPES, TagInfo __version__ = "1.3.5" DEBUG = False # Needs to be merged with the new logging approach. @@ -318,7 +318,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): """ Returns the complete tag dictionary, with named tags where possible. """ - return dict((TAGS.get(code, TagInfo()).name, value) + return dict((TAGS_V2.get(code, TagInfo()).name, value) for code, value in self.items()) def __len__(self): @@ -350,7 +350,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): if bytes is str: basetypes += unicode, - info = TAGS.get(tag, TagInfo()) + info = TAGS_V2.get(tag, TagInfo()) values = [value] if isinstance(value, basetypes) else value if tag not in self.tagtype: @@ -503,7 +503,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): for i in range(self._unpack("H", self._ensure_read(fp,2))[0]): tag, typ, count, data = self._unpack("HHL4s", self._ensure_read(fp,12)) if DEBUG: - tagname = TAGS.get(tag, TagInfo()).name + tagname = TAGS_V2.get(tag, TagInfo()).name typname = TYPES.get(typ, "unknown") print("tag: %s (%d) - type: %s (%d)" % (tagname, tag, typname, typ), end=" ") @@ -571,7 +571,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): values = value if isinstance(value, tuple) else (value,) data = self._write_dispatch[typ](self, *values) if DEBUG: - tagname = TAGS.get(tag, TagInfo()).name + tagname = TAGS_V2.get(tag, TagInfo()).name typname = TYPES.get(typ, "unknown") print("save: %s (%d) - type: %s (%d)" % (tagname, tag, typname, typ), end=" ") diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py index 3e7aa3613..8fb851cda 100644 --- a/PIL/TiffTags.py +++ b/PIL/TiffTags.py @@ -19,6 +19,9 @@ from collections import namedtuple +# Legacy Tags structure +TAGS = {} + class TagInfo(namedtuple("_TagInfo", "value name type length enum")): __slots__ = [] @@ -34,7 +37,7 @@ class TagInfo(namedtuple("_TagInfo", "value name type length enum")): # # id: (Name, Type, Length, enum_values) # -TAGS = { +TAGS_V2 = { 254: ("NewSubfileType", 4, 1), 255: ("SubfileType", 3, 1), @@ -160,12 +163,17 @@ TAGS = { 50839: ("ImageJMetaData", 7, 1) } +def _populate(): + for k, v in TAGS_V2.items(): + # Populate legacy structure. + TAGS[k] = v[0] + if len(v) == 4: + for sk,sv in v[3].items(): + TAGS[(k,sk)] = sv + + TAGS_V2[k] = TagInfo(k, *v) -for k, v in TAGS.items(): - TAGS[k] = TagInfo(k, *v) -del k, v - - +_populate() ## # Map type numbers to type names -- defined in ImageFileDirectory. diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 05aa98bb5..1b5e05b4f 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -4,7 +4,7 @@ from helper import unittest, PillowTestCase, hopper from PIL import Image, TiffImagePlugin, TiffTags -tag_ids = dict((info.name, info.value) for info in TiffTags.TAGS.values()) +tag_ids = dict((info.name, info.value) for info in TiffTags.TAGS_V2.values()) class TestFileTiffMetadata(PillowTestCase): From 70977bcbb5e3ce970da8a43f218bdbcef441c0cd Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 13 Sep 2015 15:45:15 +0100 Subject: [PATCH 0551/1037] Got the order of the enums wrong --- PIL/TiffTags.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py index 8fb851cda..ddc7cde6d 100644 --- a/PIL/TiffTags.py +++ b/PIL/TiffTags.py @@ -157,7 +157,7 @@ TAGS_V2 = { 45580: ("PitchAngle", 10, 1), 45581: ("RollAngle", 10, 1), - 50741: ("MakerNoteSafety", 3, 1, {0: "Unsafe", 1: "Safe"}), + 50741: ("MakerNoteSafety", 3, 1, {"Unsafe": 0, "Safe": 1}), 50780: ("BestQualityScale", 5, 1), 50838: ("ImageJMetaDataByteCounts", 4, 1), 50839: ("ImageJMetaData", 7, 1) @@ -168,8 +168,8 @@ def _populate(): # Populate legacy structure. TAGS[k] = v[0] if len(v) == 4: - for sk,sv in v[3].items(): - TAGS[(k,sk)] = sv + for sk, sv in v[3].items(): + TAGS[(k, sv)] = sk TAGS_V2[k] = TagInfo(k, *v) From 86bda9a705bef90e72427d0069eece86b61175f5 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 13 Sep 2015 15:55:17 +0100 Subject: [PATCH 0552/1037] Legacy tifftags --- PIL/TiffTags.py | 126 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 122 insertions(+), 4 deletions(-) diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py index ddc7cde6d..4f3352f31 100644 --- a/PIL/TiffTags.py +++ b/PIL/TiffTags.py @@ -19,9 +19,6 @@ from collections import namedtuple -# Legacy Tags structure -TAGS = {} - class TagInfo(namedtuple("_TagInfo", "value name type length enum")): __slots__ = [] @@ -163,6 +160,127 @@ TAGS_V2 = { 50839: ("ImageJMetaData", 7, 1) } +# Legacy Tags structure +# these tags aren't included above, but were in the previous versions +TAGS = {347: 'JPEGTables', + 700: 'XMP', + + # Additional Exif Info + 33434: 'ExposureTime', + 33437: 'FNumber', + 33723: 'IptcNaaInfo', + 34377: 'PhotoshopInfo', + 34675: 'ICCProfile', + 34850: 'ExposureProgram', + 34852: 'SpectralSensitivity', + 34853: 'GPSInfoIFD', + 34855: 'ISOSpeedRatings', + 34856: 'OECF', + 34864: 'SensitivityType', + 34865: 'StandardOutputSensitivity', + 34866: 'RecommendedExposureIndex', + 34867: 'ISOSpeed', + 34868: 'ISOSpeedLatitudeyyy', + 34869: 'ISOSpeedLatitudezzz', + 36864: 'ExifVersion', + 36867: 'DateTimeOriginal', + 36868: 'DateTImeDigitized', + 37121: 'ComponentsConfiguration', + 37122: 'CompressedBitsPerPixel', + 37377: 'ShutterSpeedValue', + 37378: 'ApertureValue', + 37379: 'BrightnessValue', + 37380: 'ExposureBiasValue', + 37381: 'MaxApertureValue', + 37382: 'SubjectDistance', + 37383: 'MeteringMode', + 37384: 'LightSource', + 37385: 'Flash', + 37386: 'FocalLength', + 37396: 'SubjectArea', + 37500: 'MakerNote', + 37510: 'UserComment', + 37520: 'SubSec', + 37521: 'SubSecTimeOriginal', + 37522: 'SubsecTimeDigitized', + 40960: 'FlashPixVersion', + 40961: 'ColorSpace', + 40962: 'PixelXDimension', + 40963: 'PixelYDimension', + 40964: 'RelatedSoundFile', + 40965: 'InteroperabilityIFD', + 41483: 'FlashEnergy', + 41484: 'SpatialFrequencyResponse', + 41486: 'FocalPlaneXResolution', + 41487: 'FocalPlaneYResolution', + 41488: 'FocalPlaneResolutionUnit', + 41492: 'SubjectLocation', + 41493: 'ExposureIndex', + 41495: 'SensingMethod', + 41728: 'FileSource', + 41729: 'SceneType', + 41730: 'CFAPattern', + 41985: 'CustomRendered', + 41986: 'ExposureMode', + 41987: 'WhiteBalance', + 41988: 'DigitalZoomRatio', + 41989: 'FocalLengthIn35mmFilm', + 41990: 'SceneCaptureType', + 41991: 'GainControl', + 41992: 'Contrast', + 41993: 'Saturation', + 41994: 'Sharpness', + 41995: 'DeviceSettingDescription', + 41996: 'SubjectDistanceRange', + 42016: 'ImageUniqueID', + 42032: 'CameraOwnerName', + 42033: 'BodySerialNumber', + 42034: 'LensSpecification', + 42035: 'LensMake', + 42036: 'LensModel', + 42037: 'LensSerialNumber', + 42240: 'Gamma', + + # Adobe DNG + 50706: 'DNGVersion', + 50707: 'DNGBackwardVersion', + 50708: 'UniqueCameraModel', + 50709: 'LocalizedCameraModel', + 50710: 'CFAPlaneColor', + 50711: 'CFALayout', + 50712: 'LinearizationTable', + 50713: 'BlackLevelRepeatDim', + 50714: 'BlackLevel', + 50715: 'BlackLevelDeltaH', + 50716: 'BlackLevelDeltaV', + 50717: 'WhiteLevel', + 50718: 'DefaultScale', + 50719: 'DefaultCropOrigin', + 50720: 'DefaultCropSize', + 50721: 'ColorMatrix1', + 50722: 'ColorMatrix2', + 50723: 'CameraCalibration1', + 50724: 'CameraCalibration2', + 50725: 'ReductionMatrix1', + 50726: 'ReductionMatrix2', + 50727: 'AnalogBalance', + 50728: 'AsShotNeutral', + 50729: 'AsShotWhiteXY', + 50730: 'BaselineExposure', + 50731: 'BaselineNoise', + 50732: 'BaselineSharpness', + 50733: 'BayerGreenSplit', + 50734: 'LinearResponseLimit', + 50735: 'CameraSerialNumber', + 50736: 'LensInfo', + 50737: 'ChromaBlurRadius', + 50738: 'AntiAliasStrength', + 50740: 'DNGPrivateData', + 50778: 'CalibrationIlluminant1', + 50779: 'CalibrationIlluminant2', +} + + def _populate(): for k, v in TAGS_V2.items(): # Populate legacy structure. @@ -170,7 +288,7 @@ def _populate(): if len(v) == 4: for sk, sv in v[3].items(): TAGS[(k, sv)] = sk - + TAGS_V2[k] = TagInfo(k, *v) _populate() From c2818ee09e2f858d3c50d7a2ec30256a4bab01e2 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 13 Sep 2015 16:16:26 +0100 Subject: [PATCH 0553/1037] Add versioned api to tests --- Tests/test_file_libtiff.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 0a64b0f8b..57eeec43d 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -232,7 +232,8 @@ class TestFileLibTiff(LibTiffTestCase): orig.save(out) reread = Image.open(out) - self.assertEqual('temp.tif', reread.tag[269]) + self.assertEqual('temp.tif', reread.tag_v2[269]) + self.assertEqual('temp.tif', reread.tag[269][0]) def test_12bit_rawmode(self): """ Are we generating the same interpretation From b56d5ca40323671a500b114a42f5059bcc15e0d3 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 13 Sep 2015 16:16:50 +0100 Subject: [PATCH 0554/1037] Added indicator for multipage tiffs --- PIL/TiffImagePlugin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 648aa68f0..e3ea03ab6 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -644,6 +644,7 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2): ifd = cls(prefix=original.prefix) ifd._tagdata = original._tagdata ifd.tagtype = original.tagtype + ifd.next = original.next # an indicator for multipage tiffs return ifd def to_v2(self): From cf1a1e0893555decd62c0f71d59ad90ae64cf626 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 14 Sep 2015 19:34:19 +1000 Subject: [PATCH 0555/1037] Updated libtiff to 4.0.6 --- winbuild/config.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winbuild/config.py b/winbuild/config.py index 56039e9ef..4fa7886ef 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -23,9 +23,9 @@ libs = { 'dir': 'jpeg-9a', }, 'tiff': { - 'url': 'ftp://ftp.remotesensing.org/pub/libtiff/tiff-4.0.5.zip', - 'hash': 'md5:3a8feccb0619958281f92e81be88284a', - 'dir': 'tiff-4.0.5', + 'url': 'ftp://ftp.remotesensing.org/pub/libtiff/tiff-4.0.6.zip', + 'hash': 'md5:f5b485d750b2001255ed64224b98b857', + 'dir': 'tiff-4.0.6', }, 'freetype': { 'url': 'http://download.savannah.gnu.org/releases/freetype/freetype-2.6.tar.gz', From 05348d4f8fd75b2e6727a39afdd2da729482875e Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 14 Sep 2015 03:03:24 -0700 Subject: [PATCH 0556/1037] Reenabling failing/crashing tests, with fixes --- PIL/TiffImagePlugin.py | 6 +++++- Tests/test_file_libtiff.py | 2 +- Tests/test_file_tiff.py | 5 +---- encode.c | 6 +++++- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index e3ea03ab6..fcc84607c 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -1230,8 +1230,12 @@ def _save(im, fp, filename): # Merge the ones that we have with (optional) more bits from # the original file, e.g x,y resolution so that we can # save(load('')) == original file. + legacy_ifd = {} + if hasattr(im, 'tag'): + legacy_ifd = im.tag.to_v2() for k, v in itertools.chain(ifd.items(), - getattr(im, 'ifd', {}).items()): + getattr(im, 'tag_v2', {}).items(), + legacy_ifd.items()): if k not in atts and k not in blocklist: if isinstance(v, unicode if bytes is str else str): atts[k] = v.encode('ascii', 'replace') + b"\0" diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 57eeec43d..f900b97cf 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -198,7 +198,7 @@ class TestFileLibTiff(LibTiffTestCase): # UNDONE - libtiff defaults to writing in native endian, so # on big endian, we'll get back mode = 'I;16B' here. - def xtest_big_endian(self): + def test_big_endian(self): im = Image.open('Tests/images/16bit.MM.deflate.tif') self.assertEqual(im.getpixel((0, 0)), 480) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 2d787e66d..c21eba6f4 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -308,10 +308,7 @@ class TestFileTiff(PillowTestCase): self.assertEqual(im.mode, "L") self.assert_image_similar(im, original, 7.3) -### -# UNDONE -### Segfaulting - def xtest_page_number_x_0(self): + def test_page_number_x_0(self): # Issue 973 # Test TIFF with tag 297 (Page Number) having value of 0 0. # The first number is the current page number. diff --git a/encode.c b/encode.c index 244f5ca0d..c46d78426 100644 --- a/encode.c +++ b/encode.c @@ -775,7 +775,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) len, intav); free(intav); } - } else { + } else if (PyFloat_Check(PyTuple_GetItem(value,0))) { TRACE((" %d elements, setting as floats \n", len)); floatav = malloc(sizeof(float)*len); if (floatav) { @@ -787,6 +787,10 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) len, floatav); free(floatav); } + } else { + TRACE(("Unhandled type in tuple for key %d : %s \n", + (int)PyInt_AsLong(key), + PyBytes_AsString(PyObject_Str(value)))); } } } else { From e3a8044a38d7a58a79f05626781ef230accfe346 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 14 Sep 2015 03:38:52 -0700 Subject: [PATCH 0557/1037] Removing low level PyAccess debug logging --- PIL/PyAccess.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/PIL/PyAccess.py b/PIL/PyAccess.py index cb4f00cad..0d4c8b235 100644 --- a/PIL/PyAccess.py +++ b/PIL/PyAccess.py @@ -54,7 +54,9 @@ class PyAccess(object): self.xsize = vals['xsize'] self.ysize = vals['ysize'] - logger.debug("%s", vals) + # Debugging is polluting test traces, only useful here + # when hacking on PyAccess + #logger.debug("%s", vals) self._post_init() def _post_init(self): @@ -310,7 +312,6 @@ def new(img, readonly=False): if not access_type: logger.debug("PyAccess Not Implemented: %s", img.mode) return None - logger.debug("New PyAccess: %s", img.mode) return access_type(img, readonly) # End of file From 43f3e7504cab52ba2c1954dae6c76aee861ac71d Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 14 Sep 2015 03:42:08 -0700 Subject: [PATCH 0558/1037] Removing spammy debug logging --- PIL/Image.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/PIL/Image.py b/PIL/Image.py index 0f11ad7a9..ce000d264 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -2309,7 +2309,10 @@ def open(fp, mode="r"): _decompression_bomb_check(im.size) return im except (SyntaxError, IndexError, TypeError, struct.error): - logger.debug("", exc_info=True) + # Leave disabled by default, spams the logs with image + # opening failures that are entirely expected. + #logger.debug("", exc_info=True) + continue return None im = _open_core(fp, filename, prefix) From 4adbc9735c7528638550f2a9e591f33d6222b38b Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 14 Sep 2015 04:35:09 -0700 Subject: [PATCH 0559/1037] Reorder load_* parameters to pass master tests --- PIL/TiffImagePlugin.py | 16 ++++++++-------- Tests/test_file_tiff.py | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index fcc84607c..d1acc3496 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -329,7 +329,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): data = self._tagdata[tag] typ = self.tagtype[tag] size, handler = self._load_dispatch[typ] - self[tag] = handler(self, self.legacy_api, data) # check type + self[tag] = handler(self, data, self.legacy_api) # check type val = self._tags_v2[tag] if self.legacy_api and not isinstance(val, (tuple, bytes)): val = val, @@ -422,7 +422,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): idx, fmt, name = idx_fmt_name TYPES[idx] = name size = struct.calcsize("=" + fmt) - _load_dispatch[idx] = size, lambda self, legacy_api, data: ( + _load_dispatch[idx] = size, lambda self, data, legacy_api=True: ( self._unpack("{0}{1}".format(len(data) // size, fmt), data)) _write_dispatch[idx] = lambda self, *values: ( b"".join(self._pack(fmt, value) for value in values)) @@ -433,7 +433,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): (9, "l", "signed long"), (11, "f", "float"), (12, "d", "double")])) @_register_loader(1, 1) # Basic type, except for the legacy API. - def load_byte(self, legacy_api, data): + def load_byte(self, data, legacy_api=True): return (data if legacy_api else tuple(map(ord, data) if bytes is str else data)) @@ -442,7 +442,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): return data @_register_loader(2, 1) - def load_string(self, legacy_api, data): + def load_string(self, data, legacy_api=True): if data.endswith(b"\0"): data = data[:-1] return data.decode("latin-1", "replace") @@ -455,7 +455,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): return b"" + value.encode('ascii', 'replace') + b"\0" @_register_loader(5, 8) - def load_rational(self, legacy_api, data): + def load_rational(self, data, legacy_api=True): vals = self._unpack("{0}L".format(len(data) // 4), data) combine = lambda a, b: (a, b) if legacy_api else a / b return tuple(combine(num, denom) @@ -467,7 +467,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): for frac in values) @_register_loader(7, 1) - def load_undefined(self, legacy_api, data): + def load_undefined(self, data, legacy_api=True): return data @_register_writer(7) @@ -475,7 +475,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): return value @_register_loader(10, 8) - def load_signed_rational(self, legacy_api, data): + def load_signed_rational(self, data, legacy_api=True): vals = self._unpack("{0}l".format(len(data) // 4), data) combine = lambda a, b: (a, b) if legacy_api else a / b return tuple(combine(num, denom) @@ -678,7 +678,7 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2): typ = self.tagtype[tag] size, handler = self._load_dispatch[typ] for legacy in (False, True): - self._setitem(tag, handler(self, legacy, data), legacy) + self._setitem(tag, handler(self, data, legacy), legacy) val = self._tags_v1[tag] if not isinstance(val, (tuple, bytes)): val = val, diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index c21eba6f4..57adcdd18 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -255,25 +255,25 @@ class TestFileTiff(PillowTestCase): for legacy_api in [False, True]: ifd = TiffImagePlugin.ImageFileDirectory_v2() data = b"abc" - ret = ifd.load_byte(legacy_api, data) + ret = ifd.load_byte(data, legacy_api) self.assertEqual(ret, b"abc" if legacy_api else (97, 98, 99)) def test_load_string(self): ifd = TiffImagePlugin.ImageFileDirectory_v2() data = b"abc\0" - ret = ifd.load_string(False, data) + ret = ifd.load_string(data, False) self.assertEqual(ret, "abc") def test_load_float(self): ifd = TiffImagePlugin.ImageFileDirectory_v2() data = b"abcdabcd" - ret = ifd.load_float(False, data) + ret = ifd.load_float(data, False) self.assertEqual(ret, (1.6777999408082104e+22, 1.6777999408082104e+22)) def test_load_double(self): ifd = TiffImagePlugin.ImageFileDirectory_v2() data = b"abcdefghabcdefgh" - ret = ifd.load_double(False, data) + ret = ifd.load_double(data, False) self.assertEqual(ret, (8.540883223036124e+194, 8.540883223036124e+194)) def test_seek(self): From 9286c9e460c241cc45c479678c4bf45a5116296f Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 14 Sep 2015 05:10:27 -0700 Subject: [PATCH 0560/1037] Reenabling and fixing the former test_xyres_tiff test for integer resolutions --- Tests/test_file_tiff.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 57adcdd18..f8ce199da 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -85,12 +85,16 @@ class TestFileTiff(PillowTestCase): self.assertEqual(im.info['dpi'], (72., 72.)) - def xtest_int_resolution(self): + def test_int_resolution(self): + from PIL.TiffImagePlugin import X_RESOLUTION, Y_RESOLUTION + filename = "Tests/images/pil168.tif" + im = Image.open(filename) + # Try to read a file where X,Y_RESOLUTION are ints - im.tag[X_RESOLUTION] = (72,) - im.tag[Y_RESOLUTION] = (72,) + im.tag_v2[X_RESOLUTION] = 71 + im.tag_v2[Y_RESOLUTION] = 71 im._setup() - self.assertEqual(im.info['dpi'], (72., 72.)) + self.assertEqual(im.info['dpi'], (71., 71.)) def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" From 33cd2a5e9b0fb437ab98edee8162ba56cc967db8 Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 14 Sep 2015 16:36:04 +0300 Subject: [PATCH 0561/1037] Python 3.5.0 released and available on Travis CI --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index fc687d5ed..3f9fc199e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ python: - "2.7_with_system_site_packages" # For PyQt4 - 3.2 - 3.3 - - 3.5-dev + - 3.5 - nightly install: @@ -110,7 +110,7 @@ after_script: matrix: fast_finish: true allow_failures: - - python: 3.5-dev + - python: 3.5 - python: nightly env: From 1614f2fdb56465308d09d2b516c1c059b8cc5bc1 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 14 Sep 2015 07:01:57 -0700 Subject: [PATCH 0562/1037] Documentation for IFD Changes --- PIL/TiffImagePlugin.py | 97 +++++++++++++++++++++------- PIL/TiffTags.py | 17 +++++ docs/PIL.rst | 8 --- docs/handbook/image-file-formats.rst | 42 +++++++++--- docs/reference/index.rst | 1 + 5 files changed, 124 insertions(+), 41 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index d1acc3496..db0427267 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -231,24 +231,36 @@ class ImageFileDirectory_v2(collections.MutableMapping): """This class represents a TIFF tag directory. To speed things up, we don't decode tags unless they're asked for. - Exposes a dictionary interface of the tags in the directory + Exposes a dictionary interface of the tags in the directory:: - ImageFileDirectory[key] = value - value = ImageFileDirectory[key] + ifd = ImageFileDirectory_v2() + ifd[key] = 'Some Data' + ifd.tagtype[key] = 2 + print(ifd[key]) + 'Some Data' + + Individual values are returned as the strings or numbers, sequences are + returned as tuples of the values. - Also contains a dictionary of tag types as read from the tiff image file, - 'ImageFileDirectory.tagtype' + The tiff metadata type of each item is stored in a dictionary of + tag types in + `~PIL.TiffImagePlugin.ImageFileDirectory_v2.tagtype`. The types + are read from a tiff file, guessed from the type added, or added + manually. Data Structures: - 'public' - * self.tagtype = {} Key: numerical tiff tag number - Value: integer corresponding to the data type from - `TiffTags.TYPES` + + * self.tagtype = {} + + * Key: numerical tiff tag number + * Value: integer corresponding to the data type from `~PIL.TiffTags.TYPES` + + .. versionadded:: 3.0.0 """ """ Documentation: - 'internal' + 'internal' data structures: * self._tags_v2 = {} Key: numerical tiff tag number Value: decoded data, as tuple for multiple values * self._tagdata = {} Key: numerical tiff tag number @@ -259,11 +271,13 @@ class ImageFileDirectory_v2(collections.MutableMapping): Tags will be found in the private attributes self._tagdata, and in self._tags_v2 once decoded. - If legacy_api is true, then decoded tags will be populated into both - _tags_v1 and _tags_v2. _Tags_v2 will be used if this IFD is used in - the TIFF save routine. - - Tags will be read from tags_v1 if legacy_api == true. + Self.legacy_api is a value for internal use, and shouldn't be + changed from outside code. In cooperation with the + ImageFileDirectory_v1 class, if legacy_api is true, then decoded + tags will be populated into both _tags_v1 and _tags_v2. _Tags_v2 + will be used if this IFD is used in the TIFF save routine. Tags + should be read from tags_v1 if legacy_api == true. + """ def __init__(self, ifh=b"II\052\0\0\0\0\0", prefix=None): @@ -273,9 +287,9 @@ class ImageFileDirectory_v2(collections.MutableMapping): magic header to the constructor. To only set the endianness, pass it as the 'prefix' keyword argument. - :ifh: One of the accepted magic headers (cf. PREFIXES); also sets + :param ifh: One of the accepted magic headers (cf. PREFIXES); also sets endianness. - :prefix: Override the endianness of the file. + :param prefix: Override the endianness of the file. """ if ifh[:4] not in PREFIXES: raise SyntaxError("not a TIFF file (header %r not valid)" % ifh) @@ -310,12 +324,19 @@ class ImageFileDirectory_v2(collections.MutableMapping): return str(dict(self)) def as_dict(self): - """Return a dictionary of the image's tags.""" + """Return a dictionary of the image's tags. + + use `dict(ifd)` instead. + + .. deprecated:: 3.0.0 + """ # FIXME Deprecate: use dict(self) return dict(self) def named(self): """ + :returns: dict of name|key: value + Returns the complete tag dictionary, with named tags where possible. """ return dict((TAGS_V2.get(code, TagInfo()).name, value) @@ -625,6 +646,23 @@ del _load_dispatch, _write_dispatch, idx, name #Legacy ImageFileDirectory support. class ImageFileDirectory_v1(ImageFileDirectory_v2): + """This class represents the **legacy** interface to a TIFF tag directory. + + Exposes a dictionary interface of the tags in the directory:: + + ifd = ImageFileDirectory_v1() + ifd[key] = 'Some Data' + ifd.tagtype[key] = 2 + print ifd[key] + ('Some Data',) + + Also contains a dictionary of tag types as read from the tiff image file, + `~PIL.TiffImagePlugin.ImageFileDirectory_v1.tagtype'. + + Values are returned as a tuple. + + .. deprecated:: 3.0.0 + """ def __init__(self, *args, **kwargs): ImageFileDirectory_v2.__init__(self, *args, **kwargs) self._legacy_api=True @@ -635,11 +673,15 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2): @classmethod def from_v2(cls, original): - """ returns: ImageFileDirectory_v1 + """ Returns an + :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` + instance with the same data as is contained in the original + :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` + instance. - Returns an ImageFileDirectory_v1 instance with the same - data as is contained in the original ImageFileDirectory_v2 - instance """ + :returns: :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` + + """ ifd = cls(prefix=original.prefix) ifd._tagdata = original._tagdata @@ -648,10 +690,15 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2): return ifd def to_v2(self): - """ returns: ImageFileDirectory_v2 + """ Returns an + :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` + instance with the same data as is contained in the original + :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` + instance. - Returns an ImageFileDirectory_v2 instance with the same - data as is contained in this ImageFileDirectory_v1 instance """ + :returns: :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` + + """ ifd = ImageFileDirectory_v2(prefix=self.prefix) ifd._tagdata = dict(self._tagdata) diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py index 4f3352f31..77a067981 100644 --- a/PIL/TiffTags.py +++ b/PIL/TiffTags.py @@ -296,3 +296,20 @@ _populate() # Map type numbers to type names -- defined in ImageFileDirectory. TYPES = {} + +# was: +# TYPES = { +# 1: "byte", +# 2: "ascii", +# 3: "short", +# 4: "long", +# 5: "rational", +# 6: "signed byte", +# 7: "undefined", +# 8: "signed short", +# 9: "signed long", +# 10: "signed rational", +# 11: "float", +# 12: "double", +# } + diff --git a/docs/PIL.rst b/docs/PIL.rst index 53a61872b..32d90232e 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -144,14 +144,6 @@ can be found here. :undoc-members: :show-inheritance: -:mod:`TiffTags` Module ----------------------- - -.. automodule:: PIL.TiffTags - :members: - :undoc-members: - :show-inheritance: - :mod:`WalImageFile` Module -------------------------- diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 11ec60401..1453046ef 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -438,17 +438,37 @@ The :py:meth:`~PIL.Image.Image.open` method sets the following **compression** Compression mode. + .. versionadded:: 2.0.0 + **dpi** - Image resolution as an (xdpi, ydpi) tuple, where applicable. You can use + Image resolution as an ``(xdpi, ydpi)`` tuple, where applicable. You can use the :py:attr:`~PIL.Image.Image.tag` attribute to get more detailed information about the image resolution. .. versionadded:: 1.1.5 -In addition, the :py:attr:`~PIL.Image.Image.tag` attribute contains a -dictionary of decoded TIFF fields. Values are stored as either strings or -tuples. Note that only short, long and ASCII tags are correctly unpacked by -this release. +**resolution** + Image resolution as an ``(xres, yres)`` tuple, where applicable. This is a + measurement in whichever unit is specified by the file. + + .. versionadded:: 1.1.5 + + +The :py:attr:`~PIL.Image.Image.tag_v2` attribute contains a dictionary of +TIFF metadata. The keys are numerical indexes from `~PIL.TiffTags.TAGS_V2`. +Values are strings or numbers for single items, multiple values are returned +in a tuple of values. Rational numbers are returned as a single value. + + .. versionadded:: 3.0.0 + +For compatibility with legacy code, the +:py:attr:`~PIL.Image.Image.tag` attribute contains a dictionary of +decoded TIFF fields as returned prior to version 3.0.0. Values are +returned as either strings or tuples of numeric values. Rational +numbers are returned as a tuple of ``(numerator, denominator)``. + + .. deprecated:: 3.0.0 + Saving Tiff Images ~~~~~~~~~~~~~~~~~~ @@ -456,17 +476,23 @@ Saving Tiff Images The :py:meth:`~PIL.Image.Image.save` method can take the following keyword arguments: **tiffinfo** - A :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory` object or dict + A :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` object or dict object containing tiff tags and values. The TIFF field type is autodetected for Numeric and string values, any other types - require using an :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory` + require using an :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` object and setting the type in - :py:attr:`~PIL.TiffImagePlugin.ImageFileDirectory.tagtype` with + :py:attr:`~PIL.TiffImagePlugin.ImageFileDirectory_v2.tagtype` with the appropriate numerical value from ``TiffTags.TYPES``. .. versionadded:: 2.3.0 + For compatibility with legacy code, a + `~PIL.TiffImagePlugin.ImageFileDirectory_v1` object may be passed + in this field. This will be deprecated in a future version. + + ..versionadded:: 3.0.0 + **compression** A string containing the desired compression method for the file. (valid only with libtiff installed) Valid compression diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 73a3ecfed..2d89d2100 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -26,6 +26,7 @@ Reference ImageTk ImageWin ExifTags + TiffTags OleFileIO PSDraw PixelAccess From 6e82984e34989380bd64e387107ab4d4fb1dcaf5 Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 14 Sep 2015 17:06:13 +0300 Subject: [PATCH 0563/1037] Promote Python 3.5; must pass --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3f9fc199e..536a18582 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,13 +9,13 @@ notifications: python: - "pypy" - "pypy3" - - 3.4 + - 3.5 - 2.7 - 2.6 - "2.7_with_system_site_packages" # For PyQt4 - 3.2 - 3.3 - - 3.5 + - 3.4 - nightly install: @@ -110,7 +110,6 @@ after_script: matrix: fast_finish: true allow_failures: - - python: 3.5 - python: nightly env: From 56c00ba41287db950e869d721fd7cf584073c260 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 15 Sep 2015 00:26:05 +1000 Subject: [PATCH 0564/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 0bb8bbc13..a39c6c40e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -43,7 +43,7 @@ Changelog (Pillow) - Corrected scripts path #1407 [radarhere] -- Updated libtiff to 4.0.5 #1405 +- Updated libtiff to 4.0.6 #1405, #1421 [radarhere] - Updated Platform Support for Yosemite #1403 From e4f9b697c6f336f9de23ab7ef94fc88630d3f3a0 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 14 Sep 2015 07:33:07 -0700 Subject: [PATCH 0565/1037] Doc syntax error --- PIL/TiffImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index db0427267..17426d4e2 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -657,7 +657,7 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2): ('Some Data',) Also contains a dictionary of tag types as read from the tiff image file, - `~PIL.TiffImagePlugin.ImageFileDirectory_v1.tagtype'. + `~PIL.TiffImagePlugin.ImageFileDirectory_v1.tagtype`. Values are returned as a tuple. From 2af6971444efe04bf7478644aaea929eb347ec91 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 14 Sep 2015 07:38:46 -0700 Subject: [PATCH 0566/1037] Fix sphinx build warnings --- docs/plugins.rst | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/plugins.rst b/docs/plugins.rst index a069f80df..f7f897f59 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -146,7 +146,7 @@ Plugin reference :show-inheritance: :mod:`Jpeg2KImagePlugin` Module ------------------------------ +------------------------------- .. automodule:: PIL.Jpeg2KImagePlugin :members: @@ -228,10 +228,22 @@ Plugin reference :mod:`PngImagePlugin` Module ---------------------------- -.. automodule:: PIL.PngImagePlugin +.. automodule:: PIL.PngImagePlugin + :members: ChunkStream, PngImageFile, PngStream, getchunks, is_cid, putchunk + :show-inheritance: +.. autoclass:: PIL.PngImagePlugin.ChunkStream :members: :undoc-members: :show-inheritance: +.. autoclass:: PIL.PngImagePlugin.PngImageFile + :members: + :undoc-members: + :show-inheritance: +.. autoclass:: PIL.PngImagePlugin.PngStream + :members: + :undoc-members: + :show-inheritance: + :mod:`PpmImagePlugin` Module ---------------------------- From 63f5f688373a0b5f276e0a5ed2be27cf16804f3d Mon Sep 17 00:00:00 2001 From: homm Date: Tue, 15 Sep 2015 01:15:54 +0300 Subject: [PATCH 0567/1037] unused imports --- PIL/IptcImagePlugin.py | 4 ++-- PIL/PixarImagePlugin.py | 1 - PIL/SgiImagePlugin.py | 1 - PIL/SunImagePlugin.py | 1 - PIL/TgaImagePlugin.py | 1 - 5 files changed, 2 insertions(+), 6 deletions(-) diff --git a/PIL/IptcImagePlugin.py b/PIL/IptcImagePlugin.py index 323177039..b5aa84bad 100644 --- a/PIL/IptcImagePlugin.py +++ b/PIL/IptcImagePlugin.py @@ -217,7 +217,7 @@ def getiptcinfo(im): while app[offset:offset+4] == b"8BIM": offset += 4 # resource code - code = JpegImagePlugin.i16(app, offset) + code = i16(app, offset) offset += 2 # resource name (usually empty) name_len = i8(app[offset]) @@ -226,7 +226,7 @@ def getiptcinfo(im): if offset & 1: offset += 1 # resource data block - size = JpegImagePlugin.i32(app, offset) + size = i32(app, offset) offset += 4 if code == 0x0404: # 0x0404 contains IPTC/NAA data diff --git a/PIL/PixarImagePlugin.py b/PIL/PixarImagePlugin.py index 26b872893..7fef35408 100644 --- a/PIL/PixarImagePlugin.py +++ b/PIL/PixarImagePlugin.py @@ -27,7 +27,6 @@ __version__ = "0.1" # helpers i16 = _binary.i16le -i32 = _binary.i32le ## diff --git a/PIL/SgiImagePlugin.py b/PIL/SgiImagePlugin.py index e73cf1601..f890c7ef6 100644 --- a/PIL/SgiImagePlugin.py +++ b/PIL/SgiImagePlugin.py @@ -24,7 +24,6 @@ __version__ = "0.2" i8 = _binary.i8 i16 = _binary.i16be -i32 = _binary.i32be def _accept(prefix): diff --git a/PIL/SunImagePlugin.py b/PIL/SunImagePlugin.py index 22f27a1c0..af63144f2 100644 --- a/PIL/SunImagePlugin.py +++ b/PIL/SunImagePlugin.py @@ -21,7 +21,6 @@ from PIL import Image, ImageFile, ImagePalette, _binary __version__ = "0.3" -i16 = _binary.i16be i32 = _binary.i32be diff --git a/PIL/TgaImagePlugin.py b/PIL/TgaImagePlugin.py index 8766e3890..a75ce2986 100644 --- a/PIL/TgaImagePlugin.py +++ b/PIL/TgaImagePlugin.py @@ -28,7 +28,6 @@ __version__ = "0.3" i8 = _binary.i8 i16 = _binary.i16le -i32 = _binary.i32le MODES = { From 9930b05a331d70ad40fb12dafbe0e5cfd03c2bfc Mon Sep 17 00:00:00 2001 From: homm Date: Tue, 15 Sep 2015 04:06:51 +0300 Subject: [PATCH 0568/1037] fix tiff exif loading in case when file is empty or ended --- PIL/JpegImagePlugin.py | 2 +- Tests/test_file_tiff_metadata.py | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 3d01c5fc1..550276c02 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -428,7 +428,7 @@ def _getexif(self): # exif field 0x8825 is an offset pointer to the location # of the nested embedded gps exif ifd. # It should be a long, but may be corrupted. - file.seek(exif[0x8825]) + file.seek(exif[0x8825]) except (KeyError, TypeError): pass else: diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 1b5e05b4f..f2197ad04 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -1,5 +1,8 @@ from __future__ import division +import io +import struct + from helper import unittest, PillowTestCase, hopper from PIL import Image, TiffImagePlugin, TiffTags @@ -136,6 +139,15 @@ class TestFileTiffMetadata(PillowTestCase): self.assertEqual(tag_ids['MakerNoteSafety'], 50741) self.assertEqual(tag_ids['BestQualityScale'], 50780) + def test_empty_metadata(self): + f = io.BytesIO(b'II*\x00\x08\x00\x00\x00') + head = f.read(8) + info = TiffImagePlugin.ImageFileDirectory(head) + try: + self.assert_warning(UserWarning, lambda: info.load(f)) + except struct.error: + self.fail("Should not be struct errors there.") + if __name__ == '__main__': unittest.main() From 0f87b1f125d6447e020c7c498c77becd0370fb46 Mon Sep 17 00:00:00 2001 From: homm Date: Tue, 15 Sep 2015 02:52:02 +0300 Subject: [PATCH 0569/1037] suppress and check warning during tests --- Tests/test_file_jpeg.py | 3 ++- Tests/test_file_tiff.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index e6da7bb8b..367e57c14 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -370,7 +370,8 @@ class TestFileJpeg(PillowTestCase): # Act # Shouldn't raise error - im = Image.open("Tests/images/sugarshack_bad_mpo_header.jpg") + fn = "Tests/images/sugarshack_bad_mpo_header.jpg" + im = self.assert_warning(UserWarning, lambda: Image.open(fn)) # Assert self.assertEqual(im.format, "JPEG") diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index f8ce199da..a221f15cc 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -103,8 +103,9 @@ class TestFileTiff(PillowTestCase): lambda: TiffImagePlugin.TiffImageFile(invalid_file)) def test_bad_exif(self): + i = Image.open('Tests/images/hopper_bad_exif.jpg') try: - Image.open('Tests/images/hopper_bad_exif.jpg')._getexif() + self.assert_warning(UserWarning, lambda: i._getexif()) except struct.error: self.fail( "Bad EXIF data passed incorrect values to _binary unpack") From 5b7f6f2638216d85da31586601d6821b451b3377 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 15 Sep 2015 01:00:36 -0700 Subject: [PATCH 0570/1037] Fix docs to match code, see http://engineering.khanacademy.org/posts/making-thumbnails-fast.htm --- PIL/Image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/Image.py b/PIL/Image.py index 0f11ad7a9..4a23c56c3 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -822,7 +822,7 @@ class Image(object): :param mode: The requested mode. See: :ref:`concept-modes`. :param matrix: An optional conversion matrix. If given, this - should be 4- or 16-tuple containing floating point values. + should be 4- or 12-tuple containing floating point values. :param dither: Dithering method, used when converting from mode "RGB" to "P" or from "RGB" or "L" to "1". Available methods are NONE or FLOYDSTEINBERG (default). From 4cd29c10d396aad32a02e89895a6c8a4e1a31491 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 15 Sep 2015 01:01:04 -0700 Subject: [PATCH 0571/1037] Fix indentation to match actual code flow --- _imaging.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/_imaging.c b/_imaging.c index e49c5431f..92dfc005e 100644 --- a/_imaging.c +++ b/_imaging.c @@ -768,11 +768,12 @@ _convert_matrix(ImagingObject* self, PyObject* args) float m[12]; if (!PyArg_ParseTuple(args, "s(ffff)", &mode, m+0, m+1, m+2, m+3)) { PyErr_Clear(); - if (!PyArg_ParseTuple(args, "s(ffffffffffff)", &mode, - m+0, m+1, m+2, m+3, - m+4, m+5, m+6, m+7, - m+8, m+9, m+10, m+11)) - return NULL; + if (!PyArg_ParseTuple(args, "s(ffffffffffff)", &mode, + m+0, m+1, m+2, m+3, + m+4, m+5, m+6, m+7, + m+8, m+9, m+10, m+11)){ + return NULL; + } } return PyImagingNew(ImagingConvertMatrix(self->image, mode, m)); From 2593aa78a27f2870a037a03c6e87ba589e7b1741 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 15 Sep 2015 11:43:30 +0300 Subject: [PATCH 0572/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index a39c6c40e..c889a6f13 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Correct convert matrix docs #1426 + [wiredfool] + - Catch TypeError in _getexif #1414 [radarhere, wiredfool] From d02c57a7db81350c7c2c8d51edd878ff172afd26 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 15 Sep 2015 19:13:09 +1000 Subject: [PATCH 0573/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index c889a6f13..cb817c6a6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -11,7 +11,7 @@ Changelog (Pillow) [radarhere, wiredfool] - Fix for UnicodeDecodeError in TiffImagePlugin #1416 - [bogdan199, wirdfool] + [bogdan199, wiredfool] - Dedup code in image.open #1415 [wiredfool] From baac3c23213bd1be34ae0bfa40a8990468516b13 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 15 Sep 2015 21:37:51 +1000 Subject: [PATCH 0574/1037] Removed trailing whitespace --- PIL/JpegImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index ffc14d2b6..096286bc0 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -436,7 +436,7 @@ def _getexif(self): # get gpsinfo extension try: # exif field 0x8825 is an offset pointer to the location - # of the nested embedded gps exif ifd. + # of the nested embedded gps exif ifd. # It should be a long, but may be corrupted. file.seek(exif[0x8825]) except (KeyError, TypeError): From 613f1e241ef50e18dcf979df71ee56428c25d834 Mon Sep 17 00:00:00 2001 From: homm Date: Tue, 15 Sep 2015 19:11:45 +0300 Subject: [PATCH 0575/1037] do not raise on broken images --- PIL/ImageFile.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index 597e41783..da86e1155 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -190,9 +190,6 @@ class ImageFile(Image.Image): except AttributeError: prefix = b"" - # Buffer length read; assign a default value - t = 0 - for d, e, o, a in self.tile: d = Image._getdecoder(self.mode, d, a, self.decoderconfig) seek(o) @@ -201,7 +198,6 @@ class ImageFile(Image.Image): except ValueError: continue b = prefix - t = len(b) while True: try: s = read(self.decodermaxblock) @@ -230,7 +226,6 @@ class ImageFile(Image.Image): if n < 0: break b = b[n:] - t = t + n # Need to cleanup here to prevent leaks in PyPy d.cleanup() @@ -239,7 +234,7 @@ class ImageFile(Image.Image): self.fp = None # might be shared - if not self.map and (not LOAD_TRUNCATED_IMAGES or t == 0) and e < 0: + if not self.map and not LOAD_TRUNCATED_IMAGES and e < 0: # still raised if decoder fails to return anything raise_ioerror(e) From f46d65d0a4ab79e61c69ce1436b118954b01314a Mon Sep 17 00:00:00 2001 From: homm Date: Tue, 15 Sep 2015 20:12:16 +0300 Subject: [PATCH 0576/1037] test for this --- Tests/images/broken_data_stream.png | Bin 0 -> 34560 bytes Tests/test_imagefile.py | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 Tests/images/broken_data_stream.png diff --git a/Tests/images/broken_data_stream.png b/Tests/images/broken_data_stream.png new file mode 100644 index 0000000000000000000000000000000000000000..4626bc576119dcc6003f61a28dc12d67e1c7f602 GIT binary patch literal 34560 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 Ang9R* literal 0 HcmV?d00001 diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index 857df1441..5705180ff 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -115,6 +115,26 @@ class TestImageFile(PillowTestCase): finally: ImageFile.LOAD_TRUNCATED_IMAGES = False + def test_broken_datastream_with_errors(self): + if "zip_encoder" not in codecs: + self.skipTest("PNG (zlib) encoder not available") + + im = Image.open("Tests/images/broken_data_stream.png") + with self.assertRaises(IOError): + im.load() + + def test_broken_datastream_without_errors(self): + if "zip_encoder" not in codecs: + self.skipTest("PNG (zlib) encoder not available") + + im = Image.open("Tests/images/broken_data_stream.png") + + ImageFile.LOAD_TRUNCATED_IMAGES = True + try: + im.load() + finally: + ImageFile.LOAD_TRUNCATED_IMAGES = False + if __name__ == '__main__': unittest.main() From 01356a9ad8c713577c719d4ac077745ec10a2b88 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 18 Sep 2015 21:41:36 +1000 Subject: [PATCH 0577/1037] Improved documentation [ci skip] --- docs/handbook/image-file-formats.rst | 29 +++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 11ec60401..ec3c49ecc 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -54,8 +54,11 @@ GIF ^^^ PIL reads GIF87a and GIF89a versions of the GIF file format. The library writes -run-length encoded GIF87a files. Note that GIF files are always read as -grayscale (``L``) or palette mode (``P``) images. +run-length encoded files in GIF87a by default, unless GIF89a features +are used or GIF89a is already in use. + +Note that GIF files are always read as grayscale (``L``) +or palette mode (``P``) images. The :py:meth:`~PIL.Image.Image.open` method sets the following :py:attr:`~PIL.Image.Image.info` properties: @@ -73,12 +76,32 @@ The :py:meth:`~PIL.Image.Image.open` method sets the following **version** Version (either ``GIF87a`` or ``GIF89a``). +**duration** + May not be present. The time to display each frame of the GIF, in + milliseconds. + +**loop** + May not be present. The number of times the GIF should loop. + Reading sequences ~~~~~~~~~~~~~~~~~ The GIF loader supports the :py:meth:`~file.seek` and :py:meth:`~file.tell` methods. You can seek to the next frame (``im.seek(im.tell() + 1)``), or rewind -the file by seeking to the first frame. Random access is not supported. ``im.seek()`` raises an ``EOFError`` if you try to seek after the last frame. +the file by seeking to the first frame. Random access is not supported. + +``im.seek()`` raises an ``EOFError`` if you try to seek after the last frame. + +Saving sequences +~~~~~~~~~~~~~~~~ + +When calling :py:meth:`~PIL.Image.Image.save`, if a multiframe image is used, +by default only the first frame will be saved. To save all frames, the +``save_all`` parameter must be present and set to ``True``. + +If present, the ``loop`` parameter can be used to set the number of times +the GIF should loop, and the ``duration`` parameter can set the number of +milliseconds between each frame. Reading local images ~~~~~~~~~~~~~~~~~~~~ From 3177a3807a2eb08d16450dae4e4b542b2d8fd3c4 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 18 Sep 2015 14:43:40 +0100 Subject: [PATCH 0578/1037] Update CHANGES.rst --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index cb817c6a6..ca1530678 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Save as GiF89a with suppoert for animation parameters #1384 + [radarhere] + - Correct convert matrix docs #1426 [wiredfool] From 58af64e245b3faf90dd71c04639937c0b1acd4fb Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 18 Sep 2015 14:44:01 +0100 Subject: [PATCH 0579/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index ca1530678..7373e617e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,7 +4,7 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ -- Save as GiF89a with suppoert for animation parameters #1384 +- Save as GiF89a with support for animation parameters #1384 [radarhere] - Correct convert matrix docs #1426 From 86e775daa3dfc23908d29c08f151111520251821 Mon Sep 17 00:00:00 2001 From: Eric L Frederich Date: Thu, 17 Sep 2015 09:32:15 -0400 Subject: [PATCH 0580/1037] bug fix: Qt wants data aligned to 32 bits Images in Qt show up incorrectly if each line is not aligned to 32 bits. It is pretty common for an image's lines to be 32-bit alinged by chance. Obviously any 32-bit image will not have any problem. For the bug to manifest itself you'd need... * a 1-bit image whose width is not a multiple of 32 * an 8-bit image who width is not a multiple of 4 Testing more images now and added a 7x13 png test image --- PIL/ImageQt.py | 29 ++++++++++++++++++++++++++++- Tests/images/7x13.png | Bin 0 -> 204 bytes Tests/test_image_fromqimage.py | 23 +++++++++++++++++------ 3 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 Tests/images/7x13.png diff --git a/PIL/ImageQt.py b/PIL/ImageQt.py index 0e8cee009..2131f8264 100644 --- a/PIL/ImageQt.py +++ b/PIL/ImageQt.py @@ -80,6 +80,33 @@ def fromqpixmap(im): # bytes_io.seek(0) # return PIL.Image.open(bytes_io) +def align8to32(bytes, width, mode): + """ + converts each scanline of data from 8 bit to 32 bit aligned + """ + + bits_per_pixel = { + '1': 1, + 'L': 8, + 'P': 8, + }[mode] + + # calculate bytes per line and the extra padding if needed + bits_per_line = bits_per_pixel * width + full_bytes_per_line, remaining_bits_per_line = divmod(bits_per_line, 8) + bytes_per_line = full_bytes_per_line + (1 if remaining_bits_per_line else 0) + + extra_padding = -bytes_per_line % 4 + + # already 32 bit aligned by luck + if not extra_padding: + return bytes + + new_data = [] + for i in range(len(bytes) / bytes_per_line): + new_data.append(bytes[i*bytes_per_line:(i+1)*bytes_per_line] + '\x00' * extra_padding) + + return ''.join(new_data) def _toqclass_helper(im): data = None @@ -123,7 +150,7 @@ def _toqclass_helper(im): raise ValueError("unsupported image mode %r" % im.mode) # must keep a reference, or Qt will crash! - __data = data or im.tobytes() + __data = data or align8to32(im.tobytes(), im.size[0], im.mode) return { 'data': __data, 'im': im, 'format': format, 'colortable': colortable } diff --git a/Tests/images/7x13.png b/Tests/images/7x13.png new file mode 100644 index 0000000000000000000000000000000000000000..d160c8b3877fbbdfaadf72672cc15d9a0b483fe0 GIT binary patch literal 204 zcmeAS@N?(olHy`uVBq!ia0vp^>>xG=6Oeqh`tc(m#aZAHSlKuBT-R4S2kKx4NNGHk1thcv)4v=*C=xV)BHNexMY}MvW qjrwf|W2;h%JM%L2PtV)>MqHTtpE{e|q{UxA)_A)5xvX Date: Wed, 16 Sep 2015 16:59:52 -0400 Subject: [PATCH 0581/1037] preserve alpha during conversion; add tests; found bug and added TODOs --- PIL/ImageQt.py | 7 ++++++- Tests/test_image_fromqimage.py | 12 +++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/PIL/ImageQt.py b/PIL/ImageQt.py index 2131f8264..591d85ff8 100644 --- a/PIL/ImageQt.py +++ b/PIL/ImageQt.py @@ -53,7 +53,12 @@ def rgb(r, g, b, a=255): def fromqimage(im): buffer = QBuffer() buffer.open(QIODevice.ReadWrite) - im.save(buffer, 'ppm') + # preserve alha channel with png + # otherwise ppm is more friendly with Image.open + if im.hasAlphaChannel(): + im.save(buffer, 'png') + else: + im.save(buffer, 'ppm') b = BytesIO() try: diff --git a/Tests/test_image_fromqimage.py b/Tests/test_image_fromqimage.py index 24ec19616..57a95f104 100644 --- a/Tests/test_image_fromqimage.py +++ b/Tests/test_image_fromqimage.py @@ -13,9 +13,15 @@ class TestFromQImage(PillowQtTestCase, PillowTestCase): ] def roundtrip(self, expected): - result = ImageQt.fromqimage(expected.toqimage()) - # Qt saves all images as rgb - self.assert_image_equal(result, expected.convert('RGB')) + # PIL -> Qt + intermediate = expected.toqimage() + # Qt -> PIL + result = ImageQt.fromqimage(intermediate) + + if intermediate.hasAlphaChannel(): + self.assert_image_equal(result, expected.convert('RGBA')) + else: + self.assert_image_equal(result, expected.convert('RGB')) def test_sanity_1(self): for im in self.files_to_test: From 3e47ddbeebb48cd3693970bd2ee4e56996759120 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 13 Jul 2015 17:21:09 +1000 Subject: [PATCH 0582/1037] Removed comment that Python 2 behaviour is deprecated --- PIL/ImagePalette.py | 1 - 1 file changed, 1 deletion(-) diff --git a/PIL/ImagePalette.py b/PIL/ImagePalette.py index 5aabaa138..34c87ce72 100644 --- a/PIL/ImagePalette.py +++ b/PIL/ImagePalette.py @@ -68,7 +68,6 @@ class ImagePalette(object): return self.palette arr = array.array("B", self.palette) if hasattr(arr, 'tobytes'): - # py3k has a tobytes, tostring is deprecated. return arr.tobytes() return arr.tostring() From 3841a11fb58dc5db10a4bc854e28d958b7e976c8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 13 Jul 2015 17:39:34 +1000 Subject: [PATCH 0583/1037] Removed ImageFont filename param, deprecated in 2.1 --- PIL/ImageFont.py | 22 +++------------------- Tests/test_imagefont.py | 5 ----- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index e3fb6f503..fce44caff 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -121,15 +121,8 @@ class ImageFont(object): class FreeTypeFont(object): "FreeType font wrapper (requires _imagingft service)" - def __init__(self, font=None, size=10, index=0, encoding="", file=None): + def __init__(self, font=None, size=10, index=0, encoding=""): # FIXME: use service provider instead - if file: - if warnings: - warnings.warn( - 'file parameter deprecated, ' - 'please use font parameter instead.', - DeprecationWarning) - font = file self.path = font self.size = size @@ -171,7 +164,7 @@ class FreeTypeFont(object): using any specified arguments to override the settings. Parameters are identical to the parameters used to initialize this - object, minus the deprecated 'file' argument. + object. :return: A FreeTypeFont object. """ @@ -225,7 +218,7 @@ def load(filename): return f -def truetype(font=None, size=10, index=0, encoding="", filename=None): +def truetype(font=None, size=10, index=0, encoding=""): """ Load a TrueType or OpenType font file, and create a font object. This function loads a font object from the given file, and creates @@ -243,19 +236,10 @@ def truetype(font=None, size=10, index=0, encoding="", filename=None): Symbol), "ADOB" (Adobe Standard), "ADBE" (Adobe Expert), and "armn" (Apple Roman). See the FreeType documentation for more information. - :param filename: Deprecated. Please use font instead. :return: A font object. :exception IOError: If the file could not be read. """ - if filename: - if warnings: - warnings.warn( - 'filename parameter deprecated, ' - 'please use font parameter instead.', - DeprecationWarning) - font = filename - try: return FreeTypeFont(font, size, index, encoding) except IOError: diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 1fd70b3d8..09f4942a7 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -88,11 +88,6 @@ try: self._render(f) self._clean() - def test_font_old_parameters(self): - self.assert_warning( - DeprecationWarning, - lambda: ImageFont.truetype(filename=FONT_PATH, size=FONT_SIZE)) - def _render(self, font): txt = "Hello World!" ttf = ImageFont.truetype(font, FONT_SIZE) From a3107af63fcefdf26756ec8d9ee8ad8108ee18a4 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 13 Jul 2015 17:27:52 +1000 Subject: [PATCH 0584/1037] Removed ImageFileIO, deprecated since PIL --- PIL/ImageFileIO.py | 40 --------------------------------------- Tests/test_imagefileio.py | 33 -------------------------------- docs/PIL.rst | 9 --------- 3 files changed, 82 deletions(-) delete mode 100644 PIL/ImageFileIO.py delete mode 100644 Tests/test_imagefileio.py diff --git a/PIL/ImageFileIO.py b/PIL/ImageFileIO.py deleted file mode 100644 index e57d3f43e..000000000 --- a/PIL/ImageFileIO.py +++ /dev/null @@ -1,40 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# kludge to get basic ImageFileIO functionality -# -# History: -# 1998-08-06 fl Recreated -# -# Copyright (c) Secret Labs AB 1998-2002. -# -# See the README file for information on usage and redistribution. -# -""" -The **ImageFileIO** module can be used to read an image from a -socket, or any other stream device. - -Deprecated. New code should use the :class:`PIL.ImageFile.Parser` -class in the :mod:`PIL.ImageFile` module instead. - -.. seealso:: modules :class:`PIL.ImageFile.Parser` -""" - -from io import BytesIO - - -class ImageFileIO(BytesIO): - def __init__(self, fp): - """ - Adds buffering to a stream file object, in order to - provide **seek** and **tell** methods required - by the :func:`PIL.Image.Image.open` method. The stream object must - implement **read** and **close** methods. - - :param fp: Stream file handle. - - .. seealso:: modules :func:`PIL.Image.open` - """ - data = fp.read() - BytesIO.__init__(self, data) diff --git a/Tests/test_imagefileio.py b/Tests/test_imagefileio.py deleted file mode 100644 index b06178437..000000000 --- a/Tests/test_imagefileio.py +++ /dev/null @@ -1,33 +0,0 @@ -from helper import unittest, PillowTestCase, hopper, tostring - -from PIL import Image -from PIL import ImageFileIO - - -class TestImageFileIo(PillowTestCase): - - def test_fileio(self): - - class DumbFile(object): - def __init__(self, data): - self.data = data - - def read(self, bytes=None): - assert(bytes is None) - return self.data - - def close(self): - pass - - im1 = hopper() - - io = ImageFileIO.ImageFileIO(DumbFile(tostring(im1, "PPM"))) - - im2 = Image.open(io) - self.assert_image_equal(im1, im2) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/docs/PIL.rst b/docs/PIL.rst index 53a61872b..c171b80b1 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -64,15 +64,6 @@ can be found here. .. intentionally skipped documenting this because it's deprecated -:mod:`ImageFileIO` Module -------------------------- - -.. automodule:: PIL.ImageFileIO - :members: - :undoc-members: - :show-inheritance: - - :mod:`ImageShow` Module ----------------------- From 47366692d9e6b765f413c1065a55f5b7a2f40d85 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 13 Jul 2015 17:37:14 +1000 Subject: [PATCH 0585/1037] Removed ImagePalette methods, deprecated in 2.6 --- PIL/ImagePalette.py | 21 --------------------- Tests/test_imagepalette.py | 21 --------------------- 2 files changed, 42 deletions(-) diff --git a/PIL/ImagePalette.py b/PIL/ImagePalette.py index 34c87ce72..4f6845243 100644 --- a/PIL/ImagePalette.py +++ b/PIL/ImagePalette.py @@ -17,7 +17,6 @@ # import array -import warnings from PIL import ImageColor @@ -136,26 +135,6 @@ def raw(rawmode, data): # -------------------------------------------------------------------- # Factories -def _make_linear_lut(black, white): - warnings.warn( - '_make_linear_lut() is deprecated. ' - 'Please call make_linear_lut() instead.', - DeprecationWarning, - stacklevel=2 - ) - return make_linear_lut(black, white) - - -def _make_gamma_lut(exp): - warnings.warn( - '_make_gamma_lut() is deprecated. ' - 'Please call make_gamma_lut() instead.', - DeprecationWarning, - stacklevel=2 - ) - return make_gamma_lut(exp) - - def make_linear_lut(black, white): lut = [] if black == 0: diff --git a/Tests/test_imagepalette.py b/Tests/test_imagepalette.py index 707ab4080..a175f7085 100644 --- a/Tests/test_imagepalette.py +++ b/Tests/test_imagepalette.py @@ -90,27 +90,6 @@ class TestImagePalette(PillowTestCase): self.assertEqual(lut[191], 60) self.assertEqual(lut[255], 255) - def test_private_make_linear_lut_warning(self): - # Arrange - from PIL.ImagePalette import _make_linear_lut - black = 0 - white = 255 - - # Act / Assert - self.assert_warning( - DeprecationWarning, - lambda: _make_linear_lut(black, white)) - - def test_private_make_gamma_lut_warning(self): - # Arrange - from PIL.ImagePalette import _make_gamma_lut - exp = 5 - - # Act / Assert - self.assert_warning( - DeprecationWarning, - lambda: _make_gamma_lut(exp)) - def test_rawmode_valueerrors(self): # Arrange from PIL.ImagePalette import raw From baa5143394708704328dcd46b0387f36a276a762 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 13 Jul 2015 17:41:53 +1000 Subject: [PATCH 0586/1037] Removed methods deprecated in 2.0 --- PIL/Image.py | 59 -------------------------------------- PIL/ImageWin.py | 20 ------------- Tests/test_image_offset.py | 25 ---------------- Tests/test_imagewin.py | 11 ------- 4 files changed, 115 deletions(-) delete mode 100644 Tests/test_image_offset.py diff --git a/PIL/Image.py b/PIL/Image.py index 861599bf7..be00132b2 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -681,19 +681,6 @@ class Image(object): return b"".join(data) - # Declare tostring as alias to tobytes - def tostring(self, *args, **kw): - """Deprecated alias to tobytes. - - .. deprecated:: 2.0 - """ - warnings.warn( - 'tostring() is deprecated. Please call tobytes() instead.', - DeprecationWarning, - stacklevel=2, - ) - return self.tobytes(*args, **kw) - def tobitmap(self, name="image"): """ Returns the image converted to an X11 bitmap. @@ -741,16 +728,6 @@ class Image(object): if s[1] != 0: raise ValueError("cannot decode image data") - def fromstring(self, *args, **kw): - """Deprecated alias to frombytes. - - .. deprecated:: 2.0 - """ - warnings.warn( - 'fromstring() is deprecated. Please call frombytes() instead.', - DeprecationWarning) - return self.frombytes(*args, **kw) - def load(self): """ Allocates storage for the image and loads the pixel data. In @@ -1247,29 +1224,6 @@ class Image(object): return self.im.histogram(extrema) return self.im.histogram() - def offset(self, xoffset, yoffset=None): - """ - .. deprecated:: 2.0 - - .. note:: New code should use :py:func:`PIL.ImageChops.offset`. - - Returns a copy of the image where the data has been offset by the given - distances. Data wraps around the edges. If **yoffset** is omitted, it - is assumed to be equal to **xoffset**. - - :param xoffset: The horizontal distance. - :param yoffset: The vertical distance. If omitted, both - distances are set to the same value. - :returns: An :py:class:`~PIL.Image.Image` object. - """ - if warnings: - warnings.warn( - "'offset' is deprecated; use 'ImageChops.offset' instead", - DeprecationWarning, stacklevel=2 - ) - from PIL import ImageChops - return ImageChops.offset(self, xoffset, yoffset) - def paste(self, im, box=None, mask=None): """ Pastes another image into this image. The box argument is either @@ -2080,19 +2034,6 @@ def frombytes(mode, size, data, decoder_name="raw", *args): return im -def fromstring(*args, **kw): - """Deprecated alias to frombytes. - - .. deprecated:: 2.0 - """ - warnings.warn( - 'fromstring() is deprecated. Please call frombytes() instead.', - DeprecationWarning, - stacklevel=2 - ) - return frombytes(*args, **kw) - - def frombuffer(mode, size, data, decoder_name="raw", *args): """ Creates an image memory referencing pixel data in a byte buffer. diff --git a/PIL/ImageWin.py b/PIL/ImageWin.py index bcb54bc3e..85b463afc 100644 --- a/PIL/ImageWin.py +++ b/PIL/ImageWin.py @@ -17,7 +17,6 @@ # See the README file for information on usage and redistribution. # -import warnings from PIL import Image @@ -183,25 +182,6 @@ class Dib(object): """ return self.image.tobytes() - ## - # Deprecated aliases to frombytes & tobytes. - - def fromstring(self, *args, **kw): - warnings.warn( - 'fromstring() is deprecated. Please call frombytes() instead.', - DeprecationWarning, - stacklevel=2 - ) - return self.frombytes(*args, **kw) - - def tostring(self): - warnings.warn( - 'tostring() is deprecated. Please call tobytes() instead.', - DeprecationWarning, - stacklevel=2 - ) - return self.tobytes() - ## # Create a Window with the given title size. diff --git a/Tests/test_image_offset.py b/Tests/test_image_offset.py deleted file mode 100644 index e5fe0bf47..000000000 --- a/Tests/test_image_offset.py +++ /dev/null @@ -1,25 +0,0 @@ -from helper import unittest, PillowTestCase, hopper - - -class TestImageOffset(PillowTestCase): - - def test_offset(self): - - im1 = hopper() - - im2 = self.assert_warning(DeprecationWarning, lambda: im1.offset(10)) - self.assertEqual(im1.getpixel((0, 0)), im2.getpixel((10, 10))) - - im2 = self.assert_warning( - DeprecationWarning, lambda: im1.offset(10, 20)) - self.assertEqual(im1.getpixel((0, 0)), im2.getpixel((10, 20))) - - im2 = self.assert_warning( - DeprecationWarning, lambda: im1.offset(20, 20)) - self.assertEqual(im1.getpixel((0, 0)), im2.getpixel((20, 20))) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/Tests/test_imagewin.py b/Tests/test_imagewin.py index 8a5bc125f..e719513d7 100644 --- a/Tests/test_imagewin.py +++ b/Tests/test_imagewin.py @@ -107,17 +107,6 @@ class TestImageWinDib(PillowTestCase): # Confirm they're the same self.assertEqual(dib1.tobytes(), dib2.tobytes()) - def test_dib_fromstring_tostring_deprecated(self): - # Arrange - im = hopper() - dib = ImageWin.Dib(im) - test_buffer = dib.tobytes() - - # Act/Assert - self.assert_warning(DeprecationWarning, dib.tostring) - self.assert_warning(DeprecationWarning, - lambda: dib.fromstring(test_buffer)) - if __name__ == '__main__': unittest.main() From 5835c1e09c54b6dc6c2e231cfc1aa917d82804f8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 19 Sep 2015 21:12:10 +1000 Subject: [PATCH 0587/1037] Added deprecation warning to ImageDraw setfont method, as specified in docs --- PIL/ImageDraw.py | 6 +++--- Tests/test_imagefont.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py index e70991e9f..b7260c14b 100644 --- a/PIL/ImageDraw.py +++ b/PIL/ImageDraw.py @@ -90,10 +90,10 @@ class ImageDraw(object): self.fill = 0 self.font = None - ## - # Set the default font. - def setfont(self, font): + if warnings: + warnings.warn("setfont() is deprecated. " + + "Please set the attribute directly instead.") # compatibility self.font = font diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 09f4942a7..dd2234467 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -223,11 +223,11 @@ try: font, orientation=orientation) # Original font - draw.setfont(font) + draw.font = font box_size_a = draw.textsize(word) # Rotated font - draw.setfont(transposed_font) + draw.font = transposed_font box_size_b = draw.textsize(word) # Check (w,h) of box a is (h,w) of box b @@ -245,11 +245,11 @@ try: font, orientation=orientation) # Original font - draw.setfont(font) + draw.font = font box_size_a = draw.textsize(word) # Rotated font - draw.setfont(transposed_font) + draw.font = transposed_font box_size_b = draw.textsize(word) # Check boxes a and b are same size From 71c95c8e5f3bba1845444a246d04646825e6bab3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 19 Sep 2015 21:36:19 +1000 Subject: [PATCH 0588/1037] Restored deprecated methods with errors instead --- PIL/Image.py | 17 +++++++++++++++++ PIL/ImageDraw.py | 8 ++++++++ PIL/ImageWin.py | 8 ++++++++ Tests/test_imagedraw.py | 8 ++++++++ Tests/test_imagewin.py | 9 +++++++++ 5 files changed, 50 insertions(+) diff --git a/PIL/Image.py b/PIL/Image.py index be00132b2..c08a1252d 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -681,6 +681,10 @@ class Image(object): return b"".join(data) + def tostring(self, *args, **kw): + raise Exception("tostring() has been removed. " + + "Please call tobytes() instead.") + def tobitmap(self, name="image"): """ Returns the image converted to an X11 bitmap. @@ -728,6 +732,10 @@ class Image(object): if s[1] != 0: raise ValueError("cannot decode image data") + def fromstring(self, *args, **kw): + raise Exception("fromstring() has been removed. " + + "Please call frombytes() instead.") + def load(self): """ Allocates storage for the image and loads the pixel data. In @@ -1224,6 +1232,10 @@ class Image(object): return self.im.histogram(extrema) return self.im.histogram() + def offset(self, xoffset, yoffset=None): + raise Exception("offset() has been removed. " + + "Please call ImageChops.offset() instead.") + def paste(self, im, box=None, mask=None): """ Pastes another image into this image. The box argument is either @@ -2034,6 +2046,11 @@ def frombytes(mode, size, data, decoder_name="raw", *args): return im +def fromstring(*args, **kw): + raise Exception("fromstring() has been removed. " + + "Please call frombytes() instead.") + + def frombuffer(mode, size, data, decoder_name="raw", *args): """ Creates an image memory referencing pixel data in a byte buffer. diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py index b7260c14b..9e154f236 100644 --- a/PIL/ImageDraw.py +++ b/PIL/ImageDraw.py @@ -90,6 +90,14 @@ class ImageDraw(object): self.fill = 0 self.font = None + def setink(self, ink): + raise Exception("setink() has been removed. " + + "Please use keyword arguments instead.") + + def setfill(self, onoff): + raise Exception("setfill() has been removed. " + + "Please use keyword arguments instead.") + def setfont(self, font): if warnings: warnings.warn("setfont() is deprecated. " + diff --git a/PIL/ImageWin.py b/PIL/ImageWin.py index 85b463afc..58894d604 100644 --- a/PIL/ImageWin.py +++ b/PIL/ImageWin.py @@ -182,6 +182,14 @@ class Dib(object): """ return self.image.tobytes() + def fromstring(self, *args, **kw): + raise Exception("fromstring() has been removed. " + + "Please use frombytes() instead.") + + def tostring(self, *args, **kw): + raise Exception("tostring() has been removed. " + + "Please use tobytes() instead.") + ## # Create a Window with the given title size. diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 5a85f1a90..3074fd7fa 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -44,6 +44,14 @@ class TestImageDraw(PillowTestCase): draw.polygon(list(range(100))) draw.rectangle(list(range(4))) + def test_removed_methods(self): + im = hopper() + + draw = ImageDraw.Draw(im) + + self.assertRaises(Exception, lambda: draw.setink(0)) + self.assertRaises(Exception, lambda: draw.setfill(0)) + def test_mode_mismatch(self): im = hopper("RGB").copy() diff --git a/Tests/test_imagewin.py b/Tests/test_imagewin.py index e719513d7..14cc2c7e3 100644 --- a/Tests/test_imagewin.py +++ b/Tests/test_imagewin.py @@ -107,6 +107,15 @@ class TestImageWinDib(PillowTestCase): # Confirm they're the same self.assertEqual(dib1.tobytes(), dib2.tobytes()) + def test_removed_methods(self): + # Arrange + im = hopper() + dib = ImageWin.Dib(im) + + # Act/Assert + self.assertRaises(Exception, dib.tostring) + self.assertRaises(Exception, lambda: dib.fromstring(test_buffer)) + if __name__ == '__main__': unittest.main() From 970ac4c7e1501bed405ac63a4e4b8bc6a9752cce Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 20 Sep 2015 10:33:52 +0100 Subject: [PATCH 0589/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 7373e617e..a449dfe2e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Removed spammy debug logging #1423 + [wiredfool] + - Save as GiF89a with support for animation parameters #1384 [radarhere] From 24f7bf5c373549d93d7499021740892c1ac4cdfe Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 20 Sep 2015 10:37:29 +0100 Subject: [PATCH 0590/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index a449dfe2e..f6f969bd7 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Tiff ImafeFileDirectory rewrite #1419 + [anntzer, wiredfool, homm] + - Removed spammy debug logging #1423 [wiredfool] From 85767c26d39a52b9e20e54768ed528836f23a812 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 20 Sep 2015 10:43:29 +0100 Subject: [PATCH 0591/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index f6f969bd7..17b5ca8b3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Qt needs 32 bit aligned image data #1430 + [ericfrederich] + - Tiff ImafeFileDirectory rewrite #1419 [anntzer, wiredfool, homm] From 2a3d1246847fe3011609a461b5653404ffa72415 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 20 Sep 2015 11:05:50 +0100 Subject: [PATCH 0592/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 17b5ca8b3..8ecf937cf 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Preserve alpha when converting from a QImage to a Pillow Image by using png instead of ppm #1429 + [ericfrederich] + - Qt needs 32 bit aligned image data #1430 [ericfrederich] From b4605f8045627c6ffdfb69ce2d47948d2506db0b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 20 Sep 2015 23:19:55 +1000 Subject: [PATCH 0593/1037] Update CHANGES.rst --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 8ecf937cf..d5fe2e5e4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,7 +10,7 @@ Changelog (Pillow) - Qt needs 32 bit aligned image data #1430 [ericfrederich] -- Tiff ImafeFileDirectory rewrite #1419 +- Tiff ImageFileDirectory rewrite #1419 [anntzer, wiredfool, homm] - Removed spammy debug logging #1423 From 8e39cc67dd73c76cd9af8d54e0e34e5ed5feb85a Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 22 Sep 2015 15:23:20 +0300 Subject: [PATCH 0594/1037] Coveralls 4.0 doesn't support Python 3.2 --- .travis.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 536a18582..3412cf57e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,13 +21,17 @@ python: install: - "travis_retry sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-qt4 ghostscript libffi-dev libjpeg-turbo-progs cmake imagemagick" - "travis_retry pip install cffi" - - "travis_retry pip install coverage nose" + - "travis_retry pip install nose" - "travis_retry pip install check-manifest" # Pyroma tests sometimes hang on PyPy; skip for PyPy - if [ $TRAVIS_PYTHON_VERSION != "pypy" ]; then travis_retry pip install pyroma; fi - if [ "$TRAVIS_PYTHON_VERSION" == "2.6" ]; then travis_retry pip install unittest2; fi + # Coveralls 4.0 doesn't support Python 3.2 + - if [ "$TRAVIS_PYTHON_VERSION" == "3.2" ]; then travis_retry pip install coverage==3.7.1; fi + - if [ "$TRAVIS_PYTHON_VERSION" != "3.2" ]; then travis_retry pip install coverage; fi + # webp - pushd depends && ./install_webp.sh && popd From e69306e3ff1abd1f11b707a19ac9d5e45c22d1d0 Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 22 Sep 2015 16:39:26 +0300 Subject: [PATCH 0595/1037] Skip hanging Pyroma tests on Python 2.6 --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3412cf57e..7c2c4e986 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,8 +23,8 @@ install: - "travis_retry pip install cffi" - "travis_retry pip install nose" - "travis_retry pip install check-manifest" - # Pyroma tests sometimes hang on PyPy; skip for PyPy - - if [ $TRAVIS_PYTHON_VERSION != "pypy" ]; then travis_retry pip install pyroma; fi + # Pyroma tests sometimes hang on PyPy and Python 2.6; skip for those + - if [ $TRAVIS_PYTHON_VERSION != "pypy" ]; if [ $TRAVIS_PYTHON_VERSION != "2.6" ]; then travis_retry pip install pyroma; fi; fi - if [ "$TRAVIS_PYTHON_VERSION" == "2.6" ]; then travis_retry pip install unittest2; fi From ec40c150cc3d03be5f1e29816652e6e34d4b6aab Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 22 Sep 2015 16:43:00 +0300 Subject: [PATCH 0596/1037] Skip hanging Pyroma tests on Python 2.6 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7c2c4e986..48b61d32b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,7 @@ install: - "travis_retry pip install nose" - "travis_retry pip install check-manifest" # Pyroma tests sometimes hang on PyPy and Python 2.6; skip for those - - if [ $TRAVIS_PYTHON_VERSION != "pypy" ]; if [ $TRAVIS_PYTHON_VERSION != "2.6" ]; then travis_retry pip install pyroma; fi; fi + - if [ $TRAVIS_PYTHON_VERSION != "pypy" && $TRAVIS_PYTHON_VERSION != "2.6" ]; then travis_retry pip install pyroma; fi - if [ "$TRAVIS_PYTHON_VERSION" == "2.6" ]; then travis_retry pip install unittest2; fi From 29601ca119adf3c02967ae9a8aea28d034bafe29 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 22 Sep 2015 21:07:31 +0100 Subject: [PATCH 0597/1037] Make zlib and jpeg required by default --- setup.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index bf8ec94a7..0d4bf73fd 100644 --- a/setup.py +++ b/setup.py @@ -105,7 +105,7 @@ class pil_build_ext(build_ext): class feature: zlib = jpeg = tiff = freetype = tcl = tk = lcms = webp = webpmux = None jpeg2000 = None - required = [] + required = set(['jpeg', 'zlib']) def require(self, feat): return feat in self.required @@ -139,12 +139,13 @@ class pil_build_ext(build_ext): for x in self.feature: if getattr(self, 'disable_%s' % x): setattr(self.feature, x, False) + self.feature.required.discard(x) if getattr(self, 'enable_%s' % x): raise ValueError( 'Conflicting options: --enable-%s and --disable-%s' % (x, x)) if getattr(self, 'enable_%s' % x): - self.feature.required.append(x) + self.feature.required.add(x) def build_extensions(self): @@ -505,6 +506,10 @@ class pil_build_ext(build_ext): for f in feature: if not getattr(feature, f) and feature.require(f): + if feature in ('jpeg', 'libz'): + raise ValueError('%s is required unless explicitly disabled' + + ' using --disable-%s, aborting' % + (f, f)) raise ValueError( '--enable-%s requested but %s not found, aborting.' % (f, f)) From d4afc9ed1fd3b373047ae8cf64c60b317dd8fd33 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 22 Sep 2015 21:38:17 +0100 Subject: [PATCH 0598/1037] Documentation for requiring libjpeg and zlib unless disabled --- docs/installation.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index 56dfc09db..4ba14d6f1 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -51,10 +51,16 @@ Many of Pillow's features require external libraries: * **libjpeg** provides JPEG functionality. - * Pillow has been tested with libjpeg versions **6b**, **8**, and **9** and libjpeg-turbo version **8**. + * Pillow has been tested with libjpeg versions **6b**, **8**, and + **9** and libjpeg-turbo version **8**. + * Starting with Pillow 3.0.0, libjpeg is required by default, but + may be disabled with the `--disable-jpeg` flag. * **zlib** provides access to compressed PNGs + * Starting with Pillow 3.0.0, zlib is required by default, but may + be disabled with the `--disable-zlib` flag. + * **libtiff** provides compressed TIFF functionality * Pillow has been tested with libtiff versions **3.x** and **4.0** From 62a52a7a34b69cb431823ef18422a8b8bc509d73 Mon Sep 17 00:00:00 2001 From: homm Date: Wed, 23 Sep 2015 00:33:03 +0300 Subject: [PATCH 0599/1037] replace broken image --- Tests/images/broken_data_stream.png | Bin 34560 -> 30732 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Tests/images/broken_data_stream.png b/Tests/images/broken_data_stream.png index 4626bc576119dcc6003f61a28dc12d67e1c7f602..ccf310b2f6355cb5e69848b2e547b9372cc5c191 100644 GIT binary patch literal 30732 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 Ang9R* From 31524b2e6beab206729464c5b480732aa9926797 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 23 Sep 2015 09:39:58 +0300 Subject: [PATCH 0600/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index d5fe2e5e4..767d39dcb 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Require zlib and libjpeg #1439 + [wiredfool] + - Preserve alpha when converting from a QImage to a Pillow Image by using png instead of ppm #1429 [ericfrederich] From a5bac62bc3dd70db8e0cd4a0386760e91255f2cf Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 22 Sep 2015 20:31:59 +1000 Subject: [PATCH 0601/1037] Flake8 fixes --- PIL/TiffImagePlugin.py | 73 ++++++++++++++++++++++-------------------- PIL/TiffTags.py | 6 ++-- 2 files changed, 41 insertions(+), 38 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 17426d4e2..d9edd62e1 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -227,6 +227,7 @@ def _limit_rational(val, max_val): _load_dispatch = {} _write_dispatch = {} + class ImageFileDirectory_v2(collections.MutableMapping): """This class represents a TIFF tag directory. To speed things up, we don't decode tags unless they're asked for. @@ -238,9 +239,9 @@ class ImageFileDirectory_v2(collections.MutableMapping): ifd.tagtype[key] = 2 print(ifd[key]) 'Some Data' - + Individual values are returned as the strings or numbers, sequences are - returned as tuples of the values. + returned as tuples of the values. The tiff metadata type of each item is stored in a dictionary of tag types in @@ -251,7 +252,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): Data Structures: * self.tagtype = {} - + * Key: numerical tiff tag number * Value: integer corresponding to the data type from `~PIL.TiffTags.TYPES` @@ -259,7 +260,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): """ """ Documentation: - + 'internal' data structures: * self._tags_v2 = {} Key: numerical tiff tag number Value: decoded data, as tuple for multiple values @@ -267,7 +268,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): Value: undecoded byte string from file * self._tags_v1 = {} Key: numerical tiff tag number Value: decoded data in the v1 format - + Tags will be found in the private attributes self._tagdata, and in self._tags_v2 once decoded. @@ -277,7 +278,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): tags will be populated into both _tags_v1 and _tags_v2. _Tags_v2 will be used if this IFD is used in the TIFF save routine. Tags should be read from tags_v1 if legacy_api == true. - + """ def __init__(self, ifh=b"II\052\0\0\0\0\0", prefix=None): @@ -313,10 +314,10 @@ class ImageFileDirectory_v2(collections.MutableMapping): raise Exception("Not allowing setting of legacy api") def reset(self): - self._tags_v1 = {} # will remain empty if legacy_api is false - self._tags_v2 = {} # main tag storage + self._tags_v1 = {} # will remain empty if legacy_api is false + self._tags_v2 = {} # main tag storage self._tagdata = {} - self.tagtype = {} # added 2008-06-05 by Florian Hoech + self.tagtype = {} # added 2008-06-05 by Florian Hoech self._next = None self._offset = None @@ -327,7 +328,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): """Return a dictionary of the image's tags. use `dict(ifd)` instead. - + .. deprecated:: 3.0.0 """ # FIXME Deprecate: use dict(self) @@ -336,7 +337,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): def named(self): """ :returns: dict of name|key: value - + Returns the complete tag dictionary, with named tags where possible. """ return dict((TAGS_V2.get(code, TagInfo()).name, value) @@ -395,13 +396,13 @@ class ImageFileDirectory_v2(collections.MutableMapping): self.tagtype[tag] = 2 if self.tagtype[tag] == 7 and bytes is not str: - values = [value.encode("ascii",'replace') if isinstance(value, str) else value + values = [value.encode("ascii", 'replace') if isinstance(value, str) else value for value in values] values = tuple(info.cvt_enum(value) for value in values) dest = self._tags_v1 if legacy_api else self._tags_v2 - + if info.length == 1: if legacy_api and self.tagtype[tag] in [5, 10]: values = values, @@ -453,12 +454,12 @@ class ImageFileDirectory_v2(collections.MutableMapping): (6, "b", "signed byte"), (8, "h", "signed short"), (9, "l", "signed long"), (11, "f", "float"), (12, "d", "double")])) - @_register_loader(1, 1) # Basic type, except for the legacy API. + @_register_loader(1, 1) # Basic type, except for the legacy API. def load_byte(self, data, legacy_api=True): return (data if legacy_api else tuple(map(ord, data) if bytes is str else data)) - @_register_writer(1) # Basic type, except for the legacy API. + @_register_writer(1) # Basic type, except for the legacy API. def write_byte(self, data): return data @@ -472,7 +473,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): def write_string(self, value): # remerge of https://github.com/python-pillow/Pillow/pull/1416 if sys.version_info[0] == 2: - value = value.decode('ascii', 'replace') + value = value.decode('ascii', 'replace') return b"" + value.encode('ascii', 'replace') + b"\0" @_register_loader(5, 8) @@ -521,8 +522,8 @@ class ImageFileDirectory_v2(collections.MutableMapping): self._offset = fp.tell() try: - for i in range(self._unpack("H", self._ensure_read(fp,2))[0]): - tag, typ, count, data = self._unpack("HHL4s", self._ensure_read(fp,12)) + for i in range(self._unpack("H", self._ensure_read(fp, 2))[0]): + tag, typ, count, data = self._unpack("HHL4s", self._ensure_read(fp, 12)) if DEBUG: tagname = TAGS_V2.get(tag, TagInfo()).name typname = TYPES.get(typ, "unknown") @@ -563,11 +564,11 @@ class ImageFileDirectory_v2(collections.MutableMapping): else: print("- value:", self[tag]) - self.next, = self._unpack("L", self._ensure_read(fp,4)) + self.next, = self._unpack("L", self._ensure_read(fp, 4)) except IOError as msg: warnings.warn(str(msg)) return - + def save(self, fp): if fp.tell() == 0: # skip TIFF header on subsequent pages @@ -608,7 +609,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): entries.append((tag, typ, count, data.ljust(4, b"\0"), b"")) else: entries.append((tag, typ, count, self._pack("L", offset), data)) - offset += (len(data) + 1) // 2 * 2 # pad to word + offset += (len(data) + 1) // 2 * 2 # pad to word # update strip offset data to point beyond auxiliary data if stripoffsets is not None: @@ -644,10 +645,11 @@ for idx, name in TYPES.items(): setattr(ImageFileDirectory_v2, "write_" + name, _write_dispatch[idx]) del _load_dispatch, _write_dispatch, idx, name -#Legacy ImageFileDirectory support. + +# Legacy ImageFileDirectory support. class ImageFileDirectory_v1(ImageFileDirectory_v2): """This class represents the **legacy** interface to a TIFF tag directory. - + Exposes a dictionary interface of the tags in the directory:: ifd = ImageFileDirectory_v1() @@ -655,18 +657,18 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2): ifd.tagtype[key] = 2 print ifd[key] ('Some Data',) - + Also contains a dictionary of tag types as read from the tiff image file, - `~PIL.TiffImagePlugin.ImageFileDirectory_v1.tagtype`. + `~PIL.TiffImagePlugin.ImageFileDirectory_v1.tagtype`. Values are returned as a tuple. - + .. deprecated:: 3.0.0 """ def __init__(self, *args, **kwargs): ImageFileDirectory_v2.__init__(self, *args, **kwargs) - self._legacy_api=True - #insert deprecation warning here. + self._legacy_api = True + # insert deprecation warning here. tags = property(lambda self: self._tags_v1) tagdata = property(lambda self: self._tagdata) @@ -686,7 +688,7 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2): ifd = cls(prefix=original.prefix) ifd._tagdata = original._tagdata ifd.tagtype = original.tagtype - ifd.next = original.next # an indicator for multipage tiffs + ifd.next = original.next # an indicator for multipage tiffs return ifd def to_v2(self): @@ -699,7 +701,7 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2): :returns: :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` """ - + ifd = ImageFileDirectory_v2(prefix=self.prefix) ifd._tagdata = dict(self._tagdata) ifd.tagtype = dict(self.tagtype) @@ -708,7 +710,7 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2): def __contains__(self, tag): return tag in self._tags_v1 or tag in self._tagdata - + def __len__(self): return len(set(self._tagdata) | set(self._tags_v1)) @@ -716,9 +718,9 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2): return iter(set(self._tagdata) | set(self._tags_v1)) def __setitem__(self, tag, value): - for legacy_api in (False,True): + for legacy_api in (False, True): self._setitem(tag, value, legacy_api) - + def __getitem__(self, tag): if tag not in self._tags_v1: # unpack on the fly data = self._tagdata[tag] @@ -731,10 +733,11 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2): val = val, return val - + # undone -- switch this pointer when IFD_LEGACY_API == False ImageFileDirectory = ImageFileDirectory_v1 + ## # Image plugin for TIFF files. @@ -827,7 +830,7 @@ class TiffImageFile(ImageFile.ImageFile): self.__frame += 1 self.fp.seek(self._frame_pos[frame]) self.tag_v2.load(self.fp) - # fill the legacy tag/ifd entries + # fill the legacy tag/ifd entries self.tag = self.ifd = ImageFileDirectory_v1.from_v2(self.tag_v2) self.__frame = frame self._setup() diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py index 77a067981..012d67632 100644 --- a/PIL/TiffTags.py +++ b/PIL/TiffTags.py @@ -19,6 +19,7 @@ from collections import namedtuple + class TagInfo(namedtuple("_TagInfo", "value name type length enum")): __slots__ = [] @@ -278,12 +279,12 @@ TAGS = {347: 'JPEGTables', 50740: 'DNGPrivateData', 50778: 'CalibrationIlluminant1', 50779: 'CalibrationIlluminant2', -} + } def _populate(): for k, v in TAGS_V2.items(): - # Populate legacy structure. + # Populate legacy structure. TAGS[k] = v[0] if len(v) == 4: for sk, sv in v[3].items(): @@ -312,4 +313,3 @@ TYPES = {} # 11: "float", # 12: "double", # } - From d097d44fa149d315b6a8f1015f0b1c18885c6d48 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 23 Sep 2015 22:14:06 +1000 Subject: [PATCH 0602/1037] Updated documentation and removed deprecated comment --- PIL/TiffImagePlugin.py | 3 +-- docs/handbook/image-file-formats.rst | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index d9edd62e1..b5e0a2eed 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -648,7 +648,7 @@ del _load_dispatch, _write_dispatch, idx, name # Legacy ImageFileDirectory support. class ImageFileDirectory_v1(ImageFileDirectory_v2): - """This class represents the **legacy** interface to a TIFF tag directory. + """This class represents the **legacy** interface to a TIFF tag directory. Exposes a dictionary interface of the tags in the directory:: @@ -668,7 +668,6 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2): def __init__(self, *args, **kwargs): ImageFileDirectory_v2.__init__(self, *args, **kwargs) self._legacy_api = True - # insert deprecation warning here. tags = property(lambda self: self._tags_v1) tagdata = property(lambda self: self._tagdata) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 5d2cab518..c688c7441 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -512,7 +512,7 @@ The :py:meth:`~PIL.Image.Image.save` method can take the following keyword argum For compatibility with legacy code, a `~PIL.TiffImagePlugin.ImageFileDirectory_v1` object may be passed - in this field. This will be deprecated in a future version. + in this field. However, this is deprecated. ..versionadded:: 3.0.0 From 62b54692fb95da8c491a5ec3ac99e9e8c439f128 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 24 Sep 2015 21:54:41 +0100 Subject: [PATCH 0603/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 767d39dcb..2cf164271 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Load more broken images #1428 + [homm] + - Require zlib and libjpeg #1439 [wiredfool] From 626fd6b1a0d06613fbeb5aff36323f2e41c28e1f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 26 Sep 2015 19:45:39 +1000 Subject: [PATCH 0604/1037] Updated repository URL --- Scripts/diffcover-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/diffcover-install.sh b/Scripts/diffcover-install.sh index 93e06efe3..850d368f8 100755 --- a/Scripts/diffcover-install.sh +++ b/Scripts/diffcover-install.sh @@ -1,5 +1,5 @@ # Fetch the remote master branch before running diff-cover on Travis CI. -# https://github.com/edx/diff-cover#troubleshooting +# https://github.com/Bachmann1234/diff-cover#troubleshooting git fetch origin master:refs/remotes/origin/master # CFLAGS=-O0 means build with no optimisation. From 1306f7d6af782665402e0a02cb469026cd7ca17e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 28 Sep 2015 21:53:25 +1000 Subject: [PATCH 0605/1037] Updated URLs for redirects --- PIL/JpegImagePlugin.py | 2 +- PIL/TiffImagePlugin.py | 2 +- docs/installation.rst | 2 +- winbuild/README.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index fa838258d..577951961 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -675,7 +675,7 @@ def _save(im, fp, filename): # if we optimize, libjpeg needs a buffer big enough to hold the whole image # in a shot. Guessing on the size, at im.size bytes. (raw pizel size is # channels*size, this is a value that's been used in a django patch. - # https://github.com/jdriscoll/django-imagekit/issues/50 + # https://github.com/matthewwithanm/django-imagekit/issues/50 bufsize = 0 if "optimize" in info or "progressive" in info or "progression" in info: # keep sets quality to 0, but the actual value may be high. diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 17426d4e2..4deaf8746 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -107,7 +107,7 @@ ICCPROFILE = 34675 EXIFIFD = 34665 XMP = 700 -# https://github.com/fiji/ImageJA/blob/master/src/main/java/ij/io/TiffDecoder.java +# https://github.com/imagej/ImageJA/blob/master/src/main/java/ij/io/TiffDecoder.java IMAGEJ_META_DATA_BYTE_COUNTS = 50838 IMAGEJ_META_DATA = 50839 diff --git a/docs/installation.rst b/docs/installation.rst index 4ba14d6f1..586d9d581 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -130,7 +130,7 @@ OS X Installation We provide binaries for OS X in the form of `Python Wheels `_. Alternatively you can compile Pillow from soure with XCode. -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 libtiff libjpeg webp little-cms2 diff --git a/winbuild/README.md b/winbuild/README.md index 8a303a3d5..58cf1b546 100644 --- a/winbuild/README.md +++ b/winbuild/README.md @@ -3,7 +3,7 @@ Quick README For more extensive info, see the windows build instructions `docs/build.rst`. -* See https://github.com/python-imaging/Pillow/issues/553#issuecomment-37877416 and https://github.com/matplotlib/matplotlib/issues/1717#issuecomment-13343859 +* See https://github.com/python-pillow/Pillow/issues/553#issuecomment-37877416 and https://github.com/matplotlib/matplotlib/issues/1717#issuecomment-13343859 * Works best with Python 3.4, due to virtualenv and pip batteries included. Python3+ required for fetch command. * Check config.py for virtual env paths, suffix for 64 bit releases. Defaults to `x64`, set `X64_EXT` to change. From 4d4dc99fa88ff6c3852c6d3d5ebacadab63d2e16 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 28 Sep 2015 15:34:44 +0300 Subject: [PATCH 0606/1037] Mention when OS X support was added To try and avoid confusion like http://stackoverflow.com/q/32799143/724176 --- docs/reference/ImageGrab.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/ImageGrab.rst b/docs/reference/ImageGrab.rst index 22ea72797..3d5703a40 100644 --- a/docs/reference/ImageGrab.rst +++ b/docs/reference/ImageGrab.rst @@ -7,7 +7,7 @@ The :py:mod:`ImageGrab` module can be used to copy the contents of the screen or the clipboard to a PIL image memory. -.. note:: The current version works on OS X and Windows only. +.. note:: The current version works on OS X and Windows only. OS X support was added in 3.0.0. .. versionadded:: 1.1.3 From 009bbb753db9cfcc52d699b93d11e6ae72c94f24 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 29 Sep 2015 19:51:24 +1000 Subject: [PATCH 0607/1037] Update CHANGES.rst --- CHANGES.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 2cf164271..9c658dc21 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -97,8 +97,8 @@ Changelog (Pillow) - Changed register calls to use format property #1333 [radarhere] -- Added support for ImageGrab.grab to OS X #1367 - [radarhere] +- Added support for ImageGrab.grab to OS X #1367, #1443 + [radarhere, hugovk] - Fixed PSDraw stdout Python 3 compatibility #1365 [radarhere] From 5d85ac727febd0f7734c76d4425beae6351b7c16 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 29 Sep 2015 06:08:09 -0400 Subject: [PATCH 0608/1037] Wording [ci skip] Drop the quotes on "friendly PIL fork" and add title case --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 169547be1..5c73a3aa4 100644 --- a/README.rst +++ b/README.rst @@ -4,7 +4,7 @@ Pillow Python Imaging Library (Fork) ----------------------------- -Pillow is the "friendly PIL fork" by `Alex Clark and Contributors `_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. +Pillow is the Friendly PIL Fork by `Alex Clark and Contributors `_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. .. image:: https://img.shields.io/travis/python-pillow/Pillow/master.svg?label=Linux%20build :target: https://travis-ci.org/python-pillow/Pillow From 5b8d75f812a405c1e24d90d6fba12baa5fb97ee3 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 29 Sep 2015 06:35:07 -0400 Subject: [PATCH 0609/1037] README fixes - Add Makefile target ``make readme`` to run ``viewdoc`` - Reorg "More Info" sections - Docs to the top - Less doc links (folks can find more than installation & guides from documentation link) - Add link to PRs - Reorg link to pre-fork changes --- CHANGES.rst | 7 ++++--- Makefile | 3 +++ README.rst | 15 +++++++-------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 9c658dc21..b53499578 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1184,10 +1184,11 @@ Changelog (Pillow) - Forked PIL based on `Hanno Schlichting's re-packaging `_ [aclark4life] -.. Note:: What follows is the original PIL 1.1.7 CHANGES +Pre-fork +-------- -0.2b5 - 1.1.7 (1995-2010) -------------------------- +0.2b5-1.1.7 ++++++++++++ :: diff --git a/Makefile b/Makefile index e123a74c7..24b8c5f6b 100644 --- a/Makefile +++ b/Makefile @@ -75,3 +75,6 @@ upload-test: upload: python setup.py sdist --format=gztar,zip upload + +readme: + viewdoc diff --git a/README.rst b/README.rst index 5c73a3aa4..0b56ae46d 100644 --- a/README.rst +++ b/README.rst @@ -4,7 +4,7 @@ Pillow Python Imaging Library (Fork) ----------------------------- -Pillow is the Friendly PIL Fork by `Alex Clark and Contributors `_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. +Pillow is the friendly PIL fork by `Alex Clark and Contributors `_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. .. image:: https://img.shields.io/travis/python-pillow/Pillow/master.svg?label=Linux%20build :target: https://travis-ci.org/python-pillow/Pillow @@ -37,17 +37,16 @@ Pillow is the Friendly PIL Fork by `Alex Clark and Contributors `_ +- `Documentation `_ - - `Pre-fork `_ + - `Installation `_ + - `Guides `_ - `Contribute `_ - `Issues `_ + - `Pull requests `_ -- `Documentation `_ +- `Changelog `_ - - `About `_ - - `Guides `_ - - `Installation `_ - - `Reference `_ + - `Pre-fork `_ From ff8c671f7483e007d2e3e37d432d001745469c80 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 29 Sep 2015 07:11:55 -0400 Subject: [PATCH 0610/1037] s/http/https/ [ci skip] --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 0b56ae46d..d5e23a3d8 100644 --- a/README.rst +++ b/README.rst @@ -37,10 +37,10 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors `_ +- `Documentation `_ - - `Installation `_ - - `Guides `_ + - `Installation `_ + - `Guides `_ - `Contribute `_ From 4e31fb745f4ef79edf461c70837946073c374d8e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 29 Sep 2015 22:51:52 +1000 Subject: [PATCH 0611/1037] Added PDF multipage saving --- PIL/PdfImagePlugin.py | 163 +++++++++++++++++++++++------------------ Tests/test_file_pdf.py | 22 +++++- 2 files changed, 110 insertions(+), 75 deletions(-) diff --git a/PIL/PdfImagePlugin.py b/PIL/PdfImagePlugin.py index e58e9e666..466fc6723 100644 --- a/PIL/PdfImagePlugin.py +++ b/PIL/PdfImagePlugin.py @@ -51,17 +51,21 @@ def _endobj(fp): fp.write("endobj\n") +def _save_all(im, fp, filename): + _save(im, fp, filename, save_all=True) + + ## # (Internal) Image save plugin for the PDF format. -def _save(im, fp, filename): +def _save(im, fp, filename, save_all=False): resolution = im.encoderinfo.get("resolution", 72.0) # # make sure image data is available im.load() - xref = [0]*(5+1) # placeholders + xref = [0] class TextWriter(object): def __init__(self, fp): @@ -78,11 +82,6 @@ def _save(im, fp, filename): fp.write("%PDF-1.2\n") fp.write("% created by PIL PDF driver " + __version__ + "\n") - # - # Get image characteristics - - width, height = im.size - # FIXME: Should replace ASCIIHexDecode with RunLengthDecode (packbits) # or LZWDecode (tiff/lzw compression). Note that PDF 1.2 also supports # Flatedecode (zip compression). @@ -125,7 +124,7 @@ def _save(im, fp, filename): # # catalogue - xref[1] = fp.tell() + xref.append(fp.tell()) _obj( fp, 1, Type="/Catalog", @@ -134,89 +133,108 @@ def _save(im, fp, filename): # # pages + numberOfPages = 1 + if save_all: + try: + numberOfPages = im.n_frames + except AttributeError: + # Image format does not have n_frames. It is a single frame image + pass + pages = [str(pageNumber*3+4)+" 0 R" + for pageNumber in range(0, numberOfPages)] - xref[2] = fp.tell() + xref.append(fp.tell()) _obj( fp, 2, Type="/Pages", - Count=1, - Kids="[4 0 R]") + Count=len(pages), + Kids="["+"\n".join(pages)+"]") _endobj(fp) - # - # image + for pageNumber in range(0, numberOfPages): + im.seek(pageNumber) - op = io.BytesIO() + # + # image - if filter == "/ASCIIHexDecode": - if bits == 1: - # FIXME: the hex encoder doesn't support packed 1-bit - # images; do things the hard way... - data = im.tobytes("raw", "1") - im = Image.new("L", (len(data), 1), None) - im.putdata(data) - ImageFile._save(im, op, [("hex", (0, 0)+im.size, 0, im.mode)]) - elif filter == "/DCTDecode": - Image.SAVE["JPEG"](im, op, filename) - elif filter == "/FlateDecode": - ImageFile._save(im, op, [("zip", (0, 0)+im.size, 0, im.mode)]) - elif filter == "/RunLengthDecode": - ImageFile._save(im, op, [("packbits", (0, 0)+im.size, 0, im.mode)]) - else: - raise ValueError("unsupported PDF filter (%s)" % filter) + op = io.BytesIO() - xref[3] = fp.tell() - _obj( - fp, 3, - Type="/XObject", - Subtype="/Image", - Width=width, # * 72.0 / resolution, - Height=height, # * 72.0 / resolution, - Length=len(op.getvalue()), - Filter=filter, - BitsPerComponent=bits, - DecodeParams=params, - ColorSpace=colorspace) + if filter == "/ASCIIHexDecode": + if bits == 1: + # FIXME: the hex encoder doesn't support packed 1-bit + # images; do things the hard way... + data = im.tobytes("raw", "1") + im = Image.new("L", (len(data), 1), None) + im.putdata(data) + ImageFile._save(im, op, [("hex", (0, 0)+im.size, 0, im.mode)]) + elif filter == "/DCTDecode": + Image.SAVE["JPEG"](im, op, filename) + elif filter == "/FlateDecode": + ImageFile._save(im, op, [("zip", (0, 0)+im.size, 0, im.mode)]) + elif filter == "/RunLengthDecode": + ImageFile._save(im, op, [("packbits", (0, 0)+im.size, 0, im.mode)]) + else: + raise ValueError("unsupported PDF filter (%s)" % filter) - fp.write("stream\n") - fp.fp.write(op.getvalue()) - fp.write("\nendstream\n") + # + # Get image characteristics - _endobj(fp) + width, height = im.size - # - # page + xref.append(fp.tell()) + _obj( + fp, pageNumber*3+3, + Type="/XObject", + Subtype="/Image", + Width=width, # * 72.0 / resolution, + Height=height, # * 72.0 / resolution, + Length=len(op.getvalue()), + Filter=filter, + BitsPerComponent=bits, + DecodeParams=params, + ColorSpace=colorspace) - xref[4] = fp.tell() - _obj(fp, 4) - fp.write( - "<<\n/Type /Page\n/Parent 2 0 R\n" - "/Resources <<\n/ProcSet [ /PDF %s ]\n" - "/XObject << /image 3 0 R >>\n>>\n" - "/MediaBox [ 0 0 %d %d ]\n/Contents 5 0 R\n>>\n" % ( - procset, - int(width * 72.0 / resolution), - int(height * 72.0 / resolution))) - _endobj(fp) + fp.write("stream\n") + fp.fp.write(op.getvalue()) + fp.write("\nendstream\n") - # - # page contents + _endobj(fp) - op = TextWriter(io.BytesIO()) + # + # page - op.write( - "q %d 0 0 %d 0 0 cm /image Do Q\n" % ( - int(width * 72.0 / resolution), - int(height * 72.0 / resolution))) + xref.append(fp.tell()) + _obj(fp, pageNumber*3+4) + fp.write( + "<<\n/Type /Page\n/Parent 2 0 R\n" + "/Resources <<\n/ProcSet [ /PDF %s ]\n" + "/XObject << /image %d 0 R >>\n>>\n" + "/MediaBox [ 0 0 %d %d ]\n/Contents %d 0 R\n>>\n" % ( + procset, + pageNumber*3+3, + int(width * 72.0 / resolution), + int(height * 72.0 / resolution), + pageNumber*3+5)) + _endobj(fp) - xref[5] = fp.tell() - _obj(fp, 5, Length=len(op.fp.getvalue())) + # + # page contents - fp.write("stream\n") - fp.fp.write(op.fp.getvalue()) - fp.write("\nendstream\n") + op = TextWriter(io.BytesIO()) - _endobj(fp) + op.write( + "q %d 0 0 %d 0 0 cm /image Do Q\n" % ( + int(width * 72.0 / resolution), + int(height * 72.0 / resolution))) + + xref.append(fp.tell()) + _obj(fp, pageNumber*3+5, Length=len(op.fp.getvalue())) + + fp.write("stream\n") + fp.fp.write(op.fp.getvalue()) + fp.write("\nendstream\n") + + _endobj(fp) # # trailer @@ -232,6 +250,7 @@ def _save(im, fp, filename): # -------------------------------------------------------------------- Image.register_save("PDF", _save) +Image.register_save_all("PDF", _save_all) Image.register_extension("PDF", ".pdf") diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index 8dad9822c..e3d41ffed 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -1,17 +1,20 @@ from helper import unittest, PillowTestCase, hopper - +from PIL import Image import os.path class TestFilePdf(PillowTestCase): - def helper_save_as_pdf(self, mode): + def helper_save_as_pdf(self, mode, save_all=False): # Arrange im = hopper(mode) outfile = self.tempfile("temp_" + mode + ".pdf") # Act - im.save(outfile) + if save_all: + im.save(outfile, save_all=True) + else: + im.save(outfile) # Assert self.assertTrue(os.path.isfile(outfile)) @@ -58,6 +61,19 @@ class TestFilePdf(PillowTestCase): self.assertRaises(ValueError, lambda: im.save(outfile)) + def test_save_all(self): + # Single frame image + self.helper_save_as_pdf("RGB", save_all=True) + + # Multiframe image + im = Image.open("Tests/images/dispose_bgnd.gif") + + outfile = self.tempfile('temp.pdf') + im.save(outfile, save_all=True) + + self.assertTrue(os.path.isfile(outfile)) + self.assertGreater(os.path.getsize(outfile), 0) + if __name__ == '__main__': unittest.main() From 8d615583d79e42299417096c13a0e83ac5667cca Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 29 Sep 2015 09:53:07 -0400 Subject: [PATCH 0612/1037] Wording [ci skip] Remove comments on "friendly PIL fork" --- docs/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index 9bb983179..5b0a5d4af 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,7 +1,7 @@ Pillow ====== -Pillow is the "friendly PIL fork" by `Alex Clark and Contributors `_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. +Pillow is the friendly PIL fork by `Alex Clark and Contributors `_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. .. image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master :target: https://travis-ci.org/python-pillow/Pillow From 0387cfe9127e5fffd3a1973c14347a1c78b8dae0 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 29 Sep 2015 10:03:10 -0400 Subject: [PATCH 0613/1037] Remove badge [ci skip] Landscape.io is down #1351. --- README.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index d5e23a3d8..eb8819c3f 100644 --- a/README.rst +++ b/README.rst @@ -30,7 +30,8 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors Date: Tue, 29 Sep 2015 15:38:23 +0100 Subject: [PATCH 0614/1037] Updated Changes.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index b53499578..52ebbff20 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Removed deprecated code, Image.tostring, Image.fromstring, Image.offset, ImageDraw.setink, ImageDraw.setfill, ImageFileIO, ImageFont.FreeTypeFont and ImageFont.truetype `file` kwarg, ImagePalette private _make functions, ImageWin.fromstring and ImageWin.tostring #1343 + [radarhere] + - Load more broken images #1428 [homm] From a5da0e0c4a9c9f6093ca367ba05f03644c677511 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 29 Sep 2015 18:11:15 -0400 Subject: [PATCH 0615/1037] Revert "Remove badge [ci skip]" This reverts commit 0387cfe9127e5fffd3a1973c14347a1c78b8dae0. --- README.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.rst b/README.rst index eb8819c3f..d5e23a3d8 100644 --- a/README.rst +++ b/README.rst @@ -30,8 +30,7 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors Date: Tue, 29 Sep 2015 22:28:42 -0700 Subject: [PATCH 0616/1037] Py3 fixes for ImageQt.align8to32 --- PIL/ImageQt.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PIL/ImageQt.py b/PIL/ImageQt.py index 591d85ff8..b37177aff 100644 --- a/PIL/ImageQt.py +++ b/PIL/ImageQt.py @@ -108,10 +108,10 @@ def align8to32(bytes, width, mode): return bytes new_data = [] - for i in range(len(bytes) / bytes_per_line): - new_data.append(bytes[i*bytes_per_line:(i+1)*bytes_per_line] + '\x00' * extra_padding) + for i in range(len(bytes) // bytes_per_line): + new_data.append(bytes[i*bytes_per_line:(i+1)*bytes_per_line] + b'\x00' * extra_padding) - return ''.join(new_data) + return b''.join(new_data) def _toqclass_helper(im): data = None From b808a4ed4662d0f3706b3583a5a3948c62cd13dc Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 30 Sep 2015 20:13:58 +1000 Subject: [PATCH 0617/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 52ebbff20..3e5ae11d1 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -127,6 +127,12 @@ Changelog (Pillow) - Added test for GimpPaletteFile #1324 [radarhere] +- Merged gifmaker script to allow saving of multi-frame GIF images #1320 + [radarhere] + +- Added is_animated property to multi-frame formats #1319 + [radarhere] + - Fixed ValueError in Python 2.6 #1315 #1316 [cgohlke, radarhere] From eabaef12fa2336bfae622dc99c93251b35a65b16 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Wed, 30 Sep 2015 06:16:55 -0400 Subject: [PATCH 0618/1037] Remove pre-fork-readme This exists here *too* and we probably don't need two copies: https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst#pre-fork. Hence, this one can be removed. --- docs/index.rst | 1 - docs/pre-fork-readme.rst | 306 --------------------------------------- 2 files changed, 307 deletions(-) delete mode 100644 docs/pre-fork-readme.rst diff --git a/docs/index.rst b/docs/index.rst index 5b0a5d4af..93d9bbd64 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -36,7 +36,6 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors Date: Wed, 30 Sep 2015 06:21:52 -0400 Subject: [PATCH 0619/1037] Shorten title; Clarify these are rst files - Shorten "About Pillow" to Pillow (the reader likely already knows we're talking about Pillow) - Clarify reference to rst files by using rst extension; Sphinx seems to recognize either convention, but humans (like me) forget exactly what Sphinx is doing sometimes. --- docs/about.rst | 4 ++-- docs/index.rst | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/about.rst b/docs/about.rst index 67bf20481..a36fe2c2a 100644 --- a/docs/about.rst +++ b/docs/about.rst @@ -1,5 +1,5 @@ -About Pillow -============ +About +===== Goals ----- diff --git a/docs/index.rst b/docs/index.rst index 93d9bbd64..103b21a13 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -30,12 +30,12 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors Date: Wed, 30 Sep 2015 06:38:51 -0400 Subject: [PATCH 0620/1037] Clean up Handbook A while back somebody awesome forked the PIL Handbook (http://effbot.org/imagingbook/pil-index.htm) to Pillow. IIRC we tried to incorporate the handbook "seemlessly", which apparently meant not using the word "handbook" anywhere. Now that seems confusing, so I'm reorganizing all the handbook content under one section named "Handbook". This commit: - Moves guides.rst to handbook/index.rst - Moves appendices.rst to handbook/ - Removes developer.rst from handbook index. - Removes porting-pil-to-pillow.rst from handbook index. Unrelated to the above: - Shorten title "Porting existing PIL-based code to Pillow" to "Porting" - Move text "Porting existing PIL-based code to Pillow" to faux sub-header (bold text not sub-header because there's only one section.) "Porting" may be better named "Migrating", not sure yet. --- README.rst | 2 +- docs/Makefile | 3 +++ docs/guides.rst | 11 ----------- docs/handbook/index.rst | 10 ++++++++++ docs/index.rst | 3 +-- docs/porting-pil-to-pillow.rst | 6 ++++-- 6 files changed, 19 insertions(+), 16 deletions(-) delete mode 100644 docs/guides.rst create mode 100644 docs/handbook/index.rst diff --git a/README.rst b/README.rst index d5e23a3d8..6e0f6ef52 100644 --- a/README.rst +++ b/README.rst @@ -40,7 +40,7 @@ More Information - `Documentation `_ - `Installation `_ - - `Guides `_ + - `Handbook `_ - `Contribute `_ diff --git a/docs/Makefile b/docs/Makefile index f28f802a1..1a912039e 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -154,3 +154,6 @@ doctest: livehtml: html livereload $(BUILDDIR)/html -p 33233 + +serve: + cd $(BUILDDIR)/html; python -m SimpleHTTPServer diff --git a/docs/guides.rst b/docs/guides.rst deleted file mode 100644 index 87ce75bd4..000000000 --- a/docs/guides.rst +++ /dev/null @@ -1,11 +0,0 @@ -Guides -====== - -.. toctree:: - :maxdepth: 2 - - handbook/overview - handbook/tutorial - handbook/concepts - porting-pil-to-pillow - developer diff --git a/docs/handbook/index.rst b/docs/handbook/index.rst new file mode 100644 index 000000000..acdeff7db --- /dev/null +++ b/docs/handbook/index.rst @@ -0,0 +1,10 @@ +Handbook +======== + +.. toctree:: + :maxdepth: 2 + + overview + tutorial + concepts + appendices diff --git a/docs/index.rst b/docs/index.rst index 103b21a13..4a19630b1 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -31,9 +31,8 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors Date: Wed, 30 Sep 2015 06:56:48 -0400 Subject: [PATCH 0621/1037] Create developer section - Move release notes & porting info to developer section --- docs/{developer.rst => developer/index.rst} | 9 ++++++++- docs/{ => developer}/porting-pil-to-pillow.rst | 0 docs/{ => developer}/releasenotes/2.7.0.rst | 0 docs/{ => developer}/releasenotes/2.8.0.rst | 0 docs/{ => developer}/releasenotes/index.rst | 0 docs/index.rst | 2 ++ 6 files changed, 10 insertions(+), 1 deletion(-) rename docs/{developer.rst => developer/index.rst} (80%) rename docs/{ => developer}/porting-pil-to-pillow.rst (100%) rename docs/{ => developer}/releasenotes/2.7.0.rst (100%) rename docs/{ => developer}/releasenotes/2.8.0.rst (100%) rename docs/{ => developer}/releasenotes/index.rst (100%) diff --git a/docs/developer.rst b/docs/developer/index.rst similarity index 80% rename from docs/developer.rst rename to docs/developer/index.rst index ea3b0f05e..91f00cb45 100644 --- a/docs/developer.rst +++ b/docs/developer/index.rst @@ -3,7 +3,6 @@ Developer .. Note:: When committing only trivial changes, please include [ci skip] in the commit message to avoid running tests on Travis-CI. Thank you! - Release ------- @@ -15,3 +14,11 @@ Version number The version number is currently stored in 3 places:: PIL/__init__.py _imaging.c setup.py + +Release notes +------------- + +.. toctree:: + :maxdepth: 2 + + releasenotes/index.rst diff --git a/docs/porting-pil-to-pillow.rst b/docs/developer/porting-pil-to-pillow.rst similarity index 100% rename from docs/porting-pil-to-pillow.rst rename to docs/developer/porting-pil-to-pillow.rst diff --git a/docs/releasenotes/2.7.0.rst b/docs/developer/releasenotes/2.7.0.rst similarity index 100% rename from docs/releasenotes/2.7.0.rst rename to docs/developer/releasenotes/2.7.0.rst diff --git a/docs/releasenotes/2.8.0.rst b/docs/developer/releasenotes/2.8.0.rst similarity index 100% rename from docs/releasenotes/2.8.0.rst rename to docs/developer/releasenotes/2.8.0.rst diff --git a/docs/releasenotes/index.rst b/docs/developer/releasenotes/index.rst similarity index 100% rename from docs/releasenotes/index.rst rename to docs/developer/releasenotes/index.rst diff --git a/docs/index.rst b/docs/index.rst index 4a19630b1..2ecb7d123 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -34,7 +34,9 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors Date: Wed, 30 Sep 2015 07:24:58 -0400 Subject: [PATCH 0622/1037] Remove extra header --- docs/developer/releasenotes/index.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/developer/releasenotes/index.rst b/docs/developer/releasenotes/index.rst index d70edc54d..39814d23f 100644 --- a/docs/developer/releasenotes/index.rst +++ b/docs/developer/releasenotes/index.rst @@ -1,6 +1,3 @@ -Release Notes -============= - .. note:: Contributors please include release notes as needed or appropriate with your bug fixes, feature additions and tests. .. toctree:: From e85d058f07390dc1ce359692eb908e3d8d3913f9 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Wed, 30 Sep 2015 07:27:02 -0400 Subject: [PATCH 0623/1037] Add RTD blurb --- docs/developer/index.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/developer/index.rst b/docs/developer/index.rst index 91f00cb45..19f823ee6 100644 --- a/docs/developer/index.rst +++ b/docs/developer/index.rst @@ -15,6 +15,12 @@ The version number is currently stored in 3 places:: PIL/__init__.py _imaging.c setup.py + +Readthedocs +~~~~~~~~~~~ + +Make sure the default version for Readthedocs is the latest release version e.g. 3.0.0 not latest. + Release notes ------------- From 7ff78543c86dc5be599a1bf7ebc9111bb014e3cb Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Wed, 30 Sep 2015 07:28:35 -0400 Subject: [PATCH 0624/1037] Port to the top As "how to port" (make code written for PIL work with Pillow) is likely a FAQ, back to the top. --- docs/index.rst | 1 + docs/{developer/porting-pil-to-pillow.rst => porting.rst} | 0 2 files changed, 1 insertion(+) rename docs/{developer/porting-pil-to-pillow.rst => porting.rst} (100%) diff --git a/docs/index.rst b/docs/index.rst index 2ecb7d123..8cadaebd4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -33,6 +33,7 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors Date: Wed, 30 Sep 2015 07:31:06 -0400 Subject: [PATCH 0625/1037] Move Plugins to Reference Plugin references were previously unlinked AFAICT, now moved to references/ and linked from references index. (I'm not sure I fully understand or can recall if these API references are generated once then updated or generated each time via `make html` ?) --- docs/reference/index.rst | 1 + docs/{ => reference}/plugins.rst | 0 2 files changed, 1 insertion(+) rename docs/{ => reference}/plugins.rst (100%) diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 2d89d2100..555bd2a57 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -32,3 +32,4 @@ Reference PixelAccess PyAccess ../PIL + plugins diff --git a/docs/plugins.rst b/docs/reference/plugins.rst similarity index 100% rename from docs/plugins.rst rename to docs/reference/plugins.rst From b65c170b22b605583360a28abff597f80cde991f Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Wed, 30 Sep 2015 07:33:16 -0400 Subject: [PATCH 0626/1037] Order swap and remove old ref --- docs/index.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 8cadaebd4..f986639d0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -33,9 +33,8 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors Date: Wed, 30 Sep 2015 08:30:40 -0400 Subject: [PATCH 0627/1037] Wording --- docs/developer/index.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/developer/index.rst b/docs/developer/index.rst index 19f823ee6..c7a4d9838 100644 --- a/docs/developer/index.rst +++ b/docs/developer/index.rst @@ -16,10 +16,10 @@ The version number is currently stored in 3 places:: PIL/__init__.py _imaging.c setup.py -Readthedocs -~~~~~~~~~~~ +Read the Docs +~~~~~~~~~~~~~ -Make sure the default version for Readthedocs is the latest release version e.g. 3.0.0 not latest. +Make sure the default version for Read the Docs is the latest release version e.g. 3.0.0 not latest. Release notes ------------- From cfd87ccad2690dcb1cf06e250ddcd1b702acc85b Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Wed, 30 Sep 2015 08:37:02 -0400 Subject: [PATCH 0628/1037] Move CI note to contributing --- CONTRIBUTING.md | 1 + docs/developer/index.rst | 30 ------------------------------ 2 files changed, 1 insertion(+), 30 deletions(-) delete mode 100644 docs/developer/index.rst diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5a49ab2d7..40dd9119e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,6 +17,7 @@ Please send a pull request to the master branch. Please include [documentation]( - Separate code commits from reformatting commits. - Provide tests for any newly added code. - Follow PEP8. +- When committing only trivial changes please include [ci skip] in the commit message to avoid running tests on Travis-CI. ## Reporting Issues diff --git a/docs/developer/index.rst b/docs/developer/index.rst deleted file mode 100644 index c7a4d9838..000000000 --- a/docs/developer/index.rst +++ /dev/null @@ -1,30 +0,0 @@ -Developer -========= - -.. Note:: When committing only trivial changes, please include [ci skip] in the commit message to avoid running tests on Travis-CI. Thank you! - -Release -------- - -Details about making a Pillow release. - -Version number -~~~~~~~~~~~~~~ - -The version number is currently stored in 3 places:: - - PIL/__init__.py _imaging.c setup.py - - -Read the Docs -~~~~~~~~~~~~~ - -Make sure the default version for Read the Docs is the latest release version e.g. 3.0.0 not latest. - -Release notes -------------- - -.. toctree:: - :maxdepth: 2 - - releasenotes/index.rst From 02c00c10b7eb6295b8577632083c3bb7772d2f96 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Wed, 30 Sep 2015 08:43:21 -0400 Subject: [PATCH 0629/1037] Add RTD blurb --- RELEASING.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RELEASING.md b/RELEASING.md index fbfd8f2b4..b3739746b 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -103,3 +103,7 @@ Released as needed privately to individual vendors for critical security-related ## Publicize Release * [ ] Announce release availability via [Twitter](https://twitter.com/pythonpillow) e.g. https://twitter.com/aclark4life/status/583366798302691328. + +## Documentation + +* [ ] Make sure the default version for Read the Docs is the latest release version e.g. 3.0.0 not latest: https://readthedocs.org/dashboard/pillow/versions/ From 8e05ae6fb8cefe29322dee81c9f746532f302a49 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Wed, 30 Sep 2015 09:10:28 -0400 Subject: [PATCH 0630/1037] Add doc build & linkcheck to CI --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 48b61d32b..368b2ac87 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,6 +46,8 @@ script: - if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage run --append --include=PIL/* selftest.py; fi - if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage run --append --include=PIL/* -m nose -vx Tests/test_*.py; fi - check-manifest --ignore "depends/*" + # Sphinx + - cd docs; make html; make linkcheck after_success: # gather the coverage data From 93a326dc01d64bbd496101c483d0eb2aac4b45ae Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Wed, 30 Sep 2015 09:37:54 -0400 Subject: [PATCH 0631/1037] Install Sphinx; pushd/popd instead of cd --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 368b2ac87..a2b2d7a56 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,6 +23,7 @@ install: - "travis_retry pip install cffi" - "travis_retry pip install nose" - "travis_retry pip install check-manifest" + - "travis_retry pip install Sphinx" # Pyroma tests sometimes hang on PyPy and Python 2.6; skip for those - if [ $TRAVIS_PYTHON_VERSION != "pypy" && $TRAVIS_PYTHON_VERSION != "2.6" ]; then travis_retry pip install pyroma; fi @@ -47,7 +48,7 @@ script: - if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage run --append --include=PIL/* -m nose -vx Tests/test_*.py; fi - check-manifest --ignore "depends/*" # Sphinx - - cd docs; make html; make linkcheck + - pushd docs; make html; make linkcheck; popd after_success: # gather the coverage data From 125ec9c650830d5a2e3bf79100d1f565a0761789 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 1 Oct 2015 19:45:27 +1000 Subject: [PATCH 0632/1037] Added documentation --- docs/handbook/image-file-formats.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 5d2cab518..f09a7cbbf 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -786,6 +786,11 @@ PIL can write PDF (Acrobat) images. Such images are written as binary PDF 1.1 files, using either JPEG or HEX encoding depending on the image mode (and whether JPEG support is available or not). +When calling :py:meth:`~PIL.Image.Image.save`, if a multiframe image is used, +by default, only the first image will be saved. To save all frames, each frame +to a separate page of the PDF, the ``save_all`` parameter must be present and +set to ``True``. + PIXAR (read only) ^^^^^^^^^^^^^^^^^ From 4efe2628576db47335aa2affc607c932eb785d08 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 1 Oct 2015 13:34:28 +0100 Subject: [PATCH 0633/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 3e5ae11d1..f74607686 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Added PDF multipage saving #1445 + [radarhere] + - Removed deprecated code, Image.tostring, Image.fromstring, Image.offset, ImageDraw.setink, ImageDraw.setfill, ImageFileIO, ImageFont.FreeTypeFont and ImageFont.truetype `file` kwarg, ImagePalette private _make functions, ImageWin.fromstring and ImageWin.tostring #1343 [radarhere] From 2be12dec2b231d31400f44bfa855966484997c16 Mon Sep 17 00:00:00 2001 From: nu774 Date: Sat, 19 Sep 2015 15:02:40 +0900 Subject: [PATCH 0634/1037] Don't use int to handle type Couldn't accept handle values greater than 0x7FFFFFFF, which wasn't enough even on 32bit system, and completely wrong for 64bit. --- display.c | 47 ++++++++++++++++++++++++++-------------------- libImaging/Dib.c | 6 +++--- libImaging/ImDib.h | 6 +++--- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/display.c b/display.c index e10d72c0d..9f493344e 100644 --- a/display.c +++ b/display.c @@ -35,6 +35,12 @@ #include "ImDib.h" +#if SIZEOF_VOID_P == 8 +#define F_HANDLE "K" +#else +#define F_HANDLE "k" +#endif + typedef struct { PyObject_HEAD ImagingDIB dib; @@ -74,8 +80,8 @@ _delete(ImagingDisplayObject* display) static PyObject* _expose(ImagingDisplayObject* display, PyObject* args) { - int hdc; - if (!PyArg_ParseTuple(args, "i", &hdc)) + HDC hdc; + if (!PyArg_ParseTuple(args, F_HANDLE, &hdc)) return NULL; ImagingExposeDIB(display->dib, hdc); @@ -87,10 +93,10 @@ _expose(ImagingDisplayObject* display, PyObject* args) static PyObject* _draw(ImagingDisplayObject* display, PyObject* args) { - int hdc; + HDC hdc; int dst[4]; int src[4]; - if (!PyArg_ParseTuple(args, "i(iiii)(iiii)", &hdc, + if (!PyArg_ParseTuple(args, F_HANDLE "(iiii)(iiii)", &hdc, dst+0, dst+1, dst+2, dst+3, src+0, src+1, src+2, src+3)) return NULL; @@ -131,10 +137,10 @@ _paste(ImagingDisplayObject* display, PyObject* args) static PyObject* _query_palette(ImagingDisplayObject* display, PyObject* args) { - int hdc; + HDC hdc; int status; - if (!PyArg_ParseTuple(args, "i", &hdc)) + if (!PyArg_ParseTuple(args, F_HANDLE, &hdc)) return NULL; status = ImagingQueryPaletteDIB(display->dib, hdc); @@ -145,30 +151,31 @@ _query_palette(ImagingDisplayObject* display, PyObject* args) static PyObject* _getdc(ImagingDisplayObject* display, PyObject* args) { - int window; + HWND window; HDC dc; - if (!PyArg_ParseTuple(args, "i", &window)) + if (!PyArg_ParseTuple(args, F_HANDLE, &window)) return NULL; - dc = GetDC((HWND) window); + dc = GetDC(window); if (!dc) { PyErr_SetString(PyExc_IOError, "cannot create dc"); return NULL; } - return Py_BuildValue("i", (int) dc); + return Py_BuildValue(F_HANDLE, dc); } static PyObject* _releasedc(ImagingDisplayObject* display, PyObject* args) { - int window, dc; + HWND window; + HDC dc; - if (!PyArg_ParseTuple(args, "ii", &window, &dc)) + if (!PyArg_ParseTuple(args, F_HANDLE F_HANDLE, &window, &dc)) return NULL; - ReleaseDC((HWND) window, (HDC) dc); + ReleaseDC(window, dc); Py_INCREF(Py_None); return Py_None; @@ -386,7 +393,7 @@ static BOOL CALLBACK list_windows_callback(HWND hwnd, LPARAM lParam) if (title_size > 0) { title = PyUnicode_FromStringAndSize(NULL, title_size); if (title) - GetWindowText(hwnd, PyUnicode_AS_UNICODE(title), title_size+1); + GetWindowTextW(hwnd, PyUnicode_AS_UNICODE(title), title_size+1); } else title = PyUnicode_FromString(""); if (!title) @@ -397,7 +404,7 @@ static BOOL CALLBACK list_windows_callback(HWND hwnd, LPARAM lParam) GetWindowRect(hwnd, &outer); item = Py_BuildValue( - "nN(iiii)(iiii)", (Py_ssize_t) hwnd, title, + F_HANDLE "N(iiii)(iiii)", hwnd, title, inner.left, inner.top, inner.right, inner.bottom, outer.left, outer.top, outer.right, outer.bottom ); @@ -600,10 +607,10 @@ windowCallback(HWND wnd, UINT message, WPARAM wParam, LPARAM lParam) /* fall through... */ case WM_PAINT: case WM_SIZE: - callback = (PyObject*) GetWindowLong(wnd, 0); + callback = (PyObject*) GetWindowLongPtr(wnd, 0); if (callback) { threadstate = (PyThreadState*) - GetWindowLong(wnd, sizeof(PyObject*)); + GetWindowLongPtr(wnd, sizeof(PyObject*)); current_threadstate = PyThreadState_Swap(NULL); PyEval_RestoreThread(threadstate); } else @@ -631,7 +638,7 @@ windowCallback(HWND wnd, UINT message, WPARAM wParam, LPARAM lParam) callback_error("window damage callback"); result = PyObject_CallFunction( - callback, "siiiii", "clear", (int) dc, + callback, "s" F_HANDLE "iiii", "clear", dc, 0, 0, rect.right-rect.left, rect.bottom-rect.top ); if (result) @@ -640,7 +647,7 @@ windowCallback(HWND wnd, UINT message, WPARAM wParam, LPARAM lParam) callback_error("window clear callback"); result = PyObject_CallFunction( - callback, "siiiii", "repair", (int) dc, + callback, "s" F_HANDLE "iiii", "repair", dc, 0, 0, rect.right-rect.left, rect.bottom-rect.top ); if (result) @@ -741,7 +748,7 @@ PyImaging_CreateWindowWin32(PyObject* self, PyObject* args) SetForegroundWindow(wnd); /* to make sure it's visible */ Py_END_ALLOW_THREADS - return Py_BuildValue("n", (Py_ssize_t) wnd); + return Py_BuildValue(F_HANDLE, wnd); } PyObject* diff --git a/libImaging/Dib.c b/libImaging/Dib.c index 55de7d9a5..e8ce3e713 100644 --- a/libImaging/Dib.c +++ b/libImaging/Dib.c @@ -228,7 +228,7 @@ ImagingPasteDIB(ImagingDIB dib, Imaging im, int xy[4]) } void -ImagingExposeDIB(ImagingDIB dib, int dc) +ImagingExposeDIB(ImagingDIB dib, void *dc) { /* Copy bitmap to display */ @@ -238,7 +238,7 @@ ImagingExposeDIB(ImagingDIB dib, int dc) } void -ImagingDrawDIB(ImagingDIB dib, int dc, int dst[4], int src[4]) +ImagingDrawDIB(ImagingDIB dib, void *dc, int dst[4], int src[4]) { /* Copy bitmap to printer/display */ @@ -258,7 +258,7 @@ ImagingDrawDIB(ImagingDIB dib, int dc, int dst[4], int src[4]) } int -ImagingQueryPaletteDIB(ImagingDIB dib, int dc) +ImagingQueryPaletteDIB(ImagingDIB dib, void *dc) { /* Install bitmap palette */ diff --git a/libImaging/ImDib.h b/libImaging/ImDib.h index 55ecb35aa..e5a2cc0f6 100644 --- a/libImaging/ImDib.h +++ b/libImaging/ImDib.h @@ -43,10 +43,10 @@ extern ImagingDIB ImagingNewDIB(const char *mode, int xsize, int ysize); extern void ImagingDeleteDIB(ImagingDIB im); -extern void ImagingDrawDIB(ImagingDIB dib, int dc, int dst[4], int src[4]); -extern void ImagingExposeDIB(ImagingDIB dib, int dc); +extern void ImagingDrawDIB(ImagingDIB dib, void *dc, int dst[4], int src[4]); +extern void ImagingExposeDIB(ImagingDIB dib, void *dc); -extern int ImagingQueryPaletteDIB(ImagingDIB dib, int dc); +extern int ImagingQueryPaletteDIB(ImagingDIB dib, void *dc); extern void ImagingPasteDIB(ImagingDIB dib, Imaging im, int xy[4]); From 3228860514d81efd269cb793e21828f6dd2633b5 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 1 Oct 2015 06:06:21 -0700 Subject: [PATCH 0635/1037] Test for PR#1431, following [nu744] --- Tests/test_imagewin_pointers.py | 113 ++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 Tests/test_imagewin_pointers.py diff --git a/Tests/test_imagewin_pointers.py b/Tests/test_imagewin_pointers.py new file mode 100644 index 000000000..acee6b13d --- /dev/null +++ b/Tests/test_imagewin_pointers.py @@ -0,0 +1,113 @@ +from helper import unittest, PillowTestCase, hopper + +from PIL import Image, ImageWin +import sys +import ctypes +import ctypes.wintypes +from io import BytesIO + +# see https://github.com/python-pillow/Pillow/pull/1431#issuecomment-144692652 + +if not sys.platform.startswith('win32'): + unittest.skip("Win32 only test") + +class BITMAPFILEHEADER(ctypes.Structure): + _pack_ = 2 + _fields_ = [ + ('bfType', ctypes.wintypes.WORD), + ('bfSize', ctypes.wintypes.DWORD), + ('bfReserved1', ctypes.wintypes.WORD), + ('bfReserved2', ctypes.wintypes.WORD), + ('bfOffBits', ctypes.wintypes.DWORD), + ] + +class BITMAPINFOHEADER(ctypes.Structure): + _pack_ = 2 + _fields_ = [ + ('biSize', ctypes.wintypes.DWORD), + ('biWidth', ctypes.wintypes.LONG), + ('biHeight', ctypes.wintypes.LONG), + ('biPlanes', ctypes.wintypes.WORD), + ('biBitCount', ctypes.wintypes.WORD), + ('biCompression', ctypes.wintypes.DWORD), + ('biSizeImage', ctypes.wintypes.DWORD), + ('biXPelsPerMeter', ctypes.wintypes.LONG), + ('biYPelsPerMeter', ctypes.wintypes.LONG), + ('biClrUsed', ctypes.wintypes.DWORD), + ('biClrImportant', ctypes.wintypes.DWORD), + ] + +BI_RGB = 0 +DIB_RGB_COLORS = 0 + +memcpy = ctypes.cdll.msvcrt.memcpy +memcpy.argtypes = [ ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t ] + +CreateCompatibleDC = ctypes.windll.gdi32.CreateCompatibleDC +CreateCompatibleDC.argtypes = [ ctypes.wintypes.HDC ] +CreateCompatibleDC.restype = ctypes.wintypes.HDC + +DeleteDC = ctypes.windll.gdi32.DeleteDC +DeleteDC.argtypes = [ ctypes.wintypes.HDC ] + +SelectObject = ctypes.windll.gdi32.SelectObject +SelectObject.argtypes = [ ctypes.wintypes.HDC, ctypes.wintypes.HGDIOBJ ] +SelectObject.restype = ctypes.wintypes.HGDIOBJ + +DeleteObject = ctypes.windll.gdi32.DeleteObject +DeleteObject.argtypes = [ ctypes.wintypes.HGDIOBJ ] + +CreateDIBSection = ctypes.windll.gdi32.CreateDIBSection +CreateDIBSection.argtypes = [ ctypes.wintypes.HDC, ctypes.c_void_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_void_p), ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD ] +CreateDIBSection.restype = ctypes.wintypes.HBITMAP + + +def serialize_dib(bi, pixels): + bf = BITMAPFILEHEADER() + bf.bfType = 0x4d42 + bf.bfOffBits = ctypes.sizeof(bf) + bi.biSize + bf.bfSize = bf.bfOffBits + bi.biSizeImage + bf.bfReserved1 = bf.bfReserved2 = 0 + + buf = (ctypes.c_byte * bf.bfSize)() + bp = ctypes.addressof(buf) + memcpy(bp, ctypes.byref(bf), ctypes.sizeof(bf)) + memcpy(bp + ctypes.sizeof(bf), ctypes.byref(bi), bi.biSize) + memcpy(bp + bf.bfOffBits, pixels, bi.biSizeImage) + return bytes(buf) + +class TestImageWinPointers(PillowTestCase): + def test_pointer(self): + im = hopper() + (width, height) =im.size + opath = self.tempfile('temp.png') + imdib = ImageWin.Dib(im) + + hdr = BITMAPINFOHEADER() + hdr.biSize = ctypes.sizeof(hdr) + hdr.biWidth = width + hdr.biHeight = height + hdr.biPlanes = 1 + hdr.biBitCount = 32 + hdr.biCompression = BI_RGB + hdr.biSizeImage = width * height * 4 + hdr.biClrUsed = 0 + hdr.biClrImportant = 0 + + hdc = CreateCompatibleDC(None) + print('hdc:',hex(hdc)) + pixels = ctypes.c_void_p() + dib = CreateDIBSection(hdc, ctypes.byref(hdr), DIB_RGB_COLORS, ctypes.byref(pixels), None, 0) + SelectObject(hdc, dib) + + imdib.expose(hdc) + bitmap = serialize_dib(hdr, pixels) + DeleteObject(dib) + DeleteDC(hdc) + + reloaded = Image.open(BytesIO(bitmap)).save(opath) + +if __name__ == '__main__': + unittest.main() + +# End of file From 7a48d7658dd6a79d5587c07f1a4022dd34e5c3b6 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 1 Oct 2015 06:18:10 -0700 Subject: [PATCH 0636/1037] removing the print --- Tests/test_imagewin_pointers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_imagewin_pointers.py b/Tests/test_imagewin_pointers.py index acee6b13d..ae2695d6e 100644 --- a/Tests/test_imagewin_pointers.py +++ b/Tests/test_imagewin_pointers.py @@ -95,7 +95,7 @@ class TestImageWinPointers(PillowTestCase): hdr.biClrImportant = 0 hdc = CreateCompatibleDC(None) - print('hdc:',hex(hdc)) + #print('hdc:',hex(hdc)) pixels = ctypes.c_void_p() dib = CreateDIBSection(hdc, ctypes.byref(hdr), DIB_RGB_COLORS, ctypes.byref(pixels), None, 0) SelectObject(hdc, dib) From eb80824f51b8a1b0dc2772b9d8c89df6de636b62 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 1 Oct 2015 14:47:16 +0100 Subject: [PATCH 0637/1037] Have to guard the ctypes.wintypes import --- Tests/test_imagewin_pointers.py | 168 ++++++++++++++++---------------- 1 file changed, 83 insertions(+), 85 deletions(-) diff --git a/Tests/test_imagewin_pointers.py b/Tests/test_imagewin_pointers.py index ae2695d6e..b29587cc9 100644 --- a/Tests/test_imagewin_pointers.py +++ b/Tests/test_imagewin_pointers.py @@ -1,113 +1,111 @@ from helper import unittest, PillowTestCase, hopper - from PIL import Image, ImageWin + import sys import ctypes -import ctypes.wintypes from io import BytesIO # see https://github.com/python-pillow/Pillow/pull/1431#issuecomment-144692652 -if not sys.platform.startswith('win32'): - unittest.skip("Win32 only test") +if sys.platform.startswith('win32'): + import ctypes.wintypes + + class BITMAPFILEHEADER(ctypes.Structure): + _pack_ = 2 + _fields_ = [ + ('bfType', ctypes.wintypes.WORD), + ('bfSize', ctypes.wintypes.DWORD), + ('bfReserved1', ctypes.wintypes.WORD), + ('bfReserved2', ctypes.wintypes.WORD), + ('bfOffBits', ctypes.wintypes.DWORD), + ] -class BITMAPFILEHEADER(ctypes.Structure): - _pack_ = 2 - _fields_ = [ - ('bfType', ctypes.wintypes.WORD), - ('bfSize', ctypes.wintypes.DWORD), - ('bfReserved1', ctypes.wintypes.WORD), - ('bfReserved2', ctypes.wintypes.WORD), - ('bfOffBits', ctypes.wintypes.DWORD), - ] + class BITMAPINFOHEADER(ctypes.Structure): + _pack_ = 2 + _fields_ = [ + ('biSize', ctypes.wintypes.DWORD), + ('biWidth', ctypes.wintypes.LONG), + ('biHeight', ctypes.wintypes.LONG), + ('biPlanes', ctypes.wintypes.WORD), + ('biBitCount', ctypes.wintypes.WORD), + ('biCompression', ctypes.wintypes.DWORD), + ('biSizeImage', ctypes.wintypes.DWORD), + ('biXPelsPerMeter', ctypes.wintypes.LONG), + ('biYPelsPerMeter', ctypes.wintypes.LONG), + ('biClrUsed', ctypes.wintypes.DWORD), + ('biClrImportant', ctypes.wintypes.DWORD), + ] -class BITMAPINFOHEADER(ctypes.Structure): - _pack_ = 2 - _fields_ = [ - ('biSize', ctypes.wintypes.DWORD), - ('biWidth', ctypes.wintypes.LONG), - ('biHeight', ctypes.wintypes.LONG), - ('biPlanes', ctypes.wintypes.WORD), - ('biBitCount', ctypes.wintypes.WORD), - ('biCompression', ctypes.wintypes.DWORD), - ('biSizeImage', ctypes.wintypes.DWORD), - ('biXPelsPerMeter', ctypes.wintypes.LONG), - ('biYPelsPerMeter', ctypes.wintypes.LONG), - ('biClrUsed', ctypes.wintypes.DWORD), - ('biClrImportant', ctypes.wintypes.DWORD), - ] + BI_RGB = 0 + DIB_RGB_COLORS = 0 -BI_RGB = 0 -DIB_RGB_COLORS = 0 + memcpy = ctypes.cdll.msvcrt.memcpy + memcpy.argtypes = [ ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t ] -memcpy = ctypes.cdll.msvcrt.memcpy -memcpy.argtypes = [ ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t ] + CreateCompatibleDC = ctypes.windll.gdi32.CreateCompatibleDC + CreateCompatibleDC.argtypes = [ ctypes.wintypes.HDC ] + CreateCompatibleDC.restype = ctypes.wintypes.HDC -CreateCompatibleDC = ctypes.windll.gdi32.CreateCompatibleDC -CreateCompatibleDC.argtypes = [ ctypes.wintypes.HDC ] -CreateCompatibleDC.restype = ctypes.wintypes.HDC + DeleteDC = ctypes.windll.gdi32.DeleteDC + DeleteDC.argtypes = [ ctypes.wintypes.HDC ] -DeleteDC = ctypes.windll.gdi32.DeleteDC -DeleteDC.argtypes = [ ctypes.wintypes.HDC ] + SelectObject = ctypes.windll.gdi32.SelectObject + SelectObject.argtypes = [ ctypes.wintypes.HDC, ctypes.wintypes.HGDIOBJ ] + SelectObject.restype = ctypes.wintypes.HGDIOBJ -SelectObject = ctypes.windll.gdi32.SelectObject -SelectObject.argtypes = [ ctypes.wintypes.HDC, ctypes.wintypes.HGDIOBJ ] -SelectObject.restype = ctypes.wintypes.HGDIOBJ + DeleteObject = ctypes.windll.gdi32.DeleteObject + DeleteObject.argtypes = [ ctypes.wintypes.HGDIOBJ ] -DeleteObject = ctypes.windll.gdi32.DeleteObject -DeleteObject.argtypes = [ ctypes.wintypes.HGDIOBJ ] - -CreateDIBSection = ctypes.windll.gdi32.CreateDIBSection -CreateDIBSection.argtypes = [ ctypes.wintypes.HDC, ctypes.c_void_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_void_p), ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD ] -CreateDIBSection.restype = ctypes.wintypes.HBITMAP + CreateDIBSection = ctypes.windll.gdi32.CreateDIBSection + CreateDIBSection.argtypes = [ ctypes.wintypes.HDC, ctypes.c_void_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_void_p), ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD ] + CreateDIBSection.restype = ctypes.wintypes.HBITMAP -def serialize_dib(bi, pixels): - bf = BITMAPFILEHEADER() - bf.bfType = 0x4d42 - bf.bfOffBits = ctypes.sizeof(bf) + bi.biSize - bf.bfSize = bf.bfOffBits + bi.biSizeImage - bf.bfReserved1 = bf.bfReserved2 = 0 + def serialize_dib(bi, pixels): + bf = BITMAPFILEHEADER() + bf.bfType = 0x4d42 + bf.bfOffBits = ctypes.sizeof(bf) + bi.biSize + bf.bfSize = bf.bfOffBits + bi.biSizeImage + bf.bfReserved1 = bf.bfReserved2 = 0 - buf = (ctypes.c_byte * bf.bfSize)() - bp = ctypes.addressof(buf) - memcpy(bp, ctypes.byref(bf), ctypes.sizeof(bf)) - memcpy(bp + ctypes.sizeof(bf), ctypes.byref(bi), bi.biSize) - memcpy(bp + bf.bfOffBits, pixels, bi.biSizeImage) - return bytes(buf) + buf = (ctypes.c_byte * bf.bfSize)() + bp = ctypes.addressof(buf) + memcpy(bp, ctypes.byref(bf), ctypes.sizeof(bf)) + memcpy(bp + ctypes.sizeof(bf), ctypes.byref(bi), bi.biSize) + memcpy(bp + bf.bfOffBits, pixels, bi.biSizeImage) + return bytes(buf) -class TestImageWinPointers(PillowTestCase): - def test_pointer(self): - im = hopper() - (width, height) =im.size - opath = self.tempfile('temp.png') - imdib = ImageWin.Dib(im) + class TestImageWinPointers(PillowTestCase): + def test_pointer(self): + im = hopper() + (width, height) =im.size + opath = self.tempfile('temp.png') + imdib = ImageWin.Dib(im) - hdr = BITMAPINFOHEADER() - hdr.biSize = ctypes.sizeof(hdr) - hdr.biWidth = width - hdr.biHeight = height - hdr.biPlanes = 1 - hdr.biBitCount = 32 - hdr.biCompression = BI_RGB - hdr.biSizeImage = width * height * 4 - hdr.biClrUsed = 0 - hdr.biClrImportant = 0 + hdr = BITMAPINFOHEADER() + hdr.biSize = ctypes.sizeof(hdr) + hdr.biWidth = width + hdr.biHeight = height + hdr.biPlanes = 1 + hdr.biBitCount = 32 + hdr.biCompression = BI_RGB + hdr.biSizeImage = width * height * 4 + hdr.biClrUsed = 0 + hdr.biClrImportant = 0 - hdc = CreateCompatibleDC(None) - #print('hdc:',hex(hdc)) - pixels = ctypes.c_void_p() - dib = CreateDIBSection(hdc, ctypes.byref(hdr), DIB_RGB_COLORS, ctypes.byref(pixels), None, 0) - SelectObject(hdc, dib) + hdc = CreateCompatibleDC(None) + #print('hdc:',hex(hdc)) + pixels = ctypes.c_void_p() + dib = CreateDIBSection(hdc, ctypes.byref(hdr), DIB_RGB_COLORS, ctypes.byref(pixels), None, 0) + SelectObject(hdc, dib) - imdib.expose(hdc) - bitmap = serialize_dib(hdr, pixels) - DeleteObject(dib) - DeleteDC(hdc) + imdib.expose(hdc) + bitmap = serialize_dib(hdr, pixels) + DeleteObject(dib) + DeleteDC(hdc) - reloaded = Image.open(BytesIO(bitmap)).save(opath) + reloaded = Image.open(BytesIO(bitmap)).save(opath) if __name__ == '__main__': unittest.main() -# End of file From dc5183fead5806dd8951215580cfb5df5cd2e843 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 1 Oct 2015 15:49:39 +0100 Subject: [PATCH 0638/1037] added release notes for 3.0.0, added them to index and toc --- docs/index.rst | 2 +- docs/{developer => }/releasenotes/2.7.0.rst | 0 docs/{developer => }/releasenotes/2.8.0.rst | 0 docs/releasenotes/3.0.0.rst | 45 +++++++++++++++++++++ docs/{developer => }/releasenotes/index.rst | 8 +++- 5 files changed, 53 insertions(+), 2 deletions(-) rename docs/{developer => }/releasenotes/2.7.0.rst (100%) rename docs/{developer => }/releasenotes/2.8.0.rst (100%) create mode 100644 docs/releasenotes/3.0.0.rst rename docs/{developer => }/releasenotes/index.rst (81%) diff --git a/docs/index.rst b/docs/index.rst index f986639d0..92b3f45c3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -33,7 +33,7 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors Date: Thu, 1 Oct 2015 15:55:36 +0100 Subject: [PATCH 0639/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index f74607686..a09517970 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.0.0 (Unreleased) ------------------ +- Check flush method existence for file-like object #1398 + [mrTable, radarhere] + - Added PDF multipage saving #1445 [radarhere] From c0dbeb055132a600510bf06721b8f36f2b99476b Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Thu, 1 Oct 2015 11:02:56 -0400 Subject: [PATCH 0640/1037] Ordering & remove plugins [ci skip] Ordering & remove plugins from list (moved to reference folder) --- docs/index.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 92b3f45c3..3d28040d6 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -33,10 +33,9 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors Date: Thu, 1 Oct 2015 16:29:19 +0100 Subject: [PATCH 0641/1037] Update Releasing.md [ci skip] --- RELEASING.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index b3739746b..d96a628f0 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -9,10 +9,13 @@ Released quarterly on the first day of January, April, July, October. * [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in ``master`` branch. * [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in: ``` - PIL/__init__.py setup.py _imaging.c appveyor.yml + PIL/__init__.py + setup.py + _imaging.c + appveyor.yml ``` * [ ] Update `CHANGES.rst`. -* [ ] Run pre-release check via `make pre`. +* [ ] Run pre-release check via `make release-test` in a freshly cloned repo. * [ ] Create branch and tag for release e.g.: ``` $ git branch 2.9.x From 73f3827e42e4038af5254410c2639b0e32868283 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 1 Oct 2015 16:33:15 +0100 Subject: [PATCH 0642/1037] Update Releasing.md [ci skip] --- RELEASING.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index d96a628f0..1dbdc980d 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -44,9 +44,12 @@ Released as needed for security, installation or critical bug fixes. ``` * [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in: ``` - PIL/__init__.py setup.py _imaging.c + PIL/__init__.py + setup.py + _imaging.c + appveyor.yml ``` -* [ ] Run pre-release check via `make pre`. +* [ ] Run pre-release check via `make release-test`. * [ ] Create tag for release e.g.: ``` $ git tag 2.9.1 @@ -66,7 +69,7 @@ Released as needed privately to individual vendors for critical security-related * [ ] Commit against master, cherry pick to affected release branches. * [ ] Run local test matrix on each release & Python version. * [ ] Privately send to distros. -* [ ] Run pre-release check via `make pre` +* [ ] Run pre-release check via `make release-test` * [ ] Amend any commits with the CVE # * [ ] On release date, tag and push to GitHub. ``` From efd4b9d4ff94be06c046ebc32378ab6f11ecff9a Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 1 Oct 2015 16:41:20 +0100 Subject: [PATCH 0643/1037] Added some requirements for make release-test [ci skip] --- requirements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/requirements.txt b/requirements.txt index bd37d7ba9..fec959e6c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,5 @@ nose nose-cov pep8 pyflakes +check-manifest +pyroma From ec0cd1e4d383043cc127c8e9506e4f22c78f0644 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 1 Oct 2015 21:23:49 +0100 Subject: [PATCH 0644/1037] another release-test item --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index fec959e6c..179635722 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,3 +8,4 @@ pep8 pyflakes check-manifest pyroma +jarn.viewdoc From 0177cceac4adfd0020ecbf49fb44ad275dcc1f51 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 1 Oct 2015 16:23:49 +0100 Subject: [PATCH 0645/1037] 3.0 Release versioning --- CHANGES.rst | 2 +- PIL/__init__.py | 2 +- _imaging.c | 2 +- appveyor.yml | 2 +- setup.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index a09517970..fdbe4e0ae 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,7 +1,7 @@ Changelog (Pillow) ================== -3.0.0 (Unreleased) +3.0.0 (2015-10-01) ------------------ - Check flush method existence for file-like object #1398 diff --git a/PIL/__init__.py b/PIL/__init__.py index 665474f9b..6756d7a9a 100644 --- a/PIL/__init__.py +++ b/PIL/__init__.py @@ -12,7 +12,7 @@ # ;-) VERSION = '1.1.7' # PIL version -PILLOW_VERSION = '3.0.0.dev0' # Pillow +PILLOW_VERSION = '3.0.0' # Pillow _plugins = ['BmpImagePlugin', 'BufrStubImagePlugin', diff --git a/_imaging.c b/_imaging.c index 92dfc005e..7441f8b9d 100644 --- a/_imaging.c +++ b/_imaging.c @@ -71,7 +71,7 @@ * See the README file for information on usage and redistribution. */ -#define PILLOW_VERSION "3.0.0.dev0" +#define PILLOW_VERSION "3.0.0" #include "Python.h" diff --git a/appveyor.yml b/appveyor.yml index 0e3224540..4c89abf02 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 3.0.pre.{build} +version: 3.0.0.{build} clone_folder: c:\pillow init: - ECHO %PYTHON% diff --git a/setup.py b/setup.py index 0d4bf73fd..4cb7257af 100644 --- a/setup.py +++ b/setup.py @@ -90,7 +90,7 @@ except (ImportError, OSError): NAME = 'Pillow' -PILLOW_VERSION = '3.0.0.dev0' +PILLOW_VERSION = '3.0.0' TCL_ROOT = None JPEG_ROOT = None JPEG2K_ROOT = None From 58fedea7400370a544427f2fc94a18c16078d156 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 1 Oct 2015 16:25:48 +0100 Subject: [PATCH 0646/1037] 3.1 Dev versioning --- CHANGES.rst | 4 ++++ PIL/__init__.py | 2 +- _imaging.c | 2 +- appveyor.yml | 2 +- setup.py | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index fdbe4e0ae..dd84baf00 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,10 @@ Changelog (Pillow) ================== +3.1.0 (unreleased) +------------------ + + 3.0.0 (2015-10-01) ------------------ diff --git a/PIL/__init__.py b/PIL/__init__.py index 6756d7a9a..87d56056f 100644 --- a/PIL/__init__.py +++ b/PIL/__init__.py @@ -12,7 +12,7 @@ # ;-) VERSION = '1.1.7' # PIL version -PILLOW_VERSION = '3.0.0' # Pillow +PILLOW_VERSION = '3.1.0.dev' # Pillow _plugins = ['BmpImagePlugin', 'BufrStubImagePlugin', diff --git a/_imaging.c b/_imaging.c index 7441f8b9d..24240a710 100644 --- a/_imaging.c +++ b/_imaging.c @@ -71,7 +71,7 @@ * See the README file for information on usage and redistribution. */ -#define PILLOW_VERSION "3.0.0" +#define PILLOW_VERSION "3.1.0.dev" #include "Python.h" diff --git a/appveyor.yml b/appveyor.yml index 4c89abf02..b603473a1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 3.0.0.{build} +version: 3.1.pre.{build} clone_folder: c:\pillow init: - ECHO %PYTHON% diff --git a/setup.py b/setup.py index 4cb7257af..28972ad36 100644 --- a/setup.py +++ b/setup.py @@ -90,7 +90,7 @@ except (ImportError, OSError): NAME = 'Pillow' -PILLOW_VERSION = '3.0.0' +PILLOW_VERSION = '3.1.0.dev' TCL_ROOT = None JPEG_ROOT = None JPEG2K_ROOT = None From daf8298f8ece15cd82e6c1b1e789544bd3e5d4ce Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 1 Oct 2015 22:04:09 +0100 Subject: [PATCH 0647/1037] Update Releasing.md [ci skip] --- RELEASING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASING.md b/RELEASING.md index 1dbdc980d..6c80f1e4d 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -25,7 +25,8 @@ Released quarterly on the first day of January, April, July, October. ``` * [ ] Create and upload source distributions e.g.: ``` - $ make sdistup + $ make sdist + $ make upload ``` * [ ] Create and upload [binary distributions](#binary-distributions) * [ ] Manually hide old versions on PyPI as needed, such that only the latest main release is visible when viewing https://pypi.python.org/pypi/Pillow From 8f76950d3fdd831b33492d7d8dc0cc033a7fe6f5 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Thu, 1 Oct 2015 18:44:39 -0400 Subject: [PATCH 0648/1037] Update RELEASING.md [ci skip] - Wording & add URL to PyPI version config info --- RELEASING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASING.md b/RELEASING.md index 6c80f1e4d..74170d812 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -29,7 +29,7 @@ Released quarterly on the first day of January, April, July, October. $ make upload ``` * [ ] Create and upload [binary distributions](#binary-distributions) -* [ ] Manually hide old versions on PyPI as needed, such that only the latest main release is visible when viewing https://pypi.python.org/pypi/Pillow +* [ ] Manually hide old versions on PyPI such that only the latest major release is visible when viewing https://pypi.python.org/pypi/Pillow (https://pypi.python.org/pypi?:action=pkg_edit&name=Pillow) ## Point Release From 26f5967e5bd1df87765ee1b9d9469b656ce1d477 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Thu, 1 Oct 2015 20:10:15 -0400 Subject: [PATCH 0649/1037] Add docs badge [ci skip] Because more badges! --- README.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.rst b/README.rst index 6e0f6ef52..a1819fa8e 100644 --- a/README.rst +++ b/README.rst @@ -6,6 +6,10 @@ Python Imaging Library (Fork) Pillow is the friendly PIL fork by `Alex Clark and Contributors `_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. +.. image:: https://readthedocs.org/projects/pillow/badge/?version=latest + :target: http://pillow.readthedocs.org/?badge=latest + :alt: Documentation Status + .. image:: https://img.shields.io/travis/python-pillow/Pillow/master.svg?label=Linux%20build :target: https://travis-ci.org/python-pillow/Pillow :alt: Travis CI build status (Linux) @@ -34,6 +38,7 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors Date: Thu, 1 Oct 2015 20:11:48 -0400 Subject: [PATCH 0650/1037] Wording [ci skip] --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index 586d9d581..0b36d5976 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -285,4 +285,4 @@ current versions of Linux, OS X, and Windows. Old Versions ------------ -You can download old distributions from `PyPI `_. Only the latest 1.x and 2.x releases are visible, but all releases are available by direct URL access e.g. https://pypi.python.org/pypi/Pillow/1.0. +You can download old distributions from `PyPI `_. Only the latest major releases for Python 2.x and 3.x are visible, but all releases are available by direct URL access e.g. https://pypi.python.org/pypi/Pillow/1.0. From 07d3604da8e0e09a6521aba8eebd3fc1c635159a Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Thu, 1 Oct 2015 20:17:48 -0400 Subject: [PATCH 0651/1037] Add docs badge [ci skip] Because more badges and the same badges duplicated in two places! (Maybe we only need badges in either the README.rst or docs/index.rst? Or maybe just we need to keep duplicating them. Not sure.) --- docs/index.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/index.rst b/docs/index.rst index 3d28040d6..e8e6cd9d5 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,6 +3,10 @@ Pillow Pillow is the friendly PIL fork by `Alex Clark and Contributors `_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. +.. image:: https://readthedocs.org/projects/pillow/badge/?version=latest + :target: http://pillow.readthedocs.org/?badge=latest + :alt: Documentation Status + .. image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master :target: https://travis-ci.org/python-pillow/Pillow :alt: Travis CI build status (Linux) From da7c5d1d3a0e8b32baaa5ea8351c175138262421 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 2 Oct 2015 10:23:41 +0300 Subject: [PATCH 0652/1037] Add Python 3.5 [CI skip] --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index 0b36d5976..ea339b94a 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -15,7 +15,7 @@ Notes .. note:: Pillow < 2.0.0 supports Python versions 2.4, 2.5, 2.6, 2.7. -.. note:: Pillow >= 2.0.0 supports Python versions 2.6, 2.7, 3.2, 3.3, 3.4 +.. note:: Pillow >= 2.0.0 supports Python versions 2.6, 2.7, 3.2, 3.3, 3.4, 3.5 Basic Installation ------------------ From 28d98a4b2989605763d13a2f21271dc4b5078bd3 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 2 Oct 2015 10:07:35 +0100 Subject: [PATCH 0653/1037] Update Release Notes [ci skip] Updated release notes to cover the change to the handling of libjpeg and libz --- docs/releasenotes/3.0.0.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/releasenotes/3.0.0.rst b/docs/releasenotes/3.0.0.rst index e62076052..49f91f213 100644 --- a/docs/releasenotes/3.0.0.rst +++ b/docs/releasenotes/3.0.0.rst @@ -42,4 +42,11 @@ have been removed in this release:: ImageWin.fromstring() ImageWin.tostring() +LibJpeg and Zlib are Required by Default +---------------------------------------- + +The external dependencies on libjpeg and zlib are now required by default. +If the headers or libraries are not found, then installation will abort +with an error. This behaviour can be disabled with the `--disable-libjpeg` +and `--disable-zlib` flags. From 52ab4170e0f933af45b78a9b2cc375804a87abe4 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 2 Oct 2015 10:09:13 +0100 Subject: [PATCH 0654/1037] Update installation.rst formatting --- docs/installation.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index ea339b94a..e14682212 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -54,12 +54,12 @@ Many of Pillow's features require external libraries: * Pillow has been tested with libjpeg versions **6b**, **8**, and **9** and libjpeg-turbo version **8**. * Starting with Pillow 3.0.0, libjpeg is required by default, but - may be disabled with the `--disable-jpeg` flag. + may be disabled with the ``--disable-jpeg`` flag. * **zlib** provides access to compressed PNGs * Starting with Pillow 3.0.0, zlib is required by default, but may - be disabled with the `--disable-zlib` flag. + be disabled with the ``--disable-zlib`` flag. * **libtiff** provides compressed TIFF functionality From a20449006a3857a1311fb7f772b799a42908194e Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 2 Oct 2015 10:09:51 +0100 Subject: [PATCH 0655/1037] Update 3.0.0.rst --- docs/releasenotes/3.0.0.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/releasenotes/3.0.0.rst b/docs/releasenotes/3.0.0.rst index 49f91f213..95f6be132 100644 --- a/docs/releasenotes/3.0.0.rst +++ b/docs/releasenotes/3.0.0.rst @@ -47,6 +47,6 @@ LibJpeg and Zlib are Required by Default The external dependencies on libjpeg and zlib are now required by default. If the headers or libraries are not found, then installation will abort -with an error. This behaviour can be disabled with the `--disable-libjpeg` -and `--disable-zlib` flags. +with an error. This behaviour can be disabled with the ``--disable-libjpeg`` +and ``--disable-zlib`` flags. From 5ee9dd6f886258a5ce579b45a303ddcf57714829 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 2 Oct 2015 03:03:15 -0700 Subject: [PATCH 0656/1037] Fixing test on py 2.x --- Tests/test_imagewin_pointers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_imagewin_pointers.py b/Tests/test_imagewin_pointers.py index b29587cc9..f8886f55b 100644 --- a/Tests/test_imagewin_pointers.py +++ b/Tests/test_imagewin_pointers.py @@ -73,7 +73,7 @@ if sys.platform.startswith('win32'): memcpy(bp, ctypes.byref(bf), ctypes.sizeof(bf)) memcpy(bp + ctypes.sizeof(bf), ctypes.byref(bi), bi.biSize) memcpy(bp + bf.bfOffBits, pixels, bi.biSizeImage) - return bytes(buf) + return bytearray(buf) class TestImageWinPointers(PillowTestCase): def test_pointer(self): From 187108fae9f2b1c0198644b550b570767148c288 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 2 Oct 2015 15:07:50 +0300 Subject: [PATCH 0657/1037] Add Travis CI's Python versions to matrix Travis CI uses Ubuntu 12.04 LTS Server Edition 64 bit, and is also tested on 3.4 and 3.5. Travis CI also uses OS X Mavericks, and is also tested on 3.2 and 3.3. I've left out 3.5 because it's not yet passing. [CI skip] --- docs/installation.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index e14682212..7b6ebff74 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -248,7 +248,7 @@ current versions of Linux, OS X, and Windows. +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Mac OS X 10.10 Yosemite |Yes | 2.7,3.3,3.4 | 2.8.1,2.9 |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Mac OS X 10.9 Mavericks |Yes | 2.7,3.4 | 2.6.1 |x86-64 | +| Mac OS X 10.9 Mavericks |Yes | 2.7,3.2,3.3,3.4 | 2.6.1 |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Mac OS X 10.8 Mountain Lion |Yes | 2.6,2.7,3.2,3.3 | |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ @@ -260,8 +260,8 @@ current versions of Linux, OS X, and Windows. +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Ubuntu Linux 10.04 LTS |Yes | 2.6 | 2.3.0 |x86,x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Ubuntu Linux 12.04 LTS |Yes | 2.6,2.7,3.2,3.3,PyPy2.4, | 2.6.1 |x86,x86-64 | -| | | PyPy3,v2.3 | | | +| Ubuntu Linux 12.04 LTS |Yes | 2.6,2.7,3.2,3.3,3.4,3.5 | 2.6.1 |x86,x86-64 | +| | | PyPy2.4,PyPy3,v2.3 | | | | | | | | | | | | 2.7,3.2 | 2.6.1 |ppc | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ From 9ff42d30b22069c5472001ceeb4b5c8bd76139d7 Mon Sep 17 00:00:00 2001 From: hugovk Date: Fri, 2 Oct 2015 15:44:35 +0300 Subject: [PATCH 0658/1037] Only list most recently tested --- docs/installation.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 7b6ebff74..f35d6526d 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -246,9 +246,9 @@ current versions of Linux, OS X, and Windows. +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ |**Operating system** |**Supported**|**Tested Python versions** |**Tested Pillow versions** |**Tested processors** | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Mac OS X 10.10 Yosemite |Yes | 2.7,3.3,3.4 | 2.8.1,2.9 |x86-64 | +| Mac OS X 10.10 Yosemite |Yes | 2.7,3.3,3.4 | 2.9.0 |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Mac OS X 10.9 Mavericks |Yes | 2.7,3.2,3.3,3.4 | 2.6.1 |x86-64 | +| Mac OS X 10.9 Mavericks |Yes | 2.7,3.2,3.3,3.4 | 3.0.0 |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Mac OS X 10.8 Mountain Lion |Yes | 2.6,2.7,3.2,3.3 | |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ @@ -260,7 +260,7 @@ current versions of Linux, OS X, and Windows. +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Ubuntu Linux 10.04 LTS |Yes | 2.6 | 2.3.0 |x86,x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Ubuntu Linux 12.04 LTS |Yes | 2.6,2.7,3.2,3.3,3.4,3.5 | 2.6.1 |x86,x86-64 | +| Ubuntu Linux 12.04 LTS |Yes | 2.6,2.7,3.2,3.3,3.4,3.5 | 3.0.0 |x86,x86-64 | | | | PyPy2.4,PyPy3,v2.3 | | | | | | | | | | | | 2.7,3.2 | 2.6.1 |ppc | @@ -271,7 +271,7 @@ current versions of Linux, OS X, and Windows. +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Gentoo Linux |Yes | 2.7,3.2 | 2.1.0 |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| FreeBSD 10 |Yes | 2.7,3.4 | 2.4,2.3.1 |x86-64 | +| FreeBSD 10 |Yes | 2.7,3.4 | 2.4.0 |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Windows 7 Pro |Yes | 2.7,3.2,3.3 | 2.2.1 |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ @@ -279,7 +279,7 @@ current versions of Linux, OS X, and Windows. +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Windows 8 Pro |Yes | 2.6,2.7,3.2,3.3,3.4a3 | 2.2.0 |x86,x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Windows 8.1 Pro |Yes | 2.6,2.7,3.2,3.3,3.4 | 2.3.0, 2.4.0 |x86,x86-64 | +| Windows 8.1 Pro |Yes | 2.6,2.7,3.2,3.3,3.4 | 2.4.0 |x86,x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ Old Versions From 2570354069fa9a40094c483e6878b6a652eeba1b Mon Sep 17 00:00:00 2001 From: hugovk Date: Fri, 2 Oct 2015 15:46:26 +0300 Subject: [PATCH 0659/1037] Add AppVeyor build to support matrix --- docs/installation.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/installation.rst b/docs/installation.rst index f35d6526d..d4a00c16a 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -277,6 +277,8 @@ current versions of Linux, OS X, and Windows. +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Windows Server 2008 R2 Enterprise|Yes | 3.3 | |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Windows Server 2012 R2 |Yes | 2.7,3.3,3.4 | 3.0.0 |x86-64 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Windows 8 Pro |Yes | 2.6,2.7,3.2,3.3,3.4a3 | 2.2.0 |x86,x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Windows 8.1 Pro |Yes | 2.6,2.7,3.2,3.3,3.4 | 2.4.0 |x86,x86-64 | From f48786d8cdd3b91b46850d4340b0483223a76cea Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 2 Oct 2015 14:07:37 +0100 Subject: [PATCH 0660/1037] Updated Changes.rst [ci skip] --- CHANGES.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index dd84baf00..e64367691 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,8 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Use correctly sized pointers for windows handle types. #1458 + [nu744] 3.0.0 (2015-10-01) ------------------ From 087a7fdbfa184e64f56a0f61d11ce23b109d4f4b Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 2 Oct 2015 14:08:48 +0100 Subject: [PATCH 0661/1037] Updated Changes.rst (formatting) [ci skip] --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index e64367691..ac6570c16 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,7 +5,7 @@ Changelog (Pillow) ------------------ - Use correctly sized pointers for windows handle types. #1458 - [nu744] + [nu744] 3.0.0 (2015-10-01) ------------------ From d965257616129493a38efcbcfb7f085fa961d376 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sat, 3 Oct 2015 08:12:44 +0100 Subject: [PATCH 0662/1037] Fix handling of pathlib in save. Fixes #1460 --- PIL/Image.py | 11 ++++++----- Tests/test_image.py | 11 +++++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 06bf7ce94..d90996a57 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1620,13 +1620,17 @@ class Image(object): """ filename = "" + open_fp = False if isPath(fp): filename = fp + open_fp=True elif sys.version_info >= (3, 4): from pathlib import Path if isinstance(fp, Path): filename = str(fp.resolve()) + open_fp=True elif hasattr(fp, "name") and isPath(fp.name): + # only set the name for metadata purposes filename = fp.name # may mutate self! @@ -1655,17 +1659,14 @@ class Image(object): else: save_handler = SAVE[format.upper()] - if filename: + if open_fp: fp = builtins.open(filename, "wb") - close = 1 - else: - close = 0 try: save_handler(self, fp, filename) finally: # do what we can to clean up - if close: + if open_fp: fp.close() def seek(self, frame): diff --git a/Tests/test_image.py b/Tests/test_image.py index fa7f8ec06..59ffcd114 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -57,6 +57,17 @@ class TestImage(PillowTestCase): self.assertEqual(im.mode, "RGB") self.assertEqual(im.size, (128, 128)) + def test_tempfile(self): + # see #1460, pathlib support breaks tempfile.TemporaryFile on py27 + # Will error out on save on 3.0.0 + import tempfile + im = hopper() + fp = tempfile.TemporaryFile() + im.save(fp, 'JPEG') + fp.seek(0) + reloaded = Image.open(fp) + self.assert_image_similar(im, reloaded, 20) + def test_internals(self): im = Image.new("L", (100, 100)) From 42633d7a31cf4440bb1ce3aa7b6441d2e520e82d Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sat, 3 Oct 2015 08:29:52 +0100 Subject: [PATCH 0663/1037] spaces. --- PIL/Image.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index d90996a57..f09168708 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1623,12 +1623,12 @@ class Image(object): open_fp = False if isPath(fp): filename = fp - open_fp=True + open_fp = True elif sys.version_info >= (3, 4): from pathlib import Path if isinstance(fp, Path): filename = str(fp.resolve()) - open_fp=True + open_fp = True elif hasattr(fp, "name") and isPath(fp.name): # only set the name for metadata purposes filename = fp.name From b7068dcc00064438f041a6221b7bd07b53b500f8 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 3 Oct 2015 11:12:38 +0300 Subject: [PATCH 0664/1037] Tested on El Capitan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` ⌁74% [hugo:~/github/Pillow] 0177cce+* 11s ± coverage run --append --include=PIL/* selftest.py -------------------------------------------------------------------- Pillow 3.0.0 TEST SUMMARY -------------------------------------------------------------------- Python modules loaded from /Users/hugo/github/Pillow/PIL Binary modules loaded from /Users/hugo/github/Pillow/PIL -------------------------------------------------------------------- --- PIL CORE support ok --- TKINTER support ok --- FREETYPE2 support ok --- LITTLECMS2 support ok --- WEBP support ok --- JPEG support ok *** OPENJPEG (JPEG2000) support not installed --- ZLIB (PNG/ZIP) support ok --- LIBTIFF support ok -------------------------------------------------------------------- Running selftest: --- 57 tests passed. ``` Full tests: ``` Ran 667 tests in 49.411s OK (SKIP=47) ``` Mainly skipped Qt and OpenJPEG. --- docs/installation.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/installation.rst b/docs/installation.rst index d4a00c16a..d35a0360c 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -246,6 +246,8 @@ current versions of Linux, OS X, and Windows. +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ |**Operating system** |**Supported**|**Tested Python versions** |**Tested Pillow versions** |**Tested processors** | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Mac OS X 10.11 El Capitan |Yes | 2.7 | 3.0.0 |x86-64 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Mac OS X 10.10 Yosemite |Yes | 2.7,3.3,3.4 | 2.9.0 |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Mac OS X 10.9 Mavericks |Yes | 2.7,3.2,3.3,3.4 | 3.0.0 |x86-64 | From 157df44e51ef1436fe3a878b835ec8b4c57d19b0 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sat, 3 Oct 2015 16:35:53 +0100 Subject: [PATCH 0665/1037] Add tag info for iccprofile, fixes #1462 --- PIL/TiffTags.py | 2 +- Tests/images/iccprofile.tiff | Bin 0 -> 5758 bytes Tests/test_file_tiff_metadata.py | 11 +++++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 Tests/images/iccprofile.tiff diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py index 012d67632..af6d1b386 100644 --- a/PIL/TiffTags.py +++ b/PIL/TiffTags.py @@ -133,6 +133,7 @@ TAGS_V2 = { # FIXME add more tags here 34665: ("ExifIFD", 3, 1), + 34675: ('ICCProfile', 7, 1), # MPInfo 45056: ("MPFVersion", 7, 1), @@ -171,7 +172,6 @@ TAGS = {347: 'JPEGTables', 33437: 'FNumber', 33723: 'IptcNaaInfo', 34377: 'PhotoshopInfo', - 34675: 'ICCProfile', 34850: 'ExposureProgram', 34852: 'SpectralSensitivity', 34853: 'GPSInfoIFD', diff --git a/Tests/images/iccprofile.tiff b/Tests/images/iccprofile.tiff new file mode 100644 index 0000000000000000000000000000000000000000..a7bd93799d0a0d664717ca53cc3fe61075752481 GIT binary patch literal 5758 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 dR(%@ssjfNaGw<`VFI~P=e2x5iw Date: Sat, 3 Oct 2015 22:46:01 +0100 Subject: [PATCH 0666/1037] Flatten sampleformat to initial value, fixes #1466 --- PIL/TiffImagePlugin.py | 9 +++++++-- Tests/images/copyleft.tiff | Bin 0 -> 7926 bytes Tests/test_file_tiff.py | 5 +++++ 3 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 Tests/images/copyleft.tiff diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 1f974226c..a84d01755 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -180,8 +180,6 @@ OPEN_INFO = { (MM, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), (II, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 (MM, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 - (II, 2, (1, 1, 1, 1), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBA"), # OSX Grab - (MM, 2, (1, 1, 1, 1), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBA"), # OSX Grab (II, 3, (1,), 1, (1,), ()): ("P", "P;1"), (MM, 3, (1,), 1, (1,), ()): ("P", "P;1"), (II, 3, (1,), 2, (1,), ()): ("P", "P;1R"), @@ -967,6 +965,13 @@ class TiffImageFile(ImageFile.ImageFile): print("- size:", self.size) format = self.tag_v2.get(SAMPLEFORMAT, (1,)) + if len(format) > 1 and max(format) == min(format) == 1: + # SAMPLEFORMAT is properly per band, so an RGB image will + # be (1,1,1). But, we don't support per band pixel types, + # and anything more than one band is a uint8. So, just + # take the first element. Revisit this if adding support + # for more exotic images. + format = (1,) # mode: check photometric interpretation and bits per pixel key = ( diff --git a/Tests/images/copyleft.tiff b/Tests/images/copyleft.tiff new file mode 100644 index 0000000000000000000000000000000000000000..d8b7f730c0dfcb7653299737a26946117c501d64 GIT binary patch literal 7926 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 Date: Sat, 3 Oct 2015 18:49:49 -0400 Subject: [PATCH 0667/1037] PEP 0440 compliance [ci skip] ``3.1.0.dev`` should be ``3.1.0.dev0`` to be compliant with PEP 0386 (which has since been superceded by PEP 0440 https://www.python.org/dev/peps/pep-0440/) --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 28972ad36..0029eb5c1 100644 --- a/setup.py +++ b/setup.py @@ -90,7 +90,7 @@ except (ImportError, OSError): NAME = 'Pillow' -PILLOW_VERSION = '3.1.0.dev' +PILLOW_VERSION = '3.1.0.dev0' TCL_ROOT = None JPEG_ROOT = None JPEG2K_ROOT = None From e67d90348217980e599fe33236e6ccae31349d70 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 3 Oct 2015 18:59:30 -0400 Subject: [PATCH 0668/1037] Add Gratipay Badge [ci skip] Note: anyone on the GitHub Pillow Team interested in receiving donations for their contributions may do so via the Gratipay Pillow Team (https://gratipay.com/pillow/). I'm not sure how to add folks, but if you are interested I will figure it out (I'm currently the only recipient.) --- README.rst | 8 ++++++++ docs/index.rst | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/README.rst b/README.rst index a1819fa8e..62f8ae4f4 100644 --- a/README.rst +++ b/README.rst @@ -55,3 +55,11 @@ More Information - `Changelog `_ - `Pre-fork `_ + +.. raw:: html + +

diff --git a/docs/index.rst b/docs/index.rst index e8e6cd9d5..e34ac1e9a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -41,6 +41,12 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors + Support via Gratipay + + Indices and tables ================== From 5e670e9b704686f03cd8516084bcd60515fce208 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sat, 3 Oct 2015 17:04:40 -0700 Subject: [PATCH 0669/1037] using test file with known license --- Tests/images/hopper.iccprofile.tif | Bin 0 -> 52500 bytes Tests/images/iccprofile.tiff | Bin 5758 -> 0 bytes Tests/test_file_tiff_metadata.py | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 Tests/images/hopper.iccprofile.tif delete mode 100644 Tests/images/iccprofile.tiff diff --git a/Tests/images/hopper.iccprofile.tif b/Tests/images/hopper.iccprofile.tif new file mode 100644 index 0000000000000000000000000000000000000000..f4cfeab3e4ed5449f118f568862a10d35fd4bf50 GIT binary patch literal 52500 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 dR(%@ssjfNaGw<`VFI~P=e2x5iw Date: Sun, 4 Oct 2015 06:36:17 -0400 Subject: [PATCH 0670/1037] s/aclark4life/pillow/ [ci skip] --- docs/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index e34ac1e9a..42953662d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -43,7 +43,7 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors + Support via Gratipay From fcdd64eb6b3fbb2097b8bf7b3d70e6eb3f013dc8 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 4 Oct 2015 06:53:00 -0400 Subject: [PATCH 0671/1037] Add AppVeyor badge [ci skip] --- docs/index.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/index.rst b/docs/index.rst index 42953662d..74d173a58 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -15,6 +15,10 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors Date: Sun, 4 Oct 2015 15:41:14 -0400 Subject: [PATCH 0672/1037] Update AppVeyor URL accordingly [ci skip] c.f. #1468 --- README.rst | 4 ++-- docs/index.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 62f8ae4f4..d6bc79c0f 100644 --- a/README.rst +++ b/README.rst @@ -18,8 +18,8 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors Date: Mon, 5 Oct 2015 10:27:23 +0300 Subject: [PATCH 0673/1037] Make tests more robust re: https://github.com/python-pillow/Pillow/issues/947#issuecomment-61641247 --- Tests/test_numpy.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py index 19b9a2014..7303a949b 100644 --- a/Tests/test_numpy.py +++ b/Tests/test_numpy.py @@ -76,7 +76,8 @@ class TestNumpy(PillowTestCase): self.assert_image(Image.fromarray(a[:, :, 1]), "L", (10, 10)) def _test_img_equals_nparray(self, img, np): - self.assertEqual(img.size, np.shape[0:2]) + np_size = np.shape[1], np.shape[0] + self.assertEqual(img.size, np_size) px = img.load() for x in range(0, img.size[0], int(img.size[0]/10)): for y in range(0, img.size[1], int(img.size[1]/10)): @@ -92,6 +93,11 @@ class TestNumpy(PillowTestCase): def _to_array(mode, dtype): img = hopper(mode) + + # Resize to non-square + img = img.crop((3, 0, 124, 127)) + self.assertEqual(img.size, (121, 127)) + np_img = numpy.array(img) self._test_img_equals_nparray(img, np_img) self.assertEqual(np_img.dtype, numpy.dtype(dtype)) From 497c04394862469488683c3a9d6f8ae1c0830e35 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 5 Oct 2015 09:41:38 +0100 Subject: [PATCH 0674/1037] Updated Changes.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index ac6570c16..58aa44747 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Make tests more robust #1469 + [hugovk] + - Use correctly sized pointers for windows handle types. #1458 [nu744] From 509a65cfe78e3a408c9d6c2f19df994f3aa097d2 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 5 Oct 2015 09:43:59 +0100 Subject: [PATCH 0675/1037] Updated Changes.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 58aa44747..9b780c58a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,12 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Flatten tiff metadata value SAMPLEFORMAT to initial value, fixes #1466 + [wiredfool] + +- Fix handling of pathlib in Image.save. Fixes #1460 + [wiredfool] + - Make tests more robust #1469 [hugovk] From ae9ee0f95ec31afe260cec4c161abcbc3d5dbedd Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Mon, 5 Oct 2015 05:36:25 -0400 Subject: [PATCH 0676/1037] Make file list more cut/pasteable [ci skip] --- RELEASING.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 74170d812..e30fe2821 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -9,10 +9,7 @@ Released quarterly on the first day of January, April, July, October. * [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in ``master`` branch. * [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in: ``` - PIL/__init__.py - setup.py - _imaging.c - appveyor.yml + PIL/__init__.py setup.py _imaging.c appveyor.yml ``` * [ ] Update `CHANGES.rst`. * [ ] Run pre-release check via `make release-test` in a freshly cloned repo. From 1f72110040979661aa088b55750dfe01f15a069b Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Mon, 5 Oct 2015 05:39:08 -0400 Subject: [PATCH 0677/1037] PEP440 [ci skip] --- PIL/__init__.py | 2 +- _imaging.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/PIL/__init__.py b/PIL/__init__.py index 87d56056f..c18d7830c 100644 --- a/PIL/__init__.py +++ b/PIL/__init__.py @@ -12,7 +12,7 @@ # ;-) VERSION = '1.1.7' # PIL version -PILLOW_VERSION = '3.1.0.dev' # Pillow +PILLOW_VERSION = '3.1.0.dev0' # Pillow _plugins = ['BmpImagePlugin', 'BufrStubImagePlugin', diff --git a/_imaging.c b/_imaging.c index 24240a710..d6d49aa29 100644 --- a/_imaging.c +++ b/_imaging.c @@ -71,7 +71,7 @@ * See the README file for information on usage and redistribution. */ -#define PILLOW_VERSION "3.1.0.dev" +#define PILLOW_VERSION "3.1.0.dev0" #include "Python.h" From fc52f733668c96a0e363b3563345035e35943f73 Mon Sep 17 00:00:00 2001 From: masklinn Date: Mon, 5 Oct 2015 12:27:25 +0200 Subject: [PATCH 0678/1037] Add warning to tobytes It probably isn't what most developers want. --- PIL/Image.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/PIL/Image.py b/PIL/Image.py index f09168708..d8fa6402e 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -647,7 +647,13 @@ class Image(object): def tobytes(self, encoder_name="raw", *args): """ - Return image as a bytes object + Return image as a bytes object. + + .. warning:: + + This method is for raw-ish output, for compressed image + data (e.g. PNG, JPEG) use :meth:`~.save`, with a BytesIO + parameter for in-memory data. :param encoder_name: What encoder to use. The default is to use the standard "raw" encoder. From 4e0e3cbda4f4bbd0f989bb53517368a6d195ca70 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 5 Oct 2015 15:30:45 +0300 Subject: [PATCH 0679/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 9b780c58a..1f5e9ad84 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,12 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Add tag info for iccprofile, fixes #1462. #1465 + [wiredfool] + +- Added some requirements for make release-test #1451 + [wiredfool] + - Flatten tiff metadata value SAMPLEFORMAT to initial value, fixes #1466 [wiredfool] From a7b1c6ec578a1291e6b9fdf9701f74cbaf501304 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 6 Oct 2015 00:23:11 +1100 Subject: [PATCH 0680/1037] Fixed links for viewing outside readthedocs [ci skip] --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index d6bc79c0f..44c0ebdb9 100644 --- a/README.rst +++ b/README.rst @@ -44,8 +44,8 @@ More Information - `Documentation `_ - - `Installation `_ - - `Handbook `_ + - `Installation `_ + - `Handbook `_ - `Contribute `_ From 3266341fe5b1cb87d3ed7abeb8e3442ac811dc4e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 6 Oct 2015 23:28:11 +1100 Subject: [PATCH 0681/1037] Fixed typo --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index d35a0360c..7726dd9fb 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -128,7 +128,7 @@ Sample Usage:: OS X Installation ----------------- -We provide binaries for OS X in the form of `Python Wheels `_. Alternatively you can compile Pillow from soure with XCode. +We provide binaries for OS X in the form of `Python Wheels `_. Alternatively you can compile Pillow from source with XCode. The easiest way to install external libraries is via `Homebrew `_. After you install Homebrew, run:: From aab7fde3a94980a21568ee954621c62915bf8f89 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 6 Oct 2015 23:58:52 +1100 Subject: [PATCH 0682/1037] Updated latest tested Pillow version on Yosemite [ci skip] --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index 7726dd9fb..127ec2ec2 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -248,7 +248,7 @@ current versions of Linux, OS X, and Windows. +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Mac OS X 10.11 El Capitan |Yes | 2.7 | 3.0.0 |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Mac OS X 10.10 Yosemite |Yes | 2.7,3.3,3.4 | 2.9.0 |x86-64 | +| Mac OS X 10.10 Yosemite |Yes | 2.7,3.3,3.4 | 3.0.0 |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Mac OS X 10.9 Mavericks |Yes | 2.7,3.2,3.3,3.4 | 3.0.0 |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ From 1c79ac511ea16778a29017ebdd53de6a6c019e1d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 6 Oct 2015 23:58:23 +1100 Subject: [PATCH 0683/1037] Updated Platform Support column heading [ci skip] --- docs/installation.rst | 84 +++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 127ec2ec2..9b0e46bf3 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -243,48 +243,48 @@ current versions of Linux, OS X, and Windows. Contributors please test Pillow on your platform then update this document and send a pull request. -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -|**Operating system** |**Supported**|**Tested Python versions** |**Tested Pillow versions** |**Tested processors** | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Mac OS X 10.11 El Capitan |Yes | 2.7 | 3.0.0 |x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Mac OS X 10.10 Yosemite |Yes | 2.7,3.3,3.4 | 3.0.0 |x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Mac OS X 10.9 Mavericks |Yes | 2.7,3.2,3.3,3.4 | 3.0.0 |x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Mac OS X 10.8 Mountain Lion |Yes | 2.6,2.7,3.2,3.3 | |x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Redhat Linux 6 |Yes | 2.6 | |x86 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| CentOS 6.3 |Yes | 2.7,3.3 | |x86 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Fedora 20 |Yes | 2.7,3.3 | 2.3.0 |x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Ubuntu Linux 10.04 LTS |Yes | 2.6 | 2.3.0 |x86,x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Ubuntu Linux 12.04 LTS |Yes | 2.6,2.7,3.2,3.3,3.4,3.5 | 3.0.0 |x86,x86-64 | -| | | PyPy2.4,PyPy3,v2.3 | | | -| | | | | | -| | | 2.7,3.2 | 2.6.1 |ppc | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Ubuntu Linux 14.04 LTS |Yes | 2.7,3.2,3.3,3.4 | 2.3.0 |x86 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Raspian Wheezy |Yes | 2.7,3.2 | 2.3.0 |arm | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Gentoo Linux |Yes | 2.7,3.2 | 2.1.0 |x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| FreeBSD 10 |Yes | 2.7,3.4 | 2.4.0 |x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Windows 7 Pro |Yes | 2.7,3.2,3.3 | 2.2.1 |x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Windows Server 2008 R2 Enterprise|Yes | 3.3 | |x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Windows Server 2012 R2 |Yes | 2.7,3.3,3.4 | 3.0.0 |x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Windows 8 Pro |Yes | 2.6,2.7,3.2,3.3,3.4a3 | 2.2.0 |x86,x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Windows 8.1 Pro |Yes | 2.6,2.7,3.2,3.3,3.4 | 2.4.0 |x86,x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ ++----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ +|**Operating system** |**Supported**|**Tested Python versions** |**Latest tested Pillow version**|**Tested processors** | ++----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ +| Mac OS X 10.11 El Capitan |Yes | 2.7 | 3.0.0 |x86-64 | ++----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ +| Mac OS X 10.10 Yosemite |Yes | 2.7,3.3,3.4 | 3.0.0 |x86-64 | ++----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ +| Mac OS X 10.9 Mavericks |Yes | 2.7,3.2,3.3,3.4 | 3.0.0 |x86-64 | ++----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ +| Mac OS X 10.8 Mountain Lion |Yes | 2.6,2.7,3.2,3.3 | |x86-64 | ++----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ +| Redhat Linux 6 |Yes | 2.6 | |x86 | ++----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ +| CentOS 6.3 |Yes | 2.7,3.3 | |x86 | ++----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ +| Fedora 20 |Yes | 2.7,3.3 | 2.3.0 |x86-64 | ++----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ +| Ubuntu Linux 10.04 LTS |Yes | 2.6 | 2.3.0 |x86,x86-64 | ++----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ +| Ubuntu Linux 12.04 LTS |Yes | 2.6,2.7,3.2,3.3,3.4,3.5 | 3.0.0 |x86,x86-64 | +| | | PyPy2.4,PyPy3,v2.3 | | | +| | | | | | +| | | 2.7,3.2 | 2.6.1 |ppc | ++----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ +| Ubuntu Linux 14.04 LTS |Yes | 2.7,3.2,3.3,3.4 | 2.3.0 |x86 | ++----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ +| Raspian Wheezy |Yes | 2.7,3.2 | 2.3.0 |arm | ++----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ +| Gentoo Linux |Yes | 2.7,3.2 | 2.1.0 |x86-64 | ++----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ +| FreeBSD 10 |Yes | 2.7,3.4 | 2.4.0 |x86-64 | ++----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ +| Windows 7 Pro |Yes | 2.7,3.2,3.3 | 2.2.1 |x86-64 | ++----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ +| Windows Server 2008 R2 Enterprise|Yes | 3.3 | |x86-64 | ++----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ +| Windows Server 2012 R2 |Yes | 2.7,3.3,3.4 | 3.0.0 |x86-64 | ++----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ +| Windows 8 Pro |Yes | 2.6,2.7,3.2,3.3,3.4a3 | 2.2.0 |x86,x86-64 | ++----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ +| Windows 8.1 Pro |Yes | 2.6,2.7,3.2,3.3,3.4 | 2.4.0 |x86,x86-64 | ++----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ Old Versions ------------ From 46ad0e25550c1327c3b9ee5b047738abb0ab85e4 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 6 Oct 2015 14:37:27 -0400 Subject: [PATCH 0684/1037] Add Zenodo badge for #1322 [ci skip] Looks like I had to "draft a release" in the GitHub UI to make this work. --- README.rst | 3 +++ docs/index.rst | 3 +++ 2 files changed, 6 insertions(+) diff --git a/README.rst b/README.rst index 44c0ebdb9..cc5184a75 100644 --- a/README.rst +++ b/README.rst @@ -6,6 +6,9 @@ Python Imaging Library (Fork) Pillow is the friendly PIL fork by `Alex Clark and Contributors `_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. +.. image:: https://zenodo.org/badge/17549/python-pillow/Pillow.svg + :target: https://zenodo.org/badge/latestdoi/17549/python-pillow/Pillow + .. image:: https://readthedocs.org/projects/pillow/badge/?version=latest :target: http://pillow.readthedocs.org/?badge=latest :alt: Documentation Status diff --git a/docs/index.rst b/docs/index.rst index a042758ec..a02de2334 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,6 +3,9 @@ Pillow Pillow is the friendly PIL fork by `Alex Clark and Contributors `_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. +.. image:: https://zenodo.org/badge/17549/python-pillow/Pillow.svg + :target: https://zenodo.org/badge/latestdoi/17549/python-pillow/Pillow + .. image:: https://readthedocs.org/projects/pillow/badge/?version=latest :target: http://pillow.readthedocs.org/?badge=latest :alt: Documentation Status From d37a8c7222106da20973924775a37dc727179c1c Mon Sep 17 00:00:00 2001 From: Brian Baumhover Date: Thu, 8 Oct 2015 00:16:33 -0500 Subject: [PATCH 0685/1037] Fix command to invoke ghostscript. --- PIL/EpsImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py index fb5bf7ffe..d38ad403c 100644 --- a/PIL/EpsImagePlugin.py +++ b/PIL/EpsImagePlugin.py @@ -123,8 +123,8 @@ def Ghostscript(tile, size, fp, scale=1): "-q", # quiet mode "-g%dx%d" % size, # set output geometry (pixels) "-r%fx%f" % res, # set input DPI (dots per inch) - "-dNOPAUSE -dSAFER", # don't pause between pages, - # safe mode + "-dNOPAUSE", # don't pause between pages, + "-dSAFER", # safe mode "-sDEVICE=ppmraw", # ppm driver "-sOutputFile=%s" % outfile, # output file "-c", "%d %d translate" % (-bbox[0], -bbox[1]), From 28006edd6b0f8ff255bfe5a239d8dbfc5c36bf25 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 8 Oct 2015 19:06:21 +1100 Subject: [PATCH 0686/1037] Updated freetype to 2.6.1 --- winbuild/config.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winbuild/config.py b/winbuild/config.py index 4fa7886ef..9b3bdcfb3 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -28,9 +28,9 @@ libs = { 'dir': 'tiff-4.0.6', }, 'freetype': { - 'url': 'http://download.savannah.gnu.org/releases/freetype/freetype-2.6.tar.gz', - 'hash': 'md5:1d733ea6c1b7b3df38169fbdbec47d2b', - 'dir': 'freetype-2.6', + 'url': 'http://download.savannah.gnu.org/releases/freetype/freetype-2.6.1.tar.gz', + 'hash': 'md5:348e667d728c597360e4a87c16556597', + 'dir': 'freetype-2.6.1', }, 'lcms': { 'url': SF_MIRROR+'/project/lcms/lcms/2.7/lcms2-2.7.zip', From 3b121b4513cc4bba19cafe753087ca6cc4ac3c52 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 8 Oct 2015 14:31:40 +0300 Subject: [PATCH 0687/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 1f5e9ad84..419227be5 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Updated freetype to 2.6.1 #1479 + [radarhere] + - Add tag info for iccprofile, fixes #1462. #1465 [wiredfool] From 7e5df09fa9732d3fe16765640befa914497be82b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 10 Oct 2015 14:25:08 +1100 Subject: [PATCH 0688/1037] Added Usage message to painter script --- Scripts/painter.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Scripts/painter.py b/Scripts/painter.py index 234f06171..0e2f38c37 100644 --- a/Scripts/painter.py +++ b/Scripts/painter.py @@ -68,6 +68,10 @@ class PaintCanvas(Canvas): root = Tk() +if len(sys.argv) != 2: + print("Usage: painter file") + sys.exit(1) + im = Image.open(sys.argv[1]) if im.mode != "RGB": From 5909a5f9bcbdb2ec8a265c3a404d166fbb750357 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 10 Oct 2015 14:28:53 +1100 Subject: [PATCH 0689/1037] Fixed typo [ci skip] --- Scripts/README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/README.rst b/Scripts/README.rst index f86a9a8b6..c8b06d59c 100644 --- a/Scripts/README.rst +++ b/Scripts/README.rst @@ -12,7 +12,7 @@ pildriver.py (by Eric S. Raymond) -------------------------------------------------------------------- A class implementing an image-processing calculator for scripts. -Parses lists of commnds (or, called interactively, command-line +Parses lists of commands (or, called interactively, command-line arguments) into image loads, transformations, and saves. viewer.py From 62592415ade594a744620c004a9b064feb5e6a4f Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 10 Oct 2015 11:05:09 +0300 Subject: [PATCH 0690/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 419227be5..bfa4a7204 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Added Usage message to painter script #1482 + [radarhere] + - Updated freetype to 2.6.1 #1479 [radarhere] From 8d2dd2578698c598abfb036317b5d747c876e81c Mon Sep 17 00:00:00 2001 From: hugovk Date: Sat, 10 Oct 2015 11:22:43 +0300 Subject: [PATCH 0691/1037] Update Pythons to latest with msi --- winbuild/get_pythons.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winbuild/get_pythons.py b/winbuild/get_pythons.py index 8ac3b1d4a..5ce87b200 100644 --- a/winbuild/get_pythons.py +++ b/winbuild/get_pythons.py @@ -2,7 +2,7 @@ from fetch import fetch import os if __name__ == '__main__': - for version in ['2.6.5', '2.7.6', '3.2.5', '3.3.5', '3.4.3']: + for version in ['2.6.6', '2.7.9', '3.2.5', '3.3.5', '3.4.3']: for platform in ['', '.amd64']: for extension in ['', '.asc']: fetch('https://www.python.org/ftp/python/%s/python-%s%s.msi%s' From b3aa92dc75a6f3299ea1f73be29302dbeaffeb54 Mon Sep 17 00:00:00 2001 From: hugovk Date: Sat, 10 Oct 2015 12:11:00 +0300 Subject: [PATCH 0692/1037] Update Pythons to latest with msi --- winbuild/get_pythons.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winbuild/get_pythons.py b/winbuild/get_pythons.py index 5ce87b200..57c866daa 100644 --- a/winbuild/get_pythons.py +++ b/winbuild/get_pythons.py @@ -2,7 +2,7 @@ from fetch import fetch import os if __name__ == '__main__': - for version in ['2.6.6', '2.7.9', '3.2.5', '3.3.5', '3.4.3']: + for version in ['2.6.6', '2.7.10', '3.2.5', '3.3.5', '3.4.3']: for platform in ['', '.amd64']: for extension in ['', '.asc']: fetch('https://www.python.org/ftp/python/%s/python-%s%s.msi%s' From 76579aa97775f9f3b023129b8c7e75f937ae6e4f Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 9 Oct 2015 14:27:01 +0000 Subject: [PATCH 0693/1037] added ubuntu 14.04 depends script --- depends/aws_ubuntu_14.04.sh | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100755 depends/aws_ubuntu_14.04.sh diff --git a/depends/aws_ubuntu_14.04.sh b/depends/aws_ubuntu_14.04.sh new file mode 100755 index 000000000..445cfe653 --- /dev/null +++ b/depends/aws_ubuntu_14.04.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +sudo apt-get -y install python-dev python-setuptools \ + python3-dev python-virtualenv cmake +sudo apt-get -y install libtiff5-dev libjpeg8-dev zlib1g-dev \ + libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk + +./install_openjpeg.sh From 0cffb9a86e946760a10135b8cc32ba28399e1f3f Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 9 Oct 2015 14:37:40 +0000 Subject: [PATCH 0694/1037] debian 8.2 depends installer --- depends/debian_8.2.sh | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100755 depends/debian_8.2.sh diff --git a/depends/debian_8.2.sh b/depends/debian_8.2.sh new file mode 100755 index 000000000..2bf8bf16d --- /dev/null +++ b/depends/debian_8.2.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +sudo apt-get -y install python-dev python-setuptools \ + python3-dev python-virtualenv cmake +sudo apt-get -y install libtiff5-dev libjpeg62-turbo-dev zlib1g-dev \ + libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev \ + python-tk python3-tk + +./install_openjpeg.sh From bc4ade5d2ce23b55e7d6e2c9f78b245717a9c109 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 9 Oct 2015 14:44:33 +0000 Subject: [PATCH 0695/1037] python 3 tk libraries --- depends/aws_ubuntu_14.04.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/depends/aws_ubuntu_14.04.sh b/depends/aws_ubuntu_14.04.sh index 445cfe653..335c8f07d 100755 --- a/depends/aws_ubuntu_14.04.sh +++ b/depends/aws_ubuntu_14.04.sh @@ -3,6 +3,7 @@ sudo apt-get -y install python-dev python-setuptools \ python3-dev python-virtualenv cmake sudo apt-get -y install libtiff5-dev libjpeg8-dev zlib1g-dev \ - libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk + libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev \ + python-tk python3-tk ./install_openjpeg.sh From 17a73d1bb93b51ab1801e3eb9ef6b24b039ee152 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sat, 10 Oct 2015 21:11:17 +0100 Subject: [PATCH 0696/1037] README, rename [ci skip] --- depends/README.rst | 24 ++++++++++++++++++- .../{aws_ubuntu_14.04.sh => ubuntu_14.04.sh} | 0 2 files changed, 23 insertions(+), 1 deletion(-) rename depends/{aws_ubuntu_14.04.sh => ubuntu_14.04.sh} (100%) diff --git a/depends/README.rst b/depends/README.rst index 62c101ecf..21201d015 100644 --- a/depends/README.rst +++ b/depends/README.rst @@ -1,4 +1,26 @@ Depends ======= -Scripts in this directory can be used to download, build & install non-packaged dependencies; useful for testing with Travis CI. +``install_openjpeg.sh`` and ``install_webp.sh`` can be used to +download, build & install non-packaged dependencies; useful for +testing with Travis CI. + +The other scripts can be used to install all of the dependencies for +the listed operating systems/distros, The ``ubuntu_14.04.sh`` and +``debian_8.2.sh`` have been tested on bare AWS images and will install +all required dependencies for the system python 2.7 and 3.4 for all of +the optional dependencies. Git may also be required prior to running +the script to actually download Pillow. + +e.g.:: + + $ sudo apt-get install git + $ git clone https://github.com/python-pillow/Pillow.git + $ cd Pillow/depends + $ ./debian_8.2.sh + $ cd .. + $ virtualenv -p /usr/bin/python2.7 ~/vpy27 + $ source ~/vpy27/bin/activate + $ make install + $ make test + diff --git a/depends/aws_ubuntu_14.04.sh b/depends/ubuntu_14.04.sh similarity index 100% rename from depends/aws_ubuntu_14.04.sh rename to depends/ubuntu_14.04.sh From 7af2b04c121448d995c95194a9466609bf48c7f6 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sat, 10 Oct 2015 21:21:53 +0100 Subject: [PATCH 0697/1037] platform matrix, clarify openjpeg [ci skip] --- docs/installation.rst | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 9b0e46bf3..052bd6539 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -83,6 +83,8 @@ Many of Pillow's features require external libraries: * **openjpeg** provides JPEG 2000 functionality. * Pillow has been tested with openjpeg **2.0.0** and **2.1.0**. + * Pillow does **not** support the earlier **1.5** series which ships + with Ubuntu and Debian. Once you have installed the prerequisites,run:: @@ -195,7 +197,11 @@ Linux Installation .. note:: - Most major Linux distributions, including Fedora, Debian/Ubuntu and ArchLinux include Pillow in packages that previously contained PIL e.g. ``python-imaging``. Please consider using native operating system packages first to avoid installation problems and/or missing library support later. + Most major Linux distributions, including Fedora, Debian/Ubuntu + and ArchLinux include Pillow in packages that previously contained + PIL e.g. ``python-imaging``. Please consider using native + operating system packages first to avoid installation problems + and/or missing library support later. **We do not provide binaries for Linux.** If you didn't build Python from source, make sure you have Python's development libraries installed. In Debian @@ -267,7 +273,9 @@ current versions of Linux, OS X, and Windows. | | | | | | | | | 2.7,3.2 | 2.6.1 |ppc | +----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ -| Ubuntu Linux 14.04 LTS |Yes | 2.7,3.2,3.3,3.4 | 2.3.0 |x86 | +| Ubuntu Linux 14.04 LTS |Yes | 2.7,3.4 | 3.0.0 |x86-64 | ++----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ +| Debian 8.2 Jessie |Yes | 2.7,3.4 | 3.0.0 |x86-64 | +----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ | Raspian Wheezy |Yes | 2.7,3.2 | 2.3.0 |arm | +----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ @@ -289,4 +297,8 @@ current versions of Linux, OS X, and Windows. Old Versions ------------ -You can download old distributions from `PyPI `_. Only the latest major releases for Python 2.x and 3.x are visible, but all releases are available by direct URL access e.g. https://pypi.python.org/pypi/Pillow/1.0. +You can download old distributions from `PyPI +`_. Only the latest major +releases for Python 2.x and 3.x are visible, but all releases are +available by direct URL access +e.g. https://pypi.python.org/pypi/Pillow/1.0. From 0b23e735b8e40c80354145f713f2fc33b398e1c5 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sat, 10 Oct 2015 21:29:58 +0100 Subject: [PATCH 0698/1037] wording, punctuation --- depends/README.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/depends/README.rst b/depends/README.rst index 21201d015..e8a954e18 100644 --- a/depends/README.rst +++ b/depends/README.rst @@ -6,11 +6,11 @@ download, build & install non-packaged dependencies; useful for testing with Travis CI. The other scripts can be used to install all of the dependencies for -the listed operating systems/distros, The ``ubuntu_14.04.sh`` and -``debian_8.2.sh`` have been tested on bare AWS images and will install -all required dependencies for the system python 2.7 and 3.4 for all of -the optional dependencies. Git may also be required prior to running -the script to actually download Pillow. +the listed operating systems/distros. The ``ubuntu_14.04.sh`` and +``debian_8.2.sh`` scripts have been tested on bare AWS images and will +install all required dependencies for the system python 2.7 and 3.4 +for all of the optional dependencies. Git may also be required prior +to running the script to actually download Pillow. e.g.:: From 24790ecf464d398b07b3c6c59976ee9b4c87c43c Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sat, 10 Oct 2015 21:32:25 +0100 Subject: [PATCH 0699/1037] comments --- depends/debian_8.2.sh | 5 +++++ depends/ubuntu_14.04.sh | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/depends/debian_8.2.sh b/depends/debian_8.2.sh index 2bf8bf16d..4a27a6a5d 100755 --- a/depends/debian_8.2.sh +++ b/depends/debian_8.2.sh @@ -1,5 +1,10 @@ #!/bin/sh +# +# Installs all of the dependencies for Pillow for Debian 8.2 +# for both system pythons 2.7 and 3.4 +# + sudo apt-get -y install python-dev python-setuptools \ python3-dev python-virtualenv cmake sudo apt-get -y install libtiff5-dev libjpeg62-turbo-dev zlib1g-dev \ diff --git a/depends/ubuntu_14.04.sh b/depends/ubuntu_14.04.sh index 335c8f07d..ec993e50b 100755 --- a/depends/ubuntu_14.04.sh +++ b/depends/ubuntu_14.04.sh @@ -1,5 +1,10 @@ #!/bin/sh +# +# Installs all of the dependencies for Pillow for Debian 8.2 +# for both system pythons 2.7 and 3.4 +# + sudo apt-get -y install python-dev python-setuptools \ python3-dev python-virtualenv cmake sudo apt-get -y install libtiff5-dev libjpeg8-dev zlib1g-dev \ From 6bd7e7f02ded4b119f590de944f43c7be30b3e15 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 11 Oct 2015 21:24:35 +1100 Subject: [PATCH 0700/1037] Removed trailing whitespace --- CHANGES.rst | 6 ++--- PIL/Image.py | 2 +- RELEASING.md | 4 +-- Tests/images/bmp/html/bmpsuite.html | 2 +- Tests/test_file_tiff.py | 2 +- _imaging.c | 6 ++--- _imagingcms.c | 22 ++++++++-------- _imagingft.c | 12 ++++----- _imagingmorph.c | 4 +-- docs/build.rst | 12 ++++----- docs/handbook/concepts.rst | 6 ++--- docs/handbook/image-file-formats.rst | 4 +-- docs/reference/ImageDraw.rst | 4 +-- docs/reference/ImageMath.rst | 2 +- docs/reference/ImageMorph.rst | 2 +- docs/reference/OleFileIO.rst | 6 ++--- docs/reference/PixelAccess.rst | 2 +- docs/reference/plugins.rst | 2 +- docs/releasenotes/2.7.0.rst | 6 ++--- docs/releasenotes/3.0.0.rst | 6 ++--- encode.c | 4 +-- libImaging/Convert.c | 38 ++++++++++++++-------------- libImaging/Incremental.c | 4 +-- libImaging/Jpeg2KDecode.c | 6 ++--- libImaging/Jpeg2KEncode.c | 6 ++--- libImaging/JpegEncode.c | 2 +- libImaging/Pack.c | 2 +- libImaging/PcxDecode.c | 4 +-- libImaging/PcxEncode.c | 10 ++++---- libImaging/Unpack.c | 16 ++++++------ 30 files changed, 102 insertions(+), 102 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index bfa4a7204..bba01ce42 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -325,7 +325,7 @@ Changelog (Pillow) - Adjust buffer size when quality=keep, fixes #148 (again) [wiredfool] -- Fix for corrupted bitmaps embedded in truetype fonts. #1072 +- Fix for corrupted bitmaps embedded in truetype fonts. #1072 [jackyyf, wiredfool] 2.7.0 (2015-01-01) @@ -412,7 +412,7 @@ Changelog (Pillow) 2.6.2 (2015-01-01) ------------------ -- Fix CVE-2014-9601, potential PNG decompression DOS #1060 +- Fix CVE-2014-9601, potential PNG decompression DOS #1060 [wiredfool] - Fix Regression in PyPy 2.4 in streamio #958 @@ -1081,7 +1081,7 @@ Changelog (Pillow) .. Note:: Special thanks to Christoph Gohlke and Eric Soroos for assisting with a pre-PyCon 2013 release! -- Many other bug fixes and enhancements by many other people. +- Many other bug fixes and enhancements by many other people. - Add Python 3 support. (Pillow >= 2.0.0 supports Python 2.6, 2.7, 3.2, 3.3. Pillow < 2.0.0 supports Python 2.4, 2.5, 2.6, 2.7.) [fluggo] diff --git a/PIL/Image.py b/PIL/Image.py index f09168708..bc194f754 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -2269,7 +2269,7 @@ def open(fp, mode="r"): return im except (SyntaxError, IndexError, TypeError, struct.error): # Leave disabled by default, spams the logs with image - # opening failures that are entirely expected. + # opening failures that are entirely expected. #logger.debug("", exc_info=True) continue return None diff --git a/RELEASING.md b/RELEASING.md index e30fe2821..4496af2c1 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -42,8 +42,8 @@ Released as needed for security, installation or critical bug fixes. ``` * [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in: ``` - PIL/__init__.py - setup.py + PIL/__init__.py + setup.py _imaging.c appveyor.yml ``` diff --git a/Tests/images/bmp/html/bmpsuite.html b/Tests/images/bmp/html/bmpsuite.html index 6604102bb..b8e327ed9 100644 --- a/Tests/images/bmp/html/bmpsuite.html +++ b/Tests/images/bmp/html/bmpsuite.html @@ -144,7 +144,7 @@ level of support for it is arguably not important.

q/pal8rletrns.bmp 3 -
+
or

or
diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 357b5dc9d..4cf83534d 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -251,7 +251,7 @@ class TestFileTiff(PillowTestCase): {256: (55,), 257: (43,), 258: (8, 8, 8, 8), 259: (1,), 262: (2,), 296: (2,), 273: (8,), 338: (1,), 277: (4,), 279: (9460,), 282: ((720000, 10000),), - 283: ((720000, 10000),), 284: (1,)}) + 283: ((720000, 10000),), 284: (1,)}) def test__delitem__(self): filename = "Tests/images/pil136.tiff" diff --git a/_imaging.c b/_imaging.c index d6d49aa29..8d7e01685 100644 --- a/_imaging.c +++ b/_imaging.c @@ -395,8 +395,8 @@ getlist(PyObject* arg, int* length, const char* wrong_length, int type) for (i = 0; i < n; i++) { op = PySequence_Fast_GET_ITEM(seq, i); - // DRY, branch prediction is going to work _really_ well - // on this switch. And 3 fewer loops to copy/paste. + // DRY, branch prediction is going to work _really_ well + // on this switch. And 3 fewer loops to copy/paste. switch (type) { case TYPE_UINT8: itemp = PyInt_AsLong(op); @@ -3101,7 +3101,7 @@ static struct PyMethodDef methods[] = { {"unsharp_mask", (PyCFunction)_unsharp_mask, 1}, #endif - {"box_blur", (PyCFunction)_box_blur, 1}, + {"box_blur", (PyCFunction)_box_blur, 1}, #ifdef WITH_EFFECTS /* Special effects */ diff --git a/_imagingcms.c b/_imagingcms.c index cda7c5f1f..198900c2b 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -49,7 +49,7 @@ http://www.cazabon.com\n\ /* known to-do list with current version: - Verify that PILmode->littleCMStype conversion in findLCMStype is correct for all + Verify that PILmode->littleCMStype conversion in findLCMStype is correct for all PIL modes (it probably isn't for the more obscure ones) Add support for creating custom RGB profiles on the fly @@ -454,7 +454,7 @@ createProfile(PyObject *self, PyObject *args) } else if (strcmp(sColorSpace, "XYZ") == 0) { hProfile = cmsCreateXYZProfile(); - } + } else if (strcmp(sColorSpace, "sRGB") == 0) { hProfile = cmsCreate_sRGBProfile(); } @@ -554,10 +554,10 @@ _profile_getattr(CmsProfileObject* self, cmsInfoType field) { // UNDONE -- check that I'm getting the right fields on these. // return PyUnicode_DecodeFSDefault(cmsTakeProductName(self->profile)); - //wchar_t buf[256]; -- UNDONE need wchar_t for unicode version. + //wchar_t buf[256]; -- UNDONE need wchar_t for unicode version. char buf[256]; cmsUInt32Number written; - written = cmsGetProfileInfoASCII(self->profile, + written = cmsGetProfileInfoASCII(self->profile, field, "en", "us", @@ -566,40 +566,40 @@ _profile_getattr(CmsProfileObject* self, cmsInfoType field) if (written) { return PyUnicode_FromString(buf); } - // UNDONE suppressing error here by sending back blank string. + // UNDONE suppressing error here by sending back blank string. return PyUnicode_FromString(""); } static PyObject* cms_profile_getattr_product_desc(CmsProfileObject* self, void* closure) -{ +{ // description was Description != 'Copyright' || or "%s - %s" (manufacturer, model) in 1.x return _profile_getattr(self, cmsInfoDescription); } -/* use these four for the individual fields. +/* use these four for the individual fields. */ static PyObject* cms_profile_getattr_product_description(CmsProfileObject* self, void* closure) -{ +{ return _profile_getattr(self, cmsInfoDescription); } static PyObject* cms_profile_getattr_product_model(CmsProfileObject* self, void* closure) -{ +{ return _profile_getattr(self, cmsInfoModel); } static PyObject* cms_profile_getattr_product_manufacturer(CmsProfileObject* self, void* closure) -{ +{ return _profile_getattr(self, cmsInfoManufacturer); } static PyObject* cms_profile_getattr_product_copyright(CmsProfileObject* self, void* closure) -{ +{ return _profile_getattr(self, cmsInfoCopyright); } diff --git a/_imagingft.c b/_imagingft.c index 8ec7fd471..a34fb8af9 100644 --- a/_imagingft.c +++ b/_imagingft.c @@ -128,11 +128,11 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw) /* Don't free this before FT_Done_Face */ self->font_bytes = PyMem_Malloc(font_bytes_size); if (!self->font_bytes) { - error = 65; // Out of Memory in Freetype. + error = 65; // Out of Memory in Freetype. } if (!error) { memcpy(self->font_bytes, font_bytes, (size_t)font_bytes_size); - error = FT_New_Memory_Face(library, (FT_Byte*)self->font_bytes, + error = FT_New_Memory_Face(library, (FT_Byte*)self->font_bytes, font_bytes_size, index, &self->face); } } @@ -152,7 +152,7 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw) if (error) { if (self->font_bytes) { PyMem_Free(self->font_bytes); - } + } PyObject_Del(self); return geterror(error); } @@ -228,8 +228,8 @@ font_getsize(FontObject* self, PyObject* args) x += delta.x; } - /* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960 - * Yifu Yu, 2014-10-15 + /* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960 + * Yifu Yu, 2014-10-15 */ error = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP); if (error) @@ -443,7 +443,7 @@ font_dealloc(FontObject* self) FT_Done_Face(self->face); if (self->font_bytes) { PyMem_Free(self->font_bytes); - } + } PyObject_Del(self); } diff --git a/_imagingmorph.c b/_imagingmorph.c index 1dee7eb64..70779d036 100644 --- a/_imagingmorph.c +++ b/_imagingmorph.c @@ -109,7 +109,7 @@ apply(PyObject *self, PyObject* args) unsigned char b7 = nrow[col_idx]&1; unsigned char b8 = nrow[cip]&1; - int lut_idx = (b0 + int lut_idx = (b0 |(b1 << 1) |(b2 << 2) |(b3 << 3) @@ -199,7 +199,7 @@ match(PyObject *self, PyObject* args) unsigned char b7 = nrow[col_idx]&1; unsigned char b8 = nrow[cip]&1; - int lut_idx = (b0 + int lut_idx = (b0 |(b1 << 1) |(b2 << 2) |(b3 << 3) diff --git a/docs/build.rst b/docs/build.rst index d0ee35ee6..ba75470b1 100644 --- a/docs/build.rst +++ b/docs/build.rst @@ -33,11 +33,11 @@ Download Python 3.4, install it, and add it to the path. This is the Python that we will use to bootstrap the build process. (The download routines are using 3.2+ features, and installing 3.4 gives us pip and virtualenv as well, reducing the number of packages that we need to -install.) +install.) Download the rest of the Pythons by opening a command window, changing to the `winbuild` directory, and running `python -get_pythons.py`. +get_pythons.py`. UNDONE -- gpg verify the signatures (note that we can download from https) @@ -67,13 +67,13 @@ Dependencies The script 'build_dep.py' downloads and builds the dependencies. Open a command window, change directory into `winbuild` and run `python -build_dep.py`. +build_dep.py`. This will download libjpeg, libtiff, libz, and freetype. It will then compile 32 and 64-bit versions of the libraries, with both versions of -the compilers. +the compilers. -UNDONE -- lcms fails. +UNDONE -- lcms fails. UNDONE -- webp, jpeg2k not recognized Building Pillow @@ -82,7 +82,7 @@ Building Pillow Once the dependencies are built, run `python build.py --clean` to build and install Pillow in virtualenvs for each python build. `build.py --dist` will build Windows installers instead of -installing into virtualenvs. +installing into virtualenvs. UNDONE -- suppressed output, what about failures. diff --git a/docs/handbook/concepts.rst b/docs/handbook/concepts.rst index 07b9fce4b..2dc509a13 100644 --- a/docs/handbook/concepts.rst +++ b/docs/handbook/concepts.rst @@ -12,12 +12,12 @@ Bands An image can consist of one or more bands of data. The Python Imaging Library allows you to store several bands in a single image, provided they all have the same dimensions and depth. For example, a PNG image might have 'R', 'G', 'B', -and 'A' bands for the red, green, blue, and alpha transparency values. Many +and 'A' bands for the red, green, blue, and alpha transparency values. Many operations act on each band separately, e.g., histograms. It is often useful to think of each pixel as having one value per band. To get the number and names of bands in an image, use the -:py:meth:`~PIL.Image.Image.getbands` method. +:py:meth:`~PIL.Image.Image.getbands` method. .. _concept-modes: @@ -43,7 +43,7 @@ PIL also provides limited support for a few special modes, including ``LA`` (L with alpha), ``RGBX`` (true color with padding) and ``RGBa`` (true color with premultiplied alpha). However, PIL doesn’t support user-defined modes; if you to handle band combinations that are not listed above, use a sequence of Image -objects. +objects. You can read the mode of an image through the :py:attr:`~PIL.Image.Image.mode` attribute. This is a string containing one of the above values. diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index bf999a592..2b69f2414 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -471,13 +471,13 @@ The :py:meth:`~PIL.Image.Image.open` method sets the following .. versionadded:: 1.1.5 **resolution** - Image resolution as an ``(xres, yres)`` tuple, where applicable. This is a + Image resolution as an ``(xres, yres)`` tuple, where applicable. This is a measurement in whichever unit is specified by the file. .. versionadded:: 1.1.5 -The :py:attr:`~PIL.Image.Image.tag_v2` attribute contains a dictionary of +The :py:attr:`~PIL.Image.Image.tag_v2` attribute contains a dictionary of TIFF metadata. The keys are numerical indexes from `~PIL.TiffTags.TAGS_V2`. Values are strings or numbers for single items, multiple values are returned in a tuple of values. Rational numbers are returned as a single value. diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index b93581949..7d3ad570c 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -81,7 +81,7 @@ Example: Draw Partial Opacity Text from PIL import Image, ImageDraw, ImageFont # get an image - base = Image.open('Pillow/Tests/images/lena.png').convert('RGBA') + base = Image.open('Pillow/Tests/images/lena.png').convert('RGBA') # make a blank image for the text, initialized to transparent text color txt = Image.new('RGBA', base.size, (255,255,255,0)) @@ -89,7 +89,7 @@ Example: Draw Partial Opacity Text # get a font fnt = ImageFont.truetype('Pillow/Tests/fonts/FreeMono.ttf', 40) # get a drawing context - d = ImageDraw.Draw(txt) + d = ImageDraw.Draw(txt) # draw text, half opacity d.text((10,10), "Hello", font=fnt, fill=(255,255,255,128)) diff --git a/docs/reference/ImageMath.rst b/docs/reference/ImageMath.rst index 00249a601..5d7345d79 100644 --- a/docs/reference/ImageMath.rst +++ b/docs/reference/ImageMath.rst @@ -27,7 +27,7 @@ Example: Using the :py:mod:`~PIL.ImageMath` module In the current version, :py:mod:`~PIL.ImageMath` only supports single-layer images. To process multi-band images, use the - :py:meth:`~PIL.Image.Image.split` method or :py:func:`~PIL.Image.merge` + :py:meth:`~PIL.Image.Image.split` method or :py:func:`~PIL.Image.merge` function. :param expression: A string which uses the standard Python expression diff --git a/docs/reference/ImageMorph.rst b/docs/reference/ImageMorph.rst index eaf3b1c5e..be9d59348 100644 --- a/docs/reference/ImageMorph.rst +++ b/docs/reference/ImageMorph.rst @@ -4,7 +4,7 @@ :py:mod:`ImageMorph` Module =========================== -The :py:mod:`ImageMorph` module provides morphology operations on images. +The :py:mod:`ImageMorph` module provides morphology operations on images. .. automodule:: PIL.ImageMorph :members: diff --git a/docs/reference/OleFileIO.rst b/docs/reference/OleFileIO.rst index 74c4b7b36..b66bfc60d 100644 --- a/docs/reference/OleFileIO.rst +++ b/docs/reference/OleFileIO.rst @@ -7,10 +7,10 @@ The :py:mod:`OleFileIO` module reads Microsoft OLE2 files (also called Structured Storage or Microsoft Compound Document File Format), such as Microsoft Office documents, Image Composer and FlashPix files, and -Outlook messages. +Outlook messages. This module is the `OleFileIO\_PL`_ project by Philippe Lagadec, v0.30, -merged back into Pillow. +merged back into Pillow. .. _OleFileIO\_PL: http://www.decalage.info/python/olefileio @@ -300,7 +300,7 @@ Close the OLE file Unless your application is a simple script that terminates after processing an OLE file, do not forget to close each OleFileIO object -after parsing to close the file on disk. +after parsing to close the file on disk. .. code-block:: python diff --git a/docs/reference/PixelAccess.rst b/docs/reference/PixelAccess.rst index 55ab8c578..f9d0cf31a 100644 --- a/docs/reference/PixelAccess.rst +++ b/docs/reference/PixelAccess.rst @@ -6,7 +6,7 @@ The PixelAccess class provides read and write access to :py:class:`PIL.Image` data at a pixel level. -.. note:: Accessing individual pixels is fairly slow. If you are looping over all of the pixels in an image, there is likely a faster way using other parts of the Pillow API. +.. note:: Accessing individual pixels is fairly slow. If you are looping over all of the pixels in an image, there is likely a faster way using other parts of the Pillow API. Example ------- diff --git a/docs/reference/plugins.rst b/docs/reference/plugins.rst index f7f897f59..46f657fce 100644 --- a/docs/reference/plugins.rst +++ b/docs/reference/plugins.rst @@ -228,7 +228,7 @@ Plugin reference :mod:`PngImagePlugin` Module ---------------------------- -.. automodule:: PIL.PngImagePlugin +.. automodule:: PIL.PngImagePlugin :members: ChunkStream, PngImageFile, PngStream, getchunks, is_cid, putchunk :show-inheritance: .. autoclass:: PIL.PngImagePlugin.ChunkStream diff --git a/docs/releasenotes/2.7.0.rst b/docs/releasenotes/2.7.0.rst index 65a8f2d11..a51ca81b4 100644 --- a/docs/releasenotes/2.7.0.rst +++ b/docs/releasenotes/2.7.0.rst @@ -4,8 +4,8 @@ Sane Plugin ----------- -The Sane plugin has now been split into its own repo: -https://github.com/python-pillow/Sane . +The Sane plugin has now been split into its own repo: +https://github.com/python-pillow/Sane . Png text chunk size limits @@ -14,7 +14,7 @@ Png text chunk size limits To prevent potential denial of service attacks using compressed text chunks, there are now limits to the decompressed size of text chunks decoded from PNG images. If the limits are exceeded when opening a PNG -image a ``ValueError`` will be raised. +image a ``ValueError`` will be raised. Individual text chunks are limited to :py:attr:`PIL.PngImagePlugin.MAX_TEXT_CHUNK`, set to 1MB by diff --git a/docs/releasenotes/3.0.0.rst b/docs/releasenotes/3.0.0.rst index 95f6be132..9cc1de98c 100644 --- a/docs/releasenotes/3.0.0.rst +++ b/docs/releasenotes/3.0.0.rst @@ -31,7 +31,7 @@ Deprecated Methods Several methods that have been marked as deprecated for many releases have been removed in this release:: - Image.tostring() + Image.tostring() Image.fromstring() Image.offset() ImageDraw.setink() @@ -47,6 +47,6 @@ LibJpeg and Zlib are Required by Default The external dependencies on libjpeg and zlib are now required by default. If the headers or libraries are not found, then installation will abort -with an error. This behaviour can be disabled with the ``--disable-libjpeg`` -and ``--disable-zlib`` flags. +with an error. This behaviour can be disabled with the ``--disable-libjpeg`` +and ``--disable-zlib`` flags. diff --git a/encode.c b/encode.c index c46d78426..2eb9ceb09 100644 --- a/encode.c +++ b/encode.c @@ -554,7 +554,7 @@ static unsigned int* get_qtables_arrays(PyObject* qtables, int* qtablesLen) { tables = PySequence_Fast(qtables, "expected a sequence"); num_tables = PySequence_Size(qtables); if (num_tables < 1 || num_tables > NUM_QUANT_TBLS) { - PyErr_SetString(PyExc_ValueError, + PyErr_SetString(PyExc_ValueError, "Not a valid number of quantization tables. Should be between 1 and 4."); Py_DECREF(tables); return NULL; @@ -582,7 +582,7 @@ static unsigned int* get_qtables_arrays(PyObject* qtables, int* qtablesLen) { Py_DECREF(table_data); } - *qtablesLen = num_tables; + *qtablesLen = num_tables; JPEG_QTABLES_ERR: Py_DECREF(tables); // Run on both error and not error diff --git a/libImaging/Convert.c b/libImaging/Convert.c index 757367604..f32e91bad 100644 --- a/libImaging/Convert.c +++ b/libImaging/Convert.c @@ -259,14 +259,14 @@ rgb2hsv(UINT8* out, const UINT8* in, int xsize) g = in[1]; b = in[2]; - maxc = MAX(r,MAX(g,b)); + maxc = MAX(r,MAX(g,b)); minc = MIN(r,MIN(g,b)); uv = maxc; if (minc == maxc){ *out++ = 0; *out++ = 0; *out++ = uv; - } else { + } else { cr = (float)(maxc-minc); s = cr/(float)maxc; rc = ((float)(maxc-r))/cr; @@ -280,7 +280,7 @@ rgb2hsv(UINT8* out, const UINT8* in, int xsize) h = 4.0 + gc-rc; } // incorrect hue happens if h/6 is negative. - h = fmod((h/6.0 + 1.0), 1.0); + h = fmod((h/6.0 + 1.0), 1.0); uh = (UINT8)CLIP((int)(h*255.0)); us = (UINT8)CLIP((int)(s*255.0)); @@ -313,10 +313,10 @@ hsv2rgb(UINT8* out, const UINT8* in, int xsize) *out++ = v; *out++ = v; *out++ = v; - } else { + } else { i = floor((float)h * 6.0 / 255.0); // 0 - 6 - f = (float)h * 6.0 / 255.0 - (float)i; // 0-1 : remainder. - fs = ((float)s)/255.0; + f = (float)h * 6.0 / 255.0 - (float)i; // 0-1 : remainder. + fs = ((float)s)/255.0; p = round((float)v * (1.0-fs)); q = round((float)v * (1.0-fs*f)); @@ -326,32 +326,32 @@ hsv2rgb(UINT8* out, const UINT8* in, int xsize) ut = (UINT8)CLIP(t); switch (i%6) { - case 0: + case 0: *out++ = v; *out++ = ut; *out++ = up; break; - case 1: + case 1: *out++ = uq; *out++ = v; *out++ = up; break; - case 2: + case 2: *out++ = up; *out++ = v; *out++ = ut; break; - case 3: + case 3: *out++ = up; *out++ = uq; *out++ = v; break; - case 4: + case 4: *out++ = ut; *out++ = up; *out++ = v; break; - case 5: + case 5: *out++ = v; *out++ = up; *out++ = uq; @@ -418,7 +418,7 @@ rgba2rgba(UINT8* out, const UINT8* in, int xsize) } } -/* RGBa -> RGBA conversion to remove premultiplication +/* RGBa -> RGBA conversion to remove premultiplication Needed for correct transforms/resizing on RGBA images */ static void rgba2rgbA(UINT8* out, const UINT8* in, int xsize) @@ -442,13 +442,13 @@ rgba2rgbA(UINT8* out, const UINT8* in, int xsize) } /* - * Conversion of RGB + single transparent color to RGBA, - * where any pixel that matches the color will have the + * Conversion of RGB + single transparent color to RGBA, + * where any pixel that matches the color will have the * alpha channel set to 0 */ static void -rgbT2rgba(UINT8* out, int xsize, int r, int g, int b) +rgbT2rgba(UINT8* out, int xsize, int r, int g, int b) { #ifdef WORDS_BIGENDIAN UINT32 trns = ((r & 0xff)<<24) | ((g & 0xff)<<16) | ((b & 0xff)<<8) | 0xff; @@ -1334,8 +1334,8 @@ ImagingConvertTransparent(Imaging imIn, const char *mode, return (Imaging) ImagingError_ModeError(); } - if (!((strcmp(imIn->mode, "RGB") == 0 || - strcmp(imIn->mode, "L") == 0) + if (!((strcmp(imIn->mode, "RGB") == 0 || + strcmp(imIn->mode, "L") == 0) && strcmp(mode, "RGBA") == 0)) #ifdef notdef { @@ -1370,7 +1370,7 @@ ImagingConvertTransparent(Imaging imIn, const char *mode, } ImagingSectionLeave(&cookie); - return imOut; + return imOut; } diff --git a/libImaging/Incremental.c b/libImaging/Incremental.c index 84e20acf6..f34765423 100644 --- a/libImaging/Incremental.c +++ b/libImaging/Incremental.c @@ -175,7 +175,7 @@ ImagingIncrementalCodecCreate(ImagingIncrementalCodecEntry codec_entry, codec->state = state; codec->result = 0; codec->stream.fd = fd; - codec->stream.buffer = codec->stream.ptr = codec->stream.end + codec->stream.buffer = codec->stream.ptr = codec->stream.end = codec->stream.top = NULL; codec->started = 0; codec->seekable = seekable; @@ -294,7 +294,7 @@ ImagingIncrementalCodecDestroy(ImagingIncrementalCodec codec) if (codec->seekable && codec->stream.fd < 0) free (codec->stream.buffer); - codec->stream.buffer = codec->stream.ptr = codec->stream.end + codec->stream.buffer = codec->stream.ptr = codec->stream.end = codec->stream.top = NULL; #ifdef _WIN32 diff --git a/libImaging/Jpeg2KDecode.c b/libImaging/Jpeg2KDecode.c index abf8cebbe..07bee2b5e 100644 --- a/libImaging/Jpeg2KDecode.c +++ b/libImaging/Jpeg2KDecode.c @@ -68,7 +68,7 @@ j2k_skip(OPJ_OFF_T p_nb_bytes, void *p_user_data) typedef void (*j2k_unpacker_t)(opj_image_t *in, const JPEG2KTILEINFO *tileInfo, - const UINT8 *data, + const UINT8 *data, Imaging im); struct j2k_decode_unpacker { @@ -625,7 +625,7 @@ j2k_decode_entry(Imaging im, ImagingCodecState state, } } - /* + /* Colorspace Number of components PIL mode ------------------------------------------------------ sRGB 3 RGB @@ -668,7 +668,7 @@ j2k_decode_entry(Imaging im, ImagingCodecState state, if (!unpack) { state->errcode = IMAGING_CODEC_BROKEN; state->state = J2K_STATE_FAILED; - goto quick_exit; + goto quick_exit; } /* Decode the image tile-by-tile; this means we only need use as much diff --git a/libImaging/Jpeg2KEncode.c b/libImaging/Jpeg2KEncode.c index 868cfdb41..3c6323eca 100644 --- a/libImaging/Jpeg2KEncode.c +++ b/libImaging/Jpeg2KEncode.c @@ -303,7 +303,7 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, prec = 16; bpp = 12; } else if (strcmp (im->mode, "LA") == 0) { - components = 2; + components = 2; color_space = OPJ_CLRSPC_GRAY; pack = j2k_pack_la; } else if (strcmp (im->mode, "RGB") == 0) { @@ -544,8 +544,8 @@ ImagingJpeg2KEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) return -1; if (state->state == J2K_STATE_START) { - int seekable = (context->format != OPJ_CODEC_J2K - ? INCREMENTAL_CODEC_SEEKABLE + int seekable = (context->format != OPJ_CODEC_J2K + ? INCREMENTAL_CODEC_SEEKABLE : INCREMENTAL_CODEC_NOT_SEEKABLE); context->encoder = ImagingIncrementalCodecCreate(j2k_encode_entry, diff --git a/libImaging/JpegEncode.c b/libImaging/JpegEncode.c index 3fe5d753f..5bcba2aa4 100644 --- a/libImaging/JpegEncode.c +++ b/libImaging/JpegEncode.c @@ -300,7 +300,7 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) if (context->qtables) { free(context->qtables); context->qtables = NULL; - } + } jpeg_destroy_compress(&context->cinfo); /* if (jerr.pub.num_warnings) return BROKEN; */ diff --git a/libImaging/Pack.c b/libImaging/Pack.c index d174d7d0d..d83cb8284 100644 --- a/libImaging/Pack.c +++ b/libImaging/Pack.c @@ -400,7 +400,7 @@ ImagingPackLAB(UINT8* out, const UINT8* in, int pixels) /* LAB triplets */ for (i = 0; i < pixels; i++) { out[0] = in[0]; - out[1] = in[1] ^ 128; /* signed in outside world */ + out[1] = in[1] ^ 128; /* signed in outside world */ out[2] = in[2] ^ 128; out += 3; in += 4; } diff --git a/libImaging/PcxDecode.c b/libImaging/PcxDecode.c index af282cfe4..e5417f1bd 100644 --- a/libImaging/PcxDecode.c +++ b/libImaging/PcxDecode.c @@ -62,8 +62,8 @@ ImagingPcxDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) int stride = state->bytes / bands; int i; for (i=1; i< bands; i++) { // note -- skipping first band - memmove(&state->buffer[i*state->xsize], - &state->buffer[i*stride], + memmove(&state->buffer[i*state->xsize], + &state->buffer[i*stride], state->xsize); } } diff --git a/libImaging/PcxEncode.c b/libImaging/PcxEncode.c index c1f64a33d..c73f49a24 100644 --- a/libImaging/PcxEncode.c +++ b/libImaging/PcxEncode.c @@ -94,10 +94,10 @@ ImagingPcxEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) while (state->x < planes * bytes_per_line) { /* If we're encoding an odd width file, and we've got more than one plane, we need to pad each - color row with padding bytes at the end. Since + color row with padding bytes at the end. Since The pixels are stored RRRRRGGGGGBBBBB, so we need - to have the padding be RRRRRPGGGGGPBBBBBP. Hence - the double loop + to have the padding be RRRRRPGGGGGPBBBBBP. Hence + the double loop */ while (state->x % bytes_per_line) { @@ -174,8 +174,8 @@ ImagingPcxEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) } /* reset for the next color plane. */ if (state->x < planes * bytes_per_line) { - state->count = 1; - state->LAST = state->buffer[state->x]; + state->count = 1; + state->LAST = state->buffer[state->x]; state->x++; } } diff --git a/libImaging/Unpack.c b/libImaging/Unpack.c index 522e9b04c..3fab73cec 100644 --- a/libImaging/Unpack.c +++ b/libImaging/Unpack.c @@ -758,13 +758,13 @@ unpackCMYKI(UINT8* out, const UINT8* in, int pixels) /* Unpack to "LAB" image */ /* There are two representations of LAB images for whatever precision: L: Uint (in PS, it's 0-100) - A: Int (in ps, -128 .. 128, or elsewhere 0..255, with 128 as middle. - Channels in PS display a 0 value as middle grey, + A: Int (in ps, -128 .. 128, or elsewhere 0..255, with 128 as middle. + Channels in PS display a 0 value as middle grey, LCMS appears to use 128 as the 0 value for these channels) - B: Int (as above) + B: Int (as above) - Since we don't have any signed ints, we're going with the shifted versions - internally, and we'll unshift for saving and whatnot. + Since we don't have any signed ints, we're going with the shifted versions + internally, and we'll unshift for saving and whatnot. */ void ImagingUnpackLAB(UINT8* out, const UINT8* in, int pixels) @@ -802,15 +802,15 @@ unpackI16N_I16(UINT8* out, const UINT8* in, int pixels){ static void unpackI12_I16(UINT8* out, const UINT8* in, int pixels){ - /* Fillorder 1/MSB -> LittleEndian, for 12bit integer greyscale tiffs. + /* Fillorder 1/MSB -> LittleEndian, for 12bit integer greyscale tiffs. - According to the TIFF spec: + According to the TIFF spec: FillOrder = 2 should be used only when BitsPerSample = 1 and the data is either uncompressed or compressed using CCITT 1D or 2D compression, to avoid potentially ambiguous situations. - Yeah. I thought so. We'll see how well people read the spec. + Yeah. I thought so. We'll see how well people read the spec. We've got several fillorder=2 modes in TiffImagePlugin.py There's no spec I can find. It appears that the in storage From 89fb9965efe3f58ca92917661c944301660373d6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 11 Oct 2015 21:27:18 +1100 Subject: [PATCH 0701/1037] Capitalised Python and Pillow --- docs/build.rst | 2 +- libImaging/TiffDecode.h | 2 +- winbuild/README.md | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/build.rst b/docs/build.rst index d0ee35ee6..cac366e09 100644 --- a/docs/build.rst +++ b/docs/build.rst @@ -5,7 +5,7 @@ Building Pillow on Windows ` should be sufficient This page will describe a build setup to build Pillow against the -supported python versions in 32 and 64-bit modes, using freely +supported Python versions in 32 and 64-bit modes, using freely available Microsoft compilers. This has been developed and tested against 64-bit Windows 7 Professional and Windows Server 2012 64-bit version on Amazon EC2. diff --git a/libImaging/TiffDecode.h b/libImaging/TiffDecode.h index 0f9038528..ea977d17d 100644 --- a/libImaging/TiffDecode.h +++ b/libImaging/TiffDecode.h @@ -47,7 +47,7 @@ extern int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...); /* Trace debugging - legacy, don't enable for python 3.x, unicode issues. + legacy, don't enable for Python 3.x, unicode issues. */ /* diff --git a/winbuild/README.md b/winbuild/README.md index 58cf1b546..39fab34d5 100644 --- a/winbuild/README.md +++ b/winbuild/README.md @@ -5,14 +5,14 @@ For more extensive info, see the windows build instructions `docs/build.rst`. * See https://github.com/python-pillow/Pillow/issues/553#issuecomment-37877416 and https://github.com/matplotlib/matplotlib/issues/1717#issuecomment-13343859 -* Works best with Python 3.4, due to virtualenv and pip batteries included. Python3+ required for fetch command. +* Works best with Python 3.4, due to virtualenv and pip batteries included. Python3+ required for fetch command. * Check config.py for virtual env paths, suffix for 64 bit releases. Defaults to `x64`, set `X64_EXT` to change. -* When running in CI with one python per invocation, set the `PYTHON` env variable to the python folder. (e.g. `PYTHON`=`c:\Python27\`) This overrides the matrix in config.py and will just build and test for the specific python. -* `python get_pythons.py` downloads all the python releases, and their signatures. (Manually) Install in `c:\PythonXX[x64]\`. +* When running in CI with one Python per invocation, set the `PYTHON` env variable to the Python folder. (e.g. `PYTHON`=`c:\Python27\`) This overrides the matrix in config.py and will just build and test for the specific Python. +* `python get_pythons.py` downloads all the Python releases, and their signatures. (Manually) Install in `c:\PythonXX[x64]\`. * `python build_dep.py` downloads and creates a build script for all the dependencies, in 32 and 64 bit versions, and with both compiler versions. * (in powershell) `build_deps.cmd` invokes the dependency build. -* `python build.py --clean` makes pillow for the matrix of pythons. -* `python test.py` runs the tests on pillow in all the virtual envs. +* `python build.py --clean` makes Pillow for the matrix of Pythons. +* `python test.py` runs the tests on Pillow in all the virtual envs. * Currently working with zlib, libjpeg, freetype, and libtiff on Python 2.7, 3.3, and 3.4, both 32 and 64 bit, on a local win7 pro machine and appveyor.com (3.3 untested there) * Webp is built, not detected. -* LCMS and OpenJpeg are not building. +* LCMS and OpenJpeg are not building. From 81dbd0854391f885a456abddc92bc5670927e1e8 Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 12 Oct 2015 07:42:12 +0300 Subject: [PATCH 0702/1037] Get your EditorConfig plugin: http://editorconfig.org/ [CI skip] --- .editorconfig | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..b1b12b379 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,24 @@ +# Top-most EditorConfig file +root = true + +[*] +# Unix-style newlines with a newline ending every file +end_of_line = lf +insert_final_newline = true +charset = utf-8 + +# Four-space indentation +indent_size = 4 +indent_style = space + +insert_final_newline = true +trim_trailing_whitespace = true + +[*.yml] +# Two-space indentation +indent_size = 2 +indent_style = space + +# Tab indentation (no size specified) +[Makefile] +indent_style = tab From 0eabe6888266f5e122e14325a73714b57c7048fc Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 12 Oct 2015 23:12:38 +1100 Subject: [PATCH 0703/1037] Added test --- Tests/test_file_eps.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index 6e4c63e4f..7e6207976 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -124,15 +124,18 @@ class TestFileEps(PillowTestCase): # Arrange image1 = Image.open(file1) image2 = Image.open(file2) + image3 = Image.open("Tests/images/illu10_preview.eps") new_size = (100, 100) # Act image1 = image1.resize(new_size) image2 = image2.resize(new_size) + image3 = image3.resize(new_size) # Assert self.assertEqual(image1.size, new_size) self.assertEqual(image2.size, new_size) + self.assertEqual(image3.size, new_size) def test_thumbnail(self): # Issue #619 From d8a6a8cc18b3d7f59b9910f24af757ddd6acd426 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 12 Oct 2015 07:26:58 -0700 Subject: [PATCH 0704/1037] Reworded warning --- PIL/Image.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index d8fa6402e..c99bd05da 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -651,9 +651,10 @@ class Image(object): .. warning:: - This method is for raw-ish output, for compressed image - data (e.g. PNG, JPEG) use :meth:`~.save`, with a BytesIO - parameter for in-memory data. + This method returns the raw image data from the internal + storage. For compressed image data (e.g. PNG, JPEG) use + :meth:`~.save`, with a BytesIO parameter for in-memory + data. :param encoder_name: What encoder to use. The default is to use the standard "raw" encoder. From 3edf1d003ab0fc27ae943d9d8707b04f778f0284 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 12 Oct 2015 08:55:06 -0700 Subject: [PATCH 0705/1037] Typos, tweaks --- depends/README.rst | 3 ++- depends/debian_8.2.sh | 2 +- depends/ubuntu_14.04.sh | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/depends/README.rst b/depends/README.rst index e8a954e18..dd05b86ca 100644 --- a/depends/README.rst +++ b/depends/README.rst @@ -8,7 +8,7 @@ testing with Travis CI. The other scripts can be used to install all of the dependencies for the listed operating systems/distros. The ``ubuntu_14.04.sh`` and ``debian_8.2.sh`` scripts have been tested on bare AWS images and will -install all required dependencies for the system python 2.7 and 3.4 +install all required dependencies for the system Python 2.7 and 3.4 for all of the optional dependencies. Git may also be required prior to running the script to actually download Pillow. @@ -19,6 +19,7 @@ e.g.:: $ cd Pillow/depends $ ./debian_8.2.sh $ cd .. + $ git checkout [branch or tag] $ virtualenv -p /usr/bin/python2.7 ~/vpy27 $ source ~/vpy27/bin/activate $ make install diff --git a/depends/debian_8.2.sh b/depends/debian_8.2.sh index 4a27a6a5d..2a175098f 100755 --- a/depends/debian_8.2.sh +++ b/depends/debian_8.2.sh @@ -2,7 +2,7 @@ # # Installs all of the dependencies for Pillow for Debian 8.2 -# for both system pythons 2.7 and 3.4 +# for both system Pythons 2.7 and 3.4 # sudo apt-get -y install python-dev python-setuptools \ diff --git a/depends/ubuntu_14.04.sh b/depends/ubuntu_14.04.sh index ec993e50b..14b9e7066 100755 --- a/depends/ubuntu_14.04.sh +++ b/depends/ubuntu_14.04.sh @@ -1,8 +1,8 @@ #!/bin/sh # -# Installs all of the dependencies for Pillow for Debian 8.2 -# for both system pythons 2.7 and 3.4 +# Installs all of the dependencies for Pillow for Ubuntu 14.04 +# for both system Pythons 2.7 and 3.4 # sudo apt-get -y install python-dev python-setuptools \ From 778893caa16f8348e189c80fe242df6f1207da3f Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 14 Oct 2015 10:53:57 +0300 Subject: [PATCH 0706/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index bba01ce42..93fb3385f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Dependency scripts for Debian and Ubuntu #1486 + [wiredfool] + - Added Usage message to painter script #1482 [radarhere] From 0487d14d565e7bc872660164749bafba68d2630f Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 12 Oct 2015 08:30:01 -0700 Subject: [PATCH 0707/1037] installation docs reorg --- docs/installation.rst | 147 +++++++++++++++++++++++++----------------- 1 file changed, 89 insertions(+), 58 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 052bd6539..9f9da665f 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -22,30 +22,82 @@ Basic Installation .. note:: - The following instructions will install Pillow with support for most common image formats. See :ref:`external-libraries` for a full list of external libraries supported. + The following instructions will install Pillow with support for + most common image formats. See :ref:`external-libraries` for a + full list of external libraries supported. +.. note:: + + The basic installation works on Windows and OSX using the binaries + from PyPI. Other installations require building from source as + detailed below. + Install Pillow with :command:`pip`:: $ pip install Pillow -Or use :command:`easy_install` for installing `Python Eggs `_ as :command:`pip` does not support them:: +Or use :command:`easy_install` for installing `Python Eggs +`_ as +:command:`pip` does not support them:: $ easy_install Pillow -Or download and extract the `compressed archive from PyPI`_ and inside it run:: - $ python setup.py install +Windows Installation +^^^^^^^^^^^^^^^^^^^^ + +We provide Pillow binaries for Windows compiled for the matrix of +supported Pythons in both 32 and 64 bit versions in wheel, egg, and +executable installers. These binaries have all of the optional +libraries included:: + + $ pip install Pillow + +or:: + + $ easy_install Pillow + + +OSX Installation +^^^^^^^^^^^^^^^^ + +We provide binaries for OSX for each of the supported Python versions +in the wheel format. These include support for all optional libraries +except OpenJPEG:: + + $ pip install Pillow + +Linux Installation +^^^^^^^^^^^^^^^^^^ + +We do not provide binaries for Linux. Most major Linux distributions, +including Fedora, Debian/Ubuntu and ArchLinux include Pillow in +packages that previously contained PIL e.g. ``python-imaging``. Please +consider using native operating system packages first to avoid +installation problems and/or missing library support later. + + +Building From Source +-------------------- + +Download and extract the `compressed archive from PyPI`_. .. _compressed archive from PyPI: https://pypi.python.org/pypi/Pillow .. _external-libraries: External Libraries ------------------- +^^^^^^^^^^^^^^^^^^ .. note:: - You **do not need to install all external libraries supported** to use Pillow's basic features. + You **do not need to install all external libraries supported** to + use Pillow's basic features. + +.. note:: + + There are scripts to install the dependencies for some operating + systems included in the ``depends`` directory. Many of Pillow's features require external libraries: @@ -86,7 +138,7 @@ Many of Pillow's features require external libraries: * Pillow does **not** support the earlier **1.5** series which ships with Ubuntu and Debian. -Once you have installed the prerequisites,run:: +Once you have installed the prerequisites, run:: $ pip install Pillow @@ -100,8 +152,14 @@ line:: $ CFLAGS="-I/usr/pkg/include" pip install pillow +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. + + Build Options -------------- +^^^^^^^^^^^^^ * Environment Variable: ``MAX_CONCURRENCY=n``. By default, Pillow will use multiprocessing to build the extension on all available CPUs, @@ -127,12 +185,16 @@ Sample Usage:: $ MAX_CONCURRENCY=1 python setup.py build_ext --enable-[feature] install -OS X Installation ------------------ +Building on OSX +^^^^^^^^^^^^^^^ -We provide binaries for OS X in the form of `Python Wheels `_. Alternatively you can compile Pillow from source with XCode. +XCode is required to compile portions of Pillow. Either install the +full package from the app store, or run ``xcode-select --install`` +from the command line. 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 libtiff libjpeg webp little-cms2 @@ -140,43 +202,19 @@ Install Pillow with:: $ pip install Pillow -Windows Installation --------------------- +or from within the uncompressed source directory:: -We provide binaries for Windows in the form of Python Eggs and `Python Wheels -`_: + $ python setup.py install -Python Eggs -^^^^^^^^^^^ +Building on Windows +^^^^^^^^^^^^^^^^^^^ -.. note:: +We don't recommend trying to build on Windows. It is a maze of twisty +passages, mostly dead ends. There are build scripts and notes for the +Windows build in the ``winbuild`` directory. - :command:`pip` does not support Python Eggs; use :command:`easy_install` - instead. - -:: - - $ easy_install Pillow - -Python Wheels -^^^^^^^^^^^^^ - -.. Note:: Requires setuptools >=0.8 and pip >=1.4.1. Some older versions of pip required the ``--use-wheel`` flag. - -:: - - $ pip install Pillow - -If the above does not work, it's likely because we haven't uploaded a -wheel for the latest version of Pillow. In that case, try pinning it -to a specific version: - -:: - - $ pip install Pillow==2.6.1 - -FreeBSD Installation --------------------- +Building on FreeBSD +^^^^^^^^^^^^^^^^^^^ .. Note:: Only FreeBSD 10 tested @@ -192,20 +230,12 @@ Prerequisites are installed on **FreeBSD 10** with:: $ sudo pkg install jpeg tiff webp lcms2 freetype2 -Linux Installation ------------------- -.. note:: +Building on Linux +^^^^^^^^^^^^^^^^^ - Most major Linux distributions, including Fedora, Debian/Ubuntu - and ArchLinux include Pillow in packages that previously contained - PIL e.g. ``python-imaging``. Please consider using native - operating system packages first to avoid installation problems - and/or missing library support later. - -**We do not provide binaries for Linux.** If you didn't build Python from -source, make sure you have Python's development libraries installed. In Debian -or Ubuntu:: +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 python-dev python-setuptools @@ -247,7 +277,8 @@ current versions of Linux, OS X, and Windows. .. note:: - Contributors please test Pillow on your platform then update this document and send a pull request. + Contributors please test Pillow on your platform then update this + document and send a pull request. +----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ |**Operating system** |**Supported**|**Tested Python versions** |**Latest tested Pillow version**|**Tested processors** | From 6be871b1c52b1e93cf9d098b952ea39783657345 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 14 Oct 2015 01:25:20 -0700 Subject: [PATCH 0708/1037] typos --- docs/installation.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 9f9da665f..d13d9f20e 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -28,7 +28,7 @@ Basic Installation .. note:: - The basic installation works on Windows and OSX using the binaries + The basic installation works on Windows and OS X using the binaries from PyPI. Other installations require building from source as detailed below. @@ -47,7 +47,7 @@ Windows Installation ^^^^^^^^^^^^^^^^^^^^ We provide Pillow binaries for Windows compiled for the matrix of -supported Pythons in both 32 and 64 bit versions in wheel, egg, and +supported Pythons in both 32 and 64-bit versions in wheel, egg, and executable installers. These binaries have all of the optional libraries included:: @@ -58,10 +58,10 @@ or:: $ easy_install Pillow -OSX Installation -^^^^^^^^^^^^^^^^ +OS X Installation +^^^^^^^^^^^^^^^^^ -We provide binaries for OSX for each of the supported Python versions +We provide binaries for OS X for each of the supported Python versions in the wheel format. These include support for all optional libraries except OpenJPEG:: @@ -152,7 +152,7 @@ line:: $ CFLAGS="-I/usr/pkg/include" pip install pillow -If pillow has been previously built without the required +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. @@ -185,8 +185,8 @@ Sample Usage:: $ MAX_CONCURRENCY=1 python setup.py build_ext --enable-[feature] install -Building on OSX -^^^^^^^^^^^^^^^ +Building on OS X +^^^^^^^^^^^^^^^^ XCode is required to compile portions of Pillow. Either install the full package from the app store, or run ``xcode-select --install`` From b6a5512b8453ed6165a0c0f9003664135666391c Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 14 Oct 2015 01:30:09 -0700 Subject: [PATCH 0709/1037] typo - xcode --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index d13d9f20e..860c6e798 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -188,7 +188,7 @@ Sample Usage:: Building on OS X ^^^^^^^^^^^^^^^^ -XCode is required to compile portions of Pillow. Either install the +Xcode is required to compile portions of Pillow. Either install the full package from the app store, or run ``xcode-select --install`` from the command line. It may be necessary to run `` sudo xcodebuild -license`` to accept the license prior to using the tools. From 2c97870955b0972cf133cb4549984532e1b3c663 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 14 Oct 2015 19:14:05 +1100 Subject: [PATCH 0710/1037] Changed OSX to OS X [ci skip] --- CHANGES.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 93fb3385f..7bbbe1053 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -337,7 +337,7 @@ Changelog (Pillow) - Split Sane into a separate repo: https://github.com/python-pillow/Sane [hugovk] -- Look for OSX and Linux fonts in common places. #1054 +- Look for OS X and Linux fonts in common places. #1054 [charleslaw] - Fix CVE-2014-9601, potential PNG decompression DOS #1060 @@ -945,7 +945,7 @@ Changelog (Pillow) - Fix #328: _imagingcms.c: include windef.h to fix build issue on MSVC [nu774] -- Automatically discover homebrew include/ and lib/ paths on OSX +- Automatically discover homebrew include/ and lib/ paths on OS X [donspaulding] - Fix bytes which should be bytearray From 9a2903bf5b9287e9f999d45c3fa628f5df264362 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 15 Oct 2015 00:49:03 +1100 Subject: [PATCH 0711/1037] Changed 32 bit to 32-bit and 64 bit to 64-bit --- CHANGES.rst | 2 +- Tests/large_memory_numpy_test.py | 4 ++-- Tests/large_memory_test.py | 4 ++-- Tests/test_file_tiff.py | 2 +- libImaging/Resample.c | 2 +- setup.py | 2 +- winbuild/README.md | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 7bbbe1053..d86b14576 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1100,7 +1100,7 @@ Changelog (Pillow) - Backport PIL's PNG/Zip improvements. [olt] -- Various 64 bit and Windows fixes. +- Various 64-bit and Windows fixes. [cgohlke] - Add testing suite. diff --git a/Tests/large_memory_numpy_test.py b/Tests/large_memory_numpy_test.py index 5e5a58441..c642549aa 100644 --- a/Tests/large_memory_numpy_test.py +++ b/Tests/large_memory_numpy_test.py @@ -7,7 +7,7 @@ from helper import unittest, PillowTestCase # It requires > 2gb memory for the >2 gigapixel image generated in the # second test. Running this automatically would amount to a denial of # service on our testing infrastructure. I expect this test to fail -# on any 32 bit machine, as well as any smallish things (like +# on any 32-bit machine, as well as any smallish things (like # Raspberry Pis). from PIL import Image @@ -20,7 +20,7 @@ YDIM = 32769 XDIM = 48000 -@unittest.skipIf(sys.maxsize <= 2**32, "requires 64 bit system") +@unittest.skipIf(sys.maxsize <= 2**32, "requires 64-bit system") class LargeMemoryNumpyTest(PillowTestCase): def _write_png(self, xdim, ydim): diff --git a/Tests/large_memory_test.py b/Tests/large_memory_test.py index 3ee13091d..c7fe5d98c 100644 --- a/Tests/large_memory_test.py +++ b/Tests/large_memory_test.py @@ -7,7 +7,7 @@ from helper import unittest, PillowTestCase # It requires > 2gb memory for the >2 gigapixel image generated in the # second test. Running this automatically would amount to a denial of # service on our testing infrastructure. I expect this test to fail -# on any 32 bit machine, as well as any smallish things (like +# on any 32-bit machine, as well as any smallish things (like # Raspberry Pis). It does succeed on a 3gb Ubuntu 12.04x64 VM on Python # 2.7 an 3.2. @@ -16,7 +16,7 @@ YDIM = 32769 XDIM = 48000 -@unittest.skipIf(sys.maxsize <= 2**32, "requires 64 bit system") +@unittest.skipIf(sys.maxsize <= 2**32, "requires 64-bit system") class LargeMemoryTest(PillowTestCase): def _write_png(self, xdim, ydim): diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 4cf83534d..719888619 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -169,7 +169,7 @@ class TestFileTiff(PillowTestCase): self.assert_image_equal(im, im2) def test_32bit_float(self): - # Issue 614, specific 32 bit float format + # Issue 614, specific 32-bit float format path = 'Tests/images/10ct_32bit_128.tiff' im = Image.open(path) im.load() diff --git a/libImaging/Resample.c b/libImaging/Resample.c index 597fca3e9..103e07e4b 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -79,7 +79,7 @@ static inline UINT8 clip8(float in) } -/* This is work around bug in GCC prior 4.9 in 64 bit mode. +/* This is work around bug in GCC prior 4.9 in 64-bit mode. GCC generates code with partial dependency which 3 times slower. See: http://stackoverflow.com/a/26588074/253146 */ #if defined(__x86_64__) && defined(__SSE__) && ! defined(__NO_INLINE__) && \ diff --git a/setup.py b/setup.py index 0029eb5c1..101fecd5e 100644 --- a/setup.py +++ b/setup.py @@ -243,7 +243,7 @@ class pil_build_ext(build_ext): elif sys.platform.startswith("linux"): arch_tp = (plat.processor(), plat.architecture()[0]) if arch_tp == ("x86_64", "32bit"): - # 32 bit build on 64 bit machine. + # 32-bit build on 64-bit machine. _add_directory(library_dirs, "/usr/lib/i386-linux-gnu") else: for platform_ in arch_tp: diff --git a/winbuild/README.md b/winbuild/README.md index 39fab34d5..9777b1e6c 100644 --- a/winbuild/README.md +++ b/winbuild/README.md @@ -6,7 +6,7 @@ For more extensive info, see the windows build instructions `docs/build.rst`. * See https://github.com/python-pillow/Pillow/issues/553#issuecomment-37877416 and https://github.com/matplotlib/matplotlib/issues/1717#issuecomment-13343859 * Works best with Python 3.4, due to virtualenv and pip batteries included. Python3+ required for fetch command. -* Check config.py for virtual env paths, suffix for 64 bit releases. Defaults to `x64`, set `X64_EXT` to change. +* Check config.py for virtual env paths, suffix for 64-bit releases. Defaults to `x64`, set `X64_EXT` to change. * When running in CI with one Python per invocation, set the `PYTHON` env variable to the Python folder. (e.g. `PYTHON`=`c:\Python27\`) This overrides the matrix in config.py and will just build and test for the specific Python. * `python get_pythons.py` downloads all the Python releases, and their signatures. (Manually) Install in `c:\PythonXX[x64]\`. * `python build_dep.py` downloads and creates a build script for all the dependencies, in 32 and 64 bit versions, and with both compiler versions. From baea664477d97aecc802b5ba46fed55e4524dd91 Mon Sep 17 00:00:00 2001 From: hugovk Date: Fri, 16 Oct 2015 18:12:22 +0300 Subject: [PATCH 0712/1037] Remove _imagingtiff.c stuff --- setup.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/setup.py b/setup.py index 101fecd5e..e05207c0d 100644 --- a/setup.py +++ b/setup.py @@ -554,10 +554,6 @@ class pil_build_ext(build_ext): exts.append(Extension( "PIL._imagingft", ["_imagingft.c"], libraries=["freetype"])) - if os.path.isfile("_imagingtiff.c") and feature.tiff: - exts.append(Extension( - "PIL._imagingtiff", ["_imagingtiff.c"], libraries=["tiff"])) - if os.path.isfile("_imagingcms.c") and feature.lcms: extra = [] if sys.platform == "win32": From 7f83226e368723ef6dbd59af2b2f11bece1e6607 Mon Sep 17 00:00:00 2001 From: hugovk Date: Fri, 16 Oct 2015 19:48:01 +0300 Subject: [PATCH 0713/1037] Remove old, closed-source PIL Plus extensions --- _imaging.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/_imaging.c b/_imaging.c index 8d7e01685..3a305885c 100644 --- a/_imaging.c +++ b/_imaging.c @@ -98,9 +98,6 @@ #define WITH_DEBUG /* extra debugging interfaces */ -/* PIL Plus extensions */ -#undef WITH_CRACKCODE /* pil plus */ - #undef VERBOSE #define CLIP(x) ((x) <= 0 ? 0 : (x) < 256 ? (x) : 255) @@ -370,7 +367,7 @@ getlist(PyObject* arg, int* length, const char* wrong_length, int type) void* list; PyObject* seq; PyObject* op; - + if (!PySequence_Check(arg)) { PyErr_SetString(PyExc_TypeError, must_be_sequence); return NULL; @@ -392,7 +389,7 @@ getlist(PyObject* arg, int* length, const char* wrong_length, int type) PyErr_SetString(PyExc_TypeError, must_be_sequence); return NULL; } - + for (i = 0; i < n; i++) { op = PySequence_Fast_GET_ITEM(seq, i); // DRY, branch prediction is going to work _really_ well @@ -3030,9 +3027,6 @@ static struct PyMethodDef methods[] = { {"convert_transparent", (PyCFunction)_convert_transparent, 1}, {"copy", (PyCFunction)_copy, 1}, {"copy2", (PyCFunction)_copy2, 1}, -#ifdef WITH_CRACKCODE - {"crackcode", (PyCFunction)_crackcode, 1}, -#endif {"crop", (PyCFunction)_crop, 1}, {"expand", (PyCFunction)_expand_image, 1}, {"filter", (PyCFunction)_filter, 1}, From 27ced010226c021702b4e56baa87a3a571f0da67 Mon Sep 17 00:00:00 2001 From: Chris Brackert Date: Thu, 22 Oct 2015 16:48:03 -0700 Subject: [PATCH 0714/1037] Fix Exception when installing jpeg --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 101fecd5e..0397c5526 100644 --- a/setup.py +++ b/setup.py @@ -506,9 +506,9 @@ class pil_build_ext(build_ext): for f in feature: if not getattr(feature, f) and feature.require(f): - if feature in ('jpeg', 'libz'): + if f in ('jpeg', 'libz'): raise ValueError('%s is required unless explicitly disabled' - + ' using --disable-%s, aborting' % + ' using --disable-%s, aborting' % (f, f)) raise ValueError( '--enable-%s requested but %s not found, aborting.' From 1574e235f433ec76f5c565920250f254a6641dc1 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sat, 24 Oct 2015 14:16:55 +0100 Subject: [PATCH 0715/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index d86b14576..3a9372c64 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Fix Exception when requiring jpeg #1501 + [hansmosh] + - Dependency scripts for Debian and Ubuntu #1486 [wiredfool] From bba30f736b6551774a5fcfa58784723a41ccd23d Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sat, 24 Oct 2015 14:23:24 +0100 Subject: [PATCH 0716/1037] Removed more closed source PIL plus --- PIL/ImageGrab.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/PIL/ImageGrab.py b/PIL/ImageGrab.py index c52189145..c1306d611 100644 --- a/PIL/ImageGrab.py +++ b/PIL/ImageGrab.py @@ -22,13 +22,7 @@ if sys.platform not in ["win32", "darwin"]: raise ImportError("ImageGrab is OS X and Windows only") if sys.platform == "win32": - try: - # built-in driver (1.1.3 and later) - grabber = Image.core.grabscreen - except AttributeError: - # stand-alone driver (pil plus) - import _grabscreen - grabber = _grabscreen.grab + grabber = Image.core.grabscreen elif sys.platform == "darwin": import os import tempfile From 8019bc75effe2f5bbaeca8ef7f7fbebd9e1867e5 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sat, 24 Oct 2015 17:45:05 +0100 Subject: [PATCH 0717/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 3a9372c64..365f790f3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Remove old _imagingtiff.c and pilplus stuff #1499 + [hugovk] + - Fix Exception when requiring jpeg #1501 [hansmosh] From 5703789036f10e3f5323d9bff66aed50b1d61904 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 26 Oct 2015 19:33:02 +1100 Subject: [PATCH 0718/1037] Updated ImageGrab description --- PIL/ImageGrab.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/ImageGrab.py b/PIL/ImageGrab.py index c1306d611..febdb2310 100644 --- a/PIL/ImageGrab.py +++ b/PIL/ImageGrab.py @@ -2,7 +2,7 @@ # The Python Imaging Library # $Id$ # -# screen grabber (windows only) +# screen grabber (OS X and Windows only) # # History: # 2001-04-26 fl created From 0da6943472dd6056e6441928adf4dccfd8051e4c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 26 Oct 2015 19:38:46 +1100 Subject: [PATCH 0719/1037] Removed outdated Last Updated notes [ci skip] --- Tk/README.rst | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Tk/README.rst b/Tk/README.rst index dc53787f0..61385effb 100644 --- a/Tk/README.rst +++ b/Tk/README.rst @@ -143,9 +143,6 @@ function:: errPtr = errLinePtr; destBytePtr = dstLinePtr; -last updated: 97-07-03/fl - - The PIL Bitmap Booster Patch ==================================================================== @@ -285,6 +282,4 @@ Imaging.h instead of copying the struct declaration...):: /* ==================================================================== */ -3. Recompile Tk and relink the _tkinter module (where necessary). - -Last updated: 97-05-17/fl +3. Recompile Tk and relink the _tkinter module (where necessary). \ No newline at end of file From 91e6a98e6c80d97706652cd61e6f214f049aa74d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 26 Oct 2015 20:01:59 +1100 Subject: [PATCH 0720/1037] Corrected outdated URL [ci skip] --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 365f790f3..8c8e53191 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1278,7 +1278,7 @@ Pre-fork This section may not be fully complete. For changes since this file was last updated, see the repository revision history: - http://bitbucket.org/effbot/pil-2009-raclette/changesets/ + https://bitbucket.org/effbot/pil-2009-raclette/commits/all (1.1.7 final) From 672dce15fb3a923b4e8195707c22c534f012cced Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 26 Oct 2015 21:57:28 +1100 Subject: [PATCH 0721/1037] Changed CONTRIBUTING documentation [ci skip] --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 40dd9119e..6d5868e15 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,7 +17,7 @@ Please send a pull request to the master branch. Please include [documentation]( - Separate code commits from reformatting commits. - Provide tests for any newly added code. - Follow PEP8. -- When committing only trivial changes please include [ci skip] in the commit message to avoid running tests on Travis-CI. +- When committing only documentation changes please include [ci skip] in the commit message to avoid running tests on Travis-CI. ## Reporting Issues From 5e2ad14d4a52b65635320bafdcdf4296b54cba29 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 27 Oct 2015 14:46:39 +0000 Subject: [PATCH 0722/1037] Moving the depends repo to the organization --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index b603473a1..5ed603462 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,7 +12,7 @@ environment: - PYTHON: C:/Python33 - PYTHON: C:/Python33-x64 install: -- git clone https://github.com/wiredfool/pillow-depends.git c:\pillow-depends +- git clone https://github.com/python-pillow/pillow-depends.git c:\pillow-depends - xcopy c:\pillow-depends\*.zip c:\pillow\winbuild\ - xcopy c:\pillow-depends\*.tar.gz c:\pillow\winbuild\ - cd c:\pillow\winbuild\ From b687e1d1b579a270afd4f568526b2118adbcf64c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 30 Oct 2015 19:00:36 +1100 Subject: [PATCH 0723/1037] Updated WebP to 0.4.4 --- depends/install_webp.sh | 10 +++++----- winbuild/config.py | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/depends/install_webp.sh b/depends/install_webp.sh index 97edf2bcf..8df7c7aff 100755 --- a/depends/install_webp.sh +++ b/depends/install_webp.sh @@ -1,14 +1,14 @@ #!/bin/bash # install webp -if [ ! -f libwebp-0.4.3.tar.gz ]; then - wget 'http://downloads.webmproject.org/releases/webp/libwebp-0.4.3.tar.gz' +if [ ! -f libwebp-0.4.4.tar.gz ]; then + wget 'http://downloads.webmproject.org/releases/webp/libwebp-0.4.4.tar.gz' fi -rm -r libwebp-0.4.3 -tar -xvzf libwebp-0.4.3.tar.gz +rm -r libwebp-0.4.4 +tar -xvzf libwebp-0.4.4.tar.gz -pushd libwebp-0.4.3 +pushd libwebp-0.4.4 ./configure --prefix=/usr --enable-libwebpmux --enable-libwebpdemux && make -j4 && sudo make -j4 install diff --git a/winbuild/config.py b/winbuild/config.py index 9b3bdcfb3..37ffc8c1e 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -60,9 +60,9 @@ libs = { 'version': '8.6.4', }, 'webp': { - 'url': 'http://downloads.webmproject.org/releases/webp/libwebp-0.4.3.tar.gz', - 'hash': 'sha1:1c307a61c4d0018620b4ba9a58e8f48a8d6640ef', - 'dir': 'libwebp-0.4.3', + 'url': 'http://downloads.webmproject.org/releases/webp/libwebp-0.4.4.tar.gz', + 'hash': 'sha1:b8762e330b6cd7e7b4a5a6dbfc53bc787a4f0ba3', + 'dir': 'libwebp-0.4.4', }, 'openjpeg': { 'url': SF_MIRROR+'/project/openjpeg/openjpeg/2.1.0/openjpeg-2.1.0.tar.gz', From 0ff05afa56a10d62c02ca947cf6c65db41c27cf3 Mon Sep 17 00:00:00 2001 From: mattip Date: Mon, 2 Nov 2015 20:07:12 +0200 Subject: [PATCH 0724/1037] test, fix missing 'version' key value in __array_interface__ --- PIL/Image.py | 1 + Tests/test_image_array.py | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index fc028f71e..caa66ea0d 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -622,6 +622,7 @@ class Image(object): new['shape'] = shape new['typestr'] = typestr new['data'] = self.tobytes() + new['version'] = 3 return new raise AttributeError(name) diff --git a/Tests/test_image_array.py b/Tests/test_image_array.py index 0899afd02..ce8cbe78c 100644 --- a/Tests/test_image_array.py +++ b/Tests/test_image_array.py @@ -10,18 +10,18 @@ class TestImageArray(PillowTestCase): def test_toarray(self): def test(mode): ai = im.convert(mode).__array_interface__ - return ai["shape"], ai["typestr"], len(ai["data"]) - # self.assertEqual(test("1"), ((100, 128), '|b1', 1600)) - self.assertEqual(test("L"), ((100, 128), '|u1', 12800)) + return ai['version'], ai["shape"], ai["typestr"], len(ai["data"]) + # self.assertEqual(test("1"), (3, (100, 128), '|b1', 1600)) + self.assertEqual(test("L"), (3, (100, 128), '|u1', 12800)) # FIXME: wrong? - self.assertEqual(test("I"), ((100, 128), Image._ENDIAN + 'i4', 51200)) + self.assertEqual(test("I"), (3, (100, 128), Image._ENDIAN + 'i4', 51200)) # FIXME: wrong? - self.assertEqual(test("F"), ((100, 128), Image._ENDIAN + 'f4', 51200)) + self.assertEqual(test("F"), (3, (100, 128), Image._ENDIAN + 'f4', 51200)) - self.assertEqual(test("RGB"), ((100, 128, 3), '|u1', 38400)) - self.assertEqual(test("RGBA"), ((100, 128, 4), '|u1', 51200)) - self.assertEqual(test("RGBX"), ((100, 128, 4), '|u1', 51200)) + self.assertEqual(test("RGB"), (3, (100, 128, 3), '|u1', 38400)) + self.assertEqual(test("RGBA"), (3, (100, 128, 4), '|u1', 51200)) + self.assertEqual(test("RGBX"), (3, (100, 128, 4), '|u1', 51200)) def test_fromarray(self): def test(mode): From 7f95b44350dbfed97e99216f94b02d711d0901e5 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 3 Nov 2015 23:16:12 +0200 Subject: [PATCH 0725/1037] Shebang it Fixes #1521 --- Scripts/createfontdatachunk.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Scripts/createfontdatachunk.py b/Scripts/createfontdatachunk.py index b9514eb6e..b9adedc33 100644 --- a/Scripts/createfontdatachunk.py +++ b/Scripts/createfontdatachunk.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python from __future__ import print_function import base64 import os From 7d8d9e3b9e04dadea90811a52fe30d9a344af2b5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 4 Nov 2015 20:03:19 +1100 Subject: [PATCH 0726/1037] Replaced os.popen with subprocess.Popen --- Scripts/pilprint.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) mode change 100644 => 100755 Scripts/pilprint.py diff --git a/Scripts/pilprint.py b/Scripts/pilprint.py old mode 100644 new mode 100755 index 2a5e23061..e81a075a0 --- a/Scripts/pilprint.py +++ b/Scripts/pilprint.py @@ -15,6 +15,7 @@ from __future__ import print_function import getopt import os import sys +import subprocess VERSION = "pilprint 0.3/2003-05-05" @@ -46,8 +47,8 @@ except getopt.error as v: print(v) sys.exit(1) -printer = None # print to stdout -monochrome = 1 # reduce file size for most common case +printerArgs = [] # print to stdout +monochrome = 1 # reduce file size for most common case for o, a in opt: if o == "-d": @@ -60,10 +61,10 @@ for o, a in opt: monochrome = 0 elif o == "-p": # default printer channel - printer = "lpr" + printerArgs = ["lpr"] elif o == "-P": # printer channel - printer = "lpr -P%s" % a + printerArgs = ["lpr","-P%s" % a] for filepath in argv: try: @@ -76,8 +77,9 @@ for filepath in argv: im.draft("L", im.size) im = im.convert("L") - if printer: - fp = os.popen(printer, "w") + if printerArgs: + p = subprocess.Popen(printerArgs, stdin=subprocess.PIPE) + fp = p.stdin else: fp = sys.stdout @@ -91,6 +93,9 @@ for filepath in argv: ps.image(letter, im) ps.end_document() + if printerArgs: + fp.close() + except: print("cannot print image", end=' ') print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1])) From cfaf95a5a44f21a1d30d84316bd74094c0b3494f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 9 Nov 2015 23:34:34 +1100 Subject: [PATCH 0727/1037] Commented or removed unused lines --- PIL/BdfFontFile.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/PIL/BdfFontFile.py b/PIL/BdfFontFile.py index 0c1614e0f..e6cc22f91 100644 --- a/PIL/BdfFontFile.py +++ b/PIL/BdfFontFile.py @@ -109,10 +109,10 @@ class BdfFontFile(FontFile.FontFile): if s.find(b"LogicalFontDescription") < 0: comments.append(s[i+1:-1].decode('ascii')) - font = props["FONT"].split("-") + # font = props["FONT"].split("-") - font[4] = bdf_slant[font[4].upper()] - font[11] = bdf_spacing[font[11].upper()] + # font[4] = bdf_slant[font[4].upper()] + # font[11] = bdf_spacing[font[11].upper()] # ascent = int(props["FONT_ASCENT"]) # descent = int(props["FONT_DESCENT"]) @@ -123,7 +123,6 @@ class BdfFontFile(FontFile.FontFile): # for i in comments: # print "#", i - font = [] while True: c = bdf_char(fp) if not c: From 5cb68aa8fecd278d50b986d87d273484c04aed5f Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 9 Nov 2015 15:41:53 +0200 Subject: [PATCH 0728/1037] Fix code formatting [CI skip] --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index 860c6e798..599599312 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -190,7 +190,7 @@ Building on OS X Xcode is required to compile portions of Pillow. Either install the full package from the app store, or run ``xcode-select --install`` -from the command line. It may be necessary to run `` sudo xcodebuild +from the command line. 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 From 03eadad5effca436a4453ed8130d56b9c6e07c0f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 11 Nov 2015 23:25:55 +1100 Subject: [PATCH 0729/1037] Removed shortlink --- Tests/test_numpy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py index 7303a949b..64081894e 100644 --- a/Tests/test_numpy.py +++ b/Tests/test_numpy.py @@ -67,7 +67,7 @@ class TestNumpy(PillowTestCase): self.assert_image(to_image(numpy.uint8, 3), "RGB", (10, 10)) self.assert_image(to_image(numpy.uint8, 4), "RGBA", (10, 10)) - # based on an erring example at http://is.gd/6F0esS (which resolves to) + # based on an erring example at # http://stackoverflow.com/questions/10854903/what-is-causing-dimension-dependent-attributeerror-in-pil-fromarray-function def test_3d_array(self): a = numpy.ones((10, 10, 10), dtype=numpy.uint8) From 529d680aeefe06ec8e7207971ea06db35e7a89fb Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 11 Nov 2015 23:42:19 +1100 Subject: [PATCH 0730/1037] Removed comment --- Tests/test_image_point.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Tests/test_image_point.py b/Tests/test_image_point.py index 9e001dfb2..d60364c08 100644 --- a/Tests/test_image_point.py +++ b/Tests/test_image_point.py @@ -22,10 +22,6 @@ class TestImagePoint(PillowTestCase): """ Tests for 16 bit -> 8 bit lut for converting I->L images see https://github.com/python-pillow/Pillow/issues/440 """ - # This takes _forever_ on PyPy. Open Bug, - # see https://github.com/python-pillow/Pillow/issues/484 - # self.skipKnownBadTest(msg="Too Slow on pypy", interpreter='pypy') - im = hopper("I") im.point(list(range(256))*256, 'L') From a14467f10bfc269511f4bffcf6b64fbab94ac959 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 15 Nov 2015 15:49:30 +0000 Subject: [PATCH 0731/1037] Iccprofile doesn't have a defined length --- PIL/TiffTags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py index af6d1b386..5af739742 100644 --- a/PIL/TiffTags.py +++ b/PIL/TiffTags.py @@ -133,7 +133,7 @@ TAGS_V2 = { # FIXME add more tags here 34665: ("ExifIFD", 3, 1), - 34675: ('ICCProfile', 7, 1), + 34675: ('ICCProfile', 7, 0), # MPInfo 45056: ("MPFVersion", 7, 1), From f8a5ded0d6104855384429ec9ca4f82feaad3187 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 15 Nov 2015 16:41:49 +0000 Subject: [PATCH 0732/1037] Test for #1526, load binary tagged iccprofile --- Tests/test_file_tiff_metadata.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 243d1a4f3..e7a6f9e87 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -158,6 +158,14 @@ class TestFileTiffMetadata(PillowTestCase): self.assert_(type(im.info['icc_profile']) is not type(tuple)) self.assertEqual(im.info['icc_profile'], reloaded.info['icc_profile']) + def test_iccprofile_binary(self): + # https://github.com/python-pillow/Pillow/issues/1526 + # We should be able to load this, but probably won't be able to save it. + + im = Image.open('Tests/images/hopper.iccprofile_binary.tif') + self.assertEqual(im.tag_v2.tagtype[34675], 1) + self.assert_(im.info['icc_profile']) + if __name__ == '__main__': From 4883c4d2026ba8b477fc1a7da653b2279b46dbe4 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 15 Nov 2015 16:43:16 +0000 Subject: [PATCH 0733/1037] image for test --- Tests/images/hopper.iccprofile_binary.tif | Bin 0 -> 52500 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Tests/images/hopper.iccprofile_binary.tif diff --git a/Tests/images/hopper.iccprofile_binary.tif b/Tests/images/hopper.iccprofile_binary.tif new file mode 100644 index 0000000000000000000000000000000000000000..c10e4f83696177f61cf5b8a63409b3de58b8750a GIT binary patch literal 52500 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 Date: Mon, 16 Nov 2015 10:24:16 +0200 Subject: [PATCH 0734/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 8c8e53191..1880c9a49 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Fix the definition of icc_profile in TiffTags #1539 + [wiredfool] + - Remove old _imagingtiff.c and pilplus stuff #1499 [hugovk] From 4d7b630b33c58e215ea9938bb1df26d745f73523 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 18 Nov 2015 01:18:01 +1100 Subject: [PATCH 0735/1037] Catch OverflowError in SpiderImagePlugin --- PIL/SpiderImagePlugin.py | 2 ++ Tests/images/invalid.spider | Bin 0 -> 167 bytes Tests/test_file_spider.py | 5 +++++ 3 files changed, 7 insertions(+) create mode 100644 Tests/images/invalid.spider diff --git a/PIL/SpiderImagePlugin.py b/PIL/SpiderImagePlugin.py index 6344a1540..0ce63002b 100644 --- a/PIL/SpiderImagePlugin.py +++ b/PIL/SpiderImagePlugin.py @@ -50,6 +50,8 @@ def isInt(f): return 0 except ValueError: return 0 + except OverflowError: + return 0 iforms = [1, 3, -11, -12, -21, -22] diff --git a/Tests/images/invalid.spider b/Tests/images/invalid.spider new file mode 100644 index 0000000000000000000000000000000000000000..c6b024d6ebe7a46afc35468a71577345dbbd34bb GIT binary patch literal 167 zcmez0z`(#D#KjCG=ZB=H04Y%*cKHAQ$$u~aa%TQNlXm8e@ys-ajFofzf*DquPv0H^ zl1%b;cVXyYRr3HU;w Date: Wed, 18 Nov 2015 09:32:42 +0000 Subject: [PATCH 0736/1037] Updated Changes.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 1880c9a49..5df810f6f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Catch OverflowError in SpiderImagePlugin #1545 + [radarhere, MrShark] + - Fix the definition of icc_profile in TiffTags #1539 [wiredfool] From ea2982f284df3338e712b2e588052b3d53bb912f Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 18 Nov 2015 09:44:09 +0000 Subject: [PATCH 0737/1037] Updated Changes.rst [ci skip] --- CHANGES.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 5df810f6f..8f04fa4d7 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,15 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Updated WebP to 0.4.4 for Travis #1515 + [radarhere] + +- Fix missing 'version' key value in __array_interface__ #1519 + [mattip] + +- Replaced os.popen with subprocess.Popen to pilprint script #1523 + [radarhere] + - Catch OverflowError in SpiderImagePlugin #1545 [radarhere, MrShark] From a8424977530e45942bb07c852563a5d4a758deac Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 18 Nov 2015 07:02:16 -0800 Subject: [PATCH 0738/1037] consistent line spacing --- PIL/ImageDraw.py | 10 +++++----- _imagingft.c | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py index 9e154f236..161cce701 100644 --- a/PIL/ImageDraw.py +++ b/PIL/ImageDraw.py @@ -268,14 +268,14 @@ class ImageDraw(object): def multiline_text(self, xy, text, fill=None, font=None, anchor=None, spacing=0, align="left"): - widths, heights = [], [] + widths = [] max_width = 0 lines = self._multiline_split(text) + line_spacing = self.textsize('A', font=font)[1] + spacing for line in lines: line_width, line_height = self.textsize(line, font) widths.append(line_width) max_width = max(max_width, line_width) - heights.append(line_height) left, top = xy for idx, line in enumerate(lines): if align == "left": @@ -287,7 +287,7 @@ class ImageDraw(object): else: assert False, 'align must be "left", "center" or "right"' self.text((left, top), line, fill, font, anchor) - top += heights[idx] + spacing + top += line_spacing left = xy[0] ## @@ -305,11 +305,11 @@ class ImageDraw(object): max_width = 0 height = 0 lines = self._multiline_split(text) + line_spacing = self.textsize('A', font=font)[1] + spacing for line in lines: line_width, line_height = self.textsize(line, font) - height += line_height + spacing max_width = max(max_width, line_width) - return max_width, height + return max_width, len(lines)*line_spacing ## diff --git a/_imagingft.c b/_imagingft.c index a34fb8af9..dc1661f3e 100644 --- a/_imagingft.c +++ b/_imagingft.c @@ -492,6 +492,25 @@ font_getattr_descent(FontObject* self, void* closure) return PyInt_FromLong(-PIXEL(self->face->size->metrics.descender)); } +static PyObject* +font_getattr_height(FontObject* self, void* closure) +{ + return PyInt_FromLong(PIXEL(self->face->size->metrics.height)); +} + +static PyObject* +font_getattr_x_ppem(FontObject* self, void* closure) +{ + return PyInt_FromLong(self->face->size->metrics.x_ppem); +} + +static PyObject* +font_getattr_y_ppem(FontObject* self, void* closure) +{ + return PyInt_FromLong(self->face->size->metrics.y_ppem); +} + + static PyObject* font_getattr_glyphs(FontObject* self, void* closure) { @@ -503,6 +522,9 @@ static struct PyGetSetDef font_getsetters[] = { { "style", (getter) font_getattr_style }, { "ascent", (getter) font_getattr_ascent }, { "descent", (getter) font_getattr_descent }, + { "height", (getter) font_getattr_height }, + { "x_ppem", (getter) font_getattr_x_ppem }, + { "y_ppem", (getter) font_getattr_y_ppem }, { "glyphs", (getter) font_getattr_glyphs }, { NULL } }; From 8fd37070afe29084f9bcb5f25728eebc4ccce026 Mon Sep 17 00:00:00 2001 From: Christoph Gohlke Date: Mon, 23 Nov 2015 16:42:40 -0800 Subject: [PATCH 0739/1037] Fix crash in ImageTk.PhotoImage on win-amd64 --- Tk/tkImaging.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Tk/tkImaging.c b/Tk/tkImaging.c index 494349a8e..f82fc2007 100644 --- a/Tk/tkImaging.c +++ b/Tk/tkImaging.c @@ -60,7 +60,11 @@ ImagingFind(const char* name) Py_ssize_t id; /* FIXME: use CObject instead? */ +#if defined(_MSC_VER) && defined(_WIN64) + id = _atoi64(name); +#else id = atol(name); +#endif if (!id) return NULL; From 423e381b7cec539118b9a27c787477db31759cf9 Mon Sep 17 00:00:00 2001 From: John Calsbeek Date: Wed, 25 Nov 2015 20:34:44 -0800 Subject: [PATCH 0740/1037] ExtraSamples tag should be a SHORT, not a BYTE Affects saving RGBA TIFFs. --- PIL/TiffTags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py index 5af739742..2eb06d315 100644 --- a/PIL/TiffTags.py +++ b/PIL/TiffTags.py @@ -106,7 +106,7 @@ TAGS_V2 = { 334: ("NumberOfInks", 3, 1), 336: ("DotRange", 3, 0), 337: ("TargetPrinter", 2, 1), - 338: ("ExtraSamples", 1, 0), + 338: ("ExtraSamples", 3, 0), 339: ("SampleFormat", 3, 0), 340: ("SMinSampleValue", 12, 0), From 0c3e2245377f7a5be21628176540880ab5fb1306 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 26 Nov 2015 19:56:41 +1100 Subject: [PATCH 0741/1037] Throw TypeError if no cursors were found in .cur file --- PIL/CurImagePlugin.py | 2 ++ Tests/images/no_cursors.cur | Bin 0 -> 6 bytes Tests/test_file_cur.py | 15 +++++++++------ 3 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 Tests/images/no_cursors.cur diff --git a/PIL/CurImagePlugin.py b/PIL/CurImagePlugin.py index 3825e098b..4db4c4073 100644 --- a/PIL/CurImagePlugin.py +++ b/PIL/CurImagePlugin.py @@ -66,6 +66,8 @@ class CurImageFile(BmpImagePlugin.BmpImageFile): # print "hotspot y", i16(s[6:]) # print "bytes", i32(s[8:]) # print "offset", i32(s[12:]) + if not m: + raise TypeError("No cursors were found") # load as bitmap self._bitmap(i32(m[12:]) + offset) diff --git a/Tests/images/no_cursors.cur b/Tests/images/no_cursors.cur new file mode 100644 index 0000000000000000000000000000000000000000..a98e1035a778f4c7e319491a5c9fdb25ca8272ab GIT binary patch literal 6 NcmZQzU}9ik0000E00RI3 literal 0 HcmV?d00001 diff --git a/Tests/test_file_cur.py b/Tests/test_file_cur.py index fa4242629..f1c077945 100644 --- a/Tests/test_file_cur.py +++ b/Tests/test_file_cur.py @@ -2,17 +2,14 @@ from helper import unittest, PillowTestCase from PIL import Image, CurImagePlugin +TEST_FILE = "Tests/images/deerstalker.cur" + class TestFileCur(PillowTestCase): def test_sanity(self): - # Arrange - test_file = "Tests/images/deerstalker.cur" + im = Image.open(TEST_FILE) - # Act - im = Image.open(test_file) - - # Assert self.assertEqual(im.size, (32, 32)) self.assertIsInstance(im, CurImagePlugin.CurImageFile) # Check some pixel colors to ensure image is loaded properly @@ -26,6 +23,12 @@ class TestFileCur(PillowTestCase): self.assertRaises(SyntaxError, lambda: CurImagePlugin.CurImageFile(invalid_file)) + no_cursors_file = "Tests/images/no_cursors.cur" + + cur = CurImagePlugin.CurImageFile(TEST_FILE) + cur.fp = open(no_cursors_file, "rb") + self.assertRaises(TypeError, cur._open) + if __name__ == '__main__': unittest.main() From 2d0b6987a0e8ef37bf7b7cae426b85d872eefbed Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 30 Nov 2015 22:06:18 +1100 Subject: [PATCH 0742/1037] Changed ImageQt import of Image --- PIL/ImageQt.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/PIL/ImageQt.py b/PIL/ImageQt.py index b37177aff..a67dde8ee 100644 --- a/PIL/ImageQt.py +++ b/PIL/ImageQt.py @@ -16,7 +16,7 @@ # See the README file for information on usage and redistribution. # -import PIL +from PIL import Image from PIL._util import isPath from io import BytesIO @@ -69,7 +69,7 @@ def fromqimage(im): buffer.close() b.seek(0) - return PIL.Image.open(b) + return Image.open(b) def fromqpixmap(im): @@ -83,7 +83,7 @@ def fromqpixmap(im): # bytes_io.write(buffer.data()) # buffer.close() # bytes_io.seek(0) - # return PIL.Image.open(bytes_io) + # return Image.open(bytes_io) def align8to32(bytes, width, mode): """ @@ -125,7 +125,7 @@ def _toqclass_helper(im): else: im = str(im.toUtf8(), "utf-8") if isPath(im): - im = PIL.Image.open(im) + im = Image.open(im) if im.mode == "1": format = QImage.Format_Mono @@ -149,7 +149,7 @@ def _toqclass_helper(im): except SystemError: # workaround for earlier versions r, g, b, a = im.split() - im = PIL.Image.merge("RGBA", (b, g, r, a)) + im = Image.merge("RGBA", (b, g, r, a)) format = QImage.Format_ARGB32 else: raise ValueError("unsupported image mode %r" % im.mode) From b496f37e1f09c452b4db2bb52cd3eb9d8ca36801 Mon Sep 17 00:00:00 2001 From: John Calsbeek Date: Tue, 1 Dec 2015 09:14:32 -0800 Subject: [PATCH 0743/1037] Add test for #1524. --- Tests/test_file_tiff.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 719888619..926aa5602 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -115,6 +115,11 @@ class TestFileTiff(PillowTestCase): self.fail( "Bad EXIF data passed incorrect values to _binary unpack") + def test_save_rgba(self): + im = hopper("RGBA") + outfile = self.tempfile("temp.tif") + im.save(outfile) + def test_save_unsupported_mode(self): im = hopper("HSV") outfile = self.tempfile("temp.tif") From 8440696bc517b68810660b28d702d4c11232d816 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Dec 2015 15:07:09 +1100 Subject: [PATCH 0744/1037] Updated freetype to 2.6.2 --- winbuild/config.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winbuild/config.py b/winbuild/config.py index 37ffc8c1e..dd16e31bc 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -28,9 +28,9 @@ libs = { 'dir': 'tiff-4.0.6', }, 'freetype': { - 'url': 'http://download.savannah.gnu.org/releases/freetype/freetype-2.6.1.tar.gz', - 'hash': 'md5:348e667d728c597360e4a87c16556597', - 'dir': 'freetype-2.6.1', + 'url': 'http://download.savannah.gnu.org/releases/freetype/freetype-2.6.2.tar.gz', + 'hash': 'md5:c408547878f1f5a3700881a8bbf1c644', + 'dir': 'freetype-2.6.2', }, 'lcms': { 'url': SF_MIRROR+'/project/lcms/lcms/2.7/lcms2-2.7.zip', From dcca1284204b79471853acd6dcba6f07d09167df Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Dec 2015 15:40:16 +1100 Subject: [PATCH 0745/1037] Fixed length of title underlines --- docs/build.rst | 2 +- docs/reference/ImageGrab.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/build.rst b/docs/build.rst index c9c7808e5..1110fb792 100644 --- a/docs/build.rst +++ b/docs/build.rst @@ -11,7 +11,7 @@ against 64-bit Windows 7 Professional and Windows Server 2012 64-bit version on Amazon EC2. Prerequisites ------------- +------------- Extra Build Helpers ^^^^^^^^^^^^^^^^^^^ diff --git a/docs/reference/ImageGrab.rst b/docs/reference/ImageGrab.rst index 3d5703a40..ec7c42082 100644 --- a/docs/reference/ImageGrab.rst +++ b/docs/reference/ImageGrab.rst @@ -2,7 +2,7 @@ .. py:currentmodule:: PIL.ImageGrab :py:mod:`ImageGrab` Module (OS X and Windows only) -========================================= +================================================== The :py:mod:`ImageGrab` module can be used to copy the contents of the screen or the clipboard to a PIL image memory. From d32117758966fe61b6a5ea83fd846921dbee4cfd Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Dec 2015 16:09:47 +1100 Subject: [PATCH 0746/1037] Removed undefined variable --- Tests/test_imagewin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_imagewin.py b/Tests/test_imagewin.py index 14cc2c7e3..c4a8b28e4 100644 --- a/Tests/test_imagewin.py +++ b/Tests/test_imagewin.py @@ -114,7 +114,7 @@ class TestImageWinDib(PillowTestCase): # Act/Assert self.assertRaises(Exception, dib.tostring) - self.assertRaises(Exception, lambda: dib.fromstring(test_buffer)) + self.assertRaises(Exception, dib.fromstring) if __name__ == '__main__': From 4ab0cbb5ba2e55a2feac520260511ddfe3198f34 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Dec 2015 16:15:12 +1100 Subject: [PATCH 0747/1037] Removed unused variable --- Tests/test_imagewin_pointers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_imagewin_pointers.py b/Tests/test_imagewin_pointers.py index f8886f55b..2d47014f3 100644 --- a/Tests/test_imagewin_pointers.py +++ b/Tests/test_imagewin_pointers.py @@ -104,7 +104,7 @@ if sys.platform.startswith('win32'): DeleteObject(dib) DeleteDC(hdc) - reloaded = Image.open(BytesIO(bitmap)).save(opath) + Image.open(BytesIO(bitmap)).save(opath) if __name__ == '__main__': unittest.main() From e9a6a96053b9801900c8122e57632f1febf7136e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Dec 2015 16:23:49 +1100 Subject: [PATCH 0748/1037] Specified exception type --- PIL/EpsImagePlugin.py | 2 +- PIL/GifImagePlugin.py | 2 +- PIL/IptcImagePlugin.py | 2 +- PIL/JpegImagePlugin.py | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py index fb5bf7ffe..ca3c169bd 100644 --- a/PIL/EpsImagePlugin.py +++ b/PIL/EpsImagePlugin.py @@ -151,7 +151,7 @@ def Ghostscript(tile, size, fp, scale=1): os.unlink(outfile) if infile_temp: os.unlink(infile_temp) - except: + except OSError: pass return im diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 83169bf21..391ddd332 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -518,7 +518,7 @@ def _save_netpbm(im, fp, filename): try: os.unlink(file) - except: + except OSError: pass diff --git a/PIL/IptcImagePlugin.py b/PIL/IptcImagePlugin.py index b5aa84bad..56d1de424 100644 --- a/PIL/IptcImagePlugin.py +++ b/PIL/IptcImagePlugin.py @@ -179,7 +179,7 @@ class IptcImageFile(ImageFile.ImageFile): finally: try: os.unlink(outfile) - except: + except OSError: pass diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 577951961..8d25ffe66 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -379,7 +379,7 @@ class JpegImageFile(ImageFile.ImageFile): finally: try: os.unlink(path) - except: + except OSError: pass self.mode = self.im.mode @@ -699,7 +699,7 @@ def _save_cjpeg(im, fp, filename): subprocess.check_call(["cjpeg", "-outfile", filename, tempfile]) try: os.unlink(tempfile) - except: + except OSError: pass From 560774e3e0f16c55375a8c37eeea38eb6a83ae3b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Dec 2015 16:29:01 +1100 Subject: [PATCH 0749/1037] Removed unreachable code --- PIL/ImageMorph.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/PIL/ImageMorph.py b/PIL/ImageMorph.py index 44a7e8c04..902ed8db7 100644 --- a/PIL/ImageMorph.py +++ b/PIL/ImageMorph.py @@ -200,7 +200,6 @@ class MorphOp(object): if image.mode != 'L': raise Exception('Image must be binary, meaning it must use mode L') - return outimage = Image.new(image.mode, image.size, None) count = _imagingmorph.apply( bytes(self.lut), image.im.id, outimage.im.id) @@ -217,7 +216,6 @@ class MorphOp(object): if image.mode != 'L': raise Exception('Image must be binary, meaning it must use mode L') - return return _imagingmorph.match(bytes(self.lut), image.im.id) def get_on_pixels(self, image): @@ -228,7 +226,6 @@ class MorphOp(object): if image.mode != 'L': raise Exception('Image must be binary, meaning it must use mode L') - return return _imagingmorph.get_on_pixels(image.im.id) def load_lut(self, filename): From 82c7ea8489b4a6c618cc674fa8f16bd6c36d1719 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Dec 2015 16:32:44 +1100 Subject: [PATCH 0750/1037] Changed string format argument to logger parameter --- PIL/Image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/Image.py b/PIL/Image.py index caa66ea0d..af6dac1c4 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -550,7 +550,7 @@ class Image(object): try: self.fp.close() except Exception as msg: - logger.debug("Error closing: %s" % msg) + logger.debug("Error closing: %s", msg) # Instead of simply setting to None, we're setting up a # deferred error that will better explain that the core image From a9efd7ad98aa1ae0a511b2fb098f2f277745246b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Dec 2015 16:37:50 +1100 Subject: [PATCH 0751/1037] Stopped modules from importing themselves --- PIL/ImageCms.py | 6 ++---- selftest.py | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/PIL/ImageCms.py b/PIL/ImageCms.py index ebf127df3..ba5504acb 100644 --- a/PIL/ImageCms.py +++ b/PIL/ImageCms.py @@ -16,6 +16,7 @@ # below for the original description. from __future__ import print_function +import sys DESCRIPTION = """ pyCMS @@ -240,7 +241,6 @@ def get_display_profile(handle=None): :returns: None if the profile is not known. """ - import sys if sys.platform == "win32": from PIL import ImageWin if isinstance(handle, ImageWin.HDC): @@ -943,7 +943,6 @@ def versions(): (pyCMS) Fetches versions. """ - import sys return ( VERSION, core.littlecms_version, sys.version.split()[0], Image.VERSION @@ -954,10 +953,9 @@ def versions(): if __name__ == "__main__": # create a cheap manual from the __doc__ strings for the functions above - from PIL import ImageCms print(__doc__) - for f in dir(ImageCms): + for f in dir(sys.modules[__name__]): doc = None try: exec("doc = %s.__doc__" % (f)) diff --git a/selftest.py b/selftest.py index 4ffae3a16..71f2354f8 100644 --- a/selftest.py +++ b/selftest.py @@ -206,9 +206,8 @@ if __name__ == "__main__": # use doctest to make sure the test program behaves as documented! import doctest - import selftest print("Running selftest:") - status = doctest.testmod(selftest) + status = doctest.testmod(sys.modules[__name__]) if status[0]: print("*** %s tests of %d failed." % status) exit_status = 1 From 674515d568e6f9b103a8aa3bc61f1e3e4593001b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Dec 2015 16:41:26 +1100 Subject: [PATCH 0752/1037] Changed lines to be below 80 characters --- Scripts/pildriver.py | 9 ++++++--- Tests/check_jpeg_leaks.py | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Scripts/pildriver.py b/Scripts/pildriver.py index 32989ccdf..69c90ad4b 100644 --- a/Scripts/pildriver.py +++ b/Scripts/pildriver.py @@ -152,7 +152,8 @@ class PILDriver(object): self.push(Image.composite(image1, image2, mask)) def do_merge(self): - """usage: merge [ [ []]] + """usage: merge + [ [ []]] Merge top-of stack images in a way described by the mode. """ @@ -181,7 +182,8 @@ class PILDriver(object): self.dup() def do_crop(self): - """usage: crop + """usage: crop + Crop and push a rectangular region from the current image. """ @@ -243,7 +245,8 @@ class PILDriver(object): self.push(image.offset(xoff, yoff)) def do_paste(self): - """usage: paste + """usage: paste + Paste figure image into ground with upper left at given offsets. """ diff --git a/Tests/check_jpeg_leaks.py b/Tests/check_jpeg_leaks.py index 1d05cd975..7df2dfcc4 100644 --- a/Tests/check_jpeg_leaks.py +++ b/Tests/check_jpeg_leaks.py @@ -6,7 +6,8 @@ iterations = 5000 """ -When run on a system without the jpeg leak fixes, the valgrind runs look like this. +When run on a system without the jpeg leak fixes, +the valgrind runs look like this. NOSE_PROCESSES=0 NOSE_TIMEOUT=600 valgrind --tool=massif \ python test-installed.py -s -v Tests/check_jpeg_leaks.py From 407aa77db4f7142e52a512443b13453e849dd9a6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Dec 2015 16:49:30 +1100 Subject: [PATCH 0753/1037] Removed unnecessary lambda --- 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 719888619..a191793e3 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -110,7 +110,7 @@ class TestFileTiff(PillowTestCase): def test_bad_exif(self): i = Image.open('Tests/images/hopper_bad_exif.jpg') try: - self.assert_warning(UserWarning, lambda: i._getexif()) + self.assert_warning(UserWarning, i._getexif) except struct.error: self.fail( "Bad EXIF data passed incorrect values to _binary unpack") From 806df2e7db952a079dee288b4b67230434011d5e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Dec 2015 16:55:50 +1100 Subject: [PATCH 0754/1037] Removed unused argument --- Tests/test_imagesequence.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Tests/test_imagesequence.py b/Tests/test_imagesequence.py index 3bd6dc582..9e18192ee 100644 --- a/Tests/test_imagesequence.py +++ b/Tests/test_imagesequence.py @@ -24,7 +24,7 @@ class TestImageSequence(PillowTestCase): self.assertRaises(AttributeError, lambda: ImageSequence.Iterator(0)) - def _test_multipage_tiff(self, dbg=False): + def _test_multipage_tiff(self): im = Image.open('Tests/images/multipage.tiff') for index, frame in enumerate(ImageSequence.Iterator(im)): frame.load() @@ -32,8 +32,7 @@ class TestImageSequence(PillowTestCase): frame.convert('RGB') def test_tiff(self): - # self._test_multipage_tiff(True) - self._test_multipage_tiff(False) + self._test_multipage_tiff() def test_libtiff(self): codecs = dir(Image.core) @@ -42,8 +41,7 @@ class TestImageSequence(PillowTestCase): self.skipTest("tiff support not available") TiffImagePlugin.READ_LIBTIFF = True - # self._test_multipage_tiff(True) - self._test_multipage_tiff(False) + self._test_multipage_tiff() TiffImagePlugin.READ_LIBTIFF = False if __name__ == '__main__': From 69ed8c39059f181aa56cc7c8160a43f648f485cf Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 2 Dec 2015 10:46:37 +0200 Subject: [PATCH 0755/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 8f04fa4d7..f64b8dc5e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,12 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Docs and code health fixes #1565 #1566 + [radarhere] + +- Updated freetype to 2.6.2 #1564 + [radarhere] + - Updated WebP to 0.4.4 for Travis #1515 [radarhere] @@ -31,9 +37,6 @@ Changelog (Pillow) - Added Usage message to painter script #1482 [radarhere] -- Updated freetype to 2.6.1 #1479 - [radarhere] - - Add tag info for iccprofile, fixes #1462. #1465 [wiredfool] From 1051facdb7d1386be9e6cc42a5f1b227f2cb5dce Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 3 Dec 2015 12:06:04 +1100 Subject: [PATCH 0756/1037] Added Python 3 to Platform Support for El Capitan [ci skip] --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index 599599312..2d26e0429 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -283,7 +283,7 @@ current versions of Linux, OS X, and Windows. +----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ |**Operating system** |**Supported**|**Tested Python versions** |**Latest tested Pillow version**|**Tested processors** | +----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ -| Mac OS X 10.11 El Capitan |Yes | 2.7 | 3.0.0 |x86-64 | +| Mac OS X 10.11 El Capitan |Yes | 2.7,3.3,3.4,3.5 | 3.0.0 |x86-64 | +----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ | Mac OS X 10.10 Yosemite |Yes | 2.7,3.3,3.4 | 3.0.0 |x86-64 | +----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ From d098e30c5598694f91809560c1d95ae026a03590 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 6 Dec 2015 23:15:11 +1100 Subject: [PATCH 0757/1037] Updated OleFileIO version in docs [ci skip] --- docs/reference/OleFileIO.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/OleFileIO.rst b/docs/reference/OleFileIO.rst index b66bfc60d..791cb5ff3 100644 --- a/docs/reference/OleFileIO.rst +++ b/docs/reference/OleFileIO.rst @@ -9,7 +9,7 @@ Structured Storage or Microsoft Compound Document File Format), such as Microsoft Office documents, Image Composer and FlashPix files, and Outlook messages. -This module is the `OleFileIO\_PL`_ project by Philippe Lagadec, v0.30, +This module is the `OleFileIO\_PL`_ project by Philippe Lagadec, v0.42, merged back into Pillow. .. _OleFileIO\_PL: http://www.decalage.info/python/olefileio From c82b54b8962db04f75be7f110400125ec639e536 Mon Sep 17 00:00:00 2001 From: hugovk Date: Sun, 6 Dec 2015 15:57:19 +0200 Subject: [PATCH 0758/1037] Update tests --- Tests/images/multiline_text.png | Bin 2843 -> 2785 bytes Tests/images/multiline_text_center.png | Bin 2845 -> 2784 bytes Tests/images/multiline_text_right.png | Bin 2846 -> 2788 bytes Tests/images/multiline_text_spacing.png | Bin 2844 -> 2840 bytes Tests/test_imagefont.py | 2 +- 5 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/images/multiline_text.png b/Tests/images/multiline_text.png index ff1308c5ef277048d26abec36dd76610e8fbf3fe..c9b93127072b4ca8e1a3a1ef09d69e2e68f9b3b4 100644 GIT binary patch literal 2785 zcmcIm={wt58&0bmQ?06s7L!uc(y5(M#fUW;lu~OfMQmXdV=tvhG1gS<(h6;seAUc+>?Ekg3wr+f_WISQTrYP^X3g)JpTD2Db>&>tn2wR!hs z#LR34^SUg=q$H)iN0K63;-X<sW;}OXHPw8b@K4?GBPyM> zLdq2wC+XZQ8Y}}Wx7LYH~9i#6xM0GQ-Q|;hE z!c1qfAdyJKf+374!_EPmR;H1gmzNg`Rc`WXmB|dQRG9ej?Afz?_2B-sg9hE+R9-gcGNjf?@gxcC+uPxSmPhX(` z56|=aZvg8a9!nu*wbWEYv9i~%ofs)5I8B6f<^g*^OFFZxs3<19LBG^OnlDi;V8O8D zqq6si{a=BlMB-&DsGk~6^O0z5#QrxbgEBWa2h=(``q-1!=Tr|Co2>(dmT!lCX^UgA zSR12AJmkgQ?DTZ%@UW7SlB=9RZeE_EfGXadr#<_? z;NXkUnb}$8E6Lri=A{5CK`MekM@NKAcudb@kmBcddPX zJCl%-61lg%{z3=+uDKbH$Ga(9Zld{~%BTwaI5cFM|Eu$ug9HZP&@Ee2H@Eo;y9R(5 z;=C|J!!F>@6Uk`{_B>D^Ru!Ldu}mX2i$#j~-WGA7A|rDLPu(ZZa`Xpm>=zn}DO^du zuX+Khbj3I%q;Xz?a#l=sOIJ^?2DjEtp^VqKoMtx`6^TkqQ>bA8?Hd|Yo0gWBzb{d* z1*IP$AO{NjZetC_VtF7C$jdPxEBLMVx_)*0^cC70Ks=@=CcKrd)YY!gK#J*<^0#qKUm?vPm`3Jh!5}= z1q+7UuZEVsdi`2bL_|bOOG`~H6ky`%^FL44``Bq~N8`7$r($g1g**f#)YzE23Gns5 zcBEuwWfc__RaCYqnKH;ce}Dh7va%cOa2kzv-L%kkRN(BXp8Y~R#?Q&=@m`AIRi1Lk z7J33q01URWvNFI#&y^IuefxHnoT{p-n$+Q1UtPtD>7~^$4)G*Scy@ka^GGdm= ztQuV%)5{IeR^*9_n8O@K-IiE*eMgo%oZ z+S=LmP$=0QKp?MaD{()8Z`qy~DXy&aT^*@_=T%qB#X-$1EIz0_R|6|y(yC@TZU@Be z7pA6~K76>bIDkvm$JA9q99rGa`TyRCrrL zd+e_BNx%q zmhh71<)iAi8Nz+;Tnb)@41Z7D7mTvEZ$XY$`9a4tgtB9!!p_i%k$shpUt(t-@tTOi zp>O`VKHkWAA_iyUiA=)JwkKz<^CjpiD=W{>&-Y+|xtP>(C+nuABp<;2 z_Dny%T$sn6`q^dmw*^@NF|-;F|KMk8u%M^K#Q>`>2??R<&eC`8d`1POzeMgvqDI?= zealhqK|%HArAl_8I}8N8%`89M)MM60MP;nNN3C_mT8XGm$rn@9_3~PIV^!6w+1uCA z6S zP%{z5xfPCJtcB=PpjX9Yei_eBj^fWoxM;Jq!Le;KY3nxhg8{}Z-&dCEq z%gD$`vY=`%H~C)%k`faWlal)GQ|A2vuYcBzur@d6<>Q+JW)Roz41gJ0k9Gs8MS3U> zl7vW2*8gOaAQBT3GwfA279~FG))LZt^W!k80Ya)Rh>VO}UtbSqOeQ2IW*C(KV-Z)} z4A2w-{{XdDC{@FB7Y{8P85xaKB6@sF4$rmI=?)6xz>jX^_F(xxTT9WyL>GIyY)g@^ z8KlUy|M<4yGXB={HUpdr5ceo+F7-nb6MOsL+0D((?d|OV+TIQmn5nRcWB+uh|Hs;x Z7IjQ~$SnEu?nSF3`!GEd-Abra%-=z`eFXpj literal 2843 zcmb_e_g9ly6Go6;R1lFa$^ueXno=akEJ(nFq7Vd;E+AD5MOtt*f)N8sQ33%Ll=ji2 z1?g?0OGpS(gwP2HBF&JH2r;m)>)CVm5BPqWcg~sn-g#!`nVE}ru(K8ykrUzJ;Ssli zSYGAfIiwBfenPx}Z1Bx}$-^T;v$4G7bPu;N6AZZ)t|0i8OxC&aNvC4;Pw)WoimQ{K zmCph1FPfrpQdn^tkta27dfOOjezlCq=Z2c8xeJ=AT0PnWw~NGIFz{;rrH7;IqU*X> z4rtxG%9LeRNI@i*gY?+IG1g;|E4YjK8>B=jG~ky6dR#m2lw+(l4w;kv$lqYkr3mxojpE535cqs$C{#9w4t%FoAwJzA*gcf%f!U> z&5wAzG5D{cp`q5OwH%|my1FhT_cW@psp-SGB{luJ_G}BH?DDH~Sg2ZNMQ0~+yym7) zT~4SM$F=#@tA|G)uxIDdLMkOCCF=GCViFQ8I^6~W2@DC**3g*8_fsgf6&0=>aXU-> znZJR-`uh5&W@b{7k`n?~D;ir`Of)nebF{Hg)|W9YEcCLc7z`wdeqB{n1z-2xq@)X1KqNC)_Omg{0VPkZx6b=^tf{8!iKt*R%LztRn6?XQ;?Ej>vW}vJa5Tf<-XO_ zrt)%)vuFG9FdLix`db4N1Ys$)#>PfyfjQRocq$<(D(YG(G@?nVjC)cnIVb1jl=?dY zA!4rH=jZP`3w^ z&*CGN)|0jI+LfiH36F%gF=u3f*n^r(b#y{^w?AEeRaaX}=^!ewuerM?u-08?xi`YY zx7yp=X*8c`3^hbTOl%47TJMLB-{JZ0Vd(M{hr_kDwk|C#{qe^sEcAq|EEbEc7z>io zzTO@0tIV0EPR(-Xi z)V{FBLF0wajp&8mN6E=#Tid9p9ro57OfA!{%nY#%wX{Ud zScr;<5Q)Ugo_PB@*MP*qKh z(j4-ip7~dJ`6=bB7CUJM2M2TZ_K^GEsbgbNo6{{enQHXOlVSpRMt662adC02XO|r2 zT~^ldFoX9k7aoi@gw3=T6{$>V0hzQjG0)XawpQhLlT9}=k{rvet(8VLl z7VWVNe?Pz1?z-uZAHR*>kYk@5#)*O<2n1uYDOg&>DlBZX%)T&Z&d=Z9|4U@+TuefD zqEf-?>1iLpW%A0KbVGLo)zadK$rRA&G8@81i|WAE-US3{@p zEp8NmcD%g2Z-aUH_&6L6)hZWp$w2s#sl_SPBmI#r@$4{1N5?yN-T-7sOBNofH zq=SN^!((FvygKgfDSdO(K<0bF!^u#Y`*ai_BL2~ShsykH+&h1=njhHF_am3j?|!47 zVK}my(`fHD)6FQgRECy9u)xEf2tJqe@pNU-WyiGOck}N1Xg!&q#%p#*1l1O{l)!fi zJR4SAE|eZFMEMG54(a|h^LdydDv7oyx=IZM20}X*q zTUuH=I_l(-PC8H~XTND|WCE+Or>Dol!a`VB7=yv^^Ye#Ae!lP%9MV1JAU8hU$sM-LoL=(5~grVjnU zyBPPj50k}4eEaI%-G)vqY=Z6W)>%1}`#)B9foTucul4j7cnF?PK-c zUffi<=*Q3OE9K_$#N2$=8K+qa@Dzy!p^u|2N#QW-IO1F&Nv&u^O#1wUVyar|BYm?J zS+MpAK>>kIBsUjkvn=V?g(5Z-RZ(w)z@NBG7ArX=g#ibIucU-;lUwLkT!gTZaK8V4 zBA-?%h2?!59c}a{%gx?-u>R5^);P<^Y{R;|y%6Z~D=&5F@DtL~0Bpv5&qH7YK6`dl zS-G$Uu@W}d$z2^OYY3qD%L^F^qZ|H<>%1id@)%zE=@)0wdQaXT$y{$#F4{%{9m}oYrtL){s!ysj86an diff --git a/Tests/images/multiline_text_center.png b/Tests/images/multiline_text_center.png index f44d0783a0944986efdc62d4f0bcc7530b74ab5b..142e93519f641adfdb43ae56ad885aee15296b33 100644 GIT binary patch literal 2784 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 I26un_ABoeADF6Tf diff --git a/Tests/images/multiline_text_spacing.png b/Tests/images/multiline_text_spacing.png index 869070407292594765b9f1180775a7c875c4cd10..3c3bc0f267d365d5a29531d9d0f18cc8e2aac7b6 100644 GIT binary patch literal 2840 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 delta 2756 zcmXw5c{tSDA6CgSewId*3QZ3R(-fkRk=ze%TGbXc&qx^EVds`Gb&Wuin(!AnCAy#qtEK~iX*NvZCZtT_lNx2!k#H`Md5 z*Dp$VE1}c~jk~Z?brm)F3VxiZWKamvF@zmFxe%z~^I?m-k7KWY&FXJ>Os$)oCkXNK z{2Ft`@8741sZJ5^@F2a?!+GXtZuLSu=}VUd54AIh$+zXe^IyI^3=BLKXIyF%JkxwA z(bRna-w`i;@!~~=M>hF}wc79z&S*ChN!Oiav=A3t8cJbUR< zc}-1CYHBKz>FS-CmzTG{zh8wL#RmuT^YdfS=)l0hb{g%C@IS784h{||)LHf&aP${i z>z|c)%Y`-OfMY~PFG)F zSy9okSlWb!>zmz{F#ZX2didt3?=BiZ#T(=MSJRU%7g< zXWl7R+9sR8BM%fDj0TkwxF9#P0c?v?tKLnGRcB9`T+MyGl<_ z&k3sRQ$cD)#br%R24VkD`UM&IpPgK8MOm5mpPWO<10We9s>TJBmp_nBN0ZDXjvY%Rk%p(I z5B7;WtDisLonnhVPt~TLeG(KD9v*&NLPGI4C{tJfqN1YWGu=4ZL~Ke(us|Y_fq@#F zk~TTT#uWyjqBm|GCF2-ipwU_m{ag;a+__0cMg~SQYin&SbF6KhjJ(njDmFMWQtdsd zm4Qr6OJnbCV-{cwc6N5B;uiYXWPOR0OzPQankyi!ZAJYk<9xgNpf@|#DG00jPvX+j z^eI_&Wo2AX>c!r<;^N|^B`juD<>de1aQN4}v^3jCkJRf-1E2zsA*uyN=4OGFepGq+ zr6DgYwv&NJL2Dd9gIn`G8xyrdlamirGahO=J$bTcN)gkC_RT`OvN?zK_@OQ?z}~ImS#X9-4z=jVf19eh^3Oi;oqHwE1EjI_g>rB zm~J8xvDm+T&F$qOD(sUu z80^pb$|x)>1b#^bWM&X4(&etovLn0csR{0l={BEcBlPebRw<1pp-uU`oCQb3q~U?e zg2lxxC|V>k`9r{BAA!SqF!rvfqT(Y14~6N`Gq0A{*Vk89r#cw~K(PuCXCT#-w6wI0 zj9UNs?${HT?5~rOldY|--6)~omj?3ZIua^z|0D8+Jpv_FCT{}5nD8+?{0z0TG7Lku z0_7bU9UYQGe6zAJHf|rtH_6P*lm|zbmv7{oB!EDm>IdTsUOLWAAvwYd^P!=NT6hS93F|m|b zS}GwS(aW5ydpzv91E8H%8YyPw?A+uxXIyA*Xc(@a3p@obr$3$%A|pCSe;&%NH2@_6 zSwsk(fD`uJRWm+$?lclO*L8FRAv!ub6KE$VC&_8|@A!sCKHm{m`6VWDV5zuv03Nm2 z<>loZU1DHvHUNB7R1|KsY)6yD5)lzudH6y6{H?o=ZSROspj+M$SYBCi9T&gVEGZ>Z zt-jAaa@_y~ZVu81KG%NDc5i^dfO=05n-A=!oyH-tne-o!95})C=k8oWJhOEnL7QF9 z;3}WNX;Jz@j$qiM{W-cOmhl1YGk1T+`JDK@X)W@YUtu;!CiL;Tb0x?2T47L5xSL?| zM}^gC&}#Bqt9s827K7s+{rhsfd(lkzUJDfrxFLP#&6z5* zRJfs8lF=wjqk1gi4N%jM&)Nz#MCCpG$<^t+=-TY}-BlzV7T;OL>XFezDL3*BUCR-3K94Fb^xFdS+FSQl5YNWjKa!`ruSXJ$}%Kky(S|5N$I)FxHTga|i=N6ki>RENw zXX<%;d}^1wrKKe(g85@_J3YK@cW+OHVSui*D<4fGkxK2WVhGh#YR5j?AXB-nuCB{h z7XIgq#!G~B-WOjeSx%9xA$idAZRT=ISg$Y81#_<42Pga@+i`Qc2xnFW($mqg*qfm? xIXM{?7G_@>*q^PVpZoEHG9Vg7si%N!crH6y#&sscyneYygwZX-GJRC&{{eB*hUfqQ diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index dd2234467..47548bdf0 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -130,7 +130,7 @@ try: im = Image.new(mode='RGB', size=(300, 100)) draw = ImageDraw.Draw(im) ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) - line_spacing = draw.textsize('A', font=ttf)[1] + 4 + line_spacing = draw.textsize('A', font=ttf)[1] lines = TEST_TEXT.split("\n") y = 0 for line in lines: From 890bdc2c074826af9ce1fe84d5f4dea2285ec3a6 Mon Sep 17 00:00:00 2001 From: hugovk Date: Sun, 6 Dec 2015 17:31:53 +0200 Subject: [PATCH 0759/1037] sudo apt-get update --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index a2b2d7a56..e1406d466 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,6 +19,7 @@ python: - nightly install: + - "travis_retry sudo apt-get update" - "travis_retry sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-qt4 ghostscript libffi-dev libjpeg-turbo-progs cmake imagemagick" - "travis_retry pip install cffi" - "travis_retry pip install nose" From 731d0b1b7330dcacb178109081b9e8d4ced3ec08 Mon Sep 17 00:00:00 2001 From: hugovk Date: Sun, 6 Dec 2015 18:31:33 +0200 Subject: [PATCH 0760/1037] Default spacing of 4, update tests --- PIL/ImageDraw.py | 2 +- Tests/images/multiline_text.png | Bin 2785 -> 2843 bytes Tests/images/multiline_text_center.png | Bin 2784 -> 2845 bytes Tests/images/multiline_text_right.png | Bin 2788 -> 2846 bytes Tests/test_imagefont.py | 2 +- 5 files changed, 2 insertions(+), 2 deletions(-) diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py index 161cce701..f37b41557 100644 --- a/PIL/ImageDraw.py +++ b/PIL/ImageDraw.py @@ -267,7 +267,7 @@ class ImageDraw(object): self.draw.draw_bitmap(xy, mask, ink) def multiline_text(self, xy, text, fill=None, font=None, anchor=None, - spacing=0, align="left"): + spacing=4, align="left"): widths = [] max_width = 0 lines = self._multiline_split(text) diff --git a/Tests/images/multiline_text.png b/Tests/images/multiline_text.png index c9b93127072b4ca8e1a3a1ef09d69e2e68f9b3b4..ff1308c5ef277048d26abec36dd76610e8fbf3fe 100644 GIT binary patch literal 2843 zcmb_e_g9ly6Go6;R1lFa$^ueXno=akEJ(nFq7Vd;E+AD5MOtt*f)N8sQ33%Ll=ji2 z1?g?0OGpS(gwP2HBF&JH2r;m)>)CVm5BPqWcg~sn-g#!`nVE}ru(K8ykrUzJ;Ssli zSYGAfIiwBfenPx}Z1Bx}$-^T;v$4G7bPu;N6AZZ)t|0i8OxC&aNvC4;Pw)WoimQ{K zmCph1FPfrpQdn^tkta27dfOOjezlCq=Z2c8xeJ=AT0PnWw~NGIFz{;rrH7;IqU*X> z4rtxG%9LeRNI@i*gY?+IG1g;|E4YjK8>B=jG~ky6dR#m2lw+(l4w;kv$lqYkr3mxojpE535cqs$C{#9w4t%FoAwJzA*gcf%f!U> z&5wAzG5D{cp`q5OwH%|my1FhT_cW@psp-SGB{luJ_G}BH?DDH~Sg2ZNMQ0~+yym7) zT~4SM$F=#@tA|G)uxIDdLMkOCCF=GCViFQ8I^6~W2@DC**3g*8_fsgf6&0=>aXU-> znZJR-`uh5&W@b{7k`n?~D;ir`Of)nebF{Hg)|W9YEcCLc7z`wdeqB{n1z-2xq@)X1KqNC)_Omg{0VPkZx6b=^tf{8!iKt*R%LztRn6?XQ;?Ej>vW}vJa5Tf<-XO_ zrt)%)vuFG9FdLix`db4N1Ys$)#>PfyfjQRocq$<(D(YG(G@?nVjC)cnIVb1jl=?dY zA!4rH=jZP`3w^ z&*CGN)|0jI+LfiH36F%gF=u3f*n^r(b#y{^w?AEeRaaX}=^!ewuerM?u-08?xi`YY zx7yp=X*8c`3^hbTOl%47TJMLB-{JZ0Vd(M{hr_kDwk|C#{qe^sEcAq|EEbEc7z>io zzTO@0tIV0EPR(-Xi z)V{FBLF0wajp&8mN6E=#Tid9p9ro57OfA!{%nY#%wX{Ud zScr;<5Q)Ugo_PB@*MP*qKh z(j4-ip7~dJ`6=bB7CUJM2M2TZ_K^GEsbgbNo6{{enQHXOlVSpRMt662adC02XO|r2 zT~^ldFoX9k7aoi@gw3=T6{$>V0hzQjG0)XawpQhLlT9}=k{rvet(8VLl z7VWVNe?Pz1?z-uZAHR*>kYk@5#)*O<2n1uYDOg&>DlBZX%)T&Z&d=Z9|4U@+TuefD zqEf-?>1iLpW%A0KbVGLo)zadK$rRA&G8@81i|WAE-US3{@p zEp8NmcD%g2Z-aUH_&6L6)hZWp$w2s#sl_SPBmI#r@$4{1N5?yN-T-7sOBNofH zq=SN^!((FvygKgfDSdO(K<0bF!^u#Y`*ai_BL2~ShsykH+&h1=njhHF_am3j?|!47 zVK}my(`fHD)6FQgRECy9u)xEf2tJqe@pNU-WyiGOck}N1Xg!&q#%p#*1l1O{l)!fi zJR4SAE|eZFMEMG54(a|h^LdydDv7oyx=IZM20}X*q zTUuH=I_l(-PC8H~XTND|WCE+Or>Dol!a`VB7=yv^^Ye#Ae!lP%9MV1JAU8hU$sM-LoL=(5~grVjnU zyBPPj50k}4eEaI%-G)vqY=Z6W)>%1}`#)B9foTucul4j7cnF?PK-c zUffi<=*Q3OE9K_$#N2$=8K+qa@Dzy!p^u|2N#QW-IO1F&Nv&u^O#1wUVyar|BYm?J zS+MpAK>>kIBsUjkvn=V?g(5Z-RZ(w)z@NBG7ArX=g#ibIucU-;lUwLkT!gTZaK8V4 zBA-?%h2?!59c}a{%gx?-u>R5^);P<^Y{R;|y%6Z~D=&5F@DtL~0Bpv5&qH7YK6`dl zS-G$Uu@W}d$z2^OYY3qD%L^F^qZ|H<>%1id@)%zE=@)0wdQaXT$y{$#F4{%{9m}oYrtL){s!ysj86an literal 2785 zcmcIm={wt58&0bmQ?06s7L!uc(y5(M#fUW;lu~OfMQmXdV=tvhG1gS<(h6;seAUc+>?Ekg3wr+f_WISQTrYP^X3g)JpTD2Db>&>tn2wR!hs z#LR34^SUg=q$H)iN0K63;-X<sW;}OXHPw8b@K4?GBPyM> zLdq2wC+XZQ8Y}}Wx7LYH~9i#6xM0GQ-Q|;hE z!c1qfAdyJKf+374!_EPmR;H1gmzNg`Rc`WXmB|dQRG9ej?Afz?_2B-sg9hE+R9-gcGNjf?@gxcC+uPxSmPhX(` z56|=aZvg8a9!nu*wbWEYv9i~%ofs)5I8B6f<^g*^OFFZxs3<19LBG^OnlDi;V8O8D zqq6si{a=BlMB-&DsGk~6^O0z5#QrxbgEBWa2h=(``q-1!=Tr|Co2>(dmT!lCX^UgA zSR12AJmkgQ?DTZ%@UW7SlB=9RZeE_EfGXadr#<_? z;NXkUnb}$8E6Lri=A{5CK`MekM@NKAcudb@kmBcddPX zJCl%-61lg%{z3=+uDKbH$Ga(9Zld{~%BTwaI5cFM|Eu$ug9HZP&@Ee2H@Eo;y9R(5 z;=C|J!!F>@6Uk`{_B>D^Ru!Ldu}mX2i$#j~-WGA7A|rDLPu(ZZa`Xpm>=zn}DO^du zuX+Khbj3I%q;Xz?a#l=sOIJ^?2DjEtp^VqKoMtx`6^TkqQ>bA8?Hd|Yo0gWBzb{d* z1*IP$AO{NjZetC_VtF7C$jdPxEBLMVx_)*0^cC70Ks=@=CcKrd)YY!gK#J*<^0#qKUm?vPm`3Jh!5}= z1q+7UuZEVsdi`2bL_|bOOG`~H6ky`%^FL44``Bq~N8`7$r($g1g**f#)YzE23Gns5 zcBEuwWfc__RaCYqnKH;ce}Dh7va%cOa2kzv-L%kkRN(BXp8Y~R#?Q&=@m`AIRi1Lk z7J33q01URWvNFI#&y^IuefxHnoT{p-n$+Q1UtPtD>7~^$4)G*Scy@ka^GGdm= ztQuV%)5{IeR^*9_n8O@K-IiE*eMgo%oZ z+S=LmP$=0QKp?MaD{()8Z`qy~DXy&aT^*@_=T%qB#X-$1EIz0_R|6|y(yC@TZU@Be z7pA6~K76>bIDkvm$JA9q99rGa`TyRCrrL zd+e_BNx%q zmhh71<)iAi8Nz+;Tnb)@41Z7D7mTvEZ$XY$`9a4tgtB9!!p_i%k$shpUt(t-@tTOi zp>O`VKHkWAA_iyUiA=)JwkKz<^CjpiD=W{>&-Y+|xtP>(C+nuABp<;2 z_Dny%T$sn6`q^dmw*^@NF|-;F|KMk8u%M^K#Q>`>2??R<&eC`8d`1POzeMgvqDI?= zealhqK|%HArAl_8I}8N8%`89M)MM60MP;nNN3C_mT8XGm$rn@9_3~PIV^!6w+1uCA z6S zP%{z5xfPCJtcB=PpjX9Yei_eBj^fWoxM;Jq!Le;KY3nxhg8{}Z-&dCEq z%gD$`vY=`%H~C)%k`faWlal)GQ|A2vuYcBzur@d6<>Q+JW)Roz41gJ0k9Gs8MS3U> zl7vW2*8gOaAQBT3GwfA279~FG))LZt^W!k80Ya)Rh>VO}UtbSqOeQ2IW*C(KV-Z)} z4A2w-{{XdDC{@FB7Y{8P85xaKB6@sF4$rmI=?)6xz>jX^_F(xxTT9WyL>GIyY)g@^ z8KlUy|M<4yGXB={HUpdr5ceo+F7-nb6MOsL+0D((?d|OV+TIQmn5nRcWB+uh|Hs;x Z7IjQ~$SnEu?nSF3`!GEd-Abra%-=z`eFXpj diff --git a/Tests/images/multiline_text_center.png b/Tests/images/multiline_text_center.png index 142e93519f641adfdb43ae56ad885aee15296b33..f44d0783a0944986efdc62d4f0bcc7530b74ab5b 100644 GIT binary patch literal 2845 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 I26un_ABoeADF6Tf delta 2698 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 Date: Mon, 7 Dec 2015 12:55:53 +0000 Subject: [PATCH 0761/1037] Updated Changes.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index f64b8dc5e..acdcb7d89 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- ExtraSamples tag should be a SHORT, not a BYTE #1555 + [Nexuapex] + - Docs and code health fixes #1565 #1566 [radarhere] From f0944438ab347a42f2479c6a9c436ae5c66ecd67 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 8 Dec 2015 00:03:38 +1100 Subject: [PATCH 0762/1037] Removed FIXME from ImageDraw test --- Tests/test_imagedraw.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index a98d959bb..19c58054c 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -64,7 +64,6 @@ class TestImageDraw(PillowTestCase): draw = ImageDraw.Draw(im) # Act - # FIXME Fill param should be named outline. draw.arc(bbox, 0, 180) del draw From e4a1035e82852dfc58a9c708f6f3b18c01562322 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 7 Dec 2015 13:07:21 +0000 Subject: [PATCH 0763/1037] Updated Changes.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index acdcb7d89..bfd1f56a8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,12 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Throw TypeError if no cursors were found in .cur file #1556 + [radarhere] + +- Fix crash in ImageTk.PhotoImage on win-amd64 #1553 + [cgohlke] + - ExtraSamples tag should be a SHORT, not a BYTE #1555 [Nexuapex] From 9009f457aadad090509ca30ab1386edebe66d56f Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 7 Dec 2015 13:30:20 +0000 Subject: [PATCH 0764/1037] Update CHANGES.rst --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index bfd1f56a8..ac800652c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Changed ImageQt import of Image #1560 + [radarhere, ericfrederich] + - Throw TypeError if no cursors were found in .cur file #1556 [radarhere] From 7563915aa5968f9949fcff6b8ea7bd58853d2722 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 7 Dec 2015 13:32:53 +0000 Subject: [PATCH 0765/1037] Updated Changes.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index ac800652c..82d9b3e2a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Removed unused lines in BDFFontFile #1530 + [radarhere] + - Changed ImageQt import of Image #1560 [radarhere, ericfrederich] From 1848c746eb960fecc8f5d5c61f6cb59480ac60fc Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 7 Dec 2015 13:42:01 +0000 Subject: [PATCH 0766/1037] Updated Changes.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 82d9b3e2a..78de3bce0 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Consistent multiline text spacing #1574 + [hugovk] + - Removed unused lines in BDFFontFile #1530 [radarhere] From d392b423cd054c3c248305857af5cebe669e7f13 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 7 Dec 2015 14:16:05 +0000 Subject: [PATCH 0767/1037] Update CHANGES.rst --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 78de3bce0..e2a60eff9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Fix command to invoke ghostscript for eps files. #1478 + [baumatron, radarhere] + - Consistent multiline text spacing #1574 [hugovk] From 8b76f557cedc0a01255f277625dcecac3930f998 Mon Sep 17 00:00:00 2001 From: cartisan Date: Mon, 7 Dec 2015 17:40:42 +0000 Subject: [PATCH 0768/1037] Enabled conversion to numpy array for HSV images. #1559 --- CHANGES.rst | 3 +++ PIL/Image.py | 1 + Tests/test_numpy.py | 1 + 3 files changed, 5 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index e2a60eff9..cada7cc01 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Enabled conversion to numpy array for HSV images. #1559 + [cartisan] + - Fix command to invoke ghostscript for eps files. #1478 [baumatron, radarhere] diff --git a/PIL/Image.py b/PIL/Image.py index af6dac1c4..4b9f3868f 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -250,6 +250,7 @@ _MODE_CONV = { "CMYK": ('|u1', 4), "YCbCr": ('|u1', 3), "LAB": ('|u1', 3), # UNDONE - unsigned |u1i1i1 + "HSV": ('|u1', 3), # I;16 == I;16L, and I;32 == I;32L "I;16": ('u2', None), diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py index 64081894e..4f02fde23 100644 --- a/Tests/test_numpy.py +++ b/Tests/test_numpy.py @@ -113,6 +113,7 @@ class TestNumpy(PillowTestCase): ("I;16", 'u2'), ("I;16L", ' Date: Mon, 7 Dec 2015 20:24:57 +0200 Subject: [PATCH 0769/1037] Test ImageFont getters --- Tests/test_imagefont.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index dd2234467..c2dd381ff 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -423,6 +423,23 @@ try: self._test_fake_loading_font( font_directory+'/Duplicate.ttf', 'Duplicate') + def test_imagefont_getters(self): + # Arrange + t = ImageFont.truetype(FONT_PATH, FONT_SIZE) + + # Act / Assert + self.assertEqual(t.getmetrics(), (16, 4)) + self.assertEqual(t.font.ascent, 16) + self.assertEqual(t.font.descent, 4) + self.assertEqual(t.font.height, 20) + self.assertEqual(t.font.x_ppem, 20) + self.assertEqual(t.font.y_ppem, 20) + self.assertEqual(t.getsize('A'), (12, 16)) + self.assertEqual(t.getsize('AB'), (24, 16)) + self.assertEqual(t.getsize('M'), (12, 16)) + self.assertEqual(t.getsize('y'), (12, 20)) + self.assertEqual(t.getsize('a'), (12, 16)) + except ImportError: class TestImageFont(PillowTestCase): From 1110978fc77dcb36f675ab2b21ca0fb41716b097 Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 7 Dec 2015 20:44:03 +0200 Subject: [PATCH 0770/1037] Test another ImageFont getter --- Tests/test_imagefont.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index c2dd381ff..a64eb68ef 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -434,6 +434,7 @@ try: self.assertEqual(t.font.height, 20) self.assertEqual(t.font.x_ppem, 20) self.assertEqual(t.font.y_ppem, 20) + self.assertEqual(t.font.glyphs, 4177) self.assertEqual(t.getsize('A'), (12, 16)) self.assertEqual(t.getsize('AB'), (24, 16)) self.assertEqual(t.getsize('M'), (12, 16)) From b72b0295c7814f14d83c79e7674656817c8ee15a Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 7 Dec 2015 22:03:36 +0200 Subject: [PATCH 0771/1037] 'make linkcheck' fixes --- PIL/JpegPresets.py | 6 +++--- docs/build.rst | 8 ++++---- docs/reference/ImagePath.rst | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/PIL/JpegPresets.py b/PIL/JpegPresets.py index 66cbde117..c5a36b914 100644 --- a/PIL/JpegPresets.py +++ b/PIL/JpegPresets.py @@ -27,7 +27,7 @@ Subsampling Subsampling is the practice of encoding images by implementing less resolution for chroma information than for luma information. -(ref.: http://en.wikipedia.org/wiki/Chroma_subsampling) +(ref.: https://en.wikipedia.org/wiki/Chroma_subsampling) Possible subsampling values are 0, 1 and 2 that correspond to 4:4:4, 4:2:2 and 4:1:1 (or 4:2:0?). @@ -41,8 +41,8 @@ Quantization tables They are values use by the DCT (Discrete cosine transform) to remove *unnecessary* information from the image (the lossy part of the compression). -(ref.: http://en.wikipedia.org/wiki/Quantization_matrix#Quantization_matrices, -http://en.wikipedia.org/wiki/JPEG#Quantization) +(ref.: https://en.wikipedia.org/wiki/Quantization_matrix#Quantization_matrices, +https://en.wikipedia.org/wiki/JPEG#Quantization) You can get the quantization tables of a JPEG with:: diff --git a/docs/build.rst b/docs/build.rst index 1110fb792..e4c2293a9 100644 --- a/docs/build.rst +++ b/docs/build.rst @@ -52,12 +52,12 @@ Compilers Download and install: * `Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 - SP1 `_ + SP1 `_ * `Microsoft Windows SDK for Windows 7 and .NET Framework - 4 `_ + 4 `_ -* `CMake-2.8.10.2-win32-x86.exe `_ +* `CMake-2.8.10.2-win32-x86.exe `_ The samples and the .NET SDK portions aren't required, just the compilers and other tools. UNDONE -- check exact wording. @@ -83,7 +83,7 @@ Once the dependencies are built, run `python build.py --clean` to build and install Pillow in virtualenvs for each python build. `build.py --dist` will build Windows installers instead of installing into virtualenvs. - + UNDONE -- suppressed output, what about failures. Testing Pillow diff --git a/docs/reference/ImagePath.rst b/docs/reference/ImagePath.rst index 03aa39811..978db4caf 100644 --- a/docs/reference/ImagePath.rst +++ b/docs/reference/ImagePath.rst @@ -36,7 +36,7 @@ vector data. Path objects can be passed to the methods on the **distance** is measured as `Manhattan distance`_ and defaults to two pixels. -.. _Manhattan distance: http://en.wikipedia.org/wiki/Manhattan_distance +.. _Manhattan distance: https://en.wikipedia.org/wiki/Manhattan_distance .. py:method:: PIL.ImagePath.Path.getbbox() From 23309e13daffb6efb2e5b4c7e29193ee31054bd3 Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 7 Dec 2015 22:16:23 +0200 Subject: [PATCH 0772/1037] Declare encoding --- PIL/WalImageFile.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PIL/WalImageFile.py b/PIL/WalImageFile.py index fc2bb30a7..152bc6eed 100644 --- a/PIL/WalImageFile.py +++ b/PIL/WalImageFile.py @@ -1,3 +1,5 @@ +# encoding: utf-8 +# # The Python Imaging Library. # $Id$ # From 7f003125179178a4d72ed3738d049971d82b9974 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 8 Dec 2015 07:53:29 +1100 Subject: [PATCH 0773/1037] Removed __main__ code from WalImageFile --- PIL/WalImageFile.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/PIL/WalImageFile.py b/PIL/WalImageFile.py index fc2bb30a7..46ab0d5bd 100644 --- a/PIL/WalImageFile.py +++ b/PIL/WalImageFile.py @@ -124,8 +124,3 @@ quake2palette = ( b"\x10\x0f\x0d\x0d\x0b\x3c\x2e\x2a\x36\x27\x20\x30\x21\x18\x29\x1b" b"\x10\x3c\x39\x37\x37\x32\x2f\x31\x2c\x28\x2b\x26\x21\x30\x22\x20" ) - -if __name__ == "__main__": - im = open("../hacks/sample.wal") - print(im.info, im.mode, im.size) - im.save("../out.png") From ee82f7089c8932a961b575a45c5091f3dd3c0edf Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 8 Dec 2015 20:31:02 +1100 Subject: [PATCH 0774/1037] Changed assertTrue isinstance to assertIsInstance --- Tests/test_image_tobytes.py | 2 +- Tests/test_image_toqimage.py | 2 +- Tests/test_image_toqpixmap.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/test_image_tobytes.py b/Tests/test_image_tobytes.py index 31a7e1722..a15bc002d 100644 --- a/Tests/test_image_tobytes.py +++ b/Tests/test_image_tobytes.py @@ -5,7 +5,7 @@ class TestImageToBytes(PillowTestCase): def test_sanity(self): data = hopper().tobytes() - self.assertTrue(isinstance(data, bytes)) + self.assertIsInstance(data, bytes) if __name__ == '__main__': unittest.main() diff --git a/Tests/test_image_toqimage.py b/Tests/test_image_toqimage.py index 6c79b4c6d..76169258c 100644 --- a/Tests/test_image_toqimage.py +++ b/Tests/test_image_toqimage.py @@ -15,7 +15,7 @@ class TestToQImage(PillowQtTestCase, PillowTestCase): for mode in ('1', 'RGB', 'RGBA', 'L', 'P'): data = ImageQt.toqimage(hopper(mode)) - self.assertTrue(isinstance(data, QImage)) + self.assertIsInstance(data, QImage) self.assertFalse(data.isNull()) # Test saving the file diff --git a/Tests/test_image_toqpixmap.py b/Tests/test_image_toqpixmap.py index 8fabab13d..eb9f2f7c4 100644 --- a/Tests/test_image_toqpixmap.py +++ b/Tests/test_image_toqpixmap.py @@ -15,7 +15,7 @@ class TestToQPixmap(PillowQPixmapTestCase, PillowTestCase): for mode in ('1', 'RGB', 'RGBA', 'L', 'P'): data = ImageQt.toqpixmap(hopper(mode)) - self.assertTrue(isinstance(data, QPixmap)) + self.assertIsInstance(data, QPixmap) self.assertFalse(data.isNull()) # Test saving the file From 112240d3a2af33e1752651ee10d7115a40f37a63 Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 8 Dec 2015 12:19:12 +0200 Subject: [PATCH 0775/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index e2a60eff9..02a54d959 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,11 +4,14 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ -- Fix command to invoke ghostscript for eps files. #1478 +- Update unit test asserts #1584 + [radarhere] + +- Fix command to invoke ghostscript for eps files #1478 [baumatron, radarhere] - Consistent multiline text spacing #1574 - [hugovk] + [wiredfool, hugovk] - Removed unused lines in BDFFontFile #1530 [radarhere] From 8f9b3a7372537e47328867b30248cd868cc692da Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 9 Dec 2015 00:28:52 +1100 Subject: [PATCH 0776/1037] Changed arcs, chords and pie slices to use floats --- Tests/test_imagedraw.py | 30 ++++++++++++++++++------------ _imaging.c | 12 ++++++------ libImaging/Draw.c | 21 ++++++++++++++------- libImaging/Imaging.h | 6 +++--- 4 files changed, 41 insertions(+), 28 deletions(-) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 19c58054c..947e35971 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -58,13 +58,13 @@ class TestImageDraw(PillowTestCase): self.assertRaises(ValueError, lambda: ImageDraw.ImageDraw(im, mode="L")) - def helper_arc(self, bbox): + def helper_arc(self, bbox, start, end): # Arrange im = Image.new("RGB", (W, H)) draw = ImageDraw.Draw(im) # Act - draw.arc(bbox, 0, 180) + draw.arc(bbox, start, end) del draw # Assert @@ -72,10 +72,12 @@ class TestImageDraw(PillowTestCase): im, Image.open("Tests/images/imagedraw_arc.png"), 1) def test_arc1(self): - self.helper_arc(BBOX1) + self.helper_arc(BBOX1, 0, 180) + self.helper_arc(BBOX1, 0.5, 180.4) def test_arc2(self): - self.helper_arc(BBOX2) + self.helper_arc(BBOX2, 0, 180) + self.helper_arc(BBOX2, 0.5, 180.4) def test_bitmap(self): # Arrange @@ -91,13 +93,13 @@ class TestImageDraw(PillowTestCase): self.assert_image_equal( im, Image.open("Tests/images/imagedraw_bitmap.png")) - def helper_chord(self, bbox): + def helper_chord(self, bbox, start, end): # Arrange im = Image.new("RGB", (W, H)) draw = ImageDraw.Draw(im) # Act - draw.chord(bbox, 0, 180, fill="red", outline="yellow") + draw.chord(bbox, start, end, fill="red", outline="yellow") del draw # Assert @@ -105,10 +107,12 @@ class TestImageDraw(PillowTestCase): im, Image.open("Tests/images/imagedraw_chord.png"), 1) def test_chord1(self): - self.helper_chord(BBOX1) + self.helper_chord(BBOX1, 0, 180) + self.helper_chord(BBOX1, 0.5, 180.4) def test_chord2(self): - self.helper_chord(BBOX2) + self.helper_chord(BBOX2, 0, 180) + self.helper_chord(BBOX2, 0.5, 180.4) def helper_ellipse(self, bbox): # Arrange @@ -161,13 +165,13 @@ class TestImageDraw(PillowTestCase): def test_line2(self): self.helper_line(POINTS2) - def helper_pieslice(self, bbox): + def helper_pieslice(self, bbox, start, end): # Arrange im = Image.new("RGB", (W, H)) draw = ImageDraw.Draw(im) # Act - draw.pieslice(bbox, -90, 45, fill="white", outline="blue") + draw.pieslice(bbox, start, end, fill="white", outline="blue") del draw # Assert @@ -175,10 +179,12 @@ class TestImageDraw(PillowTestCase): im, Image.open("Tests/images/imagedraw_pieslice.png"), 1) def test_pieslice1(self): - self.helper_pieslice(BBOX1) + self.helper_pieslice(BBOX1, -90, 45) + self.helper_pieslice(BBOX1, -90.5, 45.4) def test_pieslice2(self): - self.helper_pieslice(BBOX2) + self.helper_pieslice(BBOX2, -90, 45) + self.helper_pieslice(BBOX2, -90.5, 45.4) def helper_point(self, points): # Arrange diff --git a/_imaging.c b/_imaging.c index 3a305885c..1aa6b8e3b 100644 --- a/_imaging.c +++ b/_imaging.c @@ -2382,9 +2382,9 @@ _draw_arc(ImagingDrawObject* self, PyObject* args) PyObject* data; int ink; - int start, end; + float start, end; int op = 0; - if (!PyArg_ParseTuple(args, "Oiii|i", &data, &start, &end, &ink)) + if (!PyArg_ParseTuple(args, "Offi|i", &data, &start, &end, &ink)) return NULL; n = PyPath_Flatten(data, &xy); @@ -2456,8 +2456,8 @@ _draw_chord(ImagingDrawObject* self, PyObject* args) PyObject* data; int ink, fill; - int start, end; - if (!PyArg_ParseTuple(args, "Oiiii", + float start, end; + if (!PyArg_ParseTuple(args, "Offii", &data, &start, &end, &ink, &fill)) return NULL; @@ -2677,8 +2677,8 @@ _draw_pieslice(ImagingDrawObject* self, PyObject* args) PyObject* data; int ink, fill; - int start, end; - if (!PyArg_ParseTuple(args, "Oiiii", &data, &start, &end, &ink, &fill)) + float start, end; + if (!PyArg_ParseTuple(args, "Offii", &data, &start, &end, &ink, &fill)) return NULL; n = PyPath_Flatten(data, &xy); diff --git a/libImaging/Draw.c b/libImaging/Draw.c index d99008197..423f1bea2 100644 --- a/libImaging/Draw.c +++ b/libImaging/Draw.c @@ -743,10 +743,11 @@ ImagingDrawBitmap(Imaging im, int x0, int y0, Imaging bitmap, const void* ink, static int ellipse(Imaging im, int x0, int y0, int x1, int y1, - int start, int end, const void* ink_, int fill, + float start, float end, const void* ink_, int fill, int mode, int op) { - int i, n; + float i; + int n; int cx, cy; int w, h; int x = 0, y = 0; @@ -779,7 +780,10 @@ ellipse(Imaging im, int x0, int y0, int x1, int y1, n = 0; - for (i = start; i <= end; i++) { + for (i = start; i < end+1; i++) { + if (i > end) { + i = end; + } x = FLOOR((cos(i*M_PI/180) * w/2) + cx + 0.5); y = FLOOR((sin(i*M_PI/180) * h/2) + cy + 0.5); if (i != start) @@ -807,7 +811,10 @@ ellipse(Imaging im, int x0, int y0, int x1, int y1, } else { - for (i = start; i <= end; i++) { + for (i = start; i < end+1; i++) { + if (i > end) { + i = end; + } x = FLOOR((cos(i*M_PI/180) * w/2) + cx + 0.5); y = FLOOR((sin(i*M_PI/180) * h/2) + cy + 0.5); if (i != start) @@ -835,14 +842,14 @@ ellipse(Imaging im, int x0, int y0, int x1, int y1, int ImagingDrawArc(Imaging im, int x0, int y0, int x1, int y1, - int start, int end, const void* ink, int op) + float start, float end, const void* ink, int op) { return ellipse(im, x0, y0, x1, y1, start, end, ink, 0, ARC, op); } int ImagingDrawChord(Imaging im, int x0, int y0, int x1, int y1, - int start, int end, const void* ink, int fill, int op) + float start, float end, const void* ink, int fill, int op) { return ellipse(im, x0, y0, x1, y1, start, end, ink, fill, CHORD, op); } @@ -856,7 +863,7 @@ ImagingDrawEllipse(Imaging im, int x0, int y0, int x1, int y1, int ImagingDrawPieslice(Imaging im, int x0, int y0, int x1, int y1, - int start, int end, const void* ink, int fill, int op) + float start, float end, const void* ink, int fill, int op) { return ellipse(im, x0, y0, x1, y1, start, end, ink, fill, PIESLICE, op); } diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h index c341ac84e..bbef0440d 100644 --- a/libImaging/Imaging.h +++ b/libImaging/Imaging.h @@ -345,11 +345,11 @@ struct ImagingAffineMatrixInstance { typedef struct ImagingAffineMatrixInstance *ImagingAffineMatrix; extern int ImagingDrawArc(Imaging im, int x0, int y0, int x1, int y1, - int start, int end, const void* ink, int op); + float start, float end, const void* ink, int op); extern int ImagingDrawBitmap(Imaging im, int x0, int y0, Imaging bitmap, const void* ink, int op); extern int ImagingDrawChord(Imaging im, int x0, int y0, int x1, int y1, - int start, int end, const void* ink, int fill, + float start, float end, const void* ink, int fill, int op); extern int ImagingDrawEllipse(Imaging im, int x0, int y0, int x1, int y1, const void* ink, int fill, int op); @@ -358,7 +358,7 @@ extern int ImagingDrawLine(Imaging im, int x0, int y0, int x1, int y1, extern int ImagingDrawWideLine(Imaging im, int x0, int y0, int x1, int y1, const void* ink, int width, int op); extern int ImagingDrawPieslice(Imaging im, int x0, int y0, int x1, int y1, - int start, int end, const void* ink, int fill, + float start, float end, const void* ink, int fill, int op); extern int ImagingDrawPoint(Imaging im, int x, int y, const void* ink, int op); extern int ImagingDrawPolygon(Imaging im, int points, int *xy, From 1698fbbf06f46b2af025e8df4cfbf7c7804a30da Mon Sep 17 00:00:00 2001 From: cartisan Date: Tue, 8 Dec 2015 17:20:29 +0100 Subject: [PATCH 0777/1037] Reverted changelog to its initial state upon forking to prevent merge conflicts --- CHANGES.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index cada7cc01..e2a60eff9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,9 +4,6 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ -- Enabled conversion to numpy array for HSV images. #1559 - [cartisan] - - Fix command to invoke ghostscript for eps files. #1478 [baumatron, radarhere] From b6880a65d1f5ebbdb9d86d651d980be7d96e0421 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 9 Dec 2015 08:40:13 +1100 Subject: [PATCH 0778/1037] Updated OleFileIO README to 0.42 [ci skip] --- PIL/OleFileIO-README.md | 403 ++++++++++++---------------------------- 1 file changed, 116 insertions(+), 287 deletions(-) diff --git a/PIL/OleFileIO-README.md b/PIL/OleFileIO-README.md index 11a0e9053..0962a5a23 100644 --- a/PIL/OleFileIO-README.md +++ b/PIL/OleFileIO-README.md @@ -1,26 +1,45 @@ -OleFileIO_PL -============ +olefile (formerly OleFileIO_PL) +=============================== -[OleFileIO_PL](http://www.decalage.info/python/olefileio) is a Python module to parse and read [Microsoft OLE2 files (also called Structured Storage, Compound File Binary Format or Compound Document File Format)](http://en.wikipedia.org/wiki/Compound_File_Binary_Format), such as Microsoft Office documents, Image Composer and FlashPix files, Outlook messages, StickyNotes, several Microscopy file formats ... +[olefile](http://www.decalage.info/olefile) is a Python package to parse, read and write +[Microsoft OLE2 files](http://en.wikipedia.org/wiki/Compound_File_Binary_Format) +(also called Structured Storage, Compound File Binary Format or Compound Document File Format), +such as Microsoft Office 97-2003 documents, vbaProject.bin in MS Office 2007+ files, Image Composer +and FlashPix files, Outlook messages, StickyNotes, several Microscopy file formats, McAfee antivirus quarantine files, +etc. + -This is an improved version of the OleFileIO module from [PIL](http://www.pythonware.com/products/pil/index.htm), the excellent Python Imaging Library, created and maintained by Fredrik Lundh. The API is still compatible with PIL, but since 2005 I have improved the internal implementation significantly, with new features, bugfixes and a more robust design. +**Quick links:** [Home page](http://www.decalage.info/olefile) - +[Download/Install](https://bitbucket.org/decalage/olefileio_pl/wiki/Install) - +[Documentation](https://bitbucket.org/decalage/olefileio_pl/wiki) - +[Report Issues/Suggestions/Questions](https://bitbucket.org/decalage/olefileio_pl/issues?status=new&status=open) - +[Contact the author](http://decalage.info/contact) - +[Repository](https://bitbucket.org/decalage/olefileio_pl) - +[Updates on Twitter](https://twitter.com/decalage2) -As far as I know, this module is now the most complete and robust Python implementation to read MS OLE2 files, portable on several operating systems. (please tell me if you know other similar Python modules) - -OleFileIO_PL can be used as an independent module or with PIL. The goal is to have it integrated into [Pillow](http://python-pillow.github.io/), the friendly fork of PIL. - -OleFileIO\_PL is mostly meant for developers. If you are looking for tools to analyze OLE files or to extract data, then please also check [python-oletools](http://www.decalage.info/python/oletools), which are built upon OleFileIO_PL. News ---- Follow all updates and news on Twitter: -- **2014-02-04 v0.30**: now compatible with Python 3.x, thanks to Martin Panter who did most of the hard work. -- 2013-07-24 v0.26: added methods to parse stream/storage timestamps, improved listdir to include storages, fixed parsing of direntry timestamps -- 2013-05-27 v0.25: improved metadata extraction, properties parsing and exception handling, fixed [issue #12](https://bitbucket.org/decalage/olefileio_pl/issue/12/error-when-converting-timestamps-in-ole) -- 2013-05-07 v0.24: new features to extract metadata (get\_metadata method and OleMetadata class), improved getproperties to convert timestamps to Python datetime -- 2012-10-09: published [python-oletools](http://www.decalage.info/python/oletools), a package of analysis tools based on OleFileIO_PL +- **2015-01-25 v0.42**: improved handling of special characters in stream/storage names on Python 2.x (using UTF-8 + instead of Latin-1), fixed bug in listdir with empty storages. +- 2014-11-25 v0.41: OleFileIO.open and isOleFile now support OLE files stored in byte strings, fixed installer for + python 3, added support for Jython (Niko Ehrenfeuchter) +- 2014-10-01 v0.40: renamed OleFileIO_PL to olefile, added initial write support for streams >4K, updated doc and + license, improved the setup script. +- 2014-07-27 v0.31: fixed support for large files with 4K sectors, thanks to Niko Ehrenfeuchter, Martijn Berger and + Dave Jones. Added test scripts from Pillow (by hugovk). Fixed setup for Python 3 (Martin Panter) +- 2014-02-04 v0.30: now compatible with Python 3.x, thanks to Martin Panter who did most of the hard work. +- 2013-07-24 v0.26: added methods to parse stream/storage timestamps, improved listdir to include storages, fixed + parsing of direntry timestamps +- 2013-05-27 v0.25: improved metadata extraction, properties parsing and exception handling, fixed + [issue #12](https://bitbucket.org/decalage/olefileio_pl/issue/12/error-when-converting-timestamps-in-ole) +- 2013-05-07 v0.24: new features to extract metadata (get\_metadata method and OleMetadata class), improved + getproperties to convert timestamps to Python datetime +- 2012-10-09: published [python-oletools](http://www.decalage.info/python/oletools), a package of analysis tools based + on OleFileIO_PL - 2012-09-11 v0.23: added support for file-like objects, fixed [issue #8](https://bitbucket.org/decalage/olefileio_pl/issue/8/bug-with-file-object) - 2012-02-17 v0.22: fixed issues #7 (bug in getproperties) and #2 (added close method) - 2011-10-20: code hosted on bitbucket to ease contributions and bug tracking @@ -29,20 +48,50 @@ Follow all updates and news on Twitter: - 2009-12-10 v0.19: fixed support for 64 bits platforms (thanks to Ben G. and Martijn for reporting the bug) - see changelog in source code for more info. -Download --------- +Download/Install +---------------- -The archive is available on [the project page](https://bitbucket.org/decalage/olefileio_pl/downloads). +If you have pip or setuptools installed (pip is included in Python 2.7.9+), you may simply run **pip install olefile** +or **easy_install olefile** for the first installation. + +To update olefile, run **pip install -U olefile**. + +Otherwise, see https://bitbucket.org/decalage/olefileio_pl/wiki/Install Features -------- -- Parse and read any OLE file such as Microsoft Office 97-2003 legacy document formats (Word .doc, Excel .xls, PowerPoint .ppt, Visio .vsd, Project .mpp), Image Composer and FlashPix files, Outlook messages, StickyNotes, Zeiss AxioVision ZVI files, Olympus FluoView OIB files, ... +- Parse, read and write any OLE file such as Microsoft Office 97-2003 legacy document formats (Word .doc, Excel .xls, + PowerPoint .ppt, Visio .vsd, Project .mpp), Image Composer and FlashPix files, Outlook messages, StickyNotes, + Zeiss AxioVision ZVI files, Olympus FluoView OIB files, etc - List all the streams and storages contained in an OLE file - Open streams as files - Parse and read property streams, containing metadata of the file - Portable, pure Python module, no dependency +olefile can be used as an independent package or with PIL/Pillow. + +olefile is mostly meant for developers. If you are looking for tools to analyze OLE files or to extract data (especially +for security purposes such as malware analysis and forensics), then please also check my +[python-oletools](http://www.decalage.info/python/oletools), which are built upon olefile and provide a higher-level interface. + + +History +------- + +olefile is based on the OleFileIO module from [PIL](http://www.pythonware.com/products/pil/index.htm), the excellent +Python Imaging Library, created and maintained by Fredrik Lundh. The olefile API is still compatible with PIL, but +since 2005 I have improved the internal implementation significantly, with new features, bugfixes and a more robust +design. From 2005 to 2014 the project was called OleFileIO_PL, and in 2014 I changed its name to olefile to celebrate +its 9 years and its new write features. + +As far as I know, olefile is the most complete and robust Python implementation to read MS OLE2 files, portable on +several operating systems. (please tell me if you know other similar Python modules) + +Since 2014 olefile/OleFileIO_PL has been integrated into [Pillow](http://python-imaging.github.io/), the friendly fork +of PIL. olefile will continue to be improved as a separate project, and new versions will be merged into Pillow +regularly. + Main improvements over the original version of OleFileIO in PIL: ---------------------------------------------------------------- @@ -58,294 +107,74 @@ Main improvements over the original version of OleFileIO in PIL: - Can open file-like objects - Added setup.py and install.bat to ease installation - More convenient slash-based syntax for stream paths +- Write features +Documentation +------------- +Please see the [online documentation](https://bitbucket.org/decalage/olefileio_pl/wiki) for more information, +especially the [OLE overview](https://bitbucket.org/decalage/olefileio_pl/wiki/OLE_Overview) and the +[API page](https://bitbucket.org/decalage/olefileio_pl/wiki/API) which describe how to use olefile in Python applications. +A copy of the same documentation is also provided in the doc subfolder of the olefile package. -How to use this module ----------------------- - -OleFileIO_PL can be used as an independent module or with PIL. The main functions and methods are explained below. - -For more information, see also the file **OleFileIO_PL.html**, sample code at the end of the module itself, and docstrings within the code. - -### About the structure of OLE files ### - -An OLE file can be seen as a mini file system or a Zip archive: It contains **streams** of data that look like files embedded within the OLE file. Each stream has a name. For example, the main stream of a MS Word document containing its text is named "WordDocument". - -An OLE file can also contain **storages**. A storage is a folder that contains streams or other storages. For example, a MS Word document with VBA macros has a storage called "Macros". - -Special streams can contain **properties**. A property is a specific value that can be used to store information such as the metadata of a document (title, author, creation date, etc). Property stream names usually start with the character '\x05'. - -For example, a typical MS Word document may look like this: - - \x05DocumentSummaryInformation (stream) - \x05SummaryInformation (stream) - WordDocument (stream) - Macros (storage) - PROJECT (stream) - PROJECTwm (stream) - VBA (storage) - Module1 (stream) - ThisDocument (stream) - _VBA_PROJECT (stream) - dir (stream) - ObjectPool (storage) - - - -### Import OleFileIO_PL ### - - :::python - import OleFileIO_PL - -As of version 0.30, the code has been changed to be compatible with Python 3.x. As a consequence, compatibility with Python 2.5 or older is not provided anymore. However, a copy of v0.26 is available as OleFileIO_PL2.py. If your application needs to be compatible with Python 2.5 or older, you may use the following code to load the old version when needed: - - :::python - try: - import OleFileIO_PL - except: - import OleFileIO_PL2 as OleFileIO_PL - -If you think OleFileIO_PL should stay compatible with Python 2.5 or older, please [contact me](http://decalage.info/contact). - - -### Test if a file is an OLE container ### - -Use isOleFile to check if the first bytes of the file contain the Magic for OLE files, before opening it. isOleFile returns True if it is an OLE file, False otherwise (new in v0.16). - - :::python - assert OleFileIO_PL.isOleFile('myfile.doc') - - -### Open an OLE file from disk ### - -Create an OleFileIO object with the file path as parameter: - - :::python - ole = OleFileIO_PL.OleFileIO('myfile.doc') - -### Open an OLE file from a file-like object ### - -This is useful if the file is not on disk, e.g. already stored in a string or as a file-like object. - - :::python - ole = OleFileIO_PL.OleFileIO(f) - -For example the code below reads a file into a string, then uses BytesIO to turn it into a file-like object. - - :::python - data = open('myfile.doc', 'rb').read() - f = io.BytesIO(data) # or StringIO.StringIO for Python 2.x - ole = OleFileIO_PL.OleFileIO(f) - -### How to handle malformed OLE files ### - -By default, the parser is configured to be as robust and permissive as possible, allowing to parse most malformed OLE files. Only fatal errors will raise an exception. It is possible to tell the parser to be more strict in order to raise exceptions for files that do not fully conform to the OLE specifications, using the raise_defect option (new in v0.14): - - :::python - ole = OleFileIO_PL.OleFileIO('myfile.doc', raise_defects=DEFECT_INCORRECT) - -When the parsing is done, the list of non-fatal issues detected is available as a list in the parsing_issues attribute of the OleFileIO object (new in 0.25): - - :::python - print('Non-fatal issues raised during parsing:') - if ole.parsing_issues: - for exctype, msg in ole.parsing_issues: - print('- %s: %s' % (exctype.__name__, msg)) - else: - print('None') - - -### Syntax for stream and storage path ### - -Two different syntaxes are allowed for methods that need or return the path of streams and storages: - -1) Either a **list of strings** including all the storages from the root up to the stream/storage name. For example a stream called "WordDocument" at the root will have ['WordDocument'] as full path. A stream called "ThisDocument" located in the storage "Macros/VBA" will be ['Macros', 'VBA', 'ThisDocument']. This is the original syntax from PIL. While hard to read and not very convenient, this syntax works in all cases. - -2) Or a **single string with slashes** to separate storage and stream names (similar to the Unix path syntax). The previous examples would be 'WordDocument' and 'Macros/VBA/ThisDocument'. This syntax is easier, but may fail if a stream or storage name contains a slash. (new in v0.15) - -Both are case-insensitive. - -Switching between the two is easy: - - :::python - slash_path = '/'.join(list_path) - list_path = slash_path.split('/') - - -### Get the list of streams ### - -listdir() returns a list of all the streams contained in the OLE file, including those stored in storages. Each stream is listed itself as a list, as described above. - - :::python - print(ole.listdir()) - -Sample result: - - :::python - [['\x01CompObj'], ['\x05DocumentSummaryInformation'], ['\x05SummaryInformation'] - , ['1Table'], ['Macros', 'PROJECT'], ['Macros', 'PROJECTwm'], ['Macros', 'VBA', - 'Module1'], ['Macros', 'VBA', 'ThisDocument'], ['Macros', 'VBA', '_VBA_PROJECT'] - , ['Macros', 'VBA', 'dir'], ['ObjectPool'], ['WordDocument']] - -As an option it is possible to choose if storages should also be listed, with or without streams (new in v0.26): - - :::python - ole.listdir (streams=False, storages=True) - - -### Test if known streams/storages exist: ### - -exists(path) checks if a given stream or storage exists in the OLE file (new in v0.16). - - :::python - if ole.exists('worddocument'): - print("This is a Word document.") - if ole.exists('macros/vba'): - print("This document seems to contain VBA macros.") - - -### Read data from a stream ### - -openstream(path) opens a stream as a file-like object. - -The following example extracts the "Pictures" stream from a PPT file: - - :::python - pics = ole.openstream('Pictures') - data = pics.read() - - -### Get information about a stream/storage ### - -Several methods can provide the size, type and timestamps of a given stream/storage: - -get_size(path) returns the size of a stream in bytes (new in v0.16): - - :::python - s = ole.get_size('WordDocument') - -get_type(path) returns the type of a stream/storage, as one of the following constants: STGTY\_STREAM for a stream, STGTY\_STORAGE for a storage, STGTY\_ROOT for the root entry, and False for a non existing path (new in v0.15). - - :::python - t = ole.get_type('WordDocument') - -get\_ctime(path) and get\_mtime(path) return the creation and modification timestamps of a stream/storage, as a Python datetime object with UTC timezone. Please note that these timestamps are only present if the application that created the OLE file explicitly stored them, which is rarely the case. When not present, these methods return None (new in v0.26). - - :::python - c = ole.get_ctime('WordDocument') - m = ole.get_mtime('WordDocument') - -The root storage is a special case: You can get its creation and modification timestamps using the OleFileIO.root attribute (new in v0.26): - - :::python - c = ole.root.getctime() - m = ole.root.getmtime() - -### Extract metadata ### - -get_metadata() will check if standard property streams exist, parse all the properties they contain, and return an OleMetadata object with the found properties as attributes (new in v0.24). - - :::python - meta = ole.get_metadata() - print('Author:', meta.author) - print('Title:', meta.title) - print('Creation date:', meta.create_time) - # print all metadata: - meta.dump() - -Available attributes include: - - codepage, title, subject, author, keywords, comments, template, - last_saved_by, revision_number, total_edit_time, last_printed, create_time, - last_saved_time, num_pages, num_words, num_chars, thumbnail, - creating_application, security, codepage_doc, category, presentation_target, - bytes, lines, paragraphs, slides, notes, hidden_slides, mm_clips, - scale_crop, heading_pairs, titles_of_parts, manager, company, links_dirty, - chars_with_spaces, unused, shared_doc, link_base, hlinks, hlinks_changed, - version, dig_sig, content_type, content_status, language, doc_version - -See the source code of the OleMetadata class for more information. - - -### Parse a property stream ### - -get\_properties(path) can be used to parse any property stream that is not handled by get\_metadata. It returns a dictionary indexed by integers. Each integer is the index of the property, pointing to its value. For example in the standard property stream '\x05SummaryInformation', the document title is property #2, and the subject is #3. - - :::python - p = ole.getproperties('specialprops') - -By default as in the original PIL version, timestamp properties are converted into a number of seconds since Jan 1,1601. With the option convert\_time, you can obtain more convenient Python datetime objects (UTC timezone). If some time properties should not be converted (such as total editing time in '\x05SummaryInformation'), the list of indexes can be passed as no_conversion (new in v0.25): - - :::python - p = ole.getproperties('specialprops', convert_time=True, no_conversion=[10]) - - -### Close the OLE file ### - -Unless your application is a simple script that terminates after processing an OLE file, do not forget to close each OleFileIO object after parsing to close the file on disk. (new in v0.22) - - :::python - ole.close() - -### Use OleFileIO_PL as a script ### - -OleFileIO_PL can also be used as a script from the command-line to display the structure of an OLE file and its metadata, for example: - - OleFileIO_PL.py myfile.doc - -You can use the option -c to check that all streams can be read fully, and -d to generate very verbose debugging information. ## Real-life examples ## A real-life example: [using OleFileIO_PL for malware analysis and forensics](http://blog.gregback.net/2011/03/using-remnux-for-forensic-puzzle-6/). -See also [this paper](https://computer-forensics.sans.org/community/papers/gcfa/grow-forensic-tools-taxonomy-python-libraries-helpful-forensic-analysis_6879) about python tools for forensics, which features OleFileIO_PL. +See also [this paper](https://computer-forensics.sans.org/community/papers/gcfa/grow-forensic-tools-taxonomy-python-libraries-helpful-forensic-analysis_6879) about python tools for forensics, which features olefile. -About Python 2 and 3 --------------------- - -OleFileIO\_PL used to support only Python 2.x. As of version 0.30, the code has been changed to be compatible with Python 3.x. As a consequence, compatibility with Python 2.5 or older is not provided anymore. However, a copy of v0.26 is available as OleFileIO_PL2.py. See above the "import" section for a workaround. - -If you think OleFileIO_PL should stay compatible with Python 2.5 or older, please [contact me](http://decalage.info/contact). - -How to contribute ------------------ - -The code is available in [a Mercurial repository on bitbucket](https://bitbucket.org/decalage/olefileio_pl). You may use it to submit enhancements or to report any issue. - -If you would like to help us improve this module, or simply provide feedback, please [contact me](http://decalage.info/contact). You can help in many ways: - -- test this module on different platforms / Python versions -- find and report bugs -- improve documentation, code samples, docstrings -- write unittest test cases -- provide tricky malformed files - -How to report bugs ------------------- - -To report a bug, for example a normal file which is not parsed correctly, please use the [issue reporting page](https://bitbucket.org/decalage/olefileio_pl/issues?status=new&status=open), or if you prefer to do it privately, use this [contact form](http://decalage.info/contact). Please provide all the information about the context and how to reproduce the bug. - -If possible please join the debugging output of OleFileIO_PL. For this, launch the following command : - - OleFileIO_PL.py -d -c file >debug.txt License ------- -OleFileIO_PL is open-source. +olefile (formerly OleFileIO_PL) is copyright (c) 2005-2015 Philippe Lagadec +([http://www.decalage.info](http://www.decalage.info)) -OleFileIO_PL changes are Copyright (c) 2005-2014 by Philippe Lagadec. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +---------- + +olefile is based on source code from the OleFileIO module of the Python Imaging Library (PIL) published by Fredrik +Lundh under the following license: The Python Imaging Library (PIL) is - Copyright (c) 1997-2005 by Secret Labs AB - - Copyright (c) 1995-2005 by Fredrik Lundh -By obtaining, using, and/or copying this software and/or its associated documentation, you agree that you have read, understood, and will comply with the following terms and conditions: +By obtaining, using, and/or copying this software and/or its associated documentation, you agree that you have read, +understood, and will comply with the following terms and conditions: -Permission to use, copy, modify, and distribute this software and its associated documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appears in all copies, and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Secret Labs AB or the author not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. +Permission to use, copy, modify, and distribute this software and its associated documentation for any purpose and +without fee is hereby granted, provided that the above copyright notice appears in all copies, and that both that +copyright notice and this permission notice appear in supporting documentation, and that the name of Secret Labs AB or +the author not be used in advertising or publicity pertaining to distribution of the software without specific, written +prior permission. -SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF +CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. From 5f904ace9d18e47fa34cc224a8bd296c9d4ceaf6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 9 Dec 2015 16:55:33 +1100 Subject: [PATCH 0779/1037] Removed warnings check --- PIL/Image.py | 13 ++++++------- PIL/ImageDraw.py | 12 +++--------- PIL/ImageFont.py | 5 ----- 3 files changed, 9 insertions(+), 21 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index af6dac1c4..85ffcf976 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -2103,13 +2103,12 @@ def frombuffer(mode, size, data, decoder_name="raw", *args): if decoder_name == "raw": if args == (): - if warnings: - warnings.warn( - "the frombuffer defaults may change in a future release; " - "for portability, change the call to read:\n" - " frombuffer(mode, size, data, 'raw', mode, 0, 1)", - RuntimeWarning, stacklevel=2 - ) + warnings.warn( + "the frombuffer defaults may change in a future release; " + "for portability, change the call to read:\n" + " frombuffer(mode, size, data, 'raw', mode, 0, 1)", + RuntimeWarning, stacklevel=2 + ) args = mode, 0, -1 # may change to (mode, 0, 1) post-1.1.6 if args[0] in _MAPMODES: im = new(mode, (1, 1)) diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py index f37b41557..701c13060 100644 --- a/PIL/ImageDraw.py +++ b/PIL/ImageDraw.py @@ -31,16 +31,11 @@ # import numbers +import warnings from PIL import Image, ImageColor from PIL._util import isStringType -try: - import warnings -except ImportError: - warnings = None - - ## # A simple 2D drawing interface for PIL images. #

@@ -99,9 +94,8 @@ class ImageDraw(object): "Please use keyword arguments instead.") def setfont(self, font): - if warnings: - warnings.warn("setfont() is deprecated. " + - "Please set the attribute directly instead.") + warnings.warn("setfont() is deprecated. " + + "Please set the attribute directly instead.") # compatibility self.font = font diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index c3ec57994..af1166dde 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -30,11 +30,6 @@ from PIL._util import isDirectory, isPath import os import sys -try: - import warnings -except ImportError: - warnings = None - class _imagingft_not_installed(object): # module placeholder From 3b3b559d9ed28bed15c5727395b98dfd259fc890 Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 9 Dec 2015 11:31:41 +0200 Subject: [PATCH 0780/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 02a54d959..53b07e47f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,12 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Removed warnings module check #1587 + [radarhere] + +- Changed arcs, chords and pie slices to use floats #1577 + [radarhere] + - Update unit test asserts #1584 [radarhere] @@ -28,7 +34,7 @@ Changelog (Pillow) - ExtraSamples tag should be a SHORT, not a BYTE #1555 [Nexuapex] -- Docs and code health fixes #1565 #1566 +- Docs and code health fixes #1565 #1566 #1581 #1586 [radarhere] - Updated freetype to 2.6.2 #1564 From 901451760226ef9b05e0e3e4a9a6013141fb4476 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 10 Dec 2015 08:39:26 +1100 Subject: [PATCH 0781/1037] Removed logger exception --- PIL/ImageFile.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index f26a7260e..9617ffb3b 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -30,13 +30,10 @@ from PIL import Image from PIL._util import isPath import io -import logging import os import sys import struct -logger = logging.getLogger(__name__) - MAXBLOCK = 65536 SAFEBLOCK = 1024*1024 @@ -103,7 +100,6 @@ class ImageFile(Image.Image): KeyError, # unsupported mode EOFError, # got header but not the first frame struct.error) as v: - logger.exception("%s") raise SyntaxError(v) if not self.mode or self.size[0] <= 0: From 8d3b240096a2f9b888942a7a643ed4574e0edd09 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 10 Dec 2015 08:48:42 +1100 Subject: [PATCH 0782/1037] Update CHANGES.rst --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 53b07e47f..24bb59e71 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -34,7 +34,7 @@ Changelog (Pillow) - ExtraSamples tag should be a SHORT, not a BYTE #1555 [Nexuapex] -- Docs and code health fixes #1565 #1566 #1581 #1586 +- Docs and code health fixes #1565 #1566 #1581 #1586 [radarhere] - Updated freetype to 2.6.2 #1564 From 384d32969d3f6701477a616356ed5c180215103a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 9 Dec 2015 15:47:53 +1100 Subject: [PATCH 0783/1037] Replaced flush try except with hasattr --- PIL/GifImagePlugin.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 391ddd332..b9d258898 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -397,10 +397,8 @@ def _save(im, fp, filename, save_all=False): fp.write(b";") # end of file - try: + if hasattr(fp, "flush"): fp.flush() - except: - pass def get_interlace(im): From 9176633d607b8528af3a48e4ba2be574d80ce35e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 9 Dec 2015 16:24:32 +1100 Subject: [PATCH 0784/1037] Specified exception type --- PIL/EpsImagePlugin.py | 2 +- Tests/large_memory_numpy_test.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py index 6b2a57c03..a950c52b9 100644 --- a/PIL/EpsImagePlugin.py +++ b/PIL/EpsImagePlugin.py @@ -295,7 +295,7 @@ class EpsImageFile(ImageFile.ImageFile): break try: self.mode = self.mode_map[int(mo)] - except: + except ValueError: break self.size = int(x), int(y) diff --git a/Tests/large_memory_numpy_test.py b/Tests/large_memory_numpy_test.py index c642549aa..5d1b42a22 100644 --- a/Tests/large_memory_numpy_test.py +++ b/Tests/large_memory_numpy_test.py @@ -13,7 +13,7 @@ from helper import unittest, PillowTestCase from PIL import Image try: import numpy as np -except: +except ImportError: raise unittest.SkipTest("numpy not installed") YDIM = 32769 From 71d45bc61efe137007319109c4465320a1b79c87 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 10 Dec 2015 09:23:04 +1100 Subject: [PATCH 0785/1037] Corrected line length greater than 80 --- PIL/BmpImagePlugin.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index d9aaf193b..3f4f403da 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -134,9 +134,13 @@ class BmpImageFile(ImageFile.ImageFile): 24: [(0xff0000, 0xff00, 0xff)], 16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]} MASK_MODES = { - (32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX", (32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA", (32, (0x0, 0x0, 0x0, 0x0)): "BGRA", - (24, (0xff0000, 0xff00, 0xff)): "BGR", - (16, (0xf800, 0x7e0, 0x1f)): "BGR;16", (16, (0x7c00, 0x3e0, 0x1f)): "BGR;15"} + (32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX", + (32, (0xff0000, 0xff00, 0xff, 0xff000000)):"BGRA", + (32, (0x0, 0x0, 0x0, 0x0)): "BGRA", + (24, (0xff0000, 0xff00, 0xff)): "BGR", + (16, (0xf800, 0x7e0, 0x1f)): "BGR;16", + (16, (0x7c00, 0x3e0, 0x1f)): "BGR;15" + } if file_info['bits'] in SUPPORTED: if file_info['bits'] == 32 and file_info['rgba_mask'] in SUPPORTED[file_info['bits']]: raw_mode = MASK_MODES[(file_info['bits'], file_info['rgba_mask'])] From 0b64d6d8bf5481d590bce683d5fdf95fc693ec26 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 9 Dec 2015 17:17:14 +1100 Subject: [PATCH 0786/1037] Removed unnecessary pass statement --- PIL/JpegImagePlugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 8d25ffe66..7bd4b5335 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -719,7 +719,6 @@ def jpeg_factory(fp=None, filename=None): except SyntaxError: warnings.warn("Image appears to be a malformed MPO file, it will be " "interpreted as a base JPEG file") - pass return im From 1c99b392391a334e426d8e32a298b1e969b2ac85 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 10 Dec 2015 09:23:58 +1100 Subject: [PATCH 0787/1037] Fixed redefined built-in --- Tests/test_imageops_usm.py | 8 ++++---- Tests/test_numpy.py | 8 ++++---- Tests/test_olefileio.py | 4 ++-- Tests/test_pickle.py | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Tests/test_imageops_usm.py b/Tests/test_imageops_usm.py index 89c63c774..ca567e38e 100644 --- a/Tests/test_imageops_usm.py +++ b/Tests/test_imageops_usm.py @@ -24,13 +24,13 @@ class TestImageOpsUsm(PillowTestCase): def test_filter_api(self): - filter = ImageFilter.GaussianBlur(2.0) - i = im.filter(filter) + test_filter = ImageFilter.GaussianBlur(2.0) + i = im.filter(test_filter) self.assertEqual(i.mode, "RGB") self.assertEqual(i.size, (128, 128)) - filter = ImageFilter.UnsharpMask(2.0, 125, 8) - i = im.filter(filter) + test_filter = ImageFilter.UnsharpMask(2.0, 125, 8) + i = im.filter(test_filter) self.assertEqual(i.mode, "RGB") self.assertEqual(i.size, (128, 128)) diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py index 64081894e..97e93c10d 100644 --- a/Tests/test_numpy.py +++ b/Tests/test_numpy.py @@ -22,9 +22,9 @@ class TestNumpy(PillowTestCase): def test_numpy_to_image(self): - def to_image(dtype, bands=1, bool=0): + def to_image(dtype, bands=1, boolean=0): if bands == 1: - if bool: + if boolean: data = [0, 1] * 50 else: data = list(range(100)) @@ -43,8 +43,8 @@ class TestNumpy(PillowTestCase): # print dtype, list(i.getdata()) return i - # self.assert_image(to_image(numpy.bool, bool=1), "1", (10, 10)) - # self.assert_image(to_image(numpy.bool8, bool=1), "1", (10, 10)) + # self.assert_image(to_image(numpy.bool, boolean=1), "1", (10, 10)) + # self.assert_image(to_image(numpy.bool8, boolean=1), "1", (10, 10)) self.assertRaises(TypeError, lambda: to_image(numpy.uint)) self.assert_image(to_image(numpy.uint8), "L", (10, 10)) diff --git a/Tests/test_olefileio.py b/Tests/test_olefileio.py index d3842e977..dae24d7f8 100644 --- a/Tests/test_olefileio.py +++ b/Tests/test_olefileio.py @@ -52,10 +52,10 @@ class TestOleFileIo(PillowTestCase): ole = OleFileIO.OleFileIO(ole_file) # Act - type = ole.get_type('worddocument') + entry_type = ole.get_type('worddocument') # Assert - self.assertEqual(type, OleFileIO.STGTY_STREAM) + self.assertEqual(entry_type, OleFileIO.STGTY_STREAM) ole.close() def test_get_size(self): diff --git a/Tests/test_pickle.py b/Tests/test_pickle.py index 52d64acee..9a74b854b 100644 --- a/Tests/test_pickle.py +++ b/Tests/test_pickle.py @@ -22,8 +22,8 @@ class TestPickle(PillowTestCase): self.assertEqual(im, loaded_im) def helper_pickle_string(self, pickle, protocol=0, - file='Tests/images/hopper.jpg', mode=None): - im = Image.open(file) + test_file='Tests/images/hopper.jpg', mode=None): + im = Image.open(test_file) if mode: im = im.convert(mode) @@ -69,7 +69,7 @@ class TestPickle(PillowTestCase): "Tests/images/p_trns_single.png", "Tests/images/pil123p.png" ]: - self.helper_pickle_string(pickle, file=test_file) + self.helper_pickle_string(pickle, test_file=test_file) def test_pickle_l_mode(self): # Arrange From c27110ab5677d9235d995a78fcbffe0423d50806 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 10 Dec 2015 09:35:35 +1100 Subject: [PATCH 0788/1037] Flake8 fixes --- PIL/BmpImagePlugin.py | 2 +- PIL/Image.py | 6 +++--- PIL/ImageDraw.py | 1 + PIL/ImageQt.py | 2 ++ PIL/PyAccess.py | 2 +- Scripts/pilprint.py | 2 +- Tests/test_file_spider.py | 2 +- Tests/test_file_tiff.py | 6 +++--- Tests/test_file_tiff_metadata.py | 8 +++----- Tests/test_image_fromqimage.py | 2 +- Tests/test_imagewin_pointers.py | 20 +++++++++----------- 11 files changed, 26 insertions(+), 27 deletions(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index 3f4f403da..e715d8583 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -135,7 +135,7 @@ class BmpImageFile(ImageFile.ImageFile): 16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]} MASK_MODES = { (32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX", - (32, (0xff0000, 0xff00, 0xff, 0xff000000)):"BGRA", + (32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA", (32, (0x0, 0x0, 0x0, 0x0)): "BGRA", (24, (0xff0000, 0xff00, 0xff)): "BGR", (16, (0xf800, 0x7e0, 0x1f)): "BGR;16", diff --git a/PIL/Image.py b/PIL/Image.py index 85ffcf976..43abf1961 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -649,9 +649,9 @@ class Image(object): def tobytes(self, encoder_name="raw", *args): """ Return image as a bytes object. - + .. warning:: - + This method returns the raw image data from the internal storage. For compressed image data (e.g. PNG, JPEG) use :meth:`~.save`, with a BytesIO parameter for in-memory @@ -2277,7 +2277,7 @@ def open(fp, mode="r"): except (SyntaxError, IndexError, TypeError, struct.error): # Leave disabled by default, spams the logs with image # opening failures that are entirely expected. - #logger.debug("", exc_info=True) + # logger.debug("", exc_info=True) continue return None diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py index 701c13060..5fabf6090 100644 --- a/PIL/ImageDraw.py +++ b/PIL/ImageDraw.py @@ -42,6 +42,7 @@ from PIL._util import isStringType # Application code should use the Draw factory, instead of # directly. + class ImageDraw(object): ## diff --git a/PIL/ImageQt.py b/PIL/ImageQt.py index a67dde8ee..aece9d62a 100644 --- a/PIL/ImageQt.py +++ b/PIL/ImageQt.py @@ -85,6 +85,7 @@ def fromqpixmap(im): # bytes_io.seek(0) # return Image.open(bytes_io) + def align8to32(bytes, width, mode): """ converts each scanline of data from 8 bit to 32 bit aligned @@ -113,6 +114,7 @@ def align8to32(bytes, width, mode): return b''.join(new_data) + def _toqclass_helper(im): data = None colortable = None diff --git a/PIL/PyAccess.py b/PIL/PyAccess.py index 0d4c8b235..faa868c12 100644 --- a/PIL/PyAccess.py +++ b/PIL/PyAccess.py @@ -56,7 +56,7 @@ class PyAccess(object): # Debugging is polluting test traces, only useful here # when hacking on PyAccess - #logger.debug("%s", vals) + # logger.debug("%s", vals) self._post_init() def _post_init(self): diff --git a/Scripts/pilprint.py b/Scripts/pilprint.py index e81a075a0..6452a3510 100755 --- a/Scripts/pilprint.py +++ b/Scripts/pilprint.py @@ -64,7 +64,7 @@ for o, a in opt: printerArgs = ["lpr"] elif o == "-P": # printer channel - printerArgs = ["lpr","-P%s" % a] + printerArgs = ["lpr", "-P%s" % a] for filepath in argv: try: diff --git a/Tests/test_file_spider.py b/Tests/test_file_spider.py index 8bd648e78..53cf3915b 100644 --- a/Tests/test_file_spider.py +++ b/Tests/test_file_spider.py @@ -82,7 +82,7 @@ class TestImageSpider(PillowTestCase): def test_invalid_file(self): invalid_file = "Tests/images/invalid.spider" - + self.assertRaises(IOError, lambda: Image.open(invalid_file)) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 308783fb3..776718420 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -80,11 +80,11 @@ class TestFileTiff(PillowTestCase): filename = "Tests/images/pil168.tif" im = Image.open(filename) - #legacy api + # legacy api self.assert_(isinstance(im.tag[X_RESOLUTION][0], tuple)) self.assert_(isinstance(im.tag[Y_RESOLUTION][0], tuple)) - #v2 api + # v2 api self.assert_(isinstance(im.tag_v2[X_RESOLUTION], float)) self.assert_(isinstance(im.tag_v2[Y_RESOLUTION], float)) @@ -249,7 +249,7 @@ class TestFileTiff(PillowTestCase): {256: 55, 257: 43, 258: (8, 8, 8, 8), 259: 1, 262: 2, 296: 2, 273: (8,), 338: (1,), 277: 4, 279: (9460,), 282: 72.0, 283: 72.0, 284: 1}) - + # legacy interface self.assertEqual( im.tag.as_dict(), diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index e7a6f9e87..620fc03ba 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -28,7 +28,7 @@ class TestFileTiffMetadata(PillowTestCase): # # For text items, we still have to decode('ascii','replace') because # the tiff file format can't take 8 bit bytes in that field. - + basetextdata = "This is some arbitrary metadata for a text field" bindata = basetextdata.encode('ascii') + b" \xff" textdata = basetextdata + " " + chr(255) @@ -40,7 +40,7 @@ class TestFileTiffMetadata(PillowTestCase): ImageJMetaData = tag_ids['ImageJMetaData'] ImageJMetaDataByteCounts = tag_ids['ImageJMetaDataByteCounts'] ImageDescription = tag_ids['ImageDescription'] - + info[ImageJMetaDataByteCounts] = len(bindata) info[ImageJMetaData] = bindata info[tag_ids['RollAngle']] = floatdata @@ -49,7 +49,7 @@ class TestFileTiffMetadata(PillowTestCase): info.tagtype[tag_ids['YawAngle']] = 12 info[ImageDescription] = textdata - + f = self.tempfile("temp.tif") img.save(f, tiffinfo=info) @@ -70,7 +70,6 @@ class TestFileTiffMetadata(PillowTestCase): loaded_double = loaded.tag[tag_ids['YawAngle']][0] self.assertAlmostEqual(loaded_double, doubledata) - def test_read_metadata(self): img = Image.open('Tests/images/hopper_g4.tif') @@ -167,7 +166,6 @@ class TestFileTiffMetadata(PillowTestCase): self.assert_(im.info['icc_profile']) - if __name__ == '__main__': unittest.main() diff --git a/Tests/test_image_fromqimage.py b/Tests/test_image_fromqimage.py index 57a95f104..a4cf76fb9 100644 --- a/Tests/test_image_fromqimage.py +++ b/Tests/test_image_fromqimage.py @@ -11,7 +11,7 @@ class TestFromQImage(PillowQtTestCase, PillowTestCase): Image.open('Tests/images/transparent.png'), Image.open('Tests/images/7x13.png'), ] - + def roundtrip(self, expected): # PIL -> Qt intermediate = expected.toqimage() diff --git a/Tests/test_imagewin_pointers.py b/Tests/test_imagewin_pointers.py index 2d47014f3..bf7f4bb7c 100644 --- a/Tests/test_imagewin_pointers.py +++ b/Tests/test_imagewin_pointers.py @@ -9,7 +9,7 @@ from io import BytesIO if sys.platform.startswith('win32'): import ctypes.wintypes - + class BITMAPFILEHEADER(ctypes.Structure): _pack_ = 2 _fields_ = [ @@ -40,27 +40,26 @@ if sys.platform.startswith('win32'): DIB_RGB_COLORS = 0 memcpy = ctypes.cdll.msvcrt.memcpy - memcpy.argtypes = [ ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t ] + memcpy.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t] CreateCompatibleDC = ctypes.windll.gdi32.CreateCompatibleDC - CreateCompatibleDC.argtypes = [ ctypes.wintypes.HDC ] + CreateCompatibleDC.argtypes = [ctypes.wintypes.HDC] CreateCompatibleDC.restype = ctypes.wintypes.HDC DeleteDC = ctypes.windll.gdi32.DeleteDC - DeleteDC.argtypes = [ ctypes.wintypes.HDC ] + DeleteDC.argtypes = [ctypes.wintypes.HDC] SelectObject = ctypes.windll.gdi32.SelectObject - SelectObject.argtypes = [ ctypes.wintypes.HDC, ctypes.wintypes.HGDIOBJ ] + SelectObject.argtypes = [ctypes.wintypes.HDC, ctypes.wintypes.HGDIOBJ] SelectObject.restype = ctypes.wintypes.HGDIOBJ DeleteObject = ctypes.windll.gdi32.DeleteObject - DeleteObject.argtypes = [ ctypes.wintypes.HGDIOBJ ] + DeleteObject.argtypes = [ctypes.wintypes.HGDIOBJ] CreateDIBSection = ctypes.windll.gdi32.CreateDIBSection - CreateDIBSection.argtypes = [ ctypes.wintypes.HDC, ctypes.c_void_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_void_p), ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD ] + CreateDIBSection.argtypes = [ctypes.wintypes.HDC, ctypes.c_void_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_void_p), ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD] CreateDIBSection.restype = ctypes.wintypes.HBITMAP - def serialize_dib(bi, pixels): bf = BITMAPFILEHEADER() bf.bfType = 0x4d42 @@ -78,7 +77,7 @@ if sys.platform.startswith('win32'): class TestImageWinPointers(PillowTestCase): def test_pointer(self): im = hopper() - (width, height) =im.size + (width, height) = im.size opath = self.tempfile('temp.png') imdib = ImageWin.Dib(im) @@ -94,7 +93,7 @@ if sys.platform.startswith('win32'): hdr.biClrImportant = 0 hdc = CreateCompatibleDC(None) - #print('hdc:',hex(hdc)) + # print('hdc:',hex(hdc)) pixels = ctypes.c_void_p() dib = CreateDIBSection(hdc, ctypes.byref(hdr), DIB_RGB_COLORS, ctypes.byref(pixels), None, 0) SelectObject(hdc, dib) @@ -108,4 +107,3 @@ if sys.platform.startswith('win32'): if __name__ == '__main__': unittest.main() - From 096b532a90878dc422ed2def61bc9713ccaf66b2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 10 Dec 2015 21:34:22 +1100 Subject: [PATCH 0789/1037] Removed unused variable --- PIL/ImageDraw.py | 1 - 1 file changed, 1 deletion(-) diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py index 5fabf6090..7f3e68c27 100644 --- a/PIL/ImageDraw.py +++ b/PIL/ImageDraw.py @@ -298,7 +298,6 @@ class ImageDraw(object): def multiline_textsize(self, text, font=None, spacing=0): max_width = 0 - height = 0 lines = self._multiline_split(text) line_spacing = self.textsize('A', font=font)[1] + spacing for line in lines: From b68bb56b647de6271a3cc8f220188af524d55675 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 14 Dec 2015 20:24:01 +0000 Subject: [PATCH 0790/1037] test for issue #1561 --- Tests/test_file_libtiff.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index f900b97cf..838b59365 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -165,6 +165,13 @@ class TestFileLibTiff(LibTiffTestCase): self.assertEqual( val, value, msg="%s didn't roundtrip" % tag) + # https://github.com/python-pillow/Pillow/issues/1561 + requested_fields = ['StripByteCounts', + 'RowsPerStrip', + 'StripOffsets'] + for field in requested_fields: + self.assertTrue(field in reloaded, "%s not in metadata" %field) + def test_g3_compression(self): i = Image.open('Tests/images/hopper_g4_500.tif') out = self.tempfile("temp.tif") From fe35a18db0f257e12dcb7d170c5a7cb35e2c5eb9 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 9 Dec 2015 20:39:49 +0000 Subject: [PATCH 0791/1037] Possible fix for issue #1561, let rows per strip through, but block the other two --- PIL/TiffImagePlugin.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index a84d01755..298c48759 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -1277,8 +1277,10 @@ def _save(im, fp, filename): except io.UnsupportedOperation: pass - # ICC Profile crashes. - blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ROWSPERSTRIP, ICCPROFILE] + # STRIPOFFSETS and STRIPBYTECOUNTS are added by the library + # based on the data in the strip. + # ICCPROFILE crashes. + blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ICCPROFILE] atts = {} # bits per sample is a single short in the tiff directory, not a list. atts[BITSPERSAMPLE] = bits[0] From 004a60964c469743f453bd26336dcfe38ddd6e6d Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 14 Dec 2015 20:47:21 +0000 Subject: [PATCH 0792/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 24bb59e71..44aca8da1 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Removed logger.exception from ImageFile.py #1590 + [radarhere] + - Removed warnings module check #1587 [radarhere] From 9232b98bc71a45651cec5ca771355e23ccf7ce93 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 14 Dec 2015 20:50:26 +0000 Subject: [PATCH 0793/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 44aca8da1..029fb455c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Changed some urls in the docs to use https #1580 + [hugovk] + - Removed logger.exception from ImageFile.py #1590 [radarhere] From 7f1f5ff248ad260d939afa5a8ffa1fe8fc42cdc3 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 14 Dec 2015 20:57:24 +0000 Subject: [PATCH 0794/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 029fb455c..15f3d57af 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,8 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Enabled conversion to numpy array for HSV images. Fixes issue #1578 + [cartisan] - Changed some urls in the docs to use https #1580 [hugovk] From d81509b6aa45086b9f1fc3a2478298a65067daf2 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 14 Dec 2015 21:01:57 +0000 Subject: [PATCH 0795/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 15f3d57af..b3697b1b0 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,10 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ + +- Health fixes #1591 + [radarhere] + - Enabled conversion to numpy array for HSV images. Fixes issue #1578 [cartisan] From ae83c18c91be73535ec788e7de6a055d70ff7ac3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 15 Dec 2015 23:36:16 +1100 Subject: [PATCH 0796/1037] Added field type constants to TiffTags --- PIL/TiffTags.py | 174 +++++++++++++++++++++++++----------------------- 1 file changed, 90 insertions(+), 84 deletions(-) diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py index 2eb06d315..d8e304d87 100644 --- a/PIL/TiffTags.py +++ b/PIL/TiffTags.py @@ -35,119 +35,125 @@ class TagInfo(namedtuple("_TagInfo", "value name type length enum")): # # id: (Name, Type, Length, enum_values) # + +ASCII = 2 +SHORT = 3 +LONG = 4 +RATIONAL = 5 + TAGS_V2 = { - 254: ("NewSubfileType", 4, 1), - 255: ("SubfileType", 3, 1), - 256: ("ImageWidth", 4, 1), - 257: ("ImageLength", 4, 1), - 258: ("BitsPerSample", 3, 0), - 259: ("Compression", 3, 1, + 254: ("NewSubfileType", LONG, 1), + 255: ("SubfileType", SHORT, 1), + 256: ("ImageWidth", LONG, 1), + 257: ("ImageLength", LONG, 1), + 258: ("BitsPerSample", SHORT, 0), + 259: ("Compression", SHORT, 1, {"Uncompressed": 1, "CCITT 1d": 2, "Group 3 Fax": 3, "Group 4 Fax": 4, "LZW": 5, "JPEG": 6, "PackBits": 32773}), - 262: ("PhotometricInterpretation", 3, 1, + 262: ("PhotometricInterpretation", SHORT, 1, {"WhiteIsZero": 0, "BlackIsZero": 1, "RGB": 2, "RBG Palette": 3, "Transparency Mask": 4, "CMYK": 5, "YCbCr": 6, "CieLAB": 8, "CFA": 32803, # TIFF/EP, Adobe DNG "LinearRaw": 32892}), # Adobe DNG - 263: ("Thresholding", 3, 1), - 264: ("CellWidth", 3, 1), - 265: ("CellHeight", 3, 1), - 266: ("FillOrder", 3, 1), - 269: ("DocumentName", 2, 1), + 263: ("Thresholding", SHORT, 1), + 264: ("CellWidth", SHORT, 1), + 265: ("CellHeight", SHORT, 1), + 266: ("FillOrder", SHORT, 1), + 269: ("DocumentName", ASCII, 1), - 270: ("ImageDescription", 2, 1), - 271: ("Make", 2, 1), - 272: ("Model", 2, 1), - 273: ("StripOffsets", 4, 0), - 274: ("Orientation", 3, 1), - 277: ("SamplesPerPixel", 3, 1), - 278: ("RowsPerStrip", 4, 1), - 279: ("StripByteCounts", 4, 0), + 270: ("ImageDescription", ASCII, 1), + 271: ("Make", ASCII, 1), + 272: ("Model", ASCII, 1), + 273: ("StripOffsets", LONG, 0), + 274: ("Orientation", SHORT, 1), + 277: ("SamplesPerPixel", SHORT, 1), + 278: ("RowsPerStrip", LONG, 1), + 279: ("StripByteCounts", LONG, 0), - 280: ("MinSampleValue", 4, 0), - 281: ("MaxSampleValue", 3, 0), - 282: ("XResolution", 5, 1), - 283: ("YResolution", 5, 1), - 284: ("PlanarConfiguration", 3, 1, {"Contigous": 1, "Separate": 2}), - 285: ("PageName", 2, 1), - 286: ("XPosition", 5, 1), - 287: ("YPosition", 5, 1), - 288: ("FreeOffsets", 4, 1), - 289: ("FreeByteCounts", 4, 1), + 280: ("MinSampleValue", LONG, 0), + 281: ("MaxSampleValue", SHORT, 0), + 282: ("XResolution", RATIONAL, 1), + 283: ("YResolution", RATIONAL, 1), + 284: ("PlanarConfiguration", SHORT, 1, {"Contigous": 1, "Separate": 2}), + 285: ("PageName", ASCII, 1), + 286: ("XPosition", RATIONAL, 1), + 287: ("YPosition", RATIONAL, 1), + 288: ("FreeOffsets", LONG, 1), + 289: ("FreeByteCounts", LONG, 1), - 290: ("GrayResponseUnit", 3, 1), - 291: ("GrayResponseCurve", 3, 0), - 292: ("T4Options", 4, 1), - 293: ("T6Options", 4, 1), - 296: ("ResolutionUnit", 3, 1, {"inch": 1, "cm": 2}), - 297: ("PageNumber", 3, 2), + 290: ("GrayResponseUnit", SHORT, 1), + 291: ("GrayResponseCurve", SHORT, 0), + 292: ("T4Options", LONG, 1), + 293: ("T6Options", LONG, 1), + 296: ("ResolutionUnit", SHORT, 1, {"inch": 1, "cm": 2}), + 297: ("PageNumber", SHORT, 2), - 301: ("TransferFunction", 3, 0), - 305: ("Software", 2, 1), - 306: ("DateTime", 2, 1), + 301: ("TransferFunction", SHORT, 0), + 305: ("Software", ASCII, 1), + 306: ("DateTime", ASCII, 1), - 315: ("Artist", 2, 1), - 316: ("HostComputer", 2, 1), - 317: ("Predictor", 3, 1), - 318: ("WhitePoint", 5, 2), - 319: ("PrimaryChromaticies", 3, 6), + 315: ("Artist", ASCII, 1), + 316: ("HostComputer", ASCII, 1), + 317: ("Predictor", SHORT, 1), + 318: ("WhitePoint", RATIONAL, 2), + 319: ("PrimaryChromaticies", SHORT, 6), - 320: ("ColorMap", 3, 0), - 321: ("HalftoneHints", 3, 2), - 322: ("TileWidth", 4, 1), - 323: ("TileLength", 4, 1), - 324: ("TileOffsets", 4, 0), - 325: ("TileByteCounts", 4, 0), + 320: ("ColorMap", SHORT, 0), + 321: ("HalftoneHints", SHORT, 2), + 322: ("TileWidth", LONG, 1), + 323: ("TileLength", LONG, 1), + 324: ("TileOffsets", LONG, 0), + 325: ("TileByteCounts", LONG, 0), - 332: ("InkSet", 3, 1), - 333: ("InkNames", 2, 1), - 334: ("NumberOfInks", 3, 1), - 336: ("DotRange", 3, 0), - 337: ("TargetPrinter", 2, 1), - 338: ("ExtraSamples", 3, 0), - 339: ("SampleFormat", 3, 0), + 332: ("InkSet", SHORT, 1), + 333: ("InkNames", ASCII, 1), + 334: ("NumberOfInks", SHORT, 1), + 336: ("DotRange", SHORT, 0), + 337: ("TargetPrinter", ASCII, 1), + 338: ("ExtraSamples", SHORT, 0), + 339: ("SampleFormat", SHORT, 0), 340: ("SMinSampleValue", 12, 0), 341: ("SMaxSampleValue", 12, 0), - 342: ("TransferRange", 3, 6), + 342: ("TransferRange", SHORT, 6), # obsolete JPEG tags - 512: ("JPEGProc", 3, 1), - 513: ("JPEGInterchangeFormat", 4, 1), - 514: ("JPEGInterchangeFormatLength", 4, 1), - 515: ("JPEGRestartInterval", 3, 1), - 517: ("JPEGLosslessPredictors", 3, 0), - 518: ("JPEGPointTransforms", 3, 0), - 519: ("JPEGQTables", 4, 0), - 520: ("JPEGDCTables", 4, 0), - 521: ("JPEGACTables", 4, 0), + 512: ("JPEGProc", SHORT, 1), + 513: ("JPEGInterchangeFormat", LONG, 1), + 514: ("JPEGInterchangeFormatLength", LONG, 1), + 515: ("JPEGRestartInterval", SHORT, 1), + 517: ("JPEGLosslessPredictors", SHORT, 0), + 518: ("JPEGPointTransforms", SHORT, 0), + 519: ("JPEGQTables", LONG, 0), + 520: ("JPEGDCTables", LONG, 0), + 521: ("JPEGACTables", LONG, 0), - 529: ("YCbCrCoefficients", 5, 3), - 530: ("YCbCrSubSampling", 3, 2), - 531: ("YCbCrPositioning", 3, 1), - 532: ("ReferenceBlackWhite", 4, 0), + 529: ("YCbCrCoefficients", RATIONAL, 3), + 530: ("YCbCrSubSampling", SHORT, 2), + 531: ("YCbCrPositioning", SHORT, 1), + 532: ("ReferenceBlackWhite", LONG, 0), - 33432: ("Copyright", 2, 1), + 33432: ("Copyright", ASCII, 1), # FIXME add more tags here - 34665: ("ExifIFD", 3, 1), + 34665: ("ExifIFD", SHORT, 1), 34675: ('ICCProfile', 7, 0), # MPInfo 45056: ("MPFVersion", 7, 1), - 45057: ("NumberOfImages", 4, 1), + 45057: ("NumberOfImages", LONG, 1), 45058: ("MPEntry", 7, 1), 45059: ("ImageUIDList", 7, 0), - 45060: ("TotalFrames", 4, 1), - 45313: ("MPIndividualNum", 4, 1), - 45569: ("PanOrientation", 4, 1), - 45570: ("PanOverlap_H", 5, 1), - 45571: ("PanOverlap_V", 5, 1), - 45572: ("BaseViewpointNum", 4, 1), + 45060: ("TotalFrames", LONG, 1), + 45313: ("MPIndividualNum", LONG, 1), + 45569: ("PanOrientation", LONG, 1), + 45570: ("PanOverlap_H", RATIONAL, 1), + 45571: ("PanOverlap_V", RATIONAL, 1), + 45572: ("BaseViewpointNum", LONG, 1), 45573: ("ConvergenceAngle", 10, 1), - 45574: ("BaselineLength", 5, 1), + 45574: ("BaselineLength", RATIONAL, 1), 45575: ("VerticalDivergence", 10, 1), 45576: ("AxisDistance_X", 10, 1), 45577: ("AxisDistance_Y", 10, 1), @@ -156,9 +162,9 @@ TAGS_V2 = { 45580: ("PitchAngle", 10, 1), 45581: ("RollAngle", 10, 1), - 50741: ("MakerNoteSafety", 3, 1, {"Unsafe": 0, "Safe": 1}), - 50780: ("BestQualityScale", 5, 1), - 50838: ("ImageJMetaDataByteCounts", 4, 1), + 50741: ("MakerNoteSafety", SHORT, 1, {"Unsafe": 0, "Safe": 1}), + 50780: ("BestQualityScale", RATIONAL, 1), + 50838: ("ImageJMetaDataByteCounts", LONG, 1), 50839: ("ImageJMetaData", 7, 1) } From 64f11cd22719a484d7a9a2b1bea31b2f7f3345ea Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 15 Dec 2015 15:47:18 +0200 Subject: [PATCH 0797/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index b3697b1b0..7973c060d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,9 +4,12 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ -- Health fixes #1591 +- Added field type constants to TiffTags #1596 [radarhere] +- Allow saving RowsPerStrip with libtiff #1594 + [wiredfool] + - Enabled conversion to numpy array for HSV images. Fixes issue #1578 [cartisan] @@ -46,7 +49,7 @@ Changelog (Pillow) - ExtraSamples tag should be a SHORT, not a BYTE #1555 [Nexuapex] -- Docs and code health fixes #1565 #1566 #1581 #1586 +- Docs and code health fixes #1565 #1566 #1581 #1586 #1591 [radarhere] - Updated freetype to 2.6.2 #1564 From afa4166a570e4f12cd6cc87794c2985cbc02e054 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 16 Dec 2015 14:30:17 +1100 Subject: [PATCH 0798/1037] Updated deprecated asserts --- Tests/test_file_tiff.py | 8 ++++---- Tests/test_file_tiff_metadata.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 776718420..312daea56 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -81,12 +81,12 @@ class TestFileTiff(PillowTestCase): im = Image.open(filename) # legacy api - self.assert_(isinstance(im.tag[X_RESOLUTION][0], tuple)) - self.assert_(isinstance(im.tag[Y_RESOLUTION][0], tuple)) + self.assertIsInstance(im.tag[X_RESOLUTION][0], tuple) + self.assertIsInstance(im.tag[Y_RESOLUTION][0], tuple) # v2 api - self.assert_(isinstance(im.tag_v2[X_RESOLUTION], float)) - self.assert_(isinstance(im.tag_v2[Y_RESOLUTION], float)) + self.assertIsInstance(im.tag_v2[X_RESOLUTION], float) + self.assertIsInstance(im.tag_v2[Y_RESOLUTION], float) self.assertEqual(im.info['dpi'], (72., 72.)) diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 620fc03ba..b0f4e402a 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -163,7 +163,7 @@ class TestFileTiffMetadata(PillowTestCase): im = Image.open('Tests/images/hopper.iccprofile_binary.tif') self.assertEqual(im.tag_v2.tagtype[34675], 1) - self.assert_(im.info['icc_profile']) + self.assertTrue(im.info['icc_profile']) if __name__ == '__main__': From c06a0e816a1b29eb77aebaca9a58fed9221ef718 Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 16 Dec 2015 09:29:33 +0200 Subject: [PATCH 0799/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 7973c060d..d5e2aa752 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -25,7 +25,7 @@ Changelog (Pillow) - Changed arcs, chords and pie slices to use floats #1577 [radarhere] -- Update unit test asserts #1584 +- Update unit test asserts #1584, #1598 [radarhere] - Fix command to invoke ghostscript for eps files #1478 From 416efb840b77ee7038e9db40ab52ff32af1cc9e7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 25 Dec 2015 12:02:18 +1100 Subject: [PATCH 0800/1037] Clarified comment --- Tests/test_scipy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_scipy.py b/Tests/test_scipy.py index 1d7568148..dda49e707 100644 --- a/Tests/test_scipy.py +++ b/Tests/test_scipy.py @@ -11,7 +11,7 @@ except ImportError: class Test_scipy_resize(PillowTestCase): - """ Tests for scipy regression in 2.6.0 + """ Tests for scipy regression in Pillow 2.6.0 Tests from https://github.com/scipy/scipy/blob/master/scipy/misc/pilutil.py """ From 5266a4506aabe9ccd796fc4b5b0c8d7726182a35 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 25 Dec 2015 14:15:01 +1100 Subject: [PATCH 0801/1037] Changed register calls to use format property --- PIL/FpxImagePlugin.py | 4 ++-- PIL/ImtImagePlugin.py | 2 +- PIL/McIdasImagePlugin.py | 2 +- PIL/PixarImagePlugin.py | 2 +- PIL/XVThumbImagePlugin.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/PIL/FpxImagePlugin.py b/PIL/FpxImagePlugin.py index d369e05fa..aefc57420 100644 --- a/PIL/FpxImagePlugin.py +++ b/PIL/FpxImagePlugin.py @@ -221,6 +221,6 @@ class FpxImageFile(ImageFile.ImageFile): # # -------------------------------------------------------------------- -Image.register_open("FPX", FpxImageFile, _accept) +Image.register_open(FpxImageFile.format, FpxImageFile, _accept) -Image.register_extension("FPX", ".fpx") +Image.register_extension(FpxImageFile.format, ".fpx") diff --git a/PIL/ImtImagePlugin.py b/PIL/ImtImagePlugin.py index 1ca2c25b0..63e892483 100644 --- a/PIL/ImtImagePlugin.py +++ b/PIL/ImtImagePlugin.py @@ -89,7 +89,7 @@ class ImtImageFile(ImageFile.ImageFile): # # -------------------------------------------------------------------- -Image.register_open("IMT", ImtImageFile) +Image.register_open(ImtImageFile.format, ImtImageFile) # # no extension registered (".im" is simply too common) diff --git a/PIL/McIdasImagePlugin.py b/PIL/McIdasImagePlugin.py index 705fa574f..b75360353 100644 --- a/PIL/McIdasImagePlugin.py +++ b/PIL/McIdasImagePlugin.py @@ -69,6 +69,6 @@ class McIdasImageFile(ImageFile.ImageFile): # -------------------------------------------------------------------- # registry -Image.register_open("MCIDAS", McIdasImageFile, _accept) +Image.register_open(McIdasImageFile.format, McIdasImageFile, _accept) # no default extension diff --git a/PIL/PixarImagePlugin.py b/PIL/PixarImagePlugin.py index 7fef35408..db2ee55f8 100644 --- a/PIL/PixarImagePlugin.py +++ b/PIL/PixarImagePlugin.py @@ -62,7 +62,7 @@ class PixarImageFile(ImageFile.ImageFile): # # -------------------------------------------------------------------- -Image.register_open("PIXAR", PixarImageFile) +Image.register_open(PixarImageFile.format, PixarImageFile) # # FIXME: what's the standard extension? diff --git a/PIL/XVThumbImagePlugin.py b/PIL/XVThumbImagePlugin.py index 9d4b704f6..311e65dc0 100644 --- a/PIL/XVThumbImagePlugin.py +++ b/PIL/XVThumbImagePlugin.py @@ -72,4 +72,4 @@ class XVThumbImageFile(ImageFile.ImageFile): # -------------------------------------------------------------------- -Image.register_open("XVThumb", XVThumbImageFile) +Image.register_open(XVThumbImageFile.format, XVThumbImageFile) From 0d1e3730730e935d496875dc1c71581a01dbeadb Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 25 Dec 2015 14:46:00 +1100 Subject: [PATCH 0802/1037] Updated libwebp to 0.5.0 --- depends/install_webp.sh | 10 +++++----- winbuild/config.py | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/depends/install_webp.sh b/depends/install_webp.sh index 8df7c7aff..72a46c8cf 100755 --- a/depends/install_webp.sh +++ b/depends/install_webp.sh @@ -1,14 +1,14 @@ #!/bin/bash # install webp -if [ ! -f libwebp-0.4.4.tar.gz ]; then - wget 'http://downloads.webmproject.org/releases/webp/libwebp-0.4.4.tar.gz' +if [ ! -f libwebp-0.5.0.tar.gz ]; then + wget 'http://downloads.webmproject.org/releases/webp/libwebp-0.5.0.tar.gz' fi -rm -r libwebp-0.4.4 -tar -xvzf libwebp-0.4.4.tar.gz +rm -r libwebp-0.5.0 +tar -xvzf libwebp-0.5.0.tar.gz -pushd libwebp-0.4.4 +pushd libwebp-0.5.0 ./configure --prefix=/usr --enable-libwebpmux --enable-libwebpdemux && make -j4 && sudo make -j4 install diff --git a/winbuild/config.py b/winbuild/config.py index dd16e31bc..f0d4b78be 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -60,9 +60,9 @@ libs = { 'version': '8.6.4', }, 'webp': { - 'url': 'http://downloads.webmproject.org/releases/webp/libwebp-0.4.4.tar.gz', - 'hash': 'sha1:b8762e330b6cd7e7b4a5a6dbfc53bc787a4f0ba3', - 'dir': 'libwebp-0.4.4', + 'url': 'http://downloads.webmproject.org/releases/webp/libwebp-0.5.0.tar.gz', + 'hash': 'sha1:d3de815b272fcf88fc4f2dc1ab65d176bcb8df68', + 'dir': 'libwebp-0.5.0', }, 'openjpeg': { 'url': SF_MIRROR+'/project/openjpeg/openjpeg/2.1.0/openjpeg-2.1.0.tar.gz', From a0d7282ea738e56c6ca38a8dd0087a207c64401b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 25 Dec 2015 21:49:22 +1100 Subject: [PATCH 0803/1037] Removed unused file --- Tests/crash_ttf_memory_error.py | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 Tests/crash_ttf_memory_error.py diff --git a/Tests/crash_ttf_memory_error.py b/Tests/crash_ttf_memory_error.py deleted file mode 100644 index 8af3dbcec..000000000 --- a/Tests/crash_ttf_memory_error.py +++ /dev/null @@ -1,14 +0,0 @@ -from PIL import Image, ImageFont, ImageDraw - -font = "../pil-archive/memory-error-2.ttf" - -s = "Test Text" -f = ImageFont.truetype(font, 64, index=0, encoding="unicode") -w, h = f.getsize(s) -i = Image.new("RGB", (500, h), "white") -d = ImageDraw.Draw(i) - -# this line causes a MemoryError -d.text((0, 0), s, font=f, fill=0) - -i.show() From 0418ffb64bdf54796626b21479ed0d61fbde7f64 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 26 Dec 2015 00:26:14 +1100 Subject: [PATCH 0804/1037] Updated installation docs [ci skip] --- docs/installation.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 2d26e0429..42c53e2a5 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -103,8 +103,8 @@ Many of Pillow's features require external libraries: * **libjpeg** provides JPEG functionality. - * Pillow has been tested with libjpeg versions **6b**, **8**, and - **9** and libjpeg-turbo version **8**. + * Pillow has been tested with libjpeg versions **6b**, **8**, **9**, and + **9a** and libjpeg-turbo version **8**. * Starting with Pillow 3.0.0, libjpeg is required by default, but may be disabled with the ``--disable-jpeg`` flag. @@ -122,12 +122,12 @@ 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.2**. + above uses liblcms2. Tested with **1.19** and **2.7**. * **libwebp** provides the WebP format. * Pillow has been tested with version **0.1.3**, which does not read - transparent WebP files. Versions **0.3.0** and **0.4.0** support + transparent WebP files. Versions **0.3.0** and above support transparency. * **tcl/tk** provides support for tkinter bitmap and photo images. From c33fc39e76ba2aa286d839194a35a51f49fd4710 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 27 Dec 2015 10:14:24 +0000 Subject: [PATCH 0805/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index d5e2aa752..e9e24f0cf 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,15 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Updated installation docs for new versions of dependencies #1611 + [radarhere] + +- Removed unrunnable test file #1610 + [radarhere] + +- Changed register calls to use format property #1608 + [radarhere] + - Added field type constants to TiffTags #1596 [radarhere] From deecbcd3a3c57302d3a17757297e4336b4c263cf Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 25 Oct 2015 12:49:45 +0000 Subject: [PATCH 0806/1037] Added a rational class for TiffIFD that allows for 0/0 --- PIL/TiffImagePlugin.py | 70 +++++++++++++++++++++++++++++--- Tests/test_file_tiff.py | 6 +-- Tests/test_file_tiff_metadata.py | 6 ++- 3 files changed, 72 insertions(+), 10 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 298c48759..0e6efc416 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -47,9 +47,10 @@ from PIL import _binary import collections from fractions import Fraction +from numbers import Number, Rational + import io import itertools -from numbers import Number import os import struct import sys @@ -215,8 +216,7 @@ def _accept(prefix): def _limit_rational(val, max_val): inv = abs(val) > 1 - f = Fraction.from_float(1 / val if inv else val).limit_denominator(max_val) - n_d = (f.numerator, f.denominator) + n_d = IFDRational(1 / val if inv else val).limit_rational(max_val) return n_d[::-1] if inv else n_d ## @@ -225,6 +225,64 @@ def _limit_rational(val, max_val): _load_dispatch = {} _write_dispatch = {} +class IFDRational(Fraction): + """ Implements a rational class where 0/0 is a legal value to match + the in the wild use of exif rationals. + + e.g., DigitalZoomRatio - 0.00/0.00 indicates that no digital zoom was used + """ + + """ If the denominator is 0, store this as a float('nan'), otherwise store + as a fractions.Fraction(). Delegate as appropriate + + """ + + __slots__ = ('numerator', 'denominator', '_val') + + def __init__(self, value, denominator=1): + """ + :param value: either an integer numerator, a + float/rational/other number, or an IFDRational + :param denominator: Optional integer denominator + """ + self.denominator = denominator + self.numerator = value + + if type(value) == IFDRational: + self.denominator = value.denominator + self.numerator = value.numerator + self._val = value._val + return + + if denominator == 0: + self._val = float('nan') + return + + try: + if denominator == 1: + self._val = Fraction(value) + else: + self._val = Fraction(value, denominator) + except: + print(type(value), type(denominator)) + raise + + def limit_rational(self, max_denominator): + """ + + :param max_denominator: Integer, the maximum denominator value + :returns: Tuple of (numerator, denominator) + """ + + if self.denominator == 0: + return (self.numerator, self.denominator) + + f = self._val.limit_denominator(max_denominator) + return (f.numerator, f.denominator) + + def __repr__(self): + return str(float(self._val)) + class ImageFileDirectory_v2(collections.MutableMapping): """This class represents a TIFF tag directory. To speed things up, we @@ -477,7 +535,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): @_register_loader(5, 8) def load_rational(self, data, legacy_api=True): vals = self._unpack("{0}L".format(len(data) // 4), data) - combine = lambda a, b: (a, b) if legacy_api else a / b + combine = lambda a, b: (a, b) if legacy_api else IFDRational(a, b) return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2])) @@ -497,7 +555,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): @_register_loader(10, 8) def load_signed_rational(self, data, legacy_api=True): vals = self._unpack("{0}l".format(len(data) // 4), data) - combine = lambda a, b: (a, b) if legacy_api else a / b + combine = lambda a, b: (a, b) if legacy_api else IFDRational(a, b) return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2])) @@ -1296,6 +1354,8 @@ def _save(im, fp, filename): if k not in atts and k not in blocklist: if isinstance(v, unicode if bytes is str else str): atts[k] = v.encode('ascii', 'replace') + b"\0" + elif isinstance(v, IFDRational): + atts[k] = float(v) else: atts[k] = v diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 312daea56..5c5958184 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -84,9 +84,9 @@ class TestFileTiff(PillowTestCase): self.assertIsInstance(im.tag[X_RESOLUTION][0], tuple) self.assertIsInstance(im.tag[Y_RESOLUTION][0], tuple) - # v2 api - self.assertIsInstance(im.tag_v2[X_RESOLUTION], float) - self.assertIsInstance(im.tag_v2[Y_RESOLUTION], float) + #v2 api + self.assert_(isinstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational)) + self.assert_(isinstance(im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational)) self.assertEqual(im.info['dpi'], (72., 72.)) diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index b0f4e402a..9c976e733 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -126,8 +126,10 @@ class TestFileTiffMetadata(PillowTestCase): for tag, value in reloaded.items(): if tag not in ignored: - self.assertEqual( - original[tag], value, "%s didn't roundtrip" % tag) + self.assertEqual(original[tag], + value, + "%s didn't roundtrip, %s, %s" % + (tag, original[tag], value)) for tag, value in original.items(): if tag not in ignored: From 3bbb9e676ff09a7a62c45175cf86fba31e279f6f Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 25 Oct 2015 14:17:50 +0000 Subject: [PATCH 0807/1037] value based equivalence --- PIL/TiffImagePlugin.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 0e6efc416..18793982d 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -282,6 +282,13 @@ class IFDRational(Fraction): def __repr__(self): return str(float(self._val)) + + def __eq__(self,other): + if type(other) == float: + return float(self) == other + if type(other) == int: + return float(self) == float(int(self)) and int(self) == other + return float(self) == float(other) class ImageFileDirectory_v2(collections.MutableMapping): From f9fe4da8b252dab9f3756c4b93793c781f836377 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 25 Oct 2015 14:49:52 +0000 Subject: [PATCH 0808/1037] Make IFDRational hashable --- PIL/TiffImagePlugin.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 18793982d..ee5bd098a 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -283,12 +283,16 @@ class IFDRational(Fraction): def __repr__(self): return str(float(self._val)) + def __hash__(self): + return self._val.__hash__() + def __eq__(self,other): if type(other) == float: return float(self) == other if type(other) == int: return float(self) == float(int(self)) and int(self) == other return float(self) == float(other) + class ImageFileDirectory_v2(collections.MutableMapping): From 722ee8240bed099accfabef8fff5085978070c3d Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 18 Nov 2015 08:51:57 -0800 Subject: [PATCH 0809/1037] Inherit from Rational instead of Fraction, some basic tests. Fixes Py2.6 --- PIL/TiffImagePlugin.py | 78 +++++++++++++++++++++++++--------- Tests/test_tiff_ifdrational.py | 46 ++++++++++++++++++++ 2 files changed, 103 insertions(+), 21 deletions(-) create mode 100644 Tests/test_tiff_ifdrational.py diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index ee5bd098a..1cb4e2c9f 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -225,7 +225,7 @@ def _limit_rational(val, max_val): _load_dispatch = {} _write_dispatch = {} -class IFDRational(Fraction): +class IFDRational(Rational): """ Implements a rational class where 0/0 is a legal value to match the in the wild use of exif rationals. @@ -247,6 +247,12 @@ class IFDRational(Fraction): """ self.denominator = denominator self.numerator = value + self._val = float(1) + + if type(value) == Fraction: + self.numerator = value.numerator + self.denominator = value.denominator + self._val = value if type(value) == IFDRational: self.denominator = value.denominator @@ -258,14 +264,16 @@ class IFDRational(Fraction): self._val = float('nan') return - try: - if denominator == 1: - self._val = Fraction(value) + + elif denominator == 1: + if sys.hexversion < 0x2070000 and type(value) == float: + # python 2.6 is different. + self._val = Fraction.from_float(value) else: - self._val = Fraction(value, denominator) - except: - print(type(value), type(denominator)) - raise + self._val = Fraction(value) + else: + self._val = Fraction(value, denominator) + def limit_rational(self, max_denominator): """ @@ -287,11 +295,45 @@ class IFDRational(Fraction): return self._val.__hash__() def __eq__(self,other): - if type(other) == float: - return float(self) == other - if type(other) == int: - return float(self) == float(int(self)) and int(self) == other - return float(self) == float(other) + return self._val == other + + def _delegate(op): + def delegate(self, *args): + return getattr(self._val,op)(*args) + return delegate + + """ a = ['add','radd', 'sub', 'rsub','div', 'rdiv', 'mul', 'rmul', + 'truediv', 'rtruediv', 'floordiv', + 'rfloordiv','mod','rmod', 'pow','rpow', 'pos', 'neg', + 'abs', 'trunc', 'lt', 'gt', 'le', 'ge', 'nonzero'] + print "\n".join("__%s__ = _delegate('__%s__')" % (s,s) for s in a) + """ + + __add__ = _delegate('__add__') + __radd__ = _delegate('__radd__') + __sub__ = _delegate('__sub__') + __rsub__ = _delegate('__rsub__') + __div__ = _delegate('__div__') + __rdiv__ = _delegate('__rdiv__') + __mul__ = _delegate('__mul__') + __rmul__ = _delegate('__rmul__') + __truediv__ = _delegate('__truediv__') + __rtruediv__ = _delegate('__rtruediv__') + __floordiv__ = _delegate('__floordiv__') + __rfloordiv__ = _delegate('__rfloordiv__') + __mod__ = _delegate('__mod__') + __rmod__ = _delegate('__rmod__') + __pow__ = _delegate('__pow__') + __rpow__ = _delegate('__rpow__') + __pos__ = _delegate('__pos__') + __neg__ = _delegate('__neg__') + __abs__ = _delegate('__abs__') + __trunc__ = _delegate('__trunc__') + __lt__ = _delegate('__lt__') + __gt__ = _delegate('__gt__') + __le__ = _delegate('__le__') + __ge__ = _delegate('__ge__') + __nonzero__ = _delegate('__nonzero__') @@ -1063,16 +1105,10 @@ class TiffImageFile(ImageFile.ImageFile): self.info["compression"] = self._compression - xres = self.tag_v2.get(X_RESOLUTION, (1, 1)) - yres = self.tag_v2.get(Y_RESOLUTION, (1, 1)) + xres = self.tag_v2.get(X_RESOLUTION,1) + yres = self.tag_v2.get(Y_RESOLUTION,1) - if xres and not isinstance(xres, tuple): - xres = (xres, 1.) - if yres and not isinstance(yres, tuple): - yres = (yres, 1.) if xres and yres: - xres = xres[0] / (xres[1] or 1) - yres = yres[0] / (yres[1] or 1) resunit = self.tag_v2.get(RESOLUTION_UNIT, 1) if resunit == 2: # dots per inch self.info["dpi"] = xres, yres diff --git a/Tests/test_tiff_ifdrational.py b/Tests/test_tiff_ifdrational.py new file mode 100644 index 000000000..05f66d5c4 --- /dev/null +++ b/Tests/test_tiff_ifdrational.py @@ -0,0 +1,46 @@ +from __future__ import print_function + +from helper import PillowTestCase + +from PIL.TiffImagePlugin import IFDRational + +from fractions import Fraction + +class Test_IFDRational(PillowTestCase): + + + def _test_equal(self, num, denom, target): + + t = IFDRational(num, denom) + + self.assertEqual(target, t) + self.assertEqual(t, target) + + def test_sanity(self): + + self._test_equal(1, 1, 1) + self._test_equal(1, 1, Fraction(1,1)) + + self._test_equal(2, 2, 1) + self._test_equal(1.0, 1, Fraction(1,1)) + + self._test_equal(Fraction(1,1), 1, Fraction(1,1)) + self._test_equal(IFDRational(1,1), 1, 1) + + + self._test_equal(1, 2, Fraction(1,2)) + self._test_equal(1, 2, IFDRational(1,2)) + + def test_nonetype(self): + " Fails if the _delegate function doesn't return a valid function" + + xres = IFDRational(72) + yres = IFDRational(72) + self.assert_(xres._val is not None) + self.assert_(xres.numerator is not None) + self.assert_(xres.denominator is not None) + self.assert_(yres._val is not None) + + self.assert_(xres and 1) + self.assert_(xres and yres) + From 79608bd7624efe008f664948d6d958437f820bdd Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 18 Nov 2015 09:00:15 -0800 Subject: [PATCH 0810/1037] Make numerator/denominator read only --- PIL/TiffImagePlugin.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 1cb4e2c9f..3cf214edd 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -237,7 +237,7 @@ class IFDRational(Rational): """ - __slots__ = ('numerator', 'denominator', '_val') + __slots__ = ('_numerator', '_denominator', '_val') def __init__(self, value, denominator=1): """ @@ -245,18 +245,18 @@ class IFDRational(Rational): float/rational/other number, or an IFDRational :param denominator: Optional integer denominator """ - self.denominator = denominator - self.numerator = value + self._denominator = denominator + self._numerator = value self._val = float(1) if type(value) == Fraction: - self.numerator = value.numerator - self.denominator = value.denominator + self._numerator = value.numerator + self._denominator = value.denominator self._val = value if type(value) == IFDRational: - self.denominator = value.denominator - self.numerator = value.numerator + self._denominator = value.denominator + self._numerator = value.numerator self._val = value._val return @@ -274,6 +274,14 @@ class IFDRational(Rational): else: self._val = Fraction(value, denominator) + @property + def numerator(a): + return a._numerator + + @property + def denominator(a): + return a._denominator + def limit_rational(self, max_denominator): """ From e992bf6b3f886e68ff62670f53b5164795cf4649 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 27 Dec 2015 18:23:26 +0000 Subject: [PATCH 0811/1037] Added note that script works with Raspbian. [ci skip] --- depends/debian_8.2.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/depends/debian_8.2.sh b/depends/debian_8.2.sh index 2a175098f..dd0679a9f 100755 --- a/depends/debian_8.2.sh +++ b/depends/debian_8.2.sh @@ -4,6 +4,8 @@ # Installs all of the dependencies for Pillow for Debian 8.2 # for both system Pythons 2.7 and 3.4 # +# Also works for Raspbian Jessie +# sudo apt-get -y install python-dev python-setuptools \ python3-dev python-virtualenv cmake From e5b6018b469a0f9f469be1fe6f9ad8db37dc1f9f Mon Sep 17 00:00:00 2001 From: Benjamin Le Forestier Date: Sun, 27 Dec 2015 20:43:27 +0100 Subject: [PATCH 0812/1037] add include and library directories for freebsd --- setup.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setup.py b/setup.py index 83beb3478..3b65b93c6 100644 --- a/setup.py +++ b/setup.py @@ -302,6 +302,10 @@ class pil_build_ext(build_ext): elif sys.platform.startswith("gnu"): self.add_multiarch_paths() + elif sys.platform.startswith("freebsd"): + _add_directory(library_dirs, "/usr/local/lib") + _add_directory(include_dirs, "/usr/local/include") + elif sys.platform.startswith("netbsd"): _add_directory(library_dirs, "/usr/pkg/lib") _add_directory(include_dirs, "/usr/pkg/include") From 8ed2d1ed02e13d57639d1fe2424e5415e9f3ec61 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 27 Dec 2015 11:27:18 +0000 Subject: [PATCH 0813/1037] Changing the type of the target values --- Tests/test_file_tiff_metadata.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 9c976e733..f3f310d8b 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -73,7 +73,7 @@ class TestFileTiffMetadata(PillowTestCase): def test_read_metadata(self): img = Image.open('Tests/images/hopper_g4.tif') - self.assertEqual({'YResolution': 4294967295 / 113653537, + self.assertEqual({'YResolution': TiffImagePlugin.IFDRational(4294967295, 113653537), 'PlanarConfiguration': 1, 'BitsPerSample': (1,), 'ImageLength': 128, @@ -83,7 +83,7 @@ class TestFileTiffMetadata(PillowTestCase): 'ResolutionUnit': 3, 'PhotometricInterpretation': 0, 'PageNumber': (0, 1), - 'XResolution': 4294967295 / 113653537, + 'XResolution': TiffImagePlugin.IFDRational(4294967295, 113653537), 'ImageWidth': 128, 'Orientation': 1, 'StripByteCounts': (1968,), @@ -125,7 +125,16 @@ class TestFileTiffMetadata(PillowTestCase): 'StripByteCounts', 'RowsPerStrip', 'PageNumber', 'StripOffsets'] for tag, value in reloaded.items(): - if tag not in ignored: + if tag in ignored: continue + if (type(original[tag]) == tuple + and type(original[tag][0]) == TiffImagePlugin.IFDRational): + # Need to compare element by element in the tuple, + # not comparing tuples of object references + self.assert_deep_equal(original[tag], + value, + "%s didn't roundtrip, %s, %s" % + (tag, original[tag], value)) + else: self.assertEqual(original[tag], value, "%s didn't roundtrip, %s, %s" % From 4b9899b1846cc326168ca0fe5d9e723342811e39 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 27 Dec 2015 21:09:21 +0000 Subject: [PATCH 0814/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index e9e24f0cf..4b6a2295b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Add /usr/local include and library directories for freebsd #1613 + [leforestier] + - Updated installation docs for new versions of dependencies #1611 [radarhere] From f1f00c0662b09338993e541490310399f83b23ee Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 28 Dec 2015 10:05:13 +1100 Subject: [PATCH 0815/1037] Updated Tox URL --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index ebc63d85a..60b277331 100644 --- a/tox.ini +++ b/tox.ini @@ -1,4 +1,4 @@ -# Tox (http://tox.testrun.org/) is a tool for running tests +# Tox (https://testrun.org/tox/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, "pip install tox" # and then run "tox" from this directory. From e5f2b3ddacf9c234ce9e2b9eff5a7461e9d0e985 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 28 Dec 2015 12:56:38 +0000 Subject: [PATCH 0816/1037] Added documentation for abusing pip to send --enable-[feature] flags to the build Ref:#1412 --- docs/installation.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/installation.rst b/docs/installation.rst index 42c53e2a5..c91825258 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -185,6 +185,11 @@ Sample Usage:: $ MAX_CONCURRENCY=1 python setup.py build_ext --enable-[feature] install +or using pip:: + + $ pip install pillow --global-option="build_ext" --global-option="--enable-[feature]" + + Building on OS X ^^^^^^^^^^^^^^^^ From 0e1eb970e5d0367b043a14ce222e46409ecfcf14 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 29 Dec 2015 00:04:39 +1100 Subject: [PATCH 0817/1037] Updated URLs --- PIL/IcoImagePlugin.py | 2 +- PIL/SpiderImagePlugin.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PIL/IcoImagePlugin.py b/PIL/IcoImagePlugin.py index 0b8f4691c..4aa7687af 100644 --- a/PIL/IcoImagePlugin.py +++ b/PIL/IcoImagePlugin.py @@ -18,7 +18,7 @@ # https://code.google.com/p/casadebender/wiki/Win32IconImagePlugin # # Icon format references: -# * http://en.wikipedia.org/wiki/ICO_(file_format) +# * https://en.wikipedia.org/wiki/ICO_(file_format) # * http://msdn.microsoft.com/en-us/library/ms997538.aspx diff --git a/PIL/SpiderImagePlugin.py b/PIL/SpiderImagePlugin.py index 0ce63002b..d5457893c 100644 --- a/PIL/SpiderImagePlugin.py +++ b/PIL/SpiderImagePlugin.py @@ -27,10 +27,10 @@ # image data from electron microscopy and tomography. # # Spider home page: -# http://www.wadsworth.org/spider_doc/spider/docs/spider.html +# http://spider.wadsworth.org/spider_doc/spider/docs/spider.html # # Details about the Spider image format: -# http://www.wadsworth.org/spider_doc/spider/docs/image_doc.html +# http://spider.wadsworth.org/spider_doc/spider/docs/image_doc.html # from __future__ import print_function From e6541cb3d621e7a8e439b7076ab4857202643fa1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 29 Dec 2015 23:51:05 +1100 Subject: [PATCH 0818/1037] Updated ICNS documentation [ci skip] --- docs/handbook/image-file-formats.rst | 38 ++++++++++++++-------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 2b69f2414..b450cd499 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -120,6 +120,25 @@ attributes before loading the file:: im.size = (x1 - x0, y1 - y0) im.tile = [(tag, (0, 0) + im.size, offset, extra)] +ICNS +^^^^ + +PIL reads and (OS X only) writes Mac OS X ``.icns`` files. By default, the +largest available icon is read, though you can override this by setting the +:py:attr:`~PIL.Image.Image.size` property before calling +:py:meth:`~PIL.Image.Image.load`. The :py:meth:`~PIL.Image.Image.open` method +sets the following :py:attr:`~PIL.Image.Image.info` property: + +**sizes** + A list of supported sizes found in this icon file; these are a + 3-tuple, ``(width, height, scale)``, where ``scale`` is 2 for a retina + icon and 1 for a standard icon. You *are* permitted to use this 3-tuple + format for the :py:attr:`~PIL.Image.Image.size` property if you set it + before calling :py:meth:`~PIL.Image.Image.load`; after loading, the size + will be reset to a 2-tuple containing pixel dimensions (so, e.g. if you + ask for ``(512, 512, 2)``, the final value of + :py:attr:`~PIL.Image.Image.size` will be ``(1024, 1024)``). + IM ^^ @@ -665,25 +684,6 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: (64, 64), (128, 128), (255, 255)]``. Any size is bigger then the original size or 255 will be ignored. -ICNS -^^^^ - -PIL reads Mac OS X ``.icns`` files. By default, the largest available icon is -read, though you can override this by setting the :py:attr:`~PIL.Image.Image.size` -property before calling :py:meth:`~PIL.Image.Image.load`. The -:py:meth:`~PIL.Image.Image.open` method sets the following -:py:attr:`~PIL.Image.Image.info` property: - -**sizes** - A list of supported sizes found in this icon file; these are a - 3-tuple, ``(width, height, scale)``, where ``scale`` is 2 for a retina - icon and 1 for a standard icon. You *are* permitted to use this 3-tuple - format for the :py:attr:`~PIL.Image.Image.size` property if you set it - before calling :py:meth:`~PIL.Image.Image.load`; after loading, the size - will be reset to a 2-tuple containing pixel dimensions (so, e.g. if you - ask for ``(512, 512, 2)``, the final value of - :py:attr:`~PIL.Image.Image.size` will be ``(1024, 1024)``). - IMT ^^^ From 4cbe4d71baeae01d3deb04e3048623a6439fdab8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 29 Dec 2015 23:58:40 +1100 Subject: [PATCH 0819/1037] Removed duplicate documentation and formatted heading [ci skip] --- docs/handbook/image-file-formats.rst | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index b450cd499..a30269237 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -700,6 +700,7 @@ MCIDAS PIL identifies and reads 8-bit McIdas area files. MIC (read only) +^^^^^^^^^^^^^^^ PIL identifies and reads Microsoft Image Composer (MIC) files. When opened, the first sprite in the file is loaded. You can use :py:meth:`~file.seek` and @@ -713,12 +714,6 @@ image when first opened. The :py:meth:`~file.seek` and :py:meth:`~file.tell` methods may be used to read other pictures from the file. The pictures are zero-indexed and random access is supported. -MIC (read only) - -Pillow identifies and reads Microsoft Image Composer (MIC) files. When opened, the -first sprite in the file is loaded. You can use :py:meth:`~file.seek` and -:py:meth:`~file.tell` to read other sprites from the file. - PCD ^^^ From 44c8a85386d874c7ae276a824a77bc3cfd29418d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 30 Dec 2015 00:05:36 +1100 Subject: [PATCH 0820/1037] Changed formats read total [ci skip] --- 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 a30269237..02ceea153 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -4,7 +4,7 @@ Image file formats ================== The Python Imaging Library supports a wide variety of raster file formats. -Nearly 30 different file formats can be identified and read by the library. +Over 30 different file formats can be identified and read by the library. Write support is less extensive, but most common interchange and presentation formats are supported. From 46b11f1bebd37743e833251e915077085ea7884b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 30 Dec 2015 00:15:13 +1100 Subject: [PATCH 0821/1037] Moved PIXAR and XV Thumbnails into the read-only section [ci skip] --- docs/handbook/image-file-formats.rst | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 02ceea153..46b0caa10 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -598,11 +598,6 @@ XBM PIL reads and writes X bitmap files (mode ``1``). -XV Thumbnails -^^^^^^^^^^^^^ - -PIL can read XV thumbnail files. - Read-only formats ----------------- @@ -699,8 +694,8 @@ MCIDAS PIL identifies and reads 8-bit McIdas area files. -MIC (read only) -^^^^^^^^^^^^^^^ +MIC +^^^ PIL identifies and reads Microsoft Image Composer (MIC) files. When opened, the first sprite in the file is loaded. You can use :py:meth:`~file.seek` and @@ -723,6 +718,14 @@ read the lower resolution versions instead, thus effectively resizing the image to 384x256 or 192x128. Higher resolutions cannot be read by the Python Imaging Library. +PIXAR +^^^^^ + +PIL provides limited support for PIXAR raster files. The library can identify +and read “dumped” RGB files. + +The format code is ``PIXAR``. + PSD ^^^ @@ -786,13 +789,10 @@ by default, only the first image will be saved. To save all frames, each frame to a separate page of the PDF, the ``save_all`` parameter must be present and set to ``True``. -PIXAR (read only) -^^^^^^^^^^^^^^^^^ +XV Thumbnails +^^^^^^^^^^^^^ -PIL provides limited support for PIXAR raster files. The library can identify -and read “dumped” RGB files. - -The format code is ``PIXAR``. +PIL can read XV thumbnail files. Identify-only formats --------------------- From 5e7a5bf237c806ed5b05512cd82dd16a9e4e080f Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 27 Dec 2015 20:47:13 +0000 Subject: [PATCH 0822/1037] Limit rationals for expected values in round trip --- Tests/test_file_tiff_metadata.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index f3f310d8b..76eb35440 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -6,6 +6,7 @@ import struct from helper import unittest, PillowTestCase, hopper from PIL import Image, TiffImagePlugin, TiffTags +from TiffImagePlugin import _limit_rational, IFDRational tag_ids = dict((info.name, info.value) for info in TiffTags.TAGS_V2.values()) @@ -73,7 +74,7 @@ class TestFileTiffMetadata(PillowTestCase): def test_read_metadata(self): img = Image.open('Tests/images/hopper_g4.tif') - self.assertEqual({'YResolution': TiffImagePlugin.IFDRational(4294967295, 113653537), + self.assertEqual({'YResolution': IFDRational(4294967295, 113653537), 'PlanarConfiguration': 1, 'BitsPerSample': (1,), 'ImageLength': 128, @@ -83,7 +84,7 @@ class TestFileTiffMetadata(PillowTestCase): 'ResolutionUnit': 3, 'PhotometricInterpretation': 0, 'PageNumber': (0, 1), - 'XResolution': TiffImagePlugin.IFDRational(4294967295, 113653537), + 'XResolution': IFDRational(4294967295, 113653537), 'ImageWidth': 128, 'Orientation': 1, 'StripByteCounts': (1968,), @@ -121,13 +122,21 @@ class TestFileTiffMetadata(PillowTestCase): original = img.tag_v2.named() reloaded = loaded.tag_v2.named() - ignored = [ - 'StripByteCounts', 'RowsPerStrip', 'PageNumber', 'StripOffsets'] + for k,v in original.items(): + if type(v) == IFDRational: + original[k] = IFDRational(*_limit_rational(v,2**31)) + if type(v) == tuple and \ + type(v[0]) == IFDRational: + original[k] = tuple([IFDRational( + *_limit_rational(elt, 2**31)) for elt in v]) + + ignored = ['StripByteCounts', 'RowsPerStrip', + 'PageNumber', 'StripOffsets'] for tag, value in reloaded.items(): if tag in ignored: continue if (type(original[tag]) == tuple - and type(original[tag][0]) == TiffImagePlugin.IFDRational): + and type(original[tag][0]) == IFDRational): # Need to compare element by element in the tuple, # not comparing tuples of object references self.assert_deep_equal(original[tag], From 3dd4b39411b666a794834317e5a92807fbf50e79 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 27 Dec 2015 20:54:14 +0000 Subject: [PATCH 0823/1037] Namespace --- Tests/test_file_tiff_metadata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 76eb35440..9290d1d2b 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -6,7 +6,7 @@ import struct from helper import unittest, PillowTestCase, hopper from PIL import Image, TiffImagePlugin, TiffTags -from TiffImagePlugin import _limit_rational, IFDRational +from PIL.TiffImagePlugin import _limit_rational, IFDRational tag_ids = dict((info.name, info.value) for info in TiffTags.TAGS_V2.values()) From bd05d66c7e499f79c011992f323640187569f083 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 27 Dec 2015 21:04:23 +0000 Subject: [PATCH 0824/1037] Python 3.4 support for the IFDRational --- PIL/TiffImagePlugin.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 3cf214edd..05a2d75d4 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -313,7 +313,8 @@ class IFDRational(Rational): """ a = ['add','radd', 'sub', 'rsub','div', 'rdiv', 'mul', 'rmul', 'truediv', 'rtruediv', 'floordiv', 'rfloordiv','mod','rmod', 'pow','rpow', 'pos', 'neg', - 'abs', 'trunc', 'lt', 'gt', 'le', 'ge', 'nonzero'] + 'abs', 'trunc', 'lt', 'gt', 'le', 'ge', 'nonzero', + 'ceil', 'floor', 'round'] print "\n".join("__%s__ = _delegate('__%s__')" % (s,s) for s in a) """ @@ -342,6 +343,9 @@ class IFDRational(Rational): __le__ = _delegate('__le__') __ge__ = _delegate('__ge__') __nonzero__ = _delegate('__nonzero__') + __ceil__ = _delegate('__ceil__') + __floor__ = _delegate('__floor__') + __round__ = _delegate('__round__') From 48e4e0722e6afd4cf38ffc70d9eeea235085ff4e Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 28 Dec 2015 11:15:27 +0000 Subject: [PATCH 0825/1037] Documentation for IFDRational --- docs/handbook/image-file-formats.rst | 42 +++++++++++++++++----------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 2b69f2414..54d9b7274 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -477,10 +477,12 @@ The :py:meth:`~PIL.Image.Image.open` method sets the following .. versionadded:: 1.1.5 -The :py:attr:`~PIL.Image.Image.tag_v2` attribute contains a dictionary of -TIFF metadata. The keys are numerical indexes from `~PIL.TiffTags.TAGS_V2`. -Values are strings or numbers for single items, multiple values are returned -in a tuple of values. Rational numbers are returned as a single value. +The :py:attr:`~PIL.Image.Image.tag_v2` attribute contains a dictionary +of TIFF metadata. The keys are numerical indexes from +`~PIL.TiffTags.TAGS_V2`. Values are strings or numbers for single +items, multiple values are returned in a tuple of values. Rational +numbers are returned as a :py:class:`~PIL.TiffImagePlugin.IFDRational` +object. .. versionadded:: 3.0.0 @@ -510,20 +512,27 @@ The :py:meth:`~PIL.Image.Image.save` method can take the following keyword argum .. versionadded:: 2.3.0 - For compatibility with legacy code, a - `~PIL.TiffImagePlugin.ImageFileDirectory_v1` object may be passed - in this field. However, this is deprecated. + Metadata values that are of the rational type should be passed in + using a :py:class:`~PIL.TiffImagePlugin.IFDRational` object. - ..versionadded:: 3.0.0 + .. versionadded:: 3.1.0 + + For compatibility with legacy code, a + :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` object may + be passed in this field. However, this is deprecated. + + .. versionadded:: 3.0.0 **compression** A string containing the desired compression method for the file. (valid only with libtiff installed) Valid compression - methods are: ``[None, "tiff_ccitt", "group3", "group4", - "tiff_jpeg", "tiff_adobe_deflate", "tiff_thunderscan", - "tiff_deflate", "tiff_sgilog", "tiff_sgilog24", "tiff_raw_16"]`` + methods are: ``None``, ``"tiff_ccitt"``, ``"group3"``, + ``"group4"``, ``"tiff_jpeg"``, ``"tiff_adobe_deflate"``, + ``"tiff_thunderscan"``, ``"tiff_deflate"``, ``"tiff_sgilog"``, + ``"tiff_sgilog24"``, ``"tiff_raw_16"`` -These arguments to set the tiff header fields are an alternative to using the general tags available through tiffinfo. +These arguments to set the tiff header fields are an alternative to +using the general tags available through tiffinfo. **description** @@ -545,10 +554,11 @@ These arguments to set the tiff header fields are an alternative to using the ge **y_resolution** -**dpi** - Either a Float, Integer, or 2 tuple of (numerator, - denominator). Resolution implies an equal x and y resolution, dpi - also implies a unit of inches. +**dpi** + Either a Float, 2 tuple of (numerator, denominator) or a + :py:class:`~PIL.TiffImagePlugin.IFDRational`. Resolution implies + an equal x and y resolution, dpi also implies a unit of inches. + WebP ^^^^ From 3ac9396e8c991e7baab66187af2a35c3f4e83605 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 29 Dec 2015 22:00:36 +0000 Subject: [PATCH 0826/1037] Write round trip for rationals, including nan value --- PIL/TiffImagePlugin.py | 8 +++++--- PIL/TiffTags.py | 2 +- Tests/test_file_tiff_metadata.py | 14 ++++++++++++++ Tests/test_tiff_ifdrational.py | 17 ++++++++++++++++- 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 05a2d75d4..521c7c726 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -497,11 +497,13 @@ class ImageFileDirectory_v2(collections.MutableMapping): values = [value] if isinstance(value, basetypes) else value if tag not in self.tagtype: - try: + if info.type: self.tagtype[tag] = info.type - except KeyError: + else: self.tagtype[tag] = 7 - if all(isinstance(v, int) for v in values): + if all(isinstance(v, IFDRational) for v in values): + self.tagtype[tag] = 5 + elif all(isinstance(v, int) for v in values): if all(v < 2 ** 16 for v in values): self.tagtype[tag] = 3 else: diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py index d8e304d87..d00164502 100644 --- a/PIL/TiffTags.py +++ b/PIL/TiffTags.py @@ -23,7 +23,7 @@ from collections import namedtuple class TagInfo(namedtuple("_TagInfo", "value name type length enum")): __slots__ = [] - def __new__(cls, value=None, name="unknown", type=4, length=0, enum=None): + def __new__(cls, value=None, name="unknown", type=None, length=0, enum=None): return super(TagInfo, cls).__new__( cls, value, name, type, length, enum or {}) diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 9290d1d2b..1b88fca99 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -185,6 +185,20 @@ class TestFileTiffMetadata(PillowTestCase): self.assertEqual(im.tag_v2.tagtype[34675], 1) self.assertTrue(im.info['icc_profile']) + def test_exif_div_zero(self): + im = hopper() + info = TiffImagePlugin.ImageFileDirectory_v2() + info[41988] = TiffImagePlugin.IFDRational(0,0) + + out = self.tempfile('temp.tiff') + im.save(out, tiffinfo=info, compression='raw') + + reloaded = Image.open(out) + self.assertEqual(0, reloaded.tag_v2[41988][0].numerator) + self.assertEqual(0, reloaded.tag_v2[41988][0].denominator) + + + if __name__ == '__main__': unittest.main() diff --git a/Tests/test_tiff_ifdrational.py b/Tests/test_tiff_ifdrational.py index 05f66d5c4..5654d4c9c 100644 --- a/Tests/test_tiff_ifdrational.py +++ b/Tests/test_tiff_ifdrational.py @@ -1,7 +1,8 @@ from __future__ import print_function -from helper import PillowTestCase +from helper import PillowTestCase, hopper +from PIL import TiffImagePlugin, Image from PIL.TiffImagePlugin import IFDRational from fractions import Fraction @@ -44,3 +45,17 @@ class Test_IFDRational(PillowTestCase): self.assert_(xres and 1) self.assert_(xres and yres) + + def test_ifd_rational_save(self): + for libtiff in (True, False): + TiffImagePlugin.WRITE_LIBTIFF = libtiff + + im = hopper() + out = self.tempfile('temp.tiff') + res = IFDRational(301,1) + im.save(out, dpi=(res,res), compression='raw') + + reloaded = Image.open(out) + self.assertEqual(float(IFDRational(301,1)), + float(reloaded.tag_v2[282])) + From 80ab12bdc0d6b546107536fb65e0b9c837cd09aa Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 29 Dec 2015 22:02:11 +0000 Subject: [PATCH 0827/1037] Lookup tag info in both _v2(info) and original(name only) dicts, delegate to lookup --- PIL/TiffImagePlugin.py | 12 +++++++----- PIL/TiffTags.py | 12 ++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 521c7c726..b502c5661 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -44,6 +44,7 @@ from __future__ import division, print_function from PIL import Image, ImageFile from PIL import ImagePalette from PIL import _binary +from PIL import TiffTags import collections from fractions import Fraction @@ -56,7 +57,8 @@ import struct import sys import warnings -from .TiffTags import TAGS_V2, TYPES, TagInfo +from .TiffTags import TYPES, TagInfo + __version__ = "1.3.5" DEBUG = False # Needs to be merged with the new logging approach. @@ -461,7 +463,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): Returns the complete tag dictionary, with named tags where possible. """ - return dict((TAGS_V2.get(code, TagInfo()).name, value) + return dict((TiffTags.lookup(code).name, value) for code, value in self.items()) def __len__(self): @@ -493,7 +495,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): if bytes is str: basetypes += unicode, - info = TAGS_V2.get(tag, TagInfo()) + info = TiffTags.lookup(tag) values = [value] if isinstance(value, basetypes) else value if tag not in self.tagtype: @@ -648,7 +650,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): for i in range(self._unpack("H", self._ensure_read(fp, 2))[0]): tag, typ, count, data = self._unpack("HHL4s", self._ensure_read(fp, 12)) if DEBUG: - tagname = TAGS_V2.get(tag, TagInfo()).name + tagname = TiffTags.lookup(tag).name typname = TYPES.get(typ, "unknown") print("tag: %s (%d) - type: %s (%d)" % (tagname, tag, typname, typ), end=" ") @@ -716,7 +718,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): values = value if isinstance(value, tuple) else (value,) data = self._write_dispatch[typ](self, *values) if DEBUG: - tagname = TAGS_V2.get(tag, TagInfo()).name + tagname = TiffTags.lookup(tag).name typname = TYPES.get(typ, "unknown") print("save: %s (%d) - type: %s (%d)" % (tagname, tag, typname, typ), end=" ") diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py index d00164502..07710fdcc 100644 --- a/PIL/TiffTags.py +++ b/PIL/TiffTags.py @@ -30,6 +30,18 @@ class TagInfo(namedtuple("_TagInfo", "value name type length enum")): def cvt_enum(self, value): return self.enum.get(value, value) +def lookup(tag): + """ + :param tag: Integer tag number + :returns: Taginfo namedtuple, From the TAGS_V2 info if possible, + otherwise just populating the value and name from TAGS. + If the tag is not recognized, "unknown" is returned for the name + + """ + + return TAGS_V2.get(tag, TagInfo(tag, TAGS.get(tag, 'unknown'))) + + ## # Map tag numbers to tag info. # From d9c90bba91d57aa3527903909f0c7cfca9f14764 Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 7 Oct 2015 12:24:15 +0300 Subject: [PATCH 0828/1037] Test passes for 2.9.0, fails for 3.0.0 --- Tests/images/exif_gps.jpg | Bin 0 -> 1097 bytes Tests/test_file_jpeg.py | 17 +++++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 Tests/images/exif_gps.jpg diff --git a/Tests/images/exif_gps.jpg b/Tests/images/exif_gps.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2b9ae905645dc405142547f6354357f2786b326f GIT binary patch literal 1097 zcmbVKNl+6(6#YFj$;>37AP_Kw09il+g;X+$LQ+x|ASe`632xCFXi&6@DnJFTvJ_N^ zln22TPNH$6yr7`sNfdE8i7Vhm-1P+RcyRnP5OU$5{_fxN`uCsT?{&90BEE!(qJqK# zAOwJ5196-@%x|cw1SlziGyvd0MJS*VN#0Dtfx@o>{Sb+eF^9N~7~~jMfDU4lbBQQo zQHPwvtSBr7iUJ1LF)oTjaiBsh)$D4invNsx_a_H2sm3{gUTP)fTYcd5dKaLB^gAv{ zTg~lUQ)iG2iSyfl#Y`n8Tm8-ufy1;+Oo#E*c19+H)ktM^9XVg={Ik|*LE+GvNKTEn7 z5<*0SgdA?9B6m{|fcOIPfKw`2rGjHwmghN@Mi-`0t2MEaQQ^9HLt;X_!DuukWu%xa z=~koBmYJGvceq@x#1waSmasU(=@O(Ogy(sUTB8pO(+g&!S@>rYZ-SP?2gT9E0+g1} zS|ScYA|8c;$R;V2j8HVAP_i7aQe#3NJ~Kko6hkW%jC2>faUB?~BGT;0QAU-Pvz8j2 z(Ad_+S#x^^qRWOSlAIN_O}r{*o<7!)JU_*jn&w)#C^O6L$y=IVuxxo@(aKe;*R1uf zTfbqWuX5Aosx5Wfw(qEK*tu)p{sYY|{)4UUM~)sle&S?D=jk(nv**rVxY&L9N^jrQ zYuEd4+`2t@XXx&|`wt#He)9C$^O4c9@mH_kynXln!^cmPpTB(l_Wj4tsb7*V0`#;j z9Ou-fMO_rb&L3T+1lT4rOFcDO+9>Wf2-VU2a=f&j4?AmQ6%g)DEj+l3e4- llae%9+209k`Y&a(!a};nKtmI}JX#A&VCr|P?Nqop`Umxw7FPfO literal 0 HcmV?d00001 diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 367e57c14..192425d2f 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -171,6 +171,23 @@ class TestFileJpeg(PillowTestCase): # Should not raise a TypeError im._getexif() + def test_exif_gps(self): + # Arrange + im = Image.open('Tests/images/exif_gps.jpg') + gps_index = 34853 + expected_exif_gps = { + 0: '\x00\x00\x00\x01', + 2: (4294967295L, 1), + 5: '\x01', + 30: 65535, + 29: u'1999:99:99 99:99:99'} + + # Act + exif = im._getexif() + + # Assert + self.assertEqual(exif[gps_index], expected_exif_gps) + def test_exif_gps_typeerror(self): im = Image.open('Tests/images/exif_gps_typeerror.jpg') From 69bead98ea318cf362dd634eaae72f9276fb58be Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 7 Oct 2015 12:36:30 +0300 Subject: [PATCH 0829/1037] Update GPSInfoIFD (34853) tag --- PIL/TiffTags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py index d8e304d87..dc8960478 100644 --- a/PIL/TiffTags.py +++ b/PIL/TiffTags.py @@ -140,6 +140,7 @@ TAGS_V2 = { # FIXME add more tags here 34665: ("ExifIFD", SHORT, 1), 34675: ('ICCProfile', 7, 0), + 34853: ('GPSInfoIFD', 1, 1), # MPInfo 45056: ("MPFVersion", 7, 1), @@ -180,7 +181,6 @@ TAGS = {347: 'JPEGTables', 34377: 'PhotoshopInfo', 34850: 'ExposureProgram', 34852: 'SpectralSensitivity', - 34853: 'GPSInfoIFD', 34855: 'ISOSpeedRatings', 34856: 'OECF', 34864: 'SensitivityType', From 33b90e9b6f3124914158ebca0a715337dc1430cb Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 20 Oct 2015 20:14:21 +0100 Subject: [PATCH 0830/1037] rolling back gps exif support to match pre 3.0.0 results --- PIL/JpegImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 7bd4b5335..5305fe6e5 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -432,9 +432,9 @@ def _getexif(self): except (KeyError, TypeError): pass else: - info = TiffImagePlugin.ImageFileDirectory_v2(head) + info = TiffImagePlugin.ImageFileDirectory_v1(head) info.load(file) - exif[0x8825] = dict(info) + exif[0x8825] = dict([(k,v[0]) if len(v) == 1 else (k,v) for k,v in info.items()]) return exif From 31428b70091c288ce7a3a9ae8efd81b4c4d86dc6 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 29 Dec 2015 23:56:47 +0000 Subject: [PATCH 0831/1037] Py3 fixes --- Tests/test_file_jpeg.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 192425d2f..5a7ea1ce4 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -176,9 +176,9 @@ class TestFileJpeg(PillowTestCase): im = Image.open('Tests/images/exif_gps.jpg') gps_index = 34853 expected_exif_gps = { - 0: '\x00\x00\x00\x01', - 2: (4294967295L, 1), - 5: '\x01', + 0: b'\x00\x00\x00\x01', + 2: (4294967295, 1), + 5: b'\x01', 30: 65535, 29: u'1999:99:99 99:99:99'} From 253bc038c5eeb8b35d9c93d3026b0330a77fa2eb Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 30 Dec 2015 00:45:48 +0000 Subject: [PATCH 0832/1037] Python 3.2, sigh --- Tests/test_file_jpeg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 5a7ea1ce4..1591c1e83 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -180,7 +180,7 @@ class TestFileJpeg(PillowTestCase): 2: (4294967295, 1), 5: b'\x01', 30: 65535, - 29: u'1999:99:99 99:99:99'} + 29: '1999:99:99 99:99:99'} # Act exif = im._getexif() From 9ba379998038928f929c5abdc6152d510cff52fc Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 30 Dec 2015 07:36:30 +0200 Subject: [PATCH 0833/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 4b6a2295b..e382f3809 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Restore exif data to the v1 form + [wiredfool] + - Add /usr/local include and library directories for freebsd #1613 [leforestier] From 9fa6634f4df3c451fab2d10dd944b45b367eb38c Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 30 Dec 2015 07:54:14 -0800 Subject: [PATCH 0834/1037] Partial fix for #1597 Haven't excercized all of the metadata that we're allowing yet, and there's clearly still something up with Arrays, as one of them is still crashing and making it impossible to save a palette with a libtiff image. --- PIL/TiffImagePlugin.py | 23 +++++++++------ PIL/TiffTags.py | 56 +++++++++++++++++++++++++++++++++++++ Tests/images/rdf.tif | Bin 0 -> 40421 bytes Tests/test_file_libtiff.py | 11 ++++++++ 4 files changed, 81 insertions(+), 9 deletions(-) create mode 100644 Tests/images/rdf.tif diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index b502c5661..096be6f56 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -1400,8 +1400,7 @@ def _save(im, fp, filename): # STRIPOFFSETS and STRIPBYTECOUNTS are added by the library # based on the data in the strip. - # ICCPROFILE crashes. - blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ICCPROFILE] + blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS] atts = {} # bits per sample is a single short in the tiff directory, not a list. atts[BITSPERSAMPLE] = bits[0] @@ -1411,16 +1410,22 @@ def _save(im, fp, filename): legacy_ifd = {} if hasattr(im, 'tag'): legacy_ifd = im.tag.to_v2() - for k, v in itertools.chain(ifd.items(), + for tag, value in itertools.chain(ifd.items(), getattr(im, 'tag_v2', {}).items(), legacy_ifd.items()): - if k not in atts and k not in blocklist: - if isinstance(v, unicode if bytes is str else str): - atts[k] = v.encode('ascii', 'replace') + b"\0" - elif isinstance(v, IFDRational): - atts[k] = float(v) + # Libtiff can only process certain core items without adding + # them to the custom dictionary. It will segfault if it attempts + # to add a custom tag without the dictionary entry + # + # UNDONE -- add code for the custom dictionary + if tag not in TiffTags.LIBTIFF_CORE: continue + if tag not in atts and tag not in blocklist: + if isinstance(value, unicode if bytes is str else str): + atts[tag] = value.encode('ascii', 'replace') + b"\0" + elif isinstance(value, IFDRational): + atts[tag] = float(value) else: - atts[k] = v + atts[tag] = value if DEBUG: print("Converted items: %s" % sorted(atts.items())) diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py index 07710fdcc..4e47871d8 100644 --- a/PIL/TiffTags.py +++ b/PIL/TiffTags.py @@ -331,3 +331,59 @@ TYPES = {} # 11: "float", # 12: "double", # } + +# +# These tags are handled by default in libtiff, without +# adding to the custom dictionary. From tif_dir.c, searching for +# case TIFFTAG in the _TIFFVSetField function: +# Line: item. +# 148: case TIFFTAG_SUBFILETYPE: +# 151: case TIFFTAG_IMAGEWIDTH: +# 154: case TIFFTAG_IMAGELENGTH: +# 157: case TIFFTAG_BITSPERSAMPLE: +# 181: case TIFFTAG_COMPRESSION: +# 202: case TIFFTAG_PHOTOMETRIC: +# 205: case TIFFTAG_THRESHHOLDING: +# 208: case TIFFTAG_FILLORDER: +# 214: case TIFFTAG_ORIENTATION: +# 221: case TIFFTAG_SAMPLESPERPIXEL: +# 228: case TIFFTAG_ROWSPERSTRIP: +# 238: case TIFFTAG_MINSAMPLEVALUE: +# 241: case TIFFTAG_MAXSAMPLEVALUE: +# 244: case TIFFTAG_SMINSAMPLEVALUE: +# 247: case TIFFTAG_SMAXSAMPLEVALUE: +# 250: case TIFFTAG_XRESOLUTION: +# 256: case TIFFTAG_YRESOLUTION: +# 262: case TIFFTAG_PLANARCONFIG: +# 268: case TIFFTAG_XPOSITION: +# 271: case TIFFTAG_YPOSITION: +# 274: case TIFFTAG_RESOLUTIONUNIT: +# 280: case TIFFTAG_PAGENUMBER: +# 284: case TIFFTAG_HALFTONEHINTS: +# 288: case TIFFTAG_COLORMAP: +# 294: case TIFFTAG_EXTRASAMPLES: +# 298: case TIFFTAG_MATTEING: +# 305: case TIFFTAG_TILEWIDTH: +# 316: case TIFFTAG_TILELENGTH: +# 327: case TIFFTAG_TILEDEPTH: +# 333: case TIFFTAG_DATATYPE: +# 344: case TIFFTAG_SAMPLEFORMAT: +# 361: case TIFFTAG_IMAGEDEPTH: +# 364: case TIFFTAG_SUBIFD: +# 376: case TIFFTAG_YCBCRPOSITIONING: +# 379: case TIFFTAG_YCBCRSUBSAMPLING: +# 383: case TIFFTAG_TRANSFERFUNCTION: +# 389: case TIFFTAG_REFERENCEBLACKWHITE: +# 393: case TIFFTAG_INKNAMES: + +# some of these are not in our TAGS_V2 dict and were included from tiff.h + +LIBTIFF_CORE = set ([255, 256, 257, 258, 259, 262, 263, 266, 274, 277, + 278, 280, 281, 340, 341, 282, 283, 284, 286, 287, + 296, 297, 321, 320, 338, 32995, 322, 323, 32998, + 32996, 339, 32997, 330, 531, 530, 301, 532, 333, + # as above + 269 # this has been in our tests forever, and works + ]) + +LIBTIFF_CORE.remove(320) # Arrays error? diff --git a/Tests/images/rdf.tif b/Tests/images/rdf.tif new file mode 100644 index 0000000000000000000000000000000000000000..524a9dbc8806329f195026e4381c1e7859fcbe86 GIT binary patch literal 40421 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 literal 0 HcmV?d00001 diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 838b59365..8d7ef072f 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -395,6 +395,17 @@ class TestFileLibTiff(LibTiffTestCase): TiffImagePlugin.WRITE_LIBTIFF = False TiffImagePlugin.READ_LIBTIFF = False + def test_crashing_metadata(self): + # issue 1597 + im = Image.open('Tests/images/rdf.tif') + out = self.tempfile('temp.tif') + + TiffImagePlugin.WRITE_LIBTIFF = True + # this shouldn't crash + im.save(out, format='TIFF') + TiffImagePlugin.WRITE_LIBTIFF = False + + if __name__ == '__main__': unittest.main() From a83bcec169e7a2b1ce41cbdbc8d16a4750b89303 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 31 Dec 2015 07:27:27 +1100 Subject: [PATCH 0835/1037] Flake8 fixes --- PIL/BmpImagePlugin.py | 7 ++++--- PIL/JpegImagePlugin.py | 2 +- Tests/test_file_jpeg.py | 4 ++-- Tests/test_file_libtiff.py | 2 +- Tests/test_file_tiff.py | 20 ++++++++++---------- Tests/test_imagefont_bitmap.py | 10 ++++++---- 6 files changed, 24 insertions(+), 21 deletions(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index e715d8583..e398445fa 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -130,9 +130,10 @@ class BmpImageFile(ImageFile.ImageFile): # ----------------- Process BMP with Bitfields compression (not palette) if file_info['compression'] == self.BITFIELDS: SUPPORTED = { - 32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0)], - 24: [(0xff0000, 0xff00, 0xff)], - 16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]} + 32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0)], + 24: [(0xff0000, 0xff00, 0xff)], + 16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)] + } MASK_MODES = { (32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX", (32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA", diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 5305fe6e5..cce5c3906 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -434,7 +434,7 @@ def _getexif(self): else: info = TiffImagePlugin.ImageFileDirectory_v1(head) info.load(file) - exif[0x8825] = dict([(k,v[0]) if len(v) == 1 else (k,v) for k,v in info.items()]) + exif[0x8825] = dict([(k, v[0]) if len(v) == 1 else (k, v) for k, v in info.items()]) return exif diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 1591c1e83..6f144bfea 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -320,8 +320,8 @@ class TestFileJpeg(PillowTestCase): # dict of qtable lists self.assert_image_similar(im, self.roundtrip(im, qtables={ - 0: standard_l_qtable, - 1: standard_chrominance_qtable + 0: standard_l_qtable, + 1: standard_chrominance_qtable }), 30) # not a sequence diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 838b59365..d18314271 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -170,7 +170,7 @@ class TestFileLibTiff(LibTiffTestCase): 'RowsPerStrip', 'StripOffsets'] for field in requested_fields: - self.assertTrue(field in reloaded, "%s not in metadata" %field) + self.assertTrue(field in reloaded, "%s not in metadata" % field) def test_g3_compression(self): i = Image.open('Tests/images/hopper_g4_500.tif') diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 312daea56..1804dfa0d 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -113,7 +113,7 @@ class TestFileTiff(PillowTestCase): self.assert_warning(UserWarning, i._getexif) except struct.error: self.fail( - "Bad EXIF data passed incorrect values to _binary unpack") + "Bad EXIF data passed incorrect values to _binary unpack") def test_save_rgba(self): im = hopper("RGBA") @@ -245,18 +245,18 @@ class TestFileTiff(PillowTestCase): im = Image.open(filename) # v2 interface self.assertEqual( - im.tag_v2.as_dict(), - {256: 55, 257: 43, 258: (8, 8, 8, 8), 259: 1, - 262: 2, 296: 2, 273: (8,), 338: (1,), 277: 4, - 279: (9460,), 282: 72.0, 283: 72.0, 284: 1}) + im.tag_v2.as_dict(), + {256: 55, 257: 43, 258: (8, 8, 8, 8), 259: 1, + 262: 2, 296: 2, 273: (8,), 338: (1,), 277: 4, + 279: (9460,), 282: 72.0, 283: 72.0, 284: 1}) # legacy interface self.assertEqual( - im.tag.as_dict(), - {256: (55,), 257: (43,), 258: (8, 8, 8, 8), 259: (1,), - 262: (2,), 296: (2,), 273: (8,), 338: (1,), 277: (4,), - 279: (9460,), 282: ((720000, 10000),), - 283: ((720000, 10000),), 284: (1,)}) + im.tag.as_dict(), + {256: (55,), 257: (43,), 258: (8, 8, 8, 8), 259: (1,), + 262: (2,), 296: (2,), 273: (8,), 338: (1,), 277: (4,), + 279: (9460,), 282: ((720000, 10000),), + 283: ((720000, 10000),), 284: (1,)}) def test__delitem__(self): filename = "Tests/images/pil136.tiff" diff --git a/Tests/test_imagefont_bitmap.py b/Tests/test_imagefont_bitmap.py index 51da499b8..7ee5f8a2b 100644 --- a/Tests/test_imagefont_bitmap.py +++ b/Tests/test_imagefont_bitmap.py @@ -14,14 +14,16 @@ class TestImageFontBitmap(PillowTestCase): def test_similar(self): text = 'EmbeddedBitmap' font_outline = ImageFont.truetype( - font='Tests/fonts/DejaVuSans.ttf', size=24) + font='Tests/fonts/DejaVuSans.ttf', size=24) font_bitmap = ImageFont.truetype( - font='Tests/fonts/DejaVuSans-bitmap.ttf', size=24) - size_outline, size_bitmap = font_outline.getsize(text), font_bitmap.getsize(text) + font='Tests/fonts/DejaVuSans-bitmap.ttf', size=24) + size_outline = font_outline.getsize(text) + size_bitmap = font_bitmap.getsize(text) size_final = max(size_outline[0], size_bitmap[0]), max(size_outline[1], size_bitmap[1]) im_bitmap = Image.new('RGB', size_final, (255, 255, 255)) im_outline = im_bitmap.copy() - draw_bitmap, draw_outline = ImageDraw.Draw(im_bitmap), ImageDraw.Draw(im_outline) + draw_bitmap = ImageDraw.Draw(im_bitmap) + draw_outline = ImageDraw.Draw(im_outline) # Metrics are different on the bitmap and ttf fonts, # more so on some platforms and versions of freetype than others. From caf9cea00fc81c68d9c03aba64523e48f4d9d6af Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 31 Dec 2015 08:10:44 +1100 Subject: [PATCH 0836/1037] Updated copyright year --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 85be12f30..a1d09cd34 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -48,7 +48,7 @@ master_doc = 'index' # General information about the project. project = u'Pillow (PIL Fork)' -copyright = u'1995-2015, Fredrik Lundh and Contributors, Alex Clark and Contributors' +copyright = u'1995-2016, Fredrik Lundh and Contributors, Alex Clark and Contributors' author = u'Fredrik Lundh and Contributors, Alex Clark and Contributors' # The version info for the project you're documenting, acts as replacement for From 503b0de547df47e28d3c79f270e00c9124dc388a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 31 Dec 2015 08:16:52 +1100 Subject: [PATCH 0837/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index e382f3809..1f86672ff 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -70,7 +70,7 @@ Changelog (Pillow) - Updated freetype to 2.6.2 #1564 [radarhere] -- Updated WebP to 0.4.4 for Travis #1515 +- Updated WebP to 0.5.0 for Travis #1515 #1609 [radarhere] - Fix missing 'version' key value in __array_interface__ #1519 From eba87ac6d9e9e515b89140b06f65e66bc3d39191 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 30 Dec 2015 13:34:33 -0800 Subject: [PATCH 0838/1037] Catch the IFD error near the source --- PIL/JpegImagePlugin.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 5305fe6e5..12399ac4e 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -453,9 +453,12 @@ def _getmp(self): head = file_contents.read(8) endianness = '>' if head[:4] == b'\x4d\x4d\x00\x2a' else '<' # process dictionary - info = TiffImagePlugin.ImageFileDirectory_v2(head) - info.load(file_contents) - mp = dict(info) + try: + info = TiffImagePlugin.ImageFileDirectory_v2(head) + info.load(file_contents) + mp = dict(info) + except: + raise SyntaxError("malformed MP Index (unreadable directory)") # it's an error not to have a number of images try: quant = mp[0xB001] From a9d1cda126b09d88d260c69d8c60a4078583752d Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 30 Dec 2015 13:52:01 -0800 Subject: [PATCH 0839/1037] Docs for crashing fix --- docs/handbook/image-file-formats.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 54d9b7274..a2ab63b8d 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -523,7 +523,13 @@ The :py:meth:`~PIL.Image.Image.save` method can take the following keyword argum .. versionadded:: 3.0.0 -**compression** + .. note:: + + Only some tags are currently supported when writing using + libtiff. The supported list is found in + :py:attr:`~PIL:TiffTags.LIBTIFF_CORE`. + +**compression** A string containing the desired compression method for the file. (valid only with libtiff installed) Valid compression methods are: ``None``, ``"tiff_ccitt"``, ``"group3"``, From 36c48150ea819041e46b2247dcc67f34557f705a Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 30 Dec 2015 13:52:13 -0800 Subject: [PATCH 0840/1037] Docs formatting --- docs/handbook/image-file-formats.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index a2ab63b8d..f41eaf4af 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -479,7 +479,7 @@ The :py:meth:`~PIL.Image.Image.open` method sets the following The :py:attr:`~PIL.Image.Image.tag_v2` attribute contains a dictionary of TIFF metadata. The keys are numerical indexes from -`~PIL.TiffTags.TAGS_V2`. Values are strings or numbers for single +:py:attr:`~PIL.TiffTags.TAGS_V2`. Values are strings or numbers for single items, multiple values are returned in a tuple of values. Rational numbers are returned as a :py:class:`~PIL.TiffImagePlugin.IFDRational` object. @@ -531,11 +531,11 @@ The :py:meth:`~PIL.Image.Image.save` method can take the following keyword argum **compression** A string containing the desired compression method for the - file. (valid only with libtiff installed) Valid compression - methods are: ``None``, ``"tiff_ccitt"``, ``"group3"``, - ``"group4"``, ``"tiff_jpeg"``, ``"tiff_adobe_deflate"``, - ``"tiff_thunderscan"``, ``"tiff_deflate"``, ``"tiff_sgilog"``, - ``"tiff_sgilog24"``, ``"tiff_raw_16"`` + file. (valid only with libtiff installed) Valid compression + methods are: ``None``, ``"tiff_ccitt"``, ``"group3"``, + ``"group4"``, ``"tiff_jpeg"``, ``"tiff_adobe_deflate"``, + ``"tiff_thunderscan"``, ``"tiff_deflate"``, ``"tiff_sgilog"``, + ``"tiff_sgilog24"``, ``"tiff_raw_16"`` These arguments to set the tiff header fields are an alternative to using the general tags available through tiffinfo. From 23a04f852915b5b9ac5a05142b516ab9a2b80c3c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 31 Dec 2015 10:20:49 +1100 Subject: [PATCH 0841/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 1f86672ff..7d129070e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -25,7 +25,7 @@ Changelog (Pillow) - Allow saving RowsPerStrip with libtiff #1594 [wiredfool] -- Enabled conversion to numpy array for HSV images. Fixes issue #1578 +- Enabled conversion to numpy array for HSV images #1578 [cartisan] - Changed some urls in the docs to use https #1580 From 10099b41e159a5691429141be75355a3571be782 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 31 Dec 2015 10:45:21 +1100 Subject: [PATCH 0842/1037] Updated spacing to be consistent between multiline methods --- PIL/ImageDraw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py index 7f3e68c27..f5b72efb7 100644 --- a/PIL/ImageDraw.py +++ b/PIL/ImageDraw.py @@ -296,7 +296,7 @@ class ImageDraw(object): font = self.getfont() return font.getsize(text) - def multiline_textsize(self, text, font=None, spacing=0): + def multiline_textsize(self, text, font=None, spacing=4): max_width = 0 lines = self._multiline_split(text) line_spacing = self.textsize('A', font=font)[1] + spacing From c4f9e91122a66da2086696d4a04bbf5389255249 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 31 Dec 2015 12:32:44 +1100 Subject: [PATCH 0843/1037] Added release notes for 3.1.0 [ci skip] --- docs/releasenotes/3.1.0.rst | 23 +++++++++++++++++++++++ docs/releasenotes/index.rst | 1 + 2 files changed, 24 insertions(+) create mode 100644 docs/releasenotes/3.1.0.rst diff --git a/docs/releasenotes/3.1.0.rst b/docs/releasenotes/3.1.0.rst new file mode 100644 index 000000000..3b4bf43a7 --- /dev/null +++ b/docs/releasenotes/3.1.0.rst @@ -0,0 +1,23 @@ + +3.1.0 +===== + +ImageDraw arc, chord and pieslice can now use floats +---------------------------------------------------- + +There is no longer a need to ensure that the start and end arguments for `arc`, +`chord` and `pieslice` are integers. + +Note that these numbers are not simply rounded internally, but are actually +utilised in the drawing process. + +Consistent multiline text spacing +--------------------------------- + +When using the ImageDraw multiline methods, the spacing between lines was +inconsistent, based on the combination on ascenders and descenders. + +This has now been fixed, so that lines are offset by their baselines, not the +absolute height of each line. + +There is also now a default spacing of 4px between lines. \ No newline at end of file diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index e85fbdb88..9d38637fb 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -6,6 +6,7 @@ Release Notes .. toctree:: :maxdepth: 2 + 3.1.0 3.0.0 2.8.0 2.7.0 From b84675dca072d1e42f2fce236ebc32ef7027248d Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 31 Dec 2015 15:24:06 +0200 Subject: [PATCH 0844/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 7d129070e..5d3023b0b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,21 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Divide by zero in Exif #1531 + [wiredfool] + +- Catch the IFD error near the source #1622 + [wiredfool] + +- Added release notes for 3.1.0 #1623 + [radarhere] + +- Updated spacing to be consistent between multiline methods #1624 + [radarhere] + +- Let EditorConfig take care of some basic formatting #1489 + [hugovk] + - Restore exif data to the v1 form [wiredfool] @@ -64,7 +79,7 @@ Changelog (Pillow) - ExtraSamples tag should be a SHORT, not a BYTE #1555 [Nexuapex] -- Docs and code health fixes #1565 #1566 #1581 #1586 #1591 +- Docs and code health fixes #1565 #1566 #1581 #1586 #1591 #1621 [radarhere] - Updated freetype to 2.6.2 #1564 From f9db616068bcd86404290d48424e9226747480c3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 1 Jan 2016 22:47:52 +1100 Subject: [PATCH 0845/1037] Exclude .editorconfig in MANIFEST --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 70ca05b01..4fd9c602e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -85,3 +85,4 @@ recursive-include winbuild *.gitignore recursive-include winbuild *.md recursive-include winbuild *.opt recursive-include winbuild *.py +exclude .editorconfig From a6c1331fdd06028bb89e2083eeda385b0b564c3b Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 1 Jan 2016 04:08:44 -0800 Subject: [PATCH 0846/1037] Rolling back exif support to pre-3.0 format --- PIL/JpegImagePlugin.py | 24 +++++++++++++++++++----- Tests/test_file_jpeg.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 61a6af633..beda4f7a0 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -394,6 +394,19 @@ class JpegImageFile(ImageFile.ImageFile): return _getmp(self) +def _fixup_dict(src_dict): + # Helper function for _getexif() + # returns a dict with any single item tuples/lists as individual values + def _fixup(value): + try: + if len(value) == 1 and type(value) != type({}): + return value[0] + except: pass + return value + + return dict([(k, _fixup(v)) for k, v in src_dict.items()]) + + def _getexif(self): # Extract EXIF information. This method is highly experimental, # and is likely to be replaced with something better in a future @@ -408,9 +421,9 @@ def _getexif(self): file = io.BytesIO(data[6:]) head = file.read(8) # process dictionary - info = TiffImagePlugin.ImageFileDirectory_v2(head) + info = TiffImagePlugin.ImageFileDirectory_v1(head) info.load(file) - exif = dict(info) + exif = dict(_fixup_dict(info)) # get exif extension try: # exif field 0x8769 is an offset pointer to the location @@ -420,9 +433,9 @@ def _getexif(self): except (KeyError, TypeError): pass else: - info = TiffImagePlugin.ImageFileDirectory_v2(head) + info = TiffImagePlugin.ImageFileDirectory_v1(head) info.load(file) - exif.update(info) + exif.update(_fixup_dict(info)) # get gpsinfo extension try: # exif field 0x8825 is an offset pointer to the location @@ -434,7 +447,8 @@ def _getexif(self): else: info = TiffImagePlugin.ImageFileDirectory_v1(head) info.load(file) - exif[0x8825] = dict([(k, v[0]) if len(v) == 1 else (k, v) for k, v in info.items()]) + exif[0x8825] = _fixup_dict(info) + return exif diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 6f144bfea..c54d08713 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -188,6 +188,38 @@ class TestFileJpeg(PillowTestCase): # Assert self.assertEqual(exif[gps_index], expected_exif_gps) + def test_exif_rollback(self): + # rolling back exif support in 3.1 to pre-3.0 formatting. + # expected from 2.9, with b/u qualifiers switched for 3.2 compatibility + # this test passes on 2.9 and 3.1, but not 3.0 + expected_exif = {34867: 4294967295, + 258: (24, 24, 24), + 36867: '2099:09:29 10:10:10', + 34853: {0: b'\x00\x00\x00\x01', + 2: (4294967295, 1), + 5: b'\x01', + 30: 65535, + 29: '1999:99:99 99:99:99'}, + 296: 65535, + 34665: 185, + 41994: 65535, + 514: 4294967295, + 271: 'Make', + 272: 'XXX-XXX', + 305: 'PIL', + 42034: ((1, 1), (1, 1), (1, 1), (1, 1)), + 42035: 'LensMake', + 34856: b'\xaa\xaa\xaa\xaa\xaa\xaa', + 282: (4294967295, 1), + 33434: (4294967295, 1)} + + im = Image.open('Tests/images/exif_gps.jpg') + exif = im._getexif() + + for tag, value in expected_exif.items(): + self.assertEqual(value, exif[tag]) + + def test_exif_gps_typeerror(self): im = Image.open('Tests/images/exif_gps_typeerror.jpg') From 36d4f5a9b4380ca46b5820d850b0ade17c6fab33 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 1 Jan 2016 05:30:40 -0800 Subject: [PATCH 0847/1037] Tests for permitted libtiff metadata --- PIL/TiffTags.py | 15 ++++++++++- Tests/test_file_libtiff.py | 55 +++++++++++++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py index 4e47871d8..6bb2409f3 100644 --- a/PIL/TiffTags.py +++ b/PIL/TiffTags.py @@ -386,4 +386,17 @@ LIBTIFF_CORE = set ([255, 256, 257, 258, 259, 262, 263, 266, 274, 277, 269 # this has been in our tests forever, and works ]) -LIBTIFF_CORE.remove(320) # Arrays error? +LIBTIFF_CORE.remove(320) # Array of short, crashes +LIBTIFF_CORE.remove(301) # Array of short, crashes +LIBTIFF_CORE.remove(532) # Array of long, crashes + +LIBTIFF_CORE.remove(255) # We don't have support for subfiletypes +LIBTIFF_CORE.remove(322) # We don't have support for tiled images in libtiff +LIBTIFF_CORE.remove(323) # Tiled images +LIBTIFF_CORE.remove(333) # Ink Names either + +# Note to advanced users: There may be combinations of these +# parameters and values that when added properly, will work and +# produce valid tiff images that may work in your application. +# It is safe to add and remove tags from this set from Pillow's point +# of view so long as you test against libtiff. diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 8d7ef072f..50e6dcc23 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -7,7 +7,7 @@ import logging import itertools import os -from PIL import Image, TiffImagePlugin +from PIL import Image, TiffImagePlugin, TiffTags logger = logging.getLogger(__name__) @@ -172,6 +172,59 @@ class TestFileLibTiff(LibTiffTestCase): for field in requested_fields: self.assertTrue(field in reloaded, "%s not in metadata" %field) + def test_additional_metadata(self): + # these should not crash. Seriously dummy data, most of it doesn't make + # any sense, so we're running up against limits where we're asking + # libtiff to do stupid things. + + # Get the list of the ones that we should be able to write + + core_items = dict((tag, info) for tag, info in [(s,TiffTags.lookup(s)) for s + in TiffTags.LIBTIFF_CORE] + if info.type is not None) + + # Exclude ones that have special meaning that we're already testing them + im = Image.open('Tests/images/hopper_g4.tif') + for tag in im.tag_v2.keys(): + try: + del(core_items[tag]) + except: pass + + # Type codes: + # 2: "ascii", + # 3: "short", + # 4: "long", + # 5: "rational", + # 12: "double", + # type: dummy value + values = { 2: 'test', + 3: 1, + 4: 2**20, + 5: TiffImagePlugin.IFDRational(100,1), + 12: 1.05 } + + + new_ifd = TiffImagePlugin.ImageFileDirectory_v2() + for tag, info in core_items.items(): + if info.length == 1: + new_ifd[tag] = values[info.type] + if info.length == 0: + new_ifd[tag] = tuple(values[info.type] for _ in range(3)) + else: + new_ifd[tag] = tuple(values[info.type] for _ in range(info.length)) + + # Extra samples really doesn't make sense in this application. + del(new_ifd[338]) + + out = self.tempfile("temp.tif") + TiffImagePlugin.WRITE_LIBTIFF = True + + im.save(out, tiffinfo=new_ifd) + + TiffImagePlugin.WRITE_LIBTIFF = False + + + def test_g3_compression(self): i = Image.open('Tests/images/hopper_g4_500.tif') out = self.tempfile("temp.tif") From 02a9ed8f90b0cbe180cf47aea76d35bbe4ad9ac1 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 1 Jan 2016 15:44:52 +0000 Subject: [PATCH 0848/1037] Added metadata details to release notes --- docs/releasenotes/3.1.0.rst | 62 ++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/docs/releasenotes/3.1.0.rst b/docs/releasenotes/3.1.0.rst index 3b4bf43a7..6dda54444 100644 --- a/docs/releasenotes/3.1.0.rst +++ b/docs/releasenotes/3.1.0.rst @@ -14,10 +14,62 @@ utilised in the drawing process. Consistent multiline text spacing --------------------------------- -When using the ImageDraw multiline methods, the spacing between lines was -inconsistent, based on the combination on ascenders and descenders. +When using the ``ImageDraw`` multiline methods, the spacing between +lines was inconsistent, based on the combination on ascenders and +descenders. -This has now been fixed, so that lines are offset by their baselines, not the -absolute height of each line. +This has now been fixed, so that lines are offset by their baselines, +not the absolute height of each line. -There is also now a default spacing of 4px between lines. \ No newline at end of file +There is also now a default spacing of 4px between lines. + +Exif, Jpeg and Tiff Metadata +---------------------------- + +There were major changes in the TIFF ImageFileDirectory support in +Pillow 3.0 that led to a number of regressions. Some of them have been +fixed in Pillow 3.1, and some of them have been extended to have +different behavior. + +TiffImagePlugin.IFDRational ++++++++++++++++++++++++++++ + +Pillow 3.0 changed rational metadata to use a float. In Pillow 3.1, +this has changed to allow the expression of 0/0 as a valid piece of +rational metadata to reflect usage in the wild. + +Rational metadata is now encapsulated in an ``IFDRational`` +instance. This class extends the Rational class to allow a denominator +of 0. It compares as a float or a number, but does allow access to the +raw numerator and denominator values through attributes. + +When used in a ``ImageFileDirectory_v1``, a 2 item tuple is returned +of the numerator and denominator, as was done previously. + +This class should be used when adding a rational value to an +ImageFileDirectory for saving to image metadata. + +JpegImagePlugin._getexif +++++++++++++++++++++++++ + +In Pillow 3.0, the dictionary returned from the private, experimental, +but generally widely used ``_getexif`` function changed to reflect the +ImageFileDirectory_v2 format, without a fallback to the previous format. + +In Pillow 3.1, ``_getexif`` now returns a dictionary compatible with +Pillow 2.9 and earlier, built with +``ImageFileDirectory_v1`` instances. Additionally, any +single item tuples have been unwrapped and return a bare element. + +The format returned by Pillow 3.0 has been abandoned. A more fully +featured interface for EXIF is anticipated in a future release. + +Out of Spec Metadata +++++++++++++++++++++ + +In Pillow 3.0 and 3.1, images that contan metadata that is internally +consistent but not in agreement with the TIFF spec may cause an +exception when reading the metadata. This can happen when a tag that +is specified to have a single value is stored with an array of values. + +It is anticipated that this behavior will change in future releases. From 33d65f7064718b564da93aa154910e8ee1c73893 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 1 Jan 2016 15:49:30 +0000 Subject: [PATCH 0849/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 5d3023b0b..104700a7b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,7 +4,13 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ -- Divide by zero in Exif #1531 +- Limit metadata tags when writing using libtiff #1620 + [wiredfool] + +- Rolling back exif support to pre-3.0 format #1627 + [wiredfool] + +- Fix Divide by zero in Exif, add IFDRational class #1531 [wiredfool] - Catch the IFD error near the source #1622 @@ -19,7 +25,7 @@ Changelog (Pillow) - Let EditorConfig take care of some basic formatting #1489 [hugovk] -- Restore exif data to the v1 form +- Restore gpsexif data to the v1 form [wiredfool] - Add /usr/local include and library directories for freebsd #1613 From 7a4ff8e66c612d9396ec59dcf2640cf727bf9923 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 1 Jan 2016 15:57:42 +0000 Subject: [PATCH 0850/1037] Forcing this one to pass --- Tests/test_pyroma.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_pyroma.py b/Tests/test_pyroma.py index 0875cc972..438e0160c 100644 --- a/Tests/test_pyroma.py +++ b/Tests/test_pyroma.py @@ -30,7 +30,7 @@ class TestPyroma(PillowTestCase): # Pyroma needs to chill about RC versions # and not kill all our tests. self.assertEqual(rating, (9, [ - 'The packages version number does not comply with PEP-386.'])) + "The package's version number does not comply with PEP-386."])) else: # Should have a perfect score From 0f9f5cd8db3365dca23b1b2490dbccf819a07b5c Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 1 Jan 2016 16:00:32 +0000 Subject: [PATCH 0851/1037] 3.1.0-rc1 versioning --- PIL/__init__.py | 2 +- _imaging.c | 2 +- appveyor.yml | 2 +- setup.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/PIL/__init__.py b/PIL/__init__.py index c18d7830c..a5ddf6406 100644 --- a/PIL/__init__.py +++ b/PIL/__init__.py @@ -12,7 +12,7 @@ # ;-) VERSION = '1.1.7' # PIL version -PILLOW_VERSION = '3.1.0.dev0' # Pillow +PILLOW_VERSION = '3.1.0.rc1' # Pillow _plugins = ['BmpImagePlugin', 'BufrStubImagePlugin', diff --git a/_imaging.c b/_imaging.c index 1aa6b8e3b..e934a8a81 100644 --- a/_imaging.c +++ b/_imaging.c @@ -71,7 +71,7 @@ * See the README file for information on usage and redistribution. */ -#define PILLOW_VERSION "3.1.0.dev0" +#define PILLOW_VERSION "3.1.0.rc1" #include "Python.h" diff --git a/appveyor.yml b/appveyor.yml index 5ed603462..db3ee7f38 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 3.1.pre.{build} +version: 3.1.rc.{build} clone_folder: c:\pillow init: - ECHO %PYTHON% diff --git a/setup.py b/setup.py index 3b65b93c6..3cd5f8771 100644 --- a/setup.py +++ b/setup.py @@ -90,7 +90,7 @@ except (ImportError, OSError): NAME = 'Pillow' -PILLOW_VERSION = '3.1.0.dev0' +PILLOW_VERSION = '3.1.0.rc1' TCL_ROOT = None JPEG_ROOT = None JPEG2K_ROOT = None From 10d5c7df6d49922a3741c3709450afb0b6e64317 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Fri, 1 Jan 2016 11:35:27 -0500 Subject: [PATCH 0852/1037] Don't break the README! Looks like my Gratipay badge broke rst generation on PyPI, sorry! Should be fixed now. (PyPI won't load external URLs unless they're part of a directive.) --- README.rst | 8 -------- 1 file changed, 8 deletions(-) diff --git a/README.rst b/README.rst index cc5184a75..c60ad6eeb 100644 --- a/README.rst +++ b/README.rst @@ -58,11 +58,3 @@ More Information - `Changelog `_ - `Pre-fork `_ - -.. raw:: html - -

From 8ece7f6c9e5b75ca21103cfdc1d8b746f28a14ab Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 3 Jan 2016 23:14:27 +1100 Subject: [PATCH 0853/1037] Added AppVeyor to RELEASING checklist [ci skip] --- RELEASING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASING.md b/RELEASING.md index 4496af2c1..c6a7df7a8 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -6,7 +6,7 @@ Released quarterly on the first day of January, April, July, October. * [ ] Open a release ticket e.g. https://github.com/python-pillow/Pillow/issues/1174 * [ ] Develop and prepare release in ``master`` branch. -* [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in ``master`` branch. +* [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) and [AppVeyor CI](https://ci.appveyor.com/project/python-pillow/Pillow) to confirm passing tests in ``master`` branch. * [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in: ``` PIL/__init__.py setup.py _imaging.c appveyor.yml From 21beef192a33518ca463ce38437750a3c846aeb2 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 3 Jan 2016 12:20:19 -0800 Subject: [PATCH 0854/1037] Fixing test failures on python 2.6/windows --- Tests/test_image_getim.py | 9 ++++++++- Tests/test_imagewin_pointers.py | 6 +++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Tests/test_image_getim.py b/Tests/test_image_getim.py index e83439789..ab0ec781c 100644 --- a/Tests/test_image_getim.py +++ b/Tests/test_image_getim.py @@ -10,7 +10,14 @@ class TestImageGetIm(PillowTestCase): if py3: self.assertIn("PyCapsule", type_repr) - self.assertIsInstance(im.im.id, int) + + try: + #py2.6 + target_types = (int, long) + except: + target_types = (int) + + self.assertIsInstance(im.im.id, target_types) if __name__ == '__main__': diff --git a/Tests/test_imagewin_pointers.py b/Tests/test_imagewin_pointers.py index bf7f4bb7c..05af24acb 100644 --- a/Tests/test_imagewin_pointers.py +++ b/Tests/test_imagewin_pointers.py @@ -72,7 +72,11 @@ if sys.platform.startswith('win32'): memcpy(bp, ctypes.byref(bf), ctypes.sizeof(bf)) memcpy(bp + ctypes.sizeof(bf), ctypes.byref(bi), bi.biSize) memcpy(bp + bf.bfOffBits, pixels, bi.biSizeImage) - return bytearray(buf) + try: + return bytearray(buf) + except: + # py2.6 + return buffer(buf)[:] class TestImageWinPointers(PillowTestCase): def test_pointer(self): From 859ce84976f28ae3ddf80d3d2017b13a8ec27cec Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 3 Jan 2016 13:04:04 -0800 Subject: [PATCH 0855/1037] Version check rather than try/except --- Tests/test_image_getim.py | 8 ++++---- Tests/test_imagewin_pointers.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Tests/test_image_getim.py b/Tests/test_image_getim.py index ab0ec781c..83c857975 100644 --- a/Tests/test_image_getim.py +++ b/Tests/test_image_getim.py @@ -1,5 +1,5 @@ from helper import unittest, PillowTestCase, hopper, py3 - +import sys class TestImageGetIm(PillowTestCase): @@ -11,10 +11,10 @@ class TestImageGetIm(PillowTestCase): self.assertIn("PyCapsule", type_repr) - try: - #py2.6 + if sys.hexversion < 0x2070000: + # py2.6 x64, windows target_types = (int, long) - except: + else: target_types = (int) self.assertIsInstance(im.im.id, target_types) diff --git a/Tests/test_imagewin_pointers.py b/Tests/test_imagewin_pointers.py index 05af24acb..692868bdf 100644 --- a/Tests/test_imagewin_pointers.py +++ b/Tests/test_imagewin_pointers.py @@ -74,7 +74,7 @@ if sys.platform.startswith('win32'): memcpy(bp + bf.bfOffBits, pixels, bi.biSizeImage) try: return bytearray(buf) - except: + except ValueError: # py2.6 return buffer(buf)[:] From 60cf27fde1fd3eed933cc1788ff6ecb960118317 Mon Sep 17 00:00:00 2001 From: Hugo Date: Sun, 3 Jan 2016 23:42:57 +0200 Subject: [PATCH 0856/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 104700a7b..b4a3c2d6b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.1.0 (unreleased) ------------------ +- Fixing test failures on Python 2.6/Windows #1633 + [wiredfool] + - Limit metadata tags when writing using libtiff #1620 [wiredfool] From b56e1f74f3513b87ec2d70da6e3279059a1d6a7f Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 4 Jan 2016 01:46:06 -0800 Subject: [PATCH 0857/1037] s/fromstring/frombytes/ --- docs/handbook/writing-your-own-file-decoder.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/handbook/writing-your-own-file-decoder.rst b/docs/handbook/writing-your-own-file-decoder.rst index 0af4007be..acc80e4ee 100644 --- a/docs/handbook/writing-your-own-file-decoder.rst +++ b/docs/handbook/writing-your-own-file-decoder.rst @@ -124,9 +124,9 @@ The raw decoder The ``raw`` decoder is used to read uncompressed data from an image file. It can be used with most uncompressed file formats, such as PPM, BMP, uncompressed TIFF, and many others. To use the raw decoder with the -:py:func:`PIL.Image.fromstring` function, use the following syntax:: +:py:func:`PIL.Image.frombytes` function, use the following syntax:: - image = Image.fromstring( + image = Image.frombytes( mode, size, data, "raw", raw mode, stride, orientation ) @@ -233,9 +233,9 @@ If the raw decoder cannot handle your format, PIL also provides a special “bit decoder that can be used to read various packed formats into a floating point image memory. -To use the bit decoder with the fromstring function, use the following syntax:: +To use the bit decoder with the frombytes function, use the following syntax:: - image = fromstring( + image = frombytes( mode, size, data, "bit", bits, pad, fill, sign, orientation ) From fff5536b37c2d619c66c1189b6925fa0a8df3822 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 4 Jan 2016 02:04:23 -0800 Subject: [PATCH 0858/1037] 3.1.0 version --- PIL/__init__.py | 2 +- _imaging.c | 2 +- appveyor.yml | 2 +- setup.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/PIL/__init__.py b/PIL/__init__.py index a5ddf6406..e251cfa1c 100644 --- a/PIL/__init__.py +++ b/PIL/__init__.py @@ -12,7 +12,7 @@ # ;-) VERSION = '1.1.7' # PIL version -PILLOW_VERSION = '3.1.0.rc1' # Pillow +PILLOW_VERSION = '3.1.0' # Pillow _plugins = ['BmpImagePlugin', 'BufrStubImagePlugin', diff --git a/_imaging.c b/_imaging.c index e934a8a81..3f86938bc 100644 --- a/_imaging.c +++ b/_imaging.c @@ -71,7 +71,7 @@ * See the README file for information on usage and redistribution. */ -#define PILLOW_VERSION "3.1.0.rc1" +#define PILLOW_VERSION "3.1.0" #include "Python.h" diff --git a/appveyor.yml b/appveyor.yml index db3ee7f38..749629356 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 3.1.rc.{build} +version: 3.1.0.{build} clone_folder: c:\pillow init: - ECHO %PYTHON% diff --git a/setup.py b/setup.py index 3cd5f8771..f31a1468d 100644 --- a/setup.py +++ b/setup.py @@ -90,7 +90,7 @@ except (ImportError, OSError): NAME = 'Pillow' -PILLOW_VERSION = '3.1.0.rc1' +PILLOW_VERSION = '3.1.0' TCL_ROOT = None JPEG_ROOT = None JPEG2K_ROOT = None From 8d518a3f4d7d76769ac76f5ee31a946fe26a02d8 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 4 Jan 2016 03:03:26 -0800 Subject: [PATCH 0859/1037] 3.2.0-dev versioning --- PIL/__init__.py | 2 +- _imaging.c | 2 +- appveyor.yml | 2 +- setup.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/PIL/__init__.py b/PIL/__init__.py index e251cfa1c..62386d059 100644 --- a/PIL/__init__.py +++ b/PIL/__init__.py @@ -12,7 +12,7 @@ # ;-) VERSION = '1.1.7' # PIL version -PILLOW_VERSION = '3.1.0' # Pillow +PILLOW_VERSION = '3.2.0.dev0' # Pillow _plugins = ['BmpImagePlugin', 'BufrStubImagePlugin', diff --git a/_imaging.c b/_imaging.c index 3f86938bc..de1fa5be5 100644 --- a/_imaging.c +++ b/_imaging.c @@ -71,7 +71,7 @@ * See the README file for information on usage and redistribution. */ -#define PILLOW_VERSION "3.1.0" +#define PILLOW_VERSION "3.2.0.dev0" #include "Python.h" diff --git a/appveyor.yml b/appveyor.yml index 749629356..6d858c615 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 3.1.0.{build} +version: 3.2.pre.{build} clone_folder: c:\pillow init: - ECHO %PYTHON% diff --git a/setup.py b/setup.py index f31a1468d..f669e2319 100644 --- a/setup.py +++ b/setup.py @@ -90,7 +90,7 @@ except (ImportError, OSError): NAME = 'Pillow' -PILLOW_VERSION = '3.1.0' +PILLOW_VERSION = '3.2.0.dev0' TCL_ROOT = None JPEG_ROOT = None JPEG2K_ROOT = None From 255a8b522022bfedf3d257fa4b3ff7259642cd01 Mon Sep 17 00:00:00 2001 From: Sandro Mani Date: Mon, 4 Jan 2016 13:44:24 +0100 Subject: [PATCH 0860/1037] Fix incorrect conditional --- encode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/encode.c b/encode.c index fef102176..63220b767 100644 --- a/encode.c +++ b/encode.c @@ -54,7 +54,7 @@ PyImaging_EncoderNew(int contextsize) ImagingEncoderObject *encoder; void *context; - if(!PyType_Ready(&ImagingEncoderType) < 0) + if(PyType_Ready(&ImagingEncoderType) < 0) return NULL; encoder = PyObject_New(ImagingEncoderObject, &ImagingEncoderType); From f2e0a8e550a2964f78aee1f416d5f48212ac658d Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 4 Jan 2016 13:51:17 +0000 Subject: [PATCH 0861/1037] Update CHANGES.rst --- CHANGES.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index b4a3c2d6b..23fb56983 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,7 +1,13 @@ Changelog (Pillow) ================== -3.1.0 (unreleased) +3.2.0 (unreleased) +------------------ + +- Fix incorrect conditional in encode.c #1638 + [manisandro] + +3.1.0 (2016-01-04) ------------------ - Fixing test failures on Python 2.6/Windows #1633 From ef58462e56c9f8c82cbba4b29ffc5aa132f4ba63 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 4 Jan 2016 07:31:08 -0800 Subject: [PATCH 0862/1037] scripts for ubuntu 12.04 and fedora 23 --- depends/fedora_23.sh | 17 +++++++++++++++++ depends/ubuntu_12.04.sh | 16 ++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100755 depends/fedora_23.sh create mode 100755 depends/ubuntu_12.04.sh diff --git a/depends/fedora_23.sh b/depends/fedora_23.sh new file mode 100755 index 000000000..b7442651a --- /dev/null +++ b/depends/fedora_23.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +# +# Installs all of the dependencies for Pillow for Fedora 23 +# for both system Pythons 2.7 and 3.4 +# + +# this is a workaround for +# "gcc: error: /usr/lib/rpm/redhat/redhat-hardened-cc1: No such file or directory" +# errors when compiling. +sudo yum install redhat-rpm-config + +sudo yum install python-devel python3-devel python-virtualenv make gcc + +# Note, I can't find a python2-tkinter package +sudo yum install libtiff-devel libjpeg-devel libzip-devel freetype-devel \ + lcms2-devel libwebp-devel openjpeg2-devel python3-tkinter tcl-devel tk-devel \ No newline at end of file diff --git a/depends/ubuntu_12.04.sh b/depends/ubuntu_12.04.sh new file mode 100755 index 000000000..e9b16d2b4 --- /dev/null +++ b/depends/ubuntu_12.04.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +# +# Installs all of the dependencies for Pillow for Ubuntu 12.04 +# for both system Pythons 2.7 and 3.2 +# + +sudo apt-get -y install python-dev python-setuptools \ + python3-dev python-virtualenv cmake +sudo apt-get install libtiff4-dev libjpeg8-dev zlib1g-dev \ + libfreetype6-dev liblcms2-dev tcl8.5-dev \ + tk8.5-dev python-tk python3-tk + + +./install_openjpeg.sh +./install_webp.sh From 804063357405baed9f4e9b8a600b8839a2533f5c Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 4 Jan 2016 07:33:46 -0800 Subject: [PATCH 0863/1037] Yum has been replaced --- depends/fedora_23.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/depends/fedora_23.sh b/depends/fedora_23.sh index b7442651a..7a0147e7f 100755 --- a/depends/fedora_23.sh +++ b/depends/fedora_23.sh @@ -8,10 +8,10 @@ # this is a workaround for # "gcc: error: /usr/lib/rpm/redhat/redhat-hardened-cc1: No such file or directory" # errors when compiling. -sudo yum install redhat-rpm-config +sudo dnf install redhat-rpm-config -sudo yum install python-devel python3-devel python-virtualenv make gcc +sudo dnf install python-devel python3-devel python-virtualenv make gcc # Note, I can't find a python2-tkinter package -sudo yum install libtiff-devel libjpeg-devel libzip-devel freetype-devel \ +sudo dnf install libtiff-devel libjpeg-devel libzip-devel freetype-devel \ lcms2-devel libwebp-devel openjpeg2-devel python3-tkinter tcl-devel tk-devel \ No newline at end of file From b952f7e3af773f024ee1da5a411dff54d80095eb Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 4 Jan 2016 07:42:03 -0800 Subject: [PATCH 0864/1037] tkinter, notes --- depends/fedora_23.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/depends/fedora_23.sh b/depends/fedora_23.sh index 7a0147e7f..2825eba56 100755 --- a/depends/fedora_23.sh +++ b/depends/fedora_23.sh @@ -4,6 +4,7 @@ # Installs all of the dependencies for Pillow for Fedora 23 # for both system Pythons 2.7 and 3.4 # +# note that Fedora does ship packages for Pillow as python-pillow # this is a workaround for # "gcc: error: /usr/lib/rpm/redhat/redhat-hardened-cc1: No such file or directory" @@ -12,6 +13,6 @@ sudo dnf install redhat-rpm-config sudo dnf install python-devel python3-devel python-virtualenv make gcc -# Note, I can't find a python2-tkinter package sudo dnf install libtiff-devel libjpeg-devel libzip-devel freetype-devel \ - lcms2-devel libwebp-devel openjpeg2-devel python3-tkinter tcl-devel tk-devel \ No newline at end of file + lcms2-devel libwebp-devel openjpeg2-devel tkinter python3-tkinter \ + tcl-devel tk-devel \ No newline at end of file From dcbb26234ae5737d75a1cf4e391017042807d5ba Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Mon, 4 Jan 2016 17:08:48 +0000 Subject: [PATCH 0865/1037] Added freebsd script --- depends/freebsd_10.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100755 depends/freebsd_10.sh diff --git a/depends/freebsd_10.sh b/depends/freebsd_10.sh new file mode 100755 index 000000000..99b4d6d0f --- /dev/null +++ b/depends/freebsd_10.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# +# Installs all of the dependencies for Pillow for Freebsd 10.x +# for both system Pythons 2.7 and 3.4 +# +sudo pkg install python2 python3 py27-pip py27-virtualenv py27-setuptools27 + +# Openjpeg fails badly using the openjpeg package. +# I can't find a python3.4 version of tkinter +sudo pkg install jpeg-turbo tiff webp lcms2 freetype2 py27-tkinter From 0b38dbb8e23bffd90caf41cd8f4db1aaf9977fcd Mon Sep 17 00:00:00 2001 From: Christoph Gohlke Date: Mon, 4 Jan 2016 11:50:03 -0800 Subject: [PATCH 0866/1037] Add Python 3.5 to classifiers --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index f669e2319..265c097ba 100644 --- a/setup.py +++ b/setup.py @@ -753,6 +753,7 @@ setup( "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', ], From b9a71cafbc35692999c96910c299cfb272d7c75a Mon Sep 17 00:00:00 2001 From: Jerome Leclanche Date: Tue, 5 Jan 2016 13:39:15 +0200 Subject: [PATCH 0867/1037] Add a basic DDS image plugin Only supports DXT1 and DXT5 for now. The pixel formats ideally should be supported in decode.c instead, but for now this is good enough. Fixes #252 --- CHANGES.rst | 2 + PIL/DdsImagePlugin.py | 253 +++++++++++++++++++++++++++ PIL/__init__.py | 1 + docs/handbook/image-file-formats.rst | 16 +- 4 files changed, 269 insertions(+), 3 deletions(-) create mode 100644 PIL/DdsImagePlugin.py diff --git a/CHANGES.rst b/CHANGES.rst index 23fb56983..96ed4f16c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,6 +6,8 @@ Changelog (Pillow) - Fix incorrect conditional in encode.c #1638 [manisandro] +- Add a basic read-only DDS plugin #252 + [jleclanche] 3.1.0 (2016-01-04) ------------------ diff --git a/PIL/DdsImagePlugin.py b/PIL/DdsImagePlugin.py new file mode 100644 index 000000000..3ca6c7409 --- /dev/null +++ b/PIL/DdsImagePlugin.py @@ -0,0 +1,253 @@ +""" +A Pillow loader for .dds files (S3TC-compressed aka DXTC) +Jerome Leclanche + +Documentation: + http://oss.sgi.com/projects/ogl-sample/registry/EXT/texture_compression_s3tc.txt + +The contents of this file are hereby released in the public domain (CC0) +Full text of the CC0 license: + https://creativecommons.org/publicdomain/zero/1.0/ +""" + +import struct +from io import BytesIO +from PIL import Image, ImageFile + + +# Magic ("DDS ") +DDS_MAGIC = 0x20534444 + +# DDS flags +DDSD_CAPS = 0x1 +DDSD_HEIGHT = 0x2 +DDSD_WIDTH = 0x4 +DDSD_PITCH = 0x8 +DDSD_PIXELFORMAT = 0x1000 +DDSD_MIPMAPCOUNT = 0x20000 +DDSD_LINEARSIZE = 0x80000 +DDSD_DEPTH = 0x800000 + +# DDS caps +DDSCAPS_COMPLEX = 0x8 +DDSCAPS_TEXTURE = 0x1000 +DDSCAPS_MIPMAP = 0x400000 + +DDSCAPS2_CUBEMAP = 0x200 +DDSCAPS2_CUBEMAP_POSITIVEX = 0x400 +DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800 +DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000 +DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000 +DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000 +DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000 +DDSCAPS2_VOLUME = 0x200000 + +# Pixel Format +DDPF_ALPHAPIXELS = 0x1 +DDPF_ALPHA = 0x2 +DDPF_FOURCC = 0x4 +DDPF_PALETTEINDEXED8 = 0x20 +DDPF_RGB = 0x40 +DDPF_LUMINANCE = 0x20000 + + +# dds.h + +DDS_FOURCC = DDPF_FOURCC +DDS_RGB = DDPF_RGB +DDS_RGBA = DDPF_RGB | DDPF_ALPHAPIXELS +DDS_LUMINANCE = DDPF_LUMINANCE +DDS_LUMINANCEA = DDPF_LUMINANCE | DDPF_ALPHAPIXELS +DDS_ALPHA = DDPF_ALPHA +DDS_PAL8 = DDPF_PALETTEINDEXED8 + +DDS_HEADER_FLAGS_TEXTURE = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT +DDS_HEADER_FLAGS_MIPMAP = DDSD_MIPMAPCOUNT +DDS_HEADER_FLAGS_VOLUME = DDSD_DEPTH +DDS_HEADER_FLAGS_PITCH = DDSD_PITCH +DDS_HEADER_FLAGS_LINEARSIZE = DDSD_LINEARSIZE + +DDS_HEIGHT = DDSD_HEIGHT +DDS_WIDTH = DDSD_WIDTH + +DDS_SURFACE_FLAGS_TEXTURE = DDSCAPS_TEXTURE +DDS_SURFACE_FLAGS_MIPMAP = DDSCAPS_COMPLEX | DDSCAPS_MIPMAP +DDS_SURFACE_FLAGS_CUBEMAP = DDSCAPS_COMPLEX + +DDS_CUBEMAP_POSITIVEX = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX +DDS_CUBEMAP_NEGATIVEX = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX +DDS_CUBEMAP_POSITIVEY = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY +DDS_CUBEMAP_NEGATIVEY = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY +DDS_CUBEMAP_POSITIVEZ = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ +DDS_CUBEMAP_NEGATIVEZ = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ + + +# DXT1 +DXT1_FOURCC = 0x31545844 + +# DXT3 +DXT3_FOURCC = 0x33545844 + +# DXT5 +DXT5_FOURCC = 0x35545844 + + +def decode565(bits): + a = ((bits >> 11) & 0x1f) << 3 + b = ((bits >> 5) & 0x3f) << 2 + c = (bits & 0x1f) << 3 + return a, b, c + + +def _c2a(a, b): + return (2 * a + b) // 3 + +def _c2b(a, b): + return (a + b) // 2 + +def _c3(a, b): + return (2 * b + a) // 3 + + +def dxt1(data, width, height): + ret = bytearray(4 * width * height) + + for y in range(0, height, 4): + for x in range(0, width, 4): + color0, color1, bits = struct.unpack("> 2 + if control == 0: + r, g, b = r0, g0, b0 + elif control == 1: + r, g, b = r1, g1, b1 + elif control == 2: + if color0 > color1: + r, g, b = _c2a(r0, r1), _c2a(g0, g1), _c2a(b0, b1) + else: + r, g, b = _c2b(r0, r1), _c2b(g0, g1), _c2b(b0, b1) + elif control == 3: + if color0 > color1: + r, g, b = _c3(r0, r1), _c3(g0, g1), _c3(b0, b1) + else: + r, g, b = 0, 0, 0 + + idx = 4 * ((y + j) * width + (x + i)) + ret[idx:idx+4] = bytes([r, g, b, 0xff]) + + return bytes(ret) + + +def _dxtc_alpha(a0, a1, ac0, ac1, ai): + if ai <= 12: + ac = (ac0 >> ai) & 7 + elif ai == 15: + ac = (ac0 >> 15) | ((ac1 << 1) & 6) + else: + ac = (ac1 >> (ai - 16)) & 7 + + if ac == 0: + alpha = a0 + elif ac == 1: + alpha = a1 + elif a0 > a1: + alpha = ((8 - ac) * a0 + (ac - 1) * a1) // 7 + elif ac == 6: + alpha = 0 + elif ac == 7: + alpha = 0xff + else: + alpha = ((6 - ac) * a0 + (ac - 1) * a1) // 5 + + return alpha + + +def dxt5(data, width, height): + ret = bytearray(4 * width * height) + + for y in range(0, height, 4): + for x in range(0, width, 4): + a0, a1, ac0, ac1, c0, c1, code = struct.unpack("<2BHI2HI", data.read(16)) + + r0, g0, b0 = decode565(c0) + r1, g1, b1 = decode565(c1) + + for j in range(4): + for i in range(4): + ai = 3 * (4 * j + i) + alpha = _dxtc_alpha(a0, a1, ac0, ac1, ai) + + cc = (code >> 2 * (4 * j + i)) & 3 + if cc == 0: + r, g, b = r0, g0, b0 + elif cc == 1: + r, g, b = r1, g1, b1 + elif cc == 2: + r, g, b = _c2a(r0, r1), _c2a(g0, g1), _c2a(b0, b1) + elif cc == 3: + r, g, b = _c3(r0, r1), _c3(g0, g1), _c3(b0, b1) + + idx = 4 * ((y + j) * width + (x + i)) + ret[idx:idx+4] = bytes([r, g, b, alpha]) + + return bytes(ret) + + +class DdsImageFile(ImageFile.ImageFile): + format = "DDS" + format_description = "DirectDraw Surface" + + def _open(self): + magic, header_size = struct.unpack(" Date: Wed, 6 Jan 2016 10:42:03 +1100 Subject: [PATCH 0868/1037] Allowed text method to pass on multiline_text method specific arguments --- PIL/ImageDraw.py | 4 ++-- Tests/test_imagefont.py | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py index f5b72efb7..21b2cbb87 100644 --- a/PIL/ImageDraw.py +++ b/PIL/ImageDraw.py @@ -241,9 +241,9 @@ class ImageDraw(object): return text.split(split_character) - def text(self, xy, text, fill=None, font=None, anchor=None): + def text(self, xy, text, fill=None, font=None, anchor=None, *args, **kwargs): if self._multiline_check(text): - return self.multiline_text(xy, text, fill, font, anchor) + return self.multiline_text(xy, text, fill, font, anchor, *args, **kwargs) ink, fill = self._getink(fill) if font is None: diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index a64eb68ef..1f4621362 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -159,12 +159,20 @@ try: self.assert_image_similar(im, target_img, .5) + # Test that text() can pass on additional arguments + # to multiline_text() + draw.text((0, 0), TEST_TEXT, fill=None, font=ttf, anchor=None, + spacing=4, align="left") + draw.text((0, 0), TEST_TEXT, None, ttf, None, 4, "left") + del draw + # Test align center and right for align, ext in {"center": "_center", "right": "_right"}.items(): im = Image.new(mode='RGB', size=(300, 100)) draw = ImageDraw.Draw(im) draw.multiline_text((0, 0), TEST_TEXT, font=ttf, align=align) + del draw target = 'Tests/images/multiline_text'+ext+'.png' target_img = Image.open(target) From a653afb10d08cc0c90ce9ceb1b95cd767b5a48b7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 6 Jan 2016 17:13:16 +1100 Subject: [PATCH 0869/1037] Allowed textsize method to pass on multiline_textsize method specific arguments --- PIL/ImageDraw.py | 4 ++-- Tests/test_imagefont.py | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py index 21b2cbb87..1d5f53746 100644 --- a/PIL/ImageDraw.py +++ b/PIL/ImageDraw.py @@ -288,9 +288,9 @@ class ImageDraw(object): ## # Get the size of a given string, in pixels. - def textsize(self, text, font=None): + def textsize(self, text, font=None, *args, **kwargs): if self._multiline_check(text): - return self.multiline_textsize(text, font) + return self.multiline_textsize(text, font, *args, **kwargs) if font is None: font = self.getfont() diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 1f4621362..cc142d459 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -121,6 +121,7 @@ try: size = draw.textsize(txt, ttf) draw.text((10, 10), txt, font=ttf) draw.rectangle((10, 10, 10 + size[0], 10 + size[1])) + del draw target = 'Tests/images/rectangle_surrounding_text.png' target_img = Image.open(target) @@ -199,6 +200,12 @@ try: self.assertEqual(draw.textsize(TEST_TEXT, font=ttf), draw.multiline_textsize(TEST_TEXT, font=ttf)) + # Test that textsize() can pass on additional arguments + # to multiline_textsize() + draw.textsize(TEST_TEXT, font=ttf, spacing=4) + draw.textsize(TEST_TEXT, ttf, 4) + del draw + def test_multiline_width(self): ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) im = Image.new(mode='RGB', size=(300, 100)) @@ -207,6 +214,7 @@ try: self.assertEqual(draw.textsize("longest line", font=ttf)[0], draw.multiline_textsize("longest line\nline", font=ttf)[0]) + del draw def test_multiline_spacing(self): ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) @@ -214,6 +222,7 @@ try: im = Image.new(mode='RGB', size=(300, 100)) draw = ImageDraw.Draw(im) draw.multiline_text((0, 0), TEST_TEXT, font=ttf, spacing=10) + del draw target = 'Tests/images/multiline_text_spacing.png' target_img = Image.open(target) @@ -237,6 +246,7 @@ try: # Rotated font draw.font = transposed_font box_size_b = draw.textsize(word) + del draw # Check (w,h) of box a is (h,w) of box b self.assertEqual(box_size_a[0], box_size_b[1]) @@ -259,6 +269,7 @@ try: # Rotated font draw.font = transposed_font box_size_b = draw.textsize(word) + del draw # Check boxes a and b are same size self.assertEqual(box_size_a, box_size_b) @@ -354,6 +365,7 @@ try: # Act default_font = ImageFont.load_default() draw.text((10, 10), txt, font=default_font) + del draw # Assert self.assert_image_equal(im, target_img) From ca216951aba6d0fa877a9bdf2fec4d42f80d0d97 Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 6 Jan 2016 10:59:37 +0200 Subject: [PATCH 0870/1037] Test DdsImagePlugin --- Tests/test_file_dds.py | 55 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 Tests/test_file_dds.py diff --git a/Tests/test_file_dds.py b/Tests/test_file_dds.py new file mode 100644 index 000000000..a68e2884a --- /dev/null +++ b/Tests/test_file_dds.py @@ -0,0 +1,55 @@ +from helper import unittest, PillowTestCase + +from PIL import Image, DdsImagePlugin + +TEST_FILE_DXT1 = "Tests/images/dxt1-rgb-4bbp-noalpha_MipMaps-1.dds" +TEST_FILE_DXT5 = "Tests/images/dxt5-argb-8bbp-interpolatedalpha_MipMaps-1.dds" + + +class TestFileCur(PillowTestCase): + + def test_sanity_dxt1(self): + im = Image.open(TEST_FILE_DXT1) + + self.assertEqual(im.size, (256, 256)) + self.assertIsInstance(im, DdsImagePlugin.DdsImageFile) + # Check some pixel colors to ensure image is loaded properly + self.assertEqual(im.getpixel((10, 1)), (49, 50, 53, 44)) + self.assertEqual(im.getpixel((11, 1)), (32, 50, 53, 53)) + self.assertEqual(im.getpixel((16, 16)), (91, 48, 44, 32)) + + def test_sanity_dxt5(self): + im = Image.open(TEST_FILE_DXT5) + + self.assertEqual(im.size, (256, 256)) + self.assertIsInstance(im, DdsImagePlugin.DdsImageFile) + # Check some pixel colors to ensure image is loaded properly + self.assertEqual(im.getpixel((10, 1)), (49, 50, 53, 44)) + self.assertEqual(im.getpixel((11, 1)), (32, 49, 50, 56)) + self.assertEqual(im.getpixel((16, 16)), (91, 48, 44, 32)) + + def test__validate_true(self): + # Arrange + prefix = b"DDS etc" + + # Act + output = DdsImagePlugin._validate(prefix) + + # Assert + self.assertTrue(output) + + def test__validate_false(self): + # Arrange + prefix = b"something invalid" + + # Act + output = DdsImagePlugin._validate(prefix) + + # Assert + self.assertFalse(output) + + +if __name__ == '__main__': + unittest.main() + +# End of file From 3515cf82b7c059df773a7c53a928e94a72885d9e Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 6 Jan 2016 11:00:08 +0200 Subject: [PATCH 0871/1037] Test images from https://github.com/python-pillow/Pillow/pull/1644#issuecomment-169101551 --- .../images/dxt1-rgb-4bbp-noalpha_MipMaps-1.dds | Bin 0 -> 32896 bytes ...t5-argb-8bbp-interpolatedalpha_MipMaps-1.dds | Bin 0 -> 65664 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100755 Tests/images/dxt1-rgb-4bbp-noalpha_MipMaps-1.dds create mode 100755 Tests/images/dxt5-argb-8bbp-interpolatedalpha_MipMaps-1.dds diff --git a/Tests/images/dxt1-rgb-4bbp-noalpha_MipMaps-1.dds b/Tests/images/dxt1-rgb-4bbp-noalpha_MipMaps-1.dds new file mode 100755 index 0000000000000000000000000000000000000000..7e12460c48c22ca10418ebad49ba2e4cd22d7f1c GIT binary patch literal 32896 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 literal 0 HcmV?d00001 diff --git a/Tests/images/dxt5-argb-8bbp-interpolatedalpha_MipMaps-1.dds b/Tests/images/dxt5-argb-8bbp-interpolatedalpha_MipMaps-1.dds new file mode 100755 index 0000000000000000000000000000000000000000..b2e56d6060429fc743f709dd5cfc42a72c2eab29 GIT binary patch literal 65664 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 KjsHU|G5-SL^XiZQ literal 0 HcmV?d00001 From f826bff3c7f174584f85f620dccd55b26f71d5fa Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 6 Jan 2016 11:24:09 +0200 Subject: [PATCH 0872/1037] Simplify sanity tests --- Tests/test_file_dds.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Tests/test_file_dds.py b/Tests/test_file_dds.py index a68e2884a..dfc21993a 100644 --- a/Tests/test_file_dds.py +++ b/Tests/test_file_dds.py @@ -11,22 +11,18 @@ class TestFileCur(PillowTestCase): def test_sanity_dxt1(self): im = Image.open(TEST_FILE_DXT1) + self.assertEqual(im.format, "DDS") + self.assertEqual(im.mode, "RGBA") self.assertEqual(im.size, (256, 256)) self.assertIsInstance(im, DdsImagePlugin.DdsImageFile) - # Check some pixel colors to ensure image is loaded properly - self.assertEqual(im.getpixel((10, 1)), (49, 50, 53, 44)) - self.assertEqual(im.getpixel((11, 1)), (32, 50, 53, 53)) - self.assertEqual(im.getpixel((16, 16)), (91, 48, 44, 32)) def test_sanity_dxt5(self): im = Image.open(TEST_FILE_DXT5) + self.assertEqual(im.format, "DDS") + self.assertEqual(im.mode, "RGBA") self.assertEqual(im.size, (256, 256)) self.assertIsInstance(im, DdsImagePlugin.DdsImageFile) - # Check some pixel colors to ensure image is loaded properly - self.assertEqual(im.getpixel((10, 1)), (49, 50, 53, 44)) - self.assertEqual(im.getpixel((11, 1)), (32, 49, 50, 56)) - self.assertEqual(im.getpixel((16, 16)), (91, 48, 44, 32)) def test__validate_true(self): # Arrange From cc0d2a7b94003f0758e462af41b4d491acfda125 Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 6 Jan 2016 11:33:24 +0200 Subject: [PATCH 0873/1037] Include *.dds --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 4fd9c602e..ac7360c93 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -24,6 +24,7 @@ recursive-include Tests *.bmp recursive-include Tests *.bw recursive-include Tests *.cur recursive-include Tests *.dcx +recursive-include Tests *.dds recursive-include Tests *.doc recursive-include Tests *.eps recursive-include Tests *.fli From 02ec83ee64232e98a7cb6fc3e1b475c0325cc337 Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 6 Jan 2016 11:57:51 +0200 Subject: [PATCH 0874/1037] Test image from https://github.com/python-pillow/Pillow/pull/1644#issuecomment-169101551 --- .../dxt3-argb-8bbp-explicitalpha_MipMaps-1.dds | Bin 0 -> 65664 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100755 Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.dds diff --git a/Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.dds b/Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.dds new file mode 100755 index 0000000000000000000000000000000000000000..8ec3484fa5d8571c45f4bd072eedc8517c00ea1c GIT binary patch literal 65664 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 literal 0 HcmV?d00001 From 517cbbff08042ef8957e7a6a92bbcc1113bcb104 Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 6 Jan 2016 11:59:47 +0200 Subject: [PATCH 0875/1037] Test NotImplementedError --- Tests/test_file_dds.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Tests/test_file_dds.py b/Tests/test_file_dds.py index dfc21993a..200bebaba 100644 --- a/Tests/test_file_dds.py +++ b/Tests/test_file_dds.py @@ -3,6 +3,7 @@ from helper import unittest, PillowTestCase from PIL import Image, DdsImagePlugin TEST_FILE_DXT1 = "Tests/images/dxt1-rgb-4bbp-noalpha_MipMaps-1.dds" +TEST_FILE_DXT3 = "Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.dds" TEST_FILE_DXT5 = "Tests/images/dxt5-argb-8bbp-interpolatedalpha_MipMaps-1.dds" @@ -24,6 +25,10 @@ class TestFileCur(PillowTestCase): self.assertEqual(im.size, (256, 256)) self.assertIsInstance(im, DdsImagePlugin.DdsImageFile) + def test_sanity_dxt3(self): + self.assertRaises(NotImplementedError, + lambda: Image.open(TEST_FILE_DXT3)) + def test__validate_true(self): # Arrange prefix = b"DDS etc" From 9dd68bb0bea1fd383e84be1e713462be3311d010 Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 6 Jan 2016 12:07:40 +0200 Subject: [PATCH 0876/1037] flake8 --- PIL/DdsImagePlugin.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/PIL/DdsImagePlugin.py b/PIL/DdsImagePlugin.py index 3ca6c7409..141c85b3e 100644 --- a/PIL/DdsImagePlugin.py +++ b/PIL/DdsImagePlugin.py @@ -61,7 +61,8 @@ DDS_LUMINANCEA = DDPF_LUMINANCE | DDPF_ALPHAPIXELS DDS_ALPHA = DDPF_ALPHA DDS_PAL8 = DDPF_PALETTEINDEXED8 -DDS_HEADER_FLAGS_TEXTURE = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT +DDS_HEADER_FLAGS_TEXTURE = (DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | + DDSD_PIXELFORMAT) DDS_HEADER_FLAGS_MIPMAP = DDSD_MIPMAPCOUNT DDS_HEADER_FLAGS_VOLUME = DDSD_DEPTH DDS_HEADER_FLAGS_PITCH = DDSD_PITCH @@ -102,9 +103,11 @@ def decode565(bits): def _c2a(a, b): return (2 * a + b) // 3 + def _c2b(a, b): return (a + b) // 2 + def _c3(a, b): return (2 * b + a) // 3 @@ -175,7 +178,8 @@ def dxt5(data, width, height): for y in range(0, height, 4): for x in range(0, width, 4): - a0, a1, ac0, ac1, c0, c1, code = struct.unpack("<2BHI2HI", data.read(16)) + a0, a1, ac0, ac1, c0, c1, code = struct.unpack("<2BHI2HI", + data.read(16)) r0, g0, b0 = decode565(c0) r1, g1, b1 = decode565(c1) @@ -221,7 +225,8 @@ class DdsImageFile(ImageFile.ImageFile): # pixel format pfsize, pfflags = struct.unpack("<2I", header.read(8)) fourcc = header.read(4) - bitcount, rmask, gmask, bmask, amask = struct.unpack("<5I", header.read(20)) + bitcount, rmask, gmask, bmask, amask = struct.unpack("<5I", + header.read(20)) self.tile = [ ("raw", (0, 0) + self.size, 0, (self.mode, 0, 1)) @@ -234,7 +239,8 @@ class DdsImageFile(ImageFile.ImageFile): self.pixel_format = "DXT5" codec = dxt5 else: - raise NotImplementedError("Unimplemented pixel format %r" % (fourcc)) + raise NotImplementedError("Unimplemented pixel format %r" % + (fourcc)) decoded_data = codec(self.fp, self.width, self.height) From f4df96816badefa6f96058bc7192c5edfa307bf6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 6 Jan 2016 21:09:40 +1100 Subject: [PATCH 0877/1037] Modified ImageSequence Iterator class to be an iterator --- PIL/ImageSequence.py | 15 +++++++++++++++ Tests/test_imagesequence.py | 8 ++++++++ 2 files changed, 23 insertions(+) diff --git a/PIL/ImageSequence.py b/PIL/ImageSequence.py index 256bcbedb..76bc2d826 100644 --- a/PIL/ImageSequence.py +++ b/PIL/ImageSequence.py @@ -32,6 +32,7 @@ class Iterator(object): if not hasattr(im, "seek"): raise AttributeError("im must have seek method") self.im = im + self.position = 0 def __getitem__(self, ix): try: @@ -40,3 +41,17 @@ class Iterator(object): return self.im except EOFError: raise IndexError # end of sequence + + def __iter__(self): + return self + + def __next__(self): + try: + self.im.seek(self.position) + self.position += 1 + return self.im + except EOFError: + raise StopIteration + + def next(self): + return self.__next__() diff --git a/Tests/test_imagesequence.py b/Tests/test_imagesequence.py index 9e18192ee..3e30b3ca3 100644 --- a/Tests/test_imagesequence.py +++ b/Tests/test_imagesequence.py @@ -24,6 +24,14 @@ class TestImageSequence(PillowTestCase): self.assertRaises(AttributeError, lambda: ImageSequence.Iterator(0)) + def test_iterator(self): + im = Image.open('Tests/images/multipage.tiff') + i = ImageSequence.Iterator(im) + for index in range(0, im.n_frames): + self.assertEqual(i[index], next(i)) + self.assertRaises(IndexError, lambda: i[index+1]) + self.assertRaises(StopIteration, lambda: next(i)) + def _test_multipage_tiff(self): im = Image.open('Tests/images/multipage.tiff') for index, frame in enumerate(ImageSequence.Iterator(im)): From 05b22cedd5108e90740ae4660d6c9690d202baf7 Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 6 Jan 2016 12:26:58 +0200 Subject: [PATCH 0878/1037] Add docstrings --- Tests/test_file_dds.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_dds.py b/Tests/test_file_dds.py index 200bebaba..6914c5f6e 100644 --- a/Tests/test_file_dds.py +++ b/Tests/test_file_dds.py @@ -7,9 +7,11 @@ TEST_FILE_DXT3 = "Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.dds" TEST_FILE_DXT5 = "Tests/images/dxt5-argb-8bbp-interpolatedalpha_MipMaps-1.dds" -class TestFileCur(PillowTestCase): +class TestFileDds(PillowTestCase): + """Test DdsImagePlugin""" def test_sanity_dxt1(self): + """Check DXT1 images can be opened""" im = Image.open(TEST_FILE_DXT1) self.assertEqual(im.format, "DDS") @@ -18,6 +20,7 @@ class TestFileCur(PillowTestCase): self.assertIsInstance(im, DdsImagePlugin.DdsImageFile) def test_sanity_dxt5(self): + """Check DXT5 images can be opened""" im = Image.open(TEST_FILE_DXT5) self.assertEqual(im.format, "DDS") @@ -26,10 +29,12 @@ class TestFileCur(PillowTestCase): self.assertIsInstance(im, DdsImagePlugin.DdsImageFile) def test_sanity_dxt3(self): + """Check DXT3 images are not supported""" self.assertRaises(NotImplementedError, lambda: Image.open(TEST_FILE_DXT3)) def test__validate_true(self): + """Check valid prefix""" # Arrange prefix = b"DDS etc" @@ -40,6 +45,7 @@ class TestFileCur(PillowTestCase): self.assertTrue(output) def test__validate_false(self): + """Check invalid prefix""" # Arrange prefix = b"something invalid" From 37b5a39c58bcb916fe691d2eb7dfcc4384b2d9f7 Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 6 Jan 2016 20:00:03 +0200 Subject: [PATCH 0879/1037] Note internal functions and TODOs --- PIL/DdsImagePlugin.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/PIL/DdsImagePlugin.py b/PIL/DdsImagePlugin.py index 141c85b3e..45b514d4c 100644 --- a/PIL/DdsImagePlugin.py +++ b/PIL/DdsImagePlugin.py @@ -93,7 +93,7 @@ DXT3_FOURCC = 0x33545844 DXT5_FOURCC = 0x35545844 -def decode565(bits): +def _decode565(bits): a = ((bits >> 11) & 0x1f) << 3 b = ((bits >> 5) & 0x3f) << 2 c = (bits & 0x1f) << 3 @@ -112,15 +112,16 @@ def _c3(a, b): return (2 * b + a) // 3 -def dxt1(data, width, height): +def _dxt1(data, width, height): + # TODO implement this function as pixel format in decode.c ret = bytearray(4 * width * height) for y in range(0, height, 4): for x in range(0, width, 4): color0, color1, bits = struct.unpack(" Date: Fri, 8 Jan 2016 07:18:05 -0800 Subject: [PATCH 0880/1037] Test images for GbrImagePlugin, created in GIMP 2.8 by Eric Soroos --- Tests/images/gbr.gbr | Bin 0 -> 16423 bytes Tests/images/gbr.png | Bin 0 -> 2014 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Tests/images/gbr.gbr create mode 100644 Tests/images/gbr.png diff --git a/Tests/images/gbr.gbr b/Tests/images/gbr.gbr new file mode 100644 index 0000000000000000000000000000000000000000..054b82df5ba930779dfa3e8bad304d171bb74e6b GIT binary patch literal 16423 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 Date: Fri, 8 Jan 2016 07:18:48 -0800 Subject: [PATCH 0881/1037] Working GbrImagePlugin for version 2 GBR (gimp brush) files --- PIL/GbrImagePlugin.py | 57 +++++++++++++++++++++++++++++------------- Tests/test_file_gbr.py | 9 ++++++- 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/PIL/GbrImagePlugin.py b/PIL/GbrImagePlugin.py index 15282ecee..8edb8f487 100644 --- a/PIL/GbrImagePlugin.py +++ b/PIL/GbrImagePlugin.py @@ -1,17 +1,28 @@ # # The Python Imaging Library -# $Id$ # # load a GIMP brush file # # History: # 96-03-14 fl Created +# 16-01-08 es Version 2 # # Copyright (c) Secret Labs AB 1997. # Copyright (c) Fredrik Lundh 1996. +# Copyright (c) Eric Soroos 2016. # # See the README file for information on usage and redistribution. # +# +# See https://github.com/GNOME/gimp/blob/master/devel-docs/gbr.txt for +# format documentation. +# +# This code Interprets version 1 and 2 .gbr files. +# Version 1 files are obsolete, and should not be used for new +# brushes. +# Version 2 files are saved by GIMP v2.8 (at least) +# Version 3 files have a format specifier of 18 for 16bit floats in +# the color depth field. This is currently unsupported by Pillow. from PIL import Image, ImageFile, _binary @@ -19,7 +30,7 @@ i32 = _binary.i32be def _accept(prefix): - return len(prefix) >= 8 and i32(prefix) >= 20 and i32(prefix[4:8]) == 1 + return len(prefix) >= 8 and i32(prefix[:4]) >= 20 and i32(prefix[4:8]) in (1,2) ## @@ -31,41 +42,53 @@ class GbrImageFile(ImageFile.ImageFile): format_description = "GIMP brush file" def _open(self): - header_size = i32(self.fp.read(4)) version = i32(self.fp.read(4)) - if header_size < 20 or version != 1: + if header_size < 20: raise SyntaxError("not a GIMP brush") + if version not in (1,2): + raise SyntaxError("Unsupported GIMP brush version: %s" %version) width = i32(self.fp.read(4)) height = i32(self.fp.read(4)) color_depth = i32(self.fp.read(4)) - if width <= 0 or height <= 0 or color_depth != 1: + if width <= 0 or height <= 0: raise SyntaxError("not a GIMP brush") + if color_depth not in (1,4): + raise SyntaxError("Unsupported GMP brush color depth: %s" %color_depth) + + if version == 1: + comment_length = header_size-20 + else: + comment_length = header_size-28 + magic_number = self.fp.read(4) + if magic_number != b'GIMP': + raise SyntaxError("not a GIMP brush, bad magic number") + self.info['spacing'] = i32(self.fp.read(4)) - comment = self.fp.read(header_size - 20)[:-1] + comment = self.fp.read(comment_length)[:-1] - self.mode = "L" + if color_depth == 1: + self.mode = "L" + else: + self.mode = 'RGBA' + self.size = width, height self.info["comment"] = comment - # Since the brush is so small, we read the data immediately - self.data = self.fp.read(width * height) + # Image might not be small + Image._decompression_bomb_check(self.size) + + # Data is an uncompressed block of w * h * bytes/pixel + self._data_size = width * height * color_depth def load(self): - - if not self.data: - return - - # create an image out of the brush data block self.im = Image.core.new(self.mode, self.size) - self.im.frombytes(self.data) - self.data = b"" + self.frombytes(self.fp.read(self._data_size)) # # registry Image.register_open(GbrImageFile.format, GbrImageFile, _accept) - Image.register_extension(GbrImageFile.format, ".gbr") diff --git a/Tests/test_file_gbr.py b/Tests/test_file_gbr.py index 57b301ada..d38b4a70f 100644 --- a/Tests/test_file_gbr.py +++ b/Tests/test_file_gbr.py @@ -1,6 +1,6 @@ from helper import unittest, PillowTestCase -from PIL import GbrImagePlugin +from PIL import Image, GbrImagePlugin class TestFileGbr(PillowTestCase): @@ -12,6 +12,13 @@ class TestFileGbr(PillowTestCase): lambda: GbrImagePlugin.GbrImageFile(invalid_file)) + def test_gbr_file(self): + im = Image.open('Tests/images/gbr.gbr') + + target = Image.open('Tests/images/gbr.png') + + self.assert_image_equal(target, im) + if __name__ == '__main__': unittest.main() From 00dc65782488f04e6fe419a4c600e3142b7673da Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 8 Jan 2016 07:25:42 -0800 Subject: [PATCH 0882/1037] Documentation for GBR --- docs/handbook/image-file-formats.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 2b728deef..ce5382fe1 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -660,14 +660,17 @@ into account. GBR ^^^ -The GBR decoder reads GIMP brush files. +The GBR decoder reads GIMP brush files, version 1 and 2. The :py:meth:`~PIL.Image.Image.open` method sets the following :py:attr:`~PIL.Image.Image.info` properties: -**description** +**comment** The brush name. +**spacing** + The spacing between the brushes, in pixels. Version 2 only. + GD ^^ From 13f2d227009a77c77243bd74e37a737c66af6254 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 8 Jan 2016 07:59:18 -0800 Subject: [PATCH 0883/1037] Correctness tests for DDS --- .../images/dxt1-rgb-4bbp-noalpha_MipMaps-1.png | Bin 0 -> 27769 bytes ...t5-argb-8bbp-interpolatedalpha_MipMaps-1.png | Bin 0 -> 49443 bytes Tests/test_file_dds.py | 10 ++++++++-- 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 Tests/images/dxt1-rgb-4bbp-noalpha_MipMaps-1.png create mode 100644 Tests/images/dxt5-argb-8bbp-interpolatedalpha_MipMaps-1.png diff --git a/Tests/images/dxt1-rgb-4bbp-noalpha_MipMaps-1.png b/Tests/images/dxt1-rgb-4bbp-noalpha_MipMaps-1.png new file mode 100644 index 0000000000000000000000000000000000000000..a308f0deffcc38809f680e2afe31ab436e8451fd GIT binary patch literal 27769 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 aQ4#nSI6%TfdIC5AWT Date: Fri, 8 Jan 2016 10:07:24 -0800 Subject: [PATCH 0884/1037] install before running sphinx --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index e1406d466..67f9ea3bd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,6 +49,7 @@ script: - if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage run --append --include=PIL/* -m nose -vx Tests/test_*.py; fi - check-manifest --ignore "depends/*" # Sphinx + - make install - pushd docs; make html; make linkcheck; popd after_success: From d047349958161cce42ef370717ba77ace749e8a2 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 8 Jan 2016 10:15:06 -0800 Subject: [PATCH 0885/1037] YAFile format --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 4fd9c602e..02ee5278b 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -27,6 +27,7 @@ recursive-include Tests *.dcx recursive-include Tests *.doc recursive-include Tests *.eps recursive-include Tests *.fli +recursive-include Tests *.gbr recursive-include Tests *.ggr recursive-include Tests *.gif recursive-include Tests *.gpl From 18d48dc66521420b1d34e2a371e0c8e5ea5e987a Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 8 Jan 2016 13:58:19 -0800 Subject: [PATCH 0886/1037] fixing dds plugin on Py 2.x, relaxing dxt5 test --- PIL/DdsImagePlugin.py | 4 ++-- Tests/test_file_dds.py | 25 ++++++++++++++++++------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/PIL/DdsImagePlugin.py b/PIL/DdsImagePlugin.py index 45b514d4c..68dfc0690 100644 --- a/PIL/DdsImagePlugin.py +++ b/PIL/DdsImagePlugin.py @@ -145,7 +145,7 @@ def _dxt1(data, width, height): r, g, b = 0, 0, 0 idx = 4 * ((y + j) * width + (x + i)) - ret[idx:idx+4] = bytes([r, g, b, 0xff]) + ret[idx:idx+4] = struct.pack('4B', r, g, b, 255) return bytes(ret) @@ -202,7 +202,7 @@ def _dxt5(data, width, height): r, g, b = _c3(r0, r1), _c3(g0, g1), _c3(b0, b1) idx = 4 * ((y + j) * width + (x + i)) - ret[idx:idx+4] = bytes([r, g, b, alpha]) + ret[idx:idx+4] = struct.pack('4B', r, g, b, alpha) return bytes(ret) diff --git a/Tests/test_file_dds.py b/Tests/test_file_dds.py index 57721e976..6a213db8b 100644 --- a/Tests/test_file_dds.py +++ b/Tests/test_file_dds.py @@ -12,26 +12,37 @@ class TestFileDds(PillowTestCase): def test_sanity_dxt1(self): """Check DXT1 images can be opened""" - im = Image.open(TEST_FILE_DXT1) + target = Image.open(TEST_FILE_DXT1.replace('.dds', '.png')) + im = Image.open(TEST_FILE_DXT1) + im.load() + self.assertEqual(im.format, "DDS") self.assertEqual(im.mode, "RGBA") self.assertEqual(im.size, (256, 256)) - - target = Image.open(TEST_FILE_DXT1.replace('.dds', '.png')) - target.show() + + # This target image is from the test set of images, and is exact. self.assert_image_equal(target.convert('RGBA'), im) def test_sanity_dxt5(self): """Check DXT5 images can be opened""" + + target = Image.open(TEST_FILE_DXT5.replace('.dds', '.png')) + im = Image.open(TEST_FILE_DXT5) + im.load() self.assertEqual(im.format, "DDS") self.assertEqual(im.mode, "RGBA") self.assertEqual(im.size, (256, 256)) - - target = Image.open(TEST_FILE_DXT5.replace('.dds', '.png')) - self.assert_image_equal(target, im) + + # Imagemagick, which generated this target image from the .dds + # has a slightly different decoder than is standard. It looks + # a little brighter. The 0,0 pixel is (00,6c,f8,ff) by our code, + # and by the target image for the DXT1, and the imagemagick .png + # is giving (00, 6d, ff, ff). So, assert similar, pretty tight + # I'm currently seeing about a 3 for the epsilon. + self.assert_image_similar(target, im, 5) def test_sanity_dxt3(self): From 8a27dd3b08778e2642a296d027bb66ff9bc004fa Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 9 Jan 2016 10:43:11 +1100 Subject: [PATCH 0887/1037] Updated redirected URL --- docs/index.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index a02de2334..d7bd95b9a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -23,15 +23,15 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors Date: Thu, 14 Jan 2016 08:57:36 -0800 Subject: [PATCH 0888/1037] fix uninitalized variable warning, fix logic --- _imaging.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/_imaging.c b/_imaging.c index de1fa5be5..912159772 100644 --- a/_imaging.c +++ b/_imaging.c @@ -471,13 +471,13 @@ getpixel(Imaging im, ImagingAccess access, int x, int y) static char* getink(PyObject* color, Imaging im, char* ink) { - int r, g, b, a; - double f; + int r=0, g=0, b=0, a=0; + double f=0; /* fill ink buffer (four bytes) with something that can be cast to either UINT8 or INT32 */ - int rIsInt = 1; + int rIsInt = 0; if (im->type == IMAGING_TYPE_UINT8 || im->type == IMAGING_TYPE_INT32 || im->type == IMAGING_TYPE_SPECIAL) { @@ -491,9 +491,11 @@ getink(PyObject* color, Imaging im, char* ink) else r = (int) PyLong_AsLong(color); #endif + rIsInt = 1; } - if (r == -1 && PyErr_Occurred()) + if (r == -1 && PyErr_Occurred()) { rIsInt = 0; + } } switch (im->type) { @@ -501,23 +503,16 @@ getink(PyObject* color, Imaging im, char* ink) /* unsigned integer */ if (im->bands == 1) { /* unsigned integer, single layer */ - if (rIsInt != 1) - return NULL; + if (rIsInt != 1) { + if (!PyArg_ParseTuple(color, "i", &r)) { + return NULL; + } + } ink[0] = CLIP(r); ink[1] = ink[2] = ink[3] = 0; } else { a = 255; -#if PY_VERSION_HEX >= 0x03000000 - if (PyLong_Check(color)) { - r = (int) PyLong_AsLong(color); -#else - if (PyInt_Check(color) || PyLong_Check(color)) { - if (PyInt_Check(color)) - r = PyInt_AS_LONG(color); - else - r = (int) PyLong_AsLong(color); -#endif - + if (rIsInt) { /* compatibility: ABGR */ a = (UINT8) (r >> 24); b = (UINT8) (r >> 16); From a098c88509f74e80febd6332f4c0dc88ac4c1a76 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 14 Jan 2016 08:58:13 -0800 Subject: [PATCH 0889/1037] Convert was passing in a 3tuple to putpixel for a P image --- PIL/Image.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/PIL/Image.py b/PIL/Image.py index 89549ea4a..c3f778f88 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -875,6 +875,12 @@ class Image(object): trns_im = Image()._new(core.new(self.mode, (1, 1))) if self.mode == 'P': trns_im.putpalette(self.palette) + if type(t) == tuple: + try: + t = trns_im.palette.getcolor(t) + except: + raise ValueError("Couldn't allocate a palette "+ + "color for transparency") trns_im.putpixel((0, 0), t) if mode in ('L', 'RGB'): From b0d15c9b8dd9b59266281debcbd3a0c8a642c6e6 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 15 Jan 2016 21:34:36 +0000 Subject: [PATCH 0890/1037] Catch truncated DDS files --- PIL/DdsImagePlugin.py | 18 +++++++++++++----- Tests/test_file_dds.py | 24 ++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/PIL/DdsImagePlugin.py b/PIL/DdsImagePlugin.py index 68dfc0690..86145ca8f 100644 --- a/PIL/DdsImagePlugin.py +++ b/PIL/DdsImagePlugin.py @@ -215,8 +215,11 @@ class DdsImageFile(ImageFile.ImageFile): magic, header_size = struct.unpack(" Date: Sat, 16 Jan 2016 10:07:44 +0200 Subject: [PATCH 0891/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 96ed4f16c..e1c60961f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,8 +4,12 @@ Changelog (Pillow) 3.2.0 (unreleased) ------------------ +- Add a basic DDS image plugin with more tests #1654 + [jleclanche, hugovk, wiredfool] + - Fix incorrect conditional in encode.c #1638 [manisandro] + - Add a basic read-only DDS plugin #252 [jleclanche] From b0aaa09b95220aa4ab59abe32a07a8f1174f9a49 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 16 Jan 2016 19:14:03 +1100 Subject: [PATCH 0892/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index e1c60961f..6cb66576f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,9 +10,6 @@ Changelog (Pillow) - Fix incorrect conditional in encode.c #1638 [manisandro] -- Add a basic read-only DDS plugin #252 - [jleclanche] - 3.1.0 (2016-01-04) ------------------ From b44488a3464cf65f1ca271eaeb2f9aa8d3526a5c Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sat, 16 Jan 2016 13:36:11 +0000 Subject: [PATCH 0893/1037] added TiffTags documentation --- docs/reference/TiffTags.rst | 59 +++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 docs/reference/TiffTags.rst diff --git a/docs/reference/TiffTags.rst b/docs/reference/TiffTags.rst new file mode 100644 index 000000000..cf9a97a2b --- /dev/null +++ b/docs/reference/TiffTags.rst @@ -0,0 +1,59 @@ +.. py:module:: PIL.TiffTags +.. py:currentmodule:: PIL.TiffTags + +:py:mod:`TiffTags` Module +========================= + +The :py:mod:`TiffTags` module exposes many of the stantard TIFF +metadata tag numbers, names, and type information. + +.. method:: lookup(tag) + + :param tag: Integer tag number + :returns: Taginfo namedtuple, From the ``TAGS_V2`` info if possible, + otherwise just populating the value and name from ``TAGS``. + If the tag is not recognized, "unknown" is returned for the name + +.. versionadded:: 3.1.0 + +.. class:: TagInfo + + .. method:: __init__(self, value=None, name="unknown", type=None, length=0, enum=None) + + :param value: Integer Tag Number + :param name: Tag Name + :param type: Integer type from :py:attr:`PIL.TiffTags.TYPES` + :param length: Array length: 0 == variable, 1 == single value, n = fixed + :param enum: Dict of name:integer value options for an enumeration + + .. method:: cvt_enum(self, value) + + :param value: The enumerated value name + :returns: The integer corresponding to the name. + +.. versionadded:: 3.0.0 + +.. py:attribute:: PIL.TiffTags.TAGS_V2 + + The ``TAGS_V2`` dictionary maps 16-bit integer tag numbers to + :py:class:`PIL.TagTypes.TagInfo` tuples for metadata fields defined in the TIFF + spec. + +.. versionadded:: 3.0.0 + +.. py:attribute:: PIL.TiffTags.TAGS + + The ``TAGS`` dictionary maps 16-bit integer TIFF tag number to + descriptive string names. For instance: + + >>> from PIL.TiffTags import TAGS + >>> TAGS[0x010e] + 'ImageDescription' + + This dictionary contains a superset of the tags in TAGS_V2, common + EXIF tags, and other well known metadata tags. + +.. py:attribute:: PIL.TiffTags.TYPES + + The ``TYPES`` dictionary maps the TIFF type short integer to a + human readable type name. From c9f3abf7ac533ca4bb04abe39e3f9e351c25b991 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sat, 16 Jan 2016 13:37:20 +0000 Subject: [PATCH 0894/1037] moved build.rst out of the doctree to suppress warnings, as this documentation is work in progress and not up to the standards of the rest of the documentation --- {docs => winbuild}/build.rst | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {docs => winbuild}/build.rst (100%) diff --git a/docs/build.rst b/winbuild/build.rst similarity index 100% rename from docs/build.rst rename to winbuild/build.rst From 8f82678f4c1faa23c8fab9bbed1614bd66f9e05c Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sat, 16 Jan 2016 15:41:33 +0000 Subject: [PATCH 0895/1037] added winbuild/build.rst to manifest.in --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 4fd9c602e..7091480f0 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -85,4 +85,5 @@ recursive-include winbuild *.gitignore recursive-include winbuild *.md recursive-include winbuild *.opt recursive-include winbuild *.py +recursive-include winbuild *.rst exclude .editorconfig From 957187d417c55654d7c45ae5d0e48fbf4e67a9a1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 2 Jan 2016 12:27:40 +1100 Subject: [PATCH 0896/1037] Added TIFF IFD test --- Tests/test_file_tiff.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 26b6bd4c3..a58d33809 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -107,6 +107,11 @@ class TestFileTiff(PillowTestCase): self.assertRaises(SyntaxError, lambda: TiffImagePlugin.TiffImageFile(invalid_file)) + TiffImagePlugin.PREFIXES.append("\xff\xd8\xff\xe0") + self.assertRaises(SyntaxError, + lambda: TiffImagePlugin.TiffImageFile(invalid_file)) + TiffImagePlugin.PREFIXES.pop() + def test_bad_exif(self): i = Image.open('Tests/images/hopper_bad_exif.jpg') try: From 184d40260faa5ae8aa114c2a515ce2ec2bde7af4 Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 21 Jan 2016 10:47:11 +0200 Subject: [PATCH 0897/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 6cb66576f..223c4ab8c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.2.0 (unreleased) ------------------ +- Add TIFF IFD test #1671 + [radarhere] + - Add a basic DDS image plugin with more tests #1654 [jleclanche, hugovk, wiredfool] From fcbd054f36aa60614142ee846d7edb7b7eae84d7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 21 Jan 2016 19:50:36 +1100 Subject: [PATCH 0898/1037] Updated jpeg to 9b --- winbuild/config.py | 6 +++--- winbuild/nmake.opt | 22 +++++++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/winbuild/config.py b/winbuild/config.py index f0d4b78be..5c55f23ba 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -18,9 +18,9 @@ libs = { 'dir': 'zlib-1.2.8', }, 'jpeg': { - 'url': 'http://www.ijg.org/files/jpegsr9a.zip', - 'hash': 'md5:a34f3c82760270ee1e1885b15b90a72e', # not found - generated by wiredfool - 'dir': 'jpeg-9a', + 'url': 'http://www.ijg.org/files/jpegsr9b.zip', + 'hash': 'md5:a21b8024d78ba05857a75272b4fa95ec', # not found - generated by wiredfool + 'dir': 'jpeg-9b', }, 'tiff': { 'url': 'ftp://ftp.remotesensing.org/pub/libtiff/tiff-4.0.6.zip', diff --git a/winbuild/nmake.opt b/winbuild/nmake.opt index b33421634..b155daacc 100644 --- a/winbuild/nmake.opt +++ b/winbuild/nmake.opt @@ -2,23 +2,23 @@ # # Copyright (C) 2004, Andrey Kiselev # -# Permission to use, copy, modify, distribute, and sell this software and +# Permission to use, copy, modify, distribute, and sell this software and # its documentation for any purpose is hereby granted without fee, provided # that (i) the above copyright notices and this permission notice appear in # all copies of the software and related documentation, and (ii) the names of # Sam Leffler and Silicon Graphics may not be used in any advertising or # publicity relating to the software without the specific, prior written # permission of Sam Leffler and Silicon Graphics. -# -# THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, -# EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY -# WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. -# +# +# THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, +# EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY +# WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +# # IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR # ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, # OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, -# WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF -# LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF +# LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE # OF THIS SOFTWARE. # Compile time parameters for MS Visual C++ compiler. @@ -28,7 +28,7 @@ ###### Edit the following lines to choose a feature set you need. ####### # -# +# # Select WINMODE_CONSOLE to build a library which reports errors to stderr, or # WINMODE_WINDOWED to build such that errors are reported via MessageBox(). # @@ -55,7 +55,7 @@ LOGLUV_SUPPORT = 1 # Uncomment and edit following lines to enable JPEG support. # JPEG_SUPPORT = 1 -JPEGDIR = $(BUILD)\jpeg-9a +JPEGDIR = $(BUILD)\jpeg-9b JPEG_INCLUDE = -I$(JPEGDIR) JPEG_LIB = $(JPEGDIR)/libjpeg.lib @@ -115,7 +115,7 @@ CHECK_JPEG_YCBCR_SUBSAMPLING = 1 # NOTE: /EHsc option required if you want to build the C++ stream API # OPTFLAGS = /Ox /MD /EHsc /W3 /D_CRT_SECURE_NO_DEPRECATE -#OPTFLAGS = /Zi +#OPTFLAGS = /Zi # # Uncomment following line to enable using Windows Common RunTime Library From d9466c9d92a37e829b189fec83099096575fd82f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 21 Jan 2016 22:28:30 +1100 Subject: [PATCH 0899/1037] Fixed URL [ci skip] --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6d5868e15..5ce67a080 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing to Pillow -Bug fixes, feature additions, tests, documentation and more can be contributed via [issues](https://github.com/python-pillow/Pillow/issues) and/or [pull requests](https://github.com/python-pillow/Pillow/issues). All contributions are welcome. +Bug fixes, feature additions, tests, documentation and more can be contributed via [issues](https://github.com/python-pillow/Pillow/issues) and/or [pull requests](https://github.com/python-pillow/Pillow/pulls). All contributions are welcome. ## Bug fixes, feature additions, etc. From 95e2a1507558e0f6054a22c1545cd774e4112cd6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 21 Jan 2016 22:35:30 +1100 Subject: [PATCH 0900/1037] Fixed URL [ci skip] --- RELEASING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASING.md b/RELEASING.md index c6a7df7a8..e4d669435 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -110,4 +110,4 @@ Released as needed privately to individual vendors for critical security-related ## Documentation -* [ ] Make sure the default version for Read the Docs is the latest release version e.g. 3.0.0 not latest: https://readthedocs.org/dashboard/pillow/versions/ +* [ ] Make sure the default version for Read the Docs is the latest release version e.g. 3.0.0 not latest: https://readthedocs.org/projects/pillow/versions/ From 6994da92878ca5a852a9902c34be85e5013eddb3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 23 Jan 2016 12:40:38 +1100 Subject: [PATCH 0901/1037] Clarified YCbCr handling [ci skip] --- docs/handbook/concepts.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/handbook/concepts.rst b/docs/handbook/concepts.rst index 2dc509a13..b5ad9331a 100644 --- a/docs/handbook/concepts.rst +++ b/docs/handbook/concepts.rst @@ -34,6 +34,9 @@ image. The current release supports the following standard modes: * ``RGBA`` (4x8-bit pixels, true color with transparency mask) * ``CMYK`` (4x8-bit pixels, color separation) * ``YCbCr`` (3x8-bit pixels, color video format) + + * Note that this refers to the JPEG, and not the ITU-R BT.2020, standard + * ``LAB`` (3x8-bit pixels, the L*a*b color space) * ``HSV`` (3x8-bit pixels, Hue, Saturation, Value color space) * ``I`` (32-bit signed integer pixels) From da7e712cb407911bc4766710464ad7b2c7768d46 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 23 Jan 2016 13:46:36 +1100 Subject: [PATCH 0902/1037] Added MIC test --- Tests/test_file_mic.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_mic.py b/Tests/test_file_mic.py index 0a574422a..f0a2752a9 100644 --- a/Tests/test_file_mic.py +++ b/Tests/test_file_mic.py @@ -6,11 +6,16 @@ from PIL import MicImagePlugin class TestFileMic(PillowTestCase): def test_invalid_file(self): + # Test an invalid OLE file invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, lambda: MicImagePlugin.MicImageFile(invalid_file)) + # Test a valid OLE file, but not a MIC file + ole_file = "Tests/images/test-ole-file.doc" + self.assertRaises(SyntaxError, + lambda: MicImagePlugin.MicImageFile(ole_file)) + if __name__ == '__main__': unittest.main() From a7a087e0b2c0ad80ab0270590a756d13a25300ec Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 23 Jan 2016 13:50:17 +1100 Subject: [PATCH 0903/1037] Added FPX test --- Tests/test_file_fpx.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_fpx.py b/Tests/test_file_fpx.py index 35119a612..01b285efe 100644 --- a/Tests/test_file_fpx.py +++ b/Tests/test_file_fpx.py @@ -6,11 +6,16 @@ from PIL import FpxImagePlugin class TestFileFpx(PillowTestCase): def test_invalid_file(self): + # Test an invalid OLE file invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, lambda: FpxImagePlugin.FpxImageFile(invalid_file)) + # Test a valid OLE file, but not an FPX file + ole_file = "Tests/images/test-ole-file.doc" + self.assertRaises(SyntaxError, + lambda: FpxImagePlugin.FpxImageFile(ole_file)) + if __name__ == '__main__': unittest.main() From ee76e8a05060a82fc1003bef15d66a833fe7201a Mon Sep 17 00:00:00 2001 From: Hugo Date: Sat, 23 Jan 2016 11:11:21 +0200 Subject: [PATCH 0904/1037] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 223c4ab8c..53ad29bc4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.2.0 (unreleased) ------------------ +- Add tests for OLE file based formats #1678 + [radarhere] + - Add TIFF IFD test #1671 [radarhere] From 0ad6f5ff1f906c02c620c31d48b0e7c69d75bb9a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 24 Jan 2016 12:44:31 +1100 Subject: [PATCH 0905/1037] Updated deprecated asserts --- Tests/test_file_tiff.py | 4 ++-- Tests/test_tiff_ifdrational.py | 32 ++++++++++++++++---------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index a58d33809..20a0e93e3 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -85,8 +85,8 @@ class TestFileTiff(PillowTestCase): self.assertIsInstance(im.tag[Y_RESOLUTION][0], tuple) #v2 api - self.assert_(isinstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational)) - self.assert_(isinstance(im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational)) + self.assertIsInstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational) + self.assertIsInstance(im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational) self.assertEqual(im.info['dpi'], (72., 72.)) diff --git a/Tests/test_tiff_ifdrational.py b/Tests/test_tiff_ifdrational.py index 5654d4c9c..d9dd13102 100644 --- a/Tests/test_tiff_ifdrational.py +++ b/Tests/test_tiff_ifdrational.py @@ -11,14 +11,14 @@ class Test_IFDRational(PillowTestCase): def _test_equal(self, num, denom, target): - + t = IFDRational(num, denom) self.assertEqual(target, t) self.assertEqual(t, target) - + def test_sanity(self): - + self._test_equal(1, 1, 1) self._test_equal(1, 1, Fraction(1,1)) @@ -27,29 +27,29 @@ class Test_IFDRational(PillowTestCase): self._test_equal(Fraction(1,1), 1, Fraction(1,1)) self._test_equal(IFDRational(1,1), 1, 1) - - + + self._test_equal(1, 2, Fraction(1,2)) self._test_equal(1, 2, IFDRational(1,2)) def test_nonetype(self): - " Fails if the _delegate function doesn't return a valid function" + " Fails if the _delegate function doesn't return a valid function" - xres = IFDRational(72) - yres = IFDRational(72) - self.assert_(xres._val is not None) - self.assert_(xres.numerator is not None) - self.assert_(xres.denominator is not None) - self.assert_(yres._val is not None) - - self.assert_(xres and 1) - self.assert_(xres and yres) + xres = IFDRational(72) + yres = IFDRational(72) + self.assertTrue(xres._val is not None) + self.assertTrue(xres.numerator is not None) + self.assertTrue(xres.denominator is not None) + self.assertTrue(yres._val is not None) + + self.assertTrue(xres and 1) + self.assertTrue(xres and yres) def test_ifd_rational_save(self): for libtiff in (True, False): TiffImagePlugin.WRITE_LIBTIFF = libtiff - + im = hopper() out = self.tempfile('temp.tiff') res = IFDRational(301,1) From 2b0c037d5f7b68f6ca5d7f32554200a53457321f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 27 Jan 2016 14:04:05 +1100 Subject: [PATCH 0906/1037] Allowed ImageSequence to seek to zero --- PIL/ImageSequence.py | 3 +-- Tests/test_imagesequence.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/PIL/ImageSequence.py b/PIL/ImageSequence.py index 256bcbedb..a979b8865 100644 --- a/PIL/ImageSequence.py +++ b/PIL/ImageSequence.py @@ -35,8 +35,7 @@ class Iterator(object): def __getitem__(self, ix): try: - if ix: - self.im.seek(ix) + self.im.seek(ix) return self.im except EOFError: raise IndexError # end of sequence diff --git a/Tests/test_imagesequence.py b/Tests/test_imagesequence.py index 9e18192ee..5429c2845 100644 --- a/Tests/test_imagesequence.py +++ b/Tests/test_imagesequence.py @@ -44,6 +44,17 @@ class TestImageSequence(PillowTestCase): self._test_multipage_tiff() TiffImagePlugin.READ_LIBTIFF = False + def test_consecutive(self): + im = Image.open('Tests/images/multipage.tiff') + firstFrame = None + for frame in ImageSequence.Iterator(im): + if firstFrame == None: + firstFrame = frame.copy() + pass + for frame in ImageSequence.Iterator(im): + self.assert_image_equal(frame, firstFrame) + break + if __name__ == '__main__': unittest.main() From a5b2c4da293dba4cecccee840c8627125a891519 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 31 Jan 2016 11:57:02 +1100 Subject: [PATCH 0907/1037] Corrected comment --- PIL/Image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/Image.py b/PIL/Image.py index 89549ea4a..31d25421c 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1295,7 +1295,7 @@ class Image(object): box = (0, 0) + self.size if len(box) == 2: - # lower left corner given; get size from image or mask + # upper left corner given; get size from image or mask if isImageType(im): size = im.size elif isImageType(mask): From c5bace00e3cc72e0f80a305b188260e30894628a Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 31 Jan 2016 08:42:00 -0800 Subject: [PATCH 0908/1037] more initialization --- _imaging.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/_imaging.c b/_imaging.c index 912159772..431ba7f71 100644 --- a/_imaging.c +++ b/_imaging.c @@ -585,13 +585,14 @@ _fill(PyObject* self, PyObject* args) if (!im) return NULL; + buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0; if (color) { if (!getink(color, im, buffer)) { ImagingDelete(im); return NULL; } - } else - buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0; + } + (void) ImagingFill(im, buffer); @@ -1337,6 +1338,8 @@ _putdata(ImagingObject* self, PyObject* args) INT32 inkint; } u; + u.inkint = 0; + op = PySequence_Fast_GET_ITEM(seq, i); if (!op || !getink(op, image, u.ink)) { Py_DECREF(seq); From d6bbe295323bf31dbc89d8bd2dcf3644309ad994 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 31 Jan 2016 17:10:49 +0000 Subject: [PATCH 0909/1037] Update CHANGES.rst [ci skip] --- CHANGES.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 53ad29bc4..72d04ebff 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,24 @@ Changelog (Pillow) 3.2.0 (unreleased) ------------------ +- Allow ImageSequence to seek to zero #1686 + [radarhere] + +- ImageSequence Iterator is now an iterator #1649 + [radarhere] + +- Updated windows test builds to jpeg9b + [radarhere] + +- Fixed support for .gbr version 1 images, added support for version 2 in GbrImagePlugin #1653 + [wiredfool] + +- Clarified which YCbCr format is used #1677 + [radarhere] + +- Added TiffTags documentation, Moved windows build documentation to winbuild/ #1667 + [wiredfool] + - Add tests for OLE file based formats #1678 [radarhere] From 8ec76b277bdc89c3567df6158b82afb0ba0196d5 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 8 Jan 2016 11:49:27 -0800 Subject: [PATCH 0910/1037] Pruning manifest to those items we need, and in larger chunks to reduce thrashing --- MANIFEST.in | 94 ++++++++--------------------------------------------- 1 file changed, 14 insertions(+), 80 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index d82b746aa..5ef75abfe 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -7,85 +7,19 @@ include *.py include *.rst include *.sh include *.txt -include *.yaml -include *.yml -include .coveragerc -include .gitattributes include LICENSE include Makefile -include tox.ini -recursive-include PIL *.md -recursive-include Scripts *.py -recursive-include Scripts *.rst -recursive-include Scripts *.sh -recursive-include Tests *.bdf -recursive-include Tests *.bin -recursive-include Tests *.bmp -recursive-include Tests *.bw -recursive-include Tests *.cur -recursive-include Tests *.dcx -recursive-include Tests *.dds -recursive-include Tests *.doc -recursive-include Tests *.eps -recursive-include Tests *.fli -recursive-include Tests *.gbr -recursive-include Tests *.ggr -recursive-include Tests *.gif -recursive-include Tests *.gpl -recursive-include Tests *.gnuplot -recursive-include Tests *.html -recursive-include Tests *.icc -recursive-include Tests *.icns -recursive-include Tests *.ico -recursive-include Tests *.im -recursive-include Tests *.j2k -recursive-include Tests *.jp2 -recursive-include Tests *.jpg -recursive-include Tests *.lut -recursive-include Tests *.mpo -recursive-include Tests *.msp -recursive-include Tests *.pbm -recursive-include Tests *.pcf -recursive-include Tests *.pcx -recursive-include Tests *.pgm -recursive-include Tests *.pil -recursive-include Tests *.png -recursive-include Tests *.ppm -recursive-include Tests *.psd -recursive-include Tests *.py -recursive-include Tests *.ras -recursive-include Tests *.rgb -recursive-include Tests *.rst -recursive-include Tests *.sgi -recursive-include Tests *.spider -recursive-include Tests *.tar -recursive-include Tests *.tga -recursive-include Tests *.tif -recursive-include Tests *.tiff -recursive-include Tests *.ttf -recursive-include Tests *.txt -recursive-include Tests *.webp -recursive-include Tests *.xbm -recursive-include Tests *.xpm -recursive-include Tk *.c -recursive-include Tk *.rst -recursive-include depends *.rst -recursive-include depends *.sh -recursive-include docs *.bat -recursive-include docs *.gitignore -recursive-include docs *.html -recursive-include docs *.py -recursive-include docs *.rst -recursive-include docs *.txt -recursive-include docs Makefile -recursive-include docs Guardfile -recursive-include docs COPYING -recursive-include docs BUILDME -recursive-include libImaging *.c -recursive-include libImaging *.h -recursive-include winbuild *.gitignore -recursive-include winbuild *.md -recursive-include winbuild *.opt -recursive-include winbuild *.py -recursive-include winbuild *.rst -exclude .editorconfig +graft Tests/fonts +graft Tests/images +graft Tests/icc +graft Tk +graft libImaging +graft depends +graft winbuild +graft docs +prune docs/_static + +# build/src control detritus +exclude build_children.sh +global-exclude .git* + From 1dc961d6c13dedaed8f6543eeafb4a763ff583e7 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 17 Jan 2016 14:36:10 +0000 Subject: [PATCH 0911/1037] Added Tests/*.py --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 5ef75abfe..92539eb9a 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -9,6 +9,7 @@ include *.sh include *.txt include LICENSE include Makefile +recursive-include Tests *.py graft Tests/fonts graft Tests/images graft Tests/icc From 1f97f25ae1e0e6ca76e7976567c8a94add09e98e Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 31 Jan 2016 14:58:48 +0000 Subject: [PATCH 0912/1037] Move travis cruft out of scripts deployable directory --- .travis.yml | 4 ++-- {Scripts => depends}/diffcover-install.sh | 0 {Scripts => depends}/diffcover-run.sh | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename {Scripts => depends}/diffcover-install.sh (100%) rename {Scripts => depends}/diffcover-run.sh (100%) diff --git a/.travis.yml b/.travis.yml index 67f9ea3bd..7c9b91f4b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -75,8 +75,8 @@ after_success: # Coverage and quality reports on just the latest diff. # (Installation is very slow on Py3, so just do it for Py2.) - - if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then Scripts/diffcover-install.sh; fi - - if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then Scripts/diffcover-run.sh; fi + - if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then depends/diffcover-install.sh; fi + - if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then depends/diffcover-run.sh; fi # after_all - | diff --git a/Scripts/diffcover-install.sh b/depends/diffcover-install.sh similarity index 100% rename from Scripts/diffcover-install.sh rename to depends/diffcover-install.sh diff --git a/Scripts/diffcover-run.sh b/depends/diffcover-run.sh similarity index 100% rename from Scripts/diffcover-run.sh rename to depends/diffcover-run.sh From 878bd8a03ba045e6fe3ebe9109d0a951ad6d2fac Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 31 Jan 2016 15:03:40 +0000 Subject: [PATCH 0913/1037] added more ignores to check-manifest --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7c9b91f4b..6eb7ab651 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,7 +47,7 @@ script: - if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage run --append --include=PIL/* selftest.py; fi - if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage run --append --include=PIL/* -m nose -vx Tests/test_*.py; fi - - check-manifest --ignore "depends/*" + - check-manifest --ignore "depends/*,.coveragerc,.editorconfig,*.yml,*.yaml,tox.ini" # Sphinx - make install - pushd docs; make html; make linkcheck; popd @@ -68,7 +68,7 @@ after_success: - travis_retry pip install pep8 pyflakes - pep8 --statistics --count PIL/*.py - - pep8 --statistics --count Tests/*.py + - pep8 --statistics --count Tests/*.py - pyflakes *.py | tee >(wc -l) - pyflakes PIL/*.py | tee >(wc -l) - pyflakes Tests/*.py | tee >(wc -l) From 30bd49d61734914ebbba4d5c9867fdf37db6de6f Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 31 Jan 2016 15:03:27 +0000 Subject: [PATCH 0914/1037] Full grafts of Tests and PIL, Scripts, excluding build artifacts --- MANIFEST.in | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 92539eb9a..5476d49df 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -9,10 +9,9 @@ include *.sh include *.txt include LICENSE include Makefile -recursive-include Tests *.py -graft Tests/fonts -graft Tests/images -graft Tests/icc +graft Scripts +graft Tests +graft PIL graft Tk graft libImaging graft depends @@ -23,4 +22,6 @@ prune docs/_static # build/src control detritus exclude build_children.sh global-exclude .git* +global-exclude *.pyc +global-exclude *.so From 8a0c2d5634576b79a285b2d0d228bc989808adad Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 31 Jan 2016 16:23:58 +0000 Subject: [PATCH 0915/1037] running check-manifest in a clean checkout --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6eb7ab651..b38c47c5a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,6 +34,9 @@ install: - if [ "$TRAVIS_PYTHON_VERSION" == "3.2" ]; then travis_retry pip install coverage==3.7.1; fi - if [ "$TRAVIS_PYTHON_VERSION" != "3.2" ]; then travis_retry pip install coverage; fi + # clean checkout for manifest + - mkdir /tmp/check-manifest && cp -a . /tmp/check-manifest + # webp - pushd depends && ./install_webp.sh && popd @@ -47,7 +50,7 @@ script: - if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage run --append --include=PIL/* selftest.py; fi - if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage run --append --include=PIL/* -m nose -vx Tests/test_*.py; fi - - check-manifest --ignore "depends/*,.coveragerc,.editorconfig,*.yml,*.yaml,tox.ini" + - pushd /tmp/check-manifest && check-manifest --ignore ".coveragerc,.editorconfig,*.yml,*.yaml,tox.ini" && popd # Sphinx - make install - pushd docs; make html; make linkcheck; popd From 68ea06433691ec61880e70688fb68b1db4ceafbe Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 1 Feb 2016 09:22:18 +1100 Subject: [PATCH 0916/1037] Added documentation [ci skip] --- docs/reference/ImageDraw.rst | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index 7d3ad570c..842407c90 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -227,15 +227,19 @@ Methods Draw a shape. -.. py:method:: PIL.ImageDraw.Draw.text(xy, text, fill=None, font=None, anchor=None) +.. py:method:: PIL.ImageDraw.Draw.text(xy, text, fill=None, font=None, anchor=None, spacing=0, align="left") Draws the string at the given position. :param xy: Top left corner of the text. :param text: Text to be drawn. If it contains any newline characters, - the text is passed on to mulitiline_text() + the text is passed on to multiline_text() :param fill: Color to use for the text. :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. + :param spacing: If the text is passed on to multiline_text(), + the number of pixels between lines. + :param align: If the text is passed on to multiline_text(), + "left", "center" or "right". .. py:method:: PIL.ImageDraw.Draw.multiline_text(xy, text, fill=None, font=None, anchor=None, spacing=0, align="left") @@ -249,13 +253,15 @@ Methods :param spacing: The number of pixels between lines. :param align: "left", "center" or "right". -.. py:method:: PIL.ImageDraw.Draw.textsize(text, font=None) +.. py:method:: PIL.ImageDraw.Draw.textsize(text, font=None, spacing=0) Return the size of the given string, in pixels. :param text: Text to be measured. If it contains any newline characters, - the text is passed on to mulitiline_textsize() + the text is passed on to multiline_textsize() :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. + :param spacing: If the text is passed on to multiline_textsize(), + the number of pixels between lines. .. py:method:: PIL.ImageDraw.Draw.multiline_textsize(text, font=None, spacing=0) From 70cfd5416c6f9ac0572ba98824680fb5eac24bf2 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 1 Feb 2016 09:20:11 +0000 Subject: [PATCH 0917/1037] Update CHANGES.rst --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 72d04ebff..9574cd8d6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.2.0 (unreleased) ------------------ +- Extend ImageDraw.text method to pass on multiline_text method specific arguments #1647 + [radarhere] + - Allow ImageSequence to seek to zero #1686 [radarhere] From b1f2cbe5d239ac41864cca268522244cc14793b3 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 1 Feb 2016 01:35:33 -0800 Subject: [PATCH 0918/1037] release notes for api changes in 3.2 -- in progress --- docs/releasenotes/3.2.0.rst | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 docs/releasenotes/3.2.0.rst diff --git a/docs/releasenotes/3.2.0.rst b/docs/releasenotes/3.2.0.rst new file mode 100644 index 000000000..0b30a5bb4 --- /dev/null +++ b/docs/releasenotes/3.2.0.rst @@ -0,0 +1,30 @@ + +3.2.0 +===== + +New DDS Image Plugins +===================== + +The `DdsImagePlugin` reading DXT1 and DXT5 encoded .dds images was +added. DXT3 images are not currently supported. + +Updates to the GbrImagePlugin +============================= + +The `GbrImagePlugin` (GIMP brush format) has been updated to fix +support for version 1 files and add support for version 2 files. + +Passthrough Parameters for ImageDraw.text +========================================= + +`ImageDraw.multiline_text` takes extra spacing paramaters above what +is used in `ImageDraw.text`. These parameters can now be passed into +`ImageDraw.text` and they will be passed through to +`ImageDraw.multiline_text`. + +ImageSequence.Iterator changes +============================== + +`ImageSequence.Iterator` is now an actual iterator implementating the +Iterator protocol. It is also now possible to seek to the first image +of the file when using direct indexing. From c30099c5d6f2a08a99f997a5d8e91249608733e5 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 1 Feb 2016 01:53:55 -0800 Subject: [PATCH 0919/1037] Added ImageDraw.*size as well --- docs/releasenotes/3.2.0.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/releasenotes/3.2.0.rst b/docs/releasenotes/3.2.0.rst index 0b30a5bb4..215f1445b 100644 --- a/docs/releasenotes/3.2.0.rst +++ b/docs/releasenotes/3.2.0.rst @@ -17,10 +17,11 @@ support for version 1 files and add support for version 2 files. Passthrough Parameters for ImageDraw.text ========================================= -`ImageDraw.multiline_text` takes extra spacing paramaters above what -is used in `ImageDraw.text`. These parameters can now be passed into -`ImageDraw.text` and they will be passed through to -`ImageDraw.multiline_text`. +`ImageDraw.multiline_text` and `ImageDraw.multiline_size` take extra +spacing paramaters above what are used in `ImageDraw.text` and +`ImageDraw.size`. These parameters can now be passed into +`ImageDraw.text` and `ImageDraw.size` and they will be passed through +to the corresponding multiline functions. ImageSequence.Iterator changes ============================== From 3a55a20712def145a102e7498e2518884f3942d5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 1 Feb 2016 21:02:43 +1100 Subject: [PATCH 0920/1037] Improved alpha_composite documentation --- PIL/Image.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 31d25421c..177f804f7 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -2302,8 +2302,8 @@ def alpha_composite(im1, im2): """ Alpha composite im2 over im1. - :param im1: The first image. - :param im2: The second image. Must have the same mode and size as + :param im1: The first image. Must have mode RGBA. + :param im2: The second image. Must have mode RGBA, and the same size as the first image. :returns: An :py:class:`~PIL.Image.Image` object. """ From b431bbc2723bc53ba361ca67a48b0e0404aa9084 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 1 Feb 2016 10:16:48 +0000 Subject: [PATCH 0921/1037] Updated Changes.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 9574cd8d6..da95dde1c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.2.0 (unreleased) ------------------ +- Improved alpha_composite documentation #1698 + [radarhere] + - Extend ImageDraw.text method to pass on multiline_text method specific arguments #1647 [radarhere] From 58c59bbad00b0f33bb2599469b2596ce29194a91 Mon Sep 17 00:00:00 2001 From: Jerome Leclanche Date: Wed, 27 Jan 2016 15:33:28 +0200 Subject: [PATCH 0922/1037] Add a loader for the FTEX format from Independence War 2: Edge of Chaos --- PIL/FtexImagePlugin.py | 97 +++++++++++++++++++++++++++++ PIL/__init__.py | 4 +- Tests/images/ftex_dxt1.ftc | Bin 0 -> 11000 bytes Tests/images/ftex_dxt1.png | Bin 0 -> 3886 bytes Tests/images/ftex_uncompressed.ftu | Bin 0 -> 65599 bytes Tests/images/ftex_uncompressed.png | Bin 0 -> 3886 bytes 6 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 PIL/FtexImagePlugin.py create mode 100644 Tests/images/ftex_dxt1.ftc create mode 100644 Tests/images/ftex_dxt1.png create mode 100644 Tests/images/ftex_uncompressed.ftu create mode 100644 Tests/images/ftex_uncompressed.png diff --git a/PIL/FtexImagePlugin.py b/PIL/FtexImagePlugin.py new file mode 100644 index 000000000..24e4c0e7d --- /dev/null +++ b/PIL/FtexImagePlugin.py @@ -0,0 +1,97 @@ +""" +A Pillow loader for .ftc and .ftu files (FTEX) +Jerome Leclanche + +The contents of this file are hereby released in the public domain (CC0) +Full text of the CC0 license: + https://creativecommons.org/publicdomain/zero/1.0/ + +Independence War 2: Edge Of Chaos - Texture File Format - 16 October 2001 + +The textures used for 3D objects in Independence War 2: Edge Of Chaos are in a +packed custom format called FTEX. This file format uses file extensions FTC and FTU. +* FTC files are compressed textures (using standard texture compression). +* FTU files are not compressed. +Texture File Format +The FTC and FTU texture files both use the same format, called. This +has the following structure: +{header} +{format_directory} +{data} +Where: +{header} = { u32:magic, u32:version, u32:width, u32:height, u32:mipmap_count, u32:format_count } + +* The "magic" number is "FTEX". +* "width" and "height" are the dimensions of the texture. +* "mipmap_count" is the number of mipmaps in the texture. +* "format_count" is the number of texture formats (different versions of the same texture) in this file. + +{format_directory} = format_count * { u32:format, u32:where } + +The format value is 0 for DXT1 compressed textures and 1 for 24-bit RGB uncompressed textures. +The texture data for a format starts at the position "where" in the file. + +Each set of texture data in the file has the following structure: +{data} = format_count * { u32:mipmap_size, mipmap_size * { u8 } } +* "mipmap_size" is the number of bytes in that mip level. For compressed textures this is the +size of the texture data compressed with DXT1. For 24 bit uncompressed textures, this is 3 * width * height. +Following this are the image bytes for that mipmap level. + +Note: All data is stored in little-Endian (Intel) byte order. +""" + +import struct +from io import BytesIO +from PIL import Image, ImageFile +from PIL.DdsImagePlugin import _dxt1 + + +MAGIC = b"FTEX" +FORMAT_DXT1 = 0 +FORMAT_UNCOMPRESSED = 1 + + +class FtexImageFile(ImageFile.ImageFile): + format = "FTEX" + format_description = "Texture File Format (IW2:EOC)" + + def _open(self): + magic = struct.unpack("cg=Rwz9#(K6W*t@`3$>&Wg%lKeh&X8!Jfz~Gl^jyZA#Arb zB7v4op~VffSPvF+SlI}Hxd@|y(B=@(Ll`{-+(Qow^)QF9Y$1fN|Nr+nEm>mLjjmZY z1MAz_o$<}@d+&SS``+762QOY}&@^pw=hXzsa~|RU{_xGwKf*vTO*1eRl+yp`H04XJ zt(9_ByVy>-sNL?O%qUsN969iL$>->b$M!Y#f~T!+;M;})D1_B6(%-E4nfKh9q+`aT<<4^n>c%~-p+dRBWd z1D@54W@fjC$y2nijZ+S-xe3bUjY}!Y7h4ZzC|mN+XDQF;PcBf_%CTct<1ziy$@;g4 zdzu_O>T?s3RtKEVXZ!kjzG-)iJF8J}1*grI8EwRUDtU6^uEp^X;-f#!B_HJS2HYL8 zC2yXk9C1w_aVo3d!TbfM|HI$T^itm3 zxIY12|4Q-X0?s9`C3)wH0X$r}=il%5)#9I*>rXoJCE}K2#dO%YwUCYaH|_0Va*w#z zQYS5TC8A4*x!%4^&|uSIX^Ozh8c7Z^w#o zy(0JX*`|-l{Q_}J`S&LLo7{87<{IMOFaPg5^V{=t&)(#=Fb+Z#Z3u9$6q$2-`f5#9 zYX6;eJH)tFunalBVQ-y&uDCaC8xy&dc#De^19V1Da-K4gDnY z7IJg_R(7wAbmse(qRtL;AZB(|cQhXT1e>kGP4pb_2ZJe`6e?+=s&MjivV= z_487G)|tR-px)A(1wZQ!90%^t>S4Z@+RNi-ymQi_cn9|9pBP4S(e4JuVrsMHQ5b+N74zRu!!5bl-Z3ODj@4a#G_*qA`0L*$Su-jQzgam)mZD?q$ zn|SYWdoqjxFr83{^>gyFj!emVDn!7$u3qI7=6yOH3bAiMZr0B!Sx3gaZ?0EvkBjvS z%=dVjl%-0YVq{+2xO8@zFt$nFZ=+$p6cr-H~WH$A9cI;fL}u&E{vCcd2sM5)&e$N zxh5Qhe1zDVK~Me+atqTe_&Y`^m$dZ+Bzo_`Y4mc@i>KZ}9MJk!M~~_i$61qv9{uYX z{{JOk_u4V^z%yFX%?z*?F2s7mTxG8Xz2GI*s?Y=eThng6rGCZbV6TAO88!Y9H{b2Z z9#&_n5Mym%Xw8x}@0DG8SxNT-j=kzvwJ0-me*%1tg+2MPR#jsUPFqsnv#rW{W_!~w zYayl?x}K7?Pwa)!yOTW}evUIrKn8hL zjq{-NdV8tY>z957#k8-gG2XQTRjD<-s$b0`Cq#d?P{bD^I9=UZ&#U@nuN28x67z_) z7wo;Zs#|MO`n~w`6Z{M%FXX;mDI;(gwGO49Iq2n$Q>U&VUUBh}{(UHWE~TITmX_oG z!vNG{&x5sZU-R1Y?{ZSJM?xq4cjOrh?1?xh@2d^dDZXtMqdnWDdsl z2st;#Kj~s2G^;OLat|Lq{7%CfbpZ-vpI;ti4B*;dD%J1i%($85*OoqqC&cg0w4A^i zF8taB-1oXQeAAZK!>6Z$YE3-1U_gZsIidQZD@Cq>rT3rD<32RngyVC!Ng!r75&3$$M4U7vyHuRobz|;j_Ip6obSNDHg?BPBHyxzEY-

    ze%isyTWLYg;l0VEJ2^S|)g&$sqk{!DhL5PvIY!tsU;+N0F(+t3 zejbk;hyRv7v-kac5L-&@sdD^{7M{_QD@8s7_47`sJ}yL9Q@K-EYnHJvC+MqmPhMl; Iv)$hR3n1ZpUjP6A literal 0 HcmV?d00001 diff --git a/Tests/images/ftex_dxt1.png b/Tests/images/ftex_dxt1.png new file mode 100644 index 0000000000000000000000000000000000000000..a5521dc987466fb9c85650061fb5ab5bc224e92c GIT binary patch literal 3886 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 Date: Sat, 30 Jan 2016 13:22:02 +0000 Subject: [PATCH 0923/1037] added tests for ftex --- Tests/test_file_ftex.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 Tests/test_file_ftex.py diff --git a/Tests/test_file_ftex.py b/Tests/test_file_ftex.py new file mode 100644 index 000000000..7a99aea9a --- /dev/null +++ b/Tests/test_file_ftex.py @@ -0,0 +1,15 @@ +from helper import unittest, PillowTestCase +from PIL import Image + +class TestFileFtex(PillowTestCase): + + def test_load_raw(self): + im = Image.open('Tests/images/ftex_uncompressed.ftu') + target = Image.open('Tests/images/ftex_uncompressed.png') + + self.assert_image_equal(im, target) + + def test_load_dxt1(self): + im = Image.open('Tests/images/ftex_dxt1.ftc') + target = Image.open('Tests/images/ftex_dxt1.png') + self.assert_image_similar(im, target, 15) From 756d8a606e9f065cb6dd475aec764b0caaecb4cf Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 1 Feb 2016 13:46:51 +0000 Subject: [PATCH 0924/1037] Updated Changes.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index da95dde1c..21bcf2f24 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 3.2.0 (unreleased) ------------------ +- Add a loader for the FTEX format from Independence War 2: Edge of Chaos #1688 + [jleclanche] + - Improved alpha_composite documentation #1698 [radarhere] From ba165e66030bc80a3fa7695d460fb0aafb9a49d3 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 1 Feb 2016 05:50:05 -0800 Subject: [PATCH 0925/1037] Added FtexImagePlugin --- docs/releasenotes/3.2.0.rst | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/releasenotes/3.2.0.rst b/docs/releasenotes/3.2.0.rst index 215f1445b..137bc0555 100644 --- a/docs/releasenotes/3.2.0.rst +++ b/docs/releasenotes/3.2.0.rst @@ -2,12 +2,17 @@ 3.2.0 ===== -New DDS Image Plugins -===================== +New DDS and FTEX Image Plugins +============================== -The `DdsImagePlugin` reading DXT1 and DXT5 encoded .dds images was +The `DdsImagePlugin` reading DXT1 and DXT5 encoded `.dds` images was added. DXT3 images are not currently supported. +The `FtexImagePlugin` reads textures used for 3D objects in +Independence War 2: Edge Of Chaos. The plugin reads a single texture +per file, in the `.ftc` (compressed) and `.ftu` (uncompressed) +formats. + Updates to the GbrImagePlugin ============================= From 94c84b49f1122d7e25d3ca19473f0a7b546bbf19 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 1 Feb 2016 08:11:42 -0800 Subject: [PATCH 0926/1037] Disable multiprocessing install on cygwin, fixes #1690 --- mp_compile.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mp_compile.py b/mp_compile.py index 078f62476..8f86f0bee 100644 --- a/mp_compile.py +++ b/mp_compile.py @@ -56,13 +56,14 @@ def install(): fl_pypy3 = hasattr(sys, 'pypy_version_info') and sys.version_info > (3, 0) fl_win = sys.platform.startswith('win') + fl_cygwin = sys.platform.startswith('cygwin') if fl_pypy3: # see https://github.com/travis-ci/travis-ci/issues/3587 print("Single threaded build for pypy3") return - if fl_win: + if fl_win or fl_cygwin: #windows barfs on multiprocessing installs print("Single threaded build for windows") return From 06dba3486e55d37fb689ceed7c2f639dcc96acda Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 2 Feb 2016 11:29:32 +1100 Subject: [PATCH 0927/1037] Updated redirected URL --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index c60ad6eeb..e996ed4f7 100644 --- a/README.rst +++ b/README.rst @@ -34,7 +34,7 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors Date: Tue, 2 Feb 2016 20:47:02 +1100 Subject: [PATCH 0928/1037] Fixed typos [ci skip] --- docs/releasenotes/3.2.0.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/releasenotes/3.2.0.rst b/docs/releasenotes/3.2.0.rst index 137bc0555..934044a2e 100644 --- a/docs/releasenotes/3.2.0.rst +++ b/docs/releasenotes/3.2.0.rst @@ -23,7 +23,7 @@ Passthrough Parameters for ImageDraw.text ========================================= `ImageDraw.multiline_text` and `ImageDraw.multiline_size` take extra -spacing paramaters above what are used in `ImageDraw.text` and +spacing parameters above what are used in `ImageDraw.text` and `ImageDraw.size`. These parameters can now be passed into `ImageDraw.text` and `ImageDraw.size` and they will be passed through to the corresponding multiline functions. @@ -31,6 +31,6 @@ to the corresponding multiline functions. ImageSequence.Iterator changes ============================== -`ImageSequence.Iterator` is now an actual iterator implementating the +`ImageSequence.Iterator` is now an actual iterator implementing the Iterator protocol. It is also now possible to seek to the first image of the file when using direct indexing. From 0fc8d5d9abb5deee954c2ef29a868401e8174a9a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 2 Feb 2016 20:49:03 +1100 Subject: [PATCH 0929/1037] Fixed typo [ci skip] --- docs/releasenotes/3.1.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/releasenotes/3.1.0.rst b/docs/releasenotes/3.1.0.rst index 6dda54444..cad3a408c 100644 --- a/docs/releasenotes/3.1.0.rst +++ b/docs/releasenotes/3.1.0.rst @@ -67,7 +67,7 @@ featured interface for EXIF is anticipated in a future release. Out of Spec Metadata ++++++++++++++++++++ -In Pillow 3.0 and 3.1, images that contan metadata that is internally +In Pillow 3.0 and 3.1, images that contain metadata that is internally consistent but not in agreement with the TIFF spec may cause an exception when reading the metadata. This can happen when a tag that is specified to have a single value is stored with an array of values. From a4384c956747c7d86c755322eda480c1eb18a142 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 2 Feb 2016 21:24:23 +1100 Subject: [PATCH 0930/1037] Corrected AppVeyor testing status documentation [ci skip] --- winbuild/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winbuild/README.md b/winbuild/README.md index 9777b1e6c..918fb2ba3 100644 --- a/winbuild/README.md +++ b/winbuild/README.md @@ -13,6 +13,6 @@ For more extensive info, see the windows build instructions `docs/build.rst`. * (in powershell) `build_deps.cmd` invokes the dependency build. * `python build.py --clean` makes Pillow for the matrix of Pythons. * `python test.py` runs the tests on Pillow in all the virtual envs. -* Currently working with zlib, libjpeg, freetype, and libtiff on Python 2.7, 3.3, and 3.4, both 32 and 64 bit, on a local win7 pro machine and appveyor.com (3.3 untested there) +* Currently working with zlib, libjpeg, freetype, and libtiff on Python 2.7, 3.3, and 3.4, both 32 and 64 bit, on a local win7 pro machine and appveyor.com * Webp is built, not detected. * LCMS and OpenJpeg are not building. From 69b1d8614642338ec694dd5eccfe4dfd928f915e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 2 Feb 2016 23:46:23 +1100 Subject: [PATCH 0931/1037] Added AppVeyor to the continuous integration description [ci skip] --- docs/about.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/about.rst b/docs/about.rst index a36fe2c2a..9881c7043 100644 --- a/docs/about.rst +++ b/docs/about.rst @@ -6,11 +6,12 @@ Goals The fork author's goal is to foster and support active development of PIL through: -- Continuous integration testing via `Travis CI`_ +- Continuous integration testing via `Travis CI`_ and `AppVeyor`_ - Publicized development activity on `GitHub`_ - Regular releases to the `Python Package Index`_ .. _Travis CI: https://travis-ci.org/python-pillow/Pillow +.. _AppVeyor: https://ci.appveyor.com/project/Python-pillow/pillow .. _GitHub: https://github.com/python-pillow/Pillow .. _Python Package Index: https://pypi.python.org/pypi/Pillow From f9f2c205050ed47e47a6b8ee4ec7189a86ec8d92 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 3 Feb 2016 00:02:46 +1100 Subject: [PATCH 0932/1037] Added FTEX documentation to Image File Formats list [ci skip] --- docs/handbook/image-file-formats.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index f36e75a14..c16228085 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -667,6 +667,15 @@ into account. library before building the Python Imaging Library. See the distribution README for details. +FTEX +^^^^ + +.. versionadded:: 3.2.0 + +The FTEX decoder reads textures used for 3D objects in +Independence War 2: Edge Of Chaos. The plugin reads a single texture +per file, in the compressed and uncompressed formats. + GBR ^^^ From eb134dff0c45fab7be5d1c8ca2e529e4dd9e2fb0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 3 Feb 2016 00:08:09 +1100 Subject: [PATCH 0933/1037] Updated El Capitan tested Pillow version [ci skip] --- docs/installation.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index c91825258..01f187c97 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -26,12 +26,12 @@ Basic Installation most common image formats. See :ref:`external-libraries` for a full list of external libraries supported. -.. note:: +.. note:: The basic installation works on Windows and OS X using the binaries from PyPI. Other installations require building from source as detailed below. - + Install Pillow with :command:`pip`:: $ pip install Pillow @@ -95,7 +95,7 @@ External Libraries use Pillow's basic features. .. note:: - + There are scripts to install the dependencies for some operating systems included in the ``depends`` directory. @@ -216,7 +216,7 @@ Building on Windows We don't recommend trying to build on Windows. It is a maze of twisty passages, mostly dead ends. There are build scripts and notes for the -Windows build in the ``winbuild`` directory. +Windows build in the ``winbuild`` directory. Building on FreeBSD ^^^^^^^^^^^^^^^^^^^ @@ -288,7 +288,7 @@ current versions of Linux, OS X, and Windows. +----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ |**Operating system** |**Supported**|**Tested Python versions** |**Latest tested Pillow version**|**Tested processors** | +----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ -| Mac OS X 10.11 El Capitan |Yes | 2.7,3.3,3.4,3.5 | 3.0.0 |x86-64 | +| Mac OS X 10.11 El Capitan |Yes | 2.7,3.3,3.4,3.5 | 3.1.0 |x86-64 | +----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ | Mac OS X 10.10 Yosemite |Yes | 2.7,3.3,3.4 | 3.0.0 |x86-64 | +----------------------------------+-------------+------------------------------+--------------------------------+-----------------------+ From ae453aa18b66af54e7ff716f4ccb33adca60afd4 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 2 Feb 2016 05:46:26 -0800 Subject: [PATCH 0934/1037] PCD decoder overruns the shuffle buffer, Fixes #568 --- Tests/images/hopper.pcd | Bin 0 -> 788480 bytes Tests/test_file_pcd.py | 18 ++++++++++++++++++ libImaging/PcdDecode.c | 4 ++-- 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 Tests/images/hopper.pcd create mode 100644 Tests/test_file_pcd.py diff --git a/Tests/images/hopper.pcd b/Tests/images/hopper.pcd new file mode 100644 index 0000000000000000000000000000000000000000..8ffaf717e80402db14984e5e627908242aadbfee GIT binary patch literal 788480 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