mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-10-30 23:47:27 +03:00 
			
		
		
		
	Merge branch 'master' into resample-roi
This commit is contained in:
		
						commit
						09a2e1641b
					
				
							
								
								
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							|  | @ -1 +1,2 @@ | |||
| *.ppm binary | ||||
| *.container binary | ||||
|  |  | |||
							
								
								
									
										4
									
								
								.github/ISSUE_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/ISSUE_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							|  | @ -6,7 +6,9 @@ | |||
| 
 | ||||
| ### What versions of Pillow and Python are you using? | ||||
| 
 | ||||
| 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. If you are using a framework such as plone, django, or buildout, try to replicate the issue just using Pillow.  | ||||
| Please include **code** that reproduces the issue and whenever possible, an **image** that demonstrates the issue. Please upload images to GitHub, not to third-party file hosting sites. If necessary, add the image to a zip or tar archive. | ||||
| 
 | ||||
| The best reproductions are self-contained scripts with minimal dependencies. If you are using a framework such as plone, Django, or buildout, try to replicate the issue just using Pillow.  | ||||
| 
 | ||||
| ```python | ||||
| code goes here | ||||
|  |  | |||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							|  | @ -34,6 +34,9 @@ htmlcov/ | |||
| nosetests.xml | ||||
| coverage.xml | ||||
| 
 | ||||
| # Test files | ||||
| test_images | ||||
| 
 | ||||
| # Translations | ||||
| *.mo | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										133
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										133
									
								
								.travis.yml
									
									
									
									
									
								
							|  | @ -5,46 +5,40 @@ notifications: | |||
| 
 | ||||
| # Run slow PyPy* first, to give them a headstart and reduce waiting time. | ||||
| # Run latest 3.x and 2.x next, to get quick compatibility results. | ||||
| # Then run the remainder. | ||||
| python: | ||||
|   - "pypy" | ||||
|   - "pypy3" | ||||
|   - 3.5 | ||||
|   - 2.7 | ||||
|   - "2.7_with_system_site_packages" # For PyQt4 | ||||
|   - 3.3 | ||||
|   - 3.4 | ||||
|   - nightly | ||||
| # Then run the remainder, with fastest Docker jobs last. | ||||
| 
 | ||||
| matrix: | ||||
|   fast_finish: true | ||||
|   include: | ||||
|     - python: "pypy-5.7.1" | ||||
|     - python: "pypy3.3-5.2-alpha1" | ||||
|     - python: '3.6' | ||||
|     - python: '2.7' | ||||
|     - python:  "2.7_with_system_site_packages" # For PyQt4 | ||||
|     - python: '3.5' | ||||
|     - python: '3.4' | ||||
|     - python: '3.3' | ||||
|     - env: DOCKER="alpine" | ||||
|     - env: DOCKER="arch" # contains PyQt5 | ||||
|     - env: DOCKER="ubuntu-trusty-x86" | ||||
|     - env: DOCKER="ubuntu-xenial-amd64" | ||||
|     - env: DOCKER="ubuntu-precise-amd64" | ||||
|     - env: DOCKER="debian-stretch-x86" | ||||
|     - env: DOCKER="centos-6-amd64" | ||||
|     - env: DOCKER="amazon-amd64" | ||||
| 
 | ||||
| dist: trusty | ||||
| 
 | ||||
| sudo: required | ||||
| 
 | ||||
| services: | ||||
|   - docker | ||||
| 
 | ||||
| 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" | ||||
|   - "travis_retry pip install check-manifest" | ||||
|     # Pyroma tests sometimes hang on PyPy; skip | ||||
|   - if [ $TRAVIS_PYTHON_VERSION != "pypy" ]; then travis_retry pip install pyroma; fi | ||||
|   - if [ "$DOCKER" == "" ]; then .travis/install.sh; fi | ||||
| 
 | ||||
|   - "travis_retry pip install coverage" | ||||
| 
 | ||||
|   # docs only on python 2.7 | ||||
|   - if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then travis_retry pip install -r requirements.txt ; fi | ||||
| 
 | ||||
|   # clean checkout for manifest | ||||
|   - mkdir /tmp/check-manifest && cp -a . /tmp/check-manifest | ||||
| 
 | ||||
|   # webp | ||||
|   - pushd depends && ./install_webp.sh && popd | ||||
| 
 | ||||
|   # openjpeg | ||||
|   - pushd depends && ./install_openjpeg.sh && popd | ||||
| 
 | ||||
|   # libimagequant | ||||
|   - pushd depends && ./install_imagequant.sh && popd | ||||
|    | ||||
|   # extra test images | ||||
|   - pushd depends && ./install_extra_test_images.sh && popd   | ||||
|    | ||||
| before_install: | ||||
|   - if [ "$DOCKER" ]; then docker pull pythonpillow/$DOCKER; fi | ||||
| 
 | ||||
| before_script: | ||||
| # Qt needs a display for some of the tests, and it's only run on the system site packages install | ||||
|  | @ -52,59 +46,17 @@ before_script: | |||
|   - "sh -e /etc/init.d/xvfb start" | ||||
| 
 | ||||
| script: | ||||
|   - coverage erase | ||||
|   - 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 | ||||
|   - pushd /tmp/check-manifest && check-manifest --ignore ".coveragerc,.editorconfig,*.yml,*.yaml,tox.ini" && popd | ||||
| 
 | ||||
|   # Docs | ||||
|   - if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then make install && make doccheck; fi | ||||
|   - | | ||||
|        if [ "$DOCKER" == "" ]; then | ||||
|           .travis/script.sh | ||||
|        else | ||||
|           # the Pillow user in the docker container is UID 1000 | ||||
|           sudo chown -R 1000 $TRAVIS_BUILD_DIR | ||||
|           docker run -v $TRAVIS_BUILD_DIR:/Pillow pythonpillow/$DOCKER | ||||
|        fi | ||||
| 
 | ||||
| after_success: | ||||
|    # gather the coverage data | ||||
|   - travis_retry sudo apt-get -qq install lcov | ||||
|   - lcov --capture --directory . -b . --output-file coverage.info | ||||
|    # filter to remove system headers | ||||
|   - lcov --remove coverage.info '/usr/*' -o coverage.filtered.info | ||||
|    # convert to json | ||||
|   - travis_retry gem install coveralls-lcov | ||||
|   - coveralls-lcov -v -n coverage.filtered.info > coverage.c.json | ||||
| 
 | ||||
|   - coverage report | ||||
|   - travis_retry pip install coveralls-merge | ||||
|   - coveralls-merge coverage.c.json | ||||
| 
 | ||||
|   - travis_retry pip install pep8 pyflakes | ||||
|   - pep8 --statistics --count PIL/*.py | ||||
|   - pep8 --statistics --count Tests/*.py | ||||
|   - pyflakes *.py       | tee >(wc -l) | ||||
|   - pyflakes PIL/*.py   | tee >(wc -l) | ||||
|   - pyflakes Tests/*.py | tee >(wc -l) | ||||
| 
 | ||||
|     # 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 depends/diffcover-install.sh; fi | ||||
|   - if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then depends/diffcover-run.sh; fi | ||||
| 
 | ||||
|   # after_all | ||||
|   - | | ||||
|       if [ "$TRAVIS_REPO_SLUG" = "python-pillow/Pillow" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ]; then | ||||
|         curl -Lo travis_after_all.py https://raw.github.com/dmakhno/travis_after_all/master/travis_after_all.py | ||||
|         python travis_after_all.py | ||||
|         export $(cat .to_export_back) | ||||
|         if [ "$BUILD_LEADER" = "YES" ]; then | ||||
|           if [ "$BUILD_AGGREGATE_STATUS" = "others_succeeded" ]; then | ||||
|             echo "All jobs succeded! Triggering macOS build..." | ||||
|             # Trigger a macOS build at the pillow-wheels repo | ||||
|             ./build_children.sh | ||||
|           else | ||||
|             echo "Some jobs failed" | ||||
|           fi | ||||
|         fi | ||||
|       fi | ||||
|   - .travis/after_success.sh | ||||
| 
 | ||||
| after_failure: | ||||
|   - | | ||||
|  | @ -127,11 +79,6 @@ after_script: | |||
|         echo leader=$BUILD_LEADER status=$BUILD_AGGREGATE_STATUS | ||||
|       fi | ||||
| 
 | ||||
| matrix: | ||||
|   fast_finish: true | ||||
|   allow_failures: | ||||
|     - python: nightly | ||||
| 
 | ||||
| env: | ||||
|   global: | ||||
|     # travis encrypt AUTH_TOKEN= | ||||
|  |  | |||
							
								
								
									
										49
									
								
								.travis/after_success.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										49
									
								
								.travis/after_success.sh
									
									
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,49 @@ | |||
| #!/bin/bash | ||||
| 
 | ||||
| # gather the coverage data | ||||
| sudo apt-get -qq install lcov | ||||
| lcov --capture --directory . -b . --output-file coverage.info | ||||
| #  filter to remove system headers | ||||
| lcov --remove coverage.info '/usr/*' -o coverage.filtered.info | ||||
| #  convert to json | ||||
| gem install coveralls-lcov | ||||
| coveralls-lcov -v -n coverage.filtered.info > coverage.c.json | ||||
| 
 | ||||
| coverage report | ||||
| pip install codecov | ||||
| pip install coveralls-merge | ||||
| coveralls-merge coverage.c.json | ||||
| codecov | ||||
| 
 | ||||
| if [ "$DOCKER" == "" ]; then | ||||
| 	pip install pep8 pyflakes | ||||
| 	pep8 --statistics --count PIL/*.py | ||||
| 	pep8 --statistics --count Tests/*.py | ||||
| 	pyflakes *.py       | tee >(wc -l) | ||||
| 	pyflakes PIL/*.py   | tee >(wc -l) | ||||
| 	pyflakes Tests/*.py | tee >(wc -l) | ||||
| fi | ||||
| 
 | ||||
| if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ] && [ "$DOCKER" == "" ]; then | ||||
| 	# Coverage and quality reports on just the latest diff. | ||||
| 	# (Installation is very slow on Py3, so just do it for Py2.) | ||||
| 	depends/diffcover-install.sh | ||||
| 	depends/diffcover-run.sh | ||||
| fi | ||||
| 
 | ||||
| # after_all | ||||
| 
 | ||||
| if [ "$TRAVIS_REPO_SLUG" = "python-pillow/Pillow" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ]; then | ||||
|     curl -Lo travis_after_all.py https://raw.github.com/dmakhno/travis_after_all/master/travis_after_all.py | ||||
|     python travis_after_all.py | ||||
|     export $(cat .to_export_back) | ||||
|     if [ "$BUILD_LEADER" = "YES" ]; then | ||||
|         if [ "$BUILD_AGGREGATE_STATUS" = "others_succeeded" ]; then | ||||
|             echo "All jobs succeeded! Triggering macOS build..." | ||||
|             # Trigger a macOS build at the pillow-wheels repo | ||||
|             ./build_children.sh | ||||
|         else | ||||
|             echo "Some jobs failed" | ||||
|         fi | ||||
|     fi | ||||
| fi | ||||
							
								
								
									
										34
									
								
								.travis/install.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										34
									
								
								.travis/install.sh
									
									
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,34 @@ | |||
| #!/bin/bash | ||||
| 
 | ||||
| set -e | ||||
| 
 | ||||
| sudo apt-get update | ||||
| sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-tk\ | ||||
| 			 python-qt4 ghostscript libffi-dev libjpeg-turbo-progs cmake imagemagick\ | ||||
|              libharfbuzz-dev libfribidi-dev | ||||
| 
 | ||||
| pip install cffi | ||||
| pip install nose | ||||
| pip install check-manifest | ||||
| pip install olefile | ||||
| pip install pyroma | ||||
| pip install coverage | ||||
| 
 | ||||
| # docs only on Python 2.7 | ||||
| if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then pip install -r requirements.txt ; fi | ||||
| 
 | ||||
| # clean checkout for manifest | ||||
| mkdir /tmp/check-manifest && cp -a . /tmp/check-manifest | ||||
| 
 | ||||
| # webp | ||||
| pushd depends && ./install_webp.sh && popd | ||||
| 
 | ||||
| # openjpeg | ||||
| pushd depends && ./install_openjpeg.sh && popd | ||||
| 
 | ||||
| # libimagequant | ||||
| pushd depends && ./install_imagequant.sh && popd | ||||
| 
 | ||||
| # extra test images | ||||
| pushd depends && ./install_extra_test_images.sh && popd | ||||
| 
 | ||||
							
								
								
									
										14
									
								
								.travis/script.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										14
									
								
								.travis/script.sh
									
									
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,14 @@ | |||
| #!/bin/bash | ||||
| 
 | ||||
| set -e | ||||
| 
 | ||||
| coverage erase | ||||
| 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 | ||||
| pushd /tmp/check-manifest && check-manifest --ignore ".coveragerc,.editorconfig,*.yml,*.yaml,tox.ini" && popd | ||||
| 
 | ||||
| # Docs | ||||
| if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then make install && make doccheck; fi | ||||
							
								
								
									
										771
									
								
								CHANGES.rst
									
									
									
									
									
								
							
							
						
						
									
										771
									
								
								CHANGES.rst
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										4
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								LICENSE
									
									
									
									
									
								
							|  | @ -5,9 +5,9 @@ The Python Imaging Library (PIL) is | |||
| 
 | ||||
| Pillow is the friendly PIL fork. It is | ||||
| 
 | ||||
|     Copyright © 2016 by Alex Clark and contributors | ||||
|     Copyright © 2010-2017 by Alex Clark and contributors | ||||
| 
 | ||||
| Like PIL, Pillow is licensed under the MIT-like open source PIL Software License: | ||||
| Like PIL, Pillow is licensed under the open source PIL Software License: | ||||
| 
 | ||||
| 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: | ||||
| 
 | ||||
|  |  | |||
|  | @ -23,6 +23,8 @@ prune docs/_static | |||
| exclude .coveragerc | ||||
| exclude .editorconfig | ||||
| exclude .landscape.yaml | ||||
| exclude .travis | ||||
| exclude .travis/* | ||||
| exclude appveyor.yml | ||||
| exclude build_children.sh | ||||
| exclude tox.ini | ||||
|  |  | |||
							
								
								
									
										13
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								Makefile
									
									
									
									
									
								
							|  | @ -58,6 +58,13 @@ install: | |||
| 	python setup.py install | ||||
| 	python selftest.py --installed | ||||
| 
 | ||||
| debug: | ||||
| # make a debug version if we don't have a -dbg python. Leaves in symbols
 | ||||
| # for our stuff, kills optimization, and redirects to dev null so we 
 | ||||
| # see any build failures.
 | ||||
| 	make clean > /dev/null | ||||
| 	CFLAGS='-g -O0' python setup.py build_ext install > /dev/null | ||||
| 
 | ||||
| install-req: | ||||
| 	pip install -r requirements.txt | ||||
| 
 | ||||
|  | @ -77,7 +84,7 @@ release-test: | |||
| 	viewdoc | ||||
| 
 | ||||
| sdist: | ||||
| 	python setup.py sdist --format=gztar,zip | ||||
| 	python setup.py sdist --format=gztar | ||||
| 
 | ||||
| test: | ||||
| 	python test-installed.py | ||||
|  | @ -88,10 +95,10 @@ upload-test: | |||
| #       username:
 | ||||
| #       password:
 | ||||
| #       repository = http://test.pythonpackages.com
 | ||||
| 	python setup.py sdist --format=gztar,zip upload -r test | ||||
| 	python setup.py sdist --format=gztar upload -r test | ||||
| 
 | ||||
| upload: | ||||
| 	python setup.py sdist --format=gztar,zip upload | ||||
| 	python setup.py sdist --format=gztar upload | ||||
| 
 | ||||
| readme: | ||||
| 	viewdoc | ||||
|  |  | |||
|  | @ -17,8 +17,9 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import Image | ||||
| from PIL import FontFile | ||||
| from __future__ import print_function | ||||
| 
 | ||||
| from . import Image, FontFile | ||||
| 
 | ||||
| 
 | ||||
| # -------------------------------------------------------------------- | ||||
|  | @ -119,9 +120,9 @@ class BdfFontFile(FontFile.FontFile): | |||
| 
 | ||||
|         # fontname = ";".join(font[1:]) | ||||
| 
 | ||||
|         # print "#", fontname | ||||
|         # print("#", fontname) | ||||
|         # for i in comments: | ||||
|         #       print "#", i | ||||
|         #       print("#", i) | ||||
| 
 | ||||
|         while True: | ||||
|             c = bdf_char(fp) | ||||
|  |  | |||
|  | @ -24,18 +24,13 @@ | |||
| # | ||||
| 
 | ||||
| 
 | ||||
| from PIL import Image, ImageFile, ImagePalette, _binary | ||||
| from . import Image, ImageFile, ImagePalette | ||||
| from ._binary import i8, i16le as i16, i32le as i32, \ | ||||
|                      o8, o16le as o16, o32le as o32 | ||||
| import math | ||||
| 
 | ||||
| __version__ = "0.7" | ||||
| 
 | ||||
| i8 = _binary.i8 | ||||
| i16 = _binary.i16le | ||||
| i32 = _binary.i32le | ||||
| o8 = _binary.o8 | ||||
| o16 = _binary.o16le | ||||
| o32 = _binary.o32le | ||||
| 
 | ||||
| # | ||||
| # -------------------------------------------------------------------- | ||||
| # Read BMP file | ||||
|  | @ -136,7 +131,7 @@ 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), (0xff000000, 0xff0000, 0xff00, 0x0) ], | ||||
|                 32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0), (0xff000000, 0xff0000, 0xff00, 0x0)], | ||||
|                 24: [(0xff0000, 0xff00, 0xff)], | ||||
|                 16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)] | ||||
|             } | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import Image, ImageFile | ||||
| from . import Image, ImageFile | ||||
| 
 | ||||
| _handler = None | ||||
| 
 | ||||
|  | @ -40,7 +40,7 @@ class BufrStubImageFile(ImageFile.StubImageFile): | |||
| 
 | ||||
|         offset = self.fp.tell() | ||||
| 
 | ||||
|         if not _accept(self.fp.read(8)): | ||||
|         if not _accept(self.fp.read(4)): | ||||
|             raise SyntaxError("Not a BUFR file") | ||||
| 
 | ||||
|         self.fp.seek(offset) | ||||
|  |  | |||
|  | @ -16,18 +16,16 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from __future__ import print_function | ||||
| 
 | ||||
| from PIL import Image, BmpImagePlugin, _binary | ||||
| from . import Image, BmpImagePlugin | ||||
| from ._binary import i8, i16le as i16, i32le as i32 | ||||
| 
 | ||||
| __version__ = "0.1" | ||||
| 
 | ||||
| # | ||||
| # -------------------------------------------------------------------- | ||||
| 
 | ||||
| i8 = _binary.i8 | ||||
| i16 = _binary.i16le | ||||
| i32 = _binary.i32le | ||||
| 
 | ||||
| 
 | ||||
| def _accept(prefix): | ||||
|     return prefix[:4] == b"\0\0\2\0" | ||||
|  | @ -58,14 +56,14 @@ class CurImageFile(BmpImagePlugin.BmpImageFile): | |||
|                 m = s | ||||
|             elif i8(s[0]) > i8(m[0]) and i8(s[1]) > i8(m[1]): | ||||
|                 m = s | ||||
|             # print "width", i8(s[0]) | ||||
|             # print "height", i8(s[1]) | ||||
|             # print "colors", i8(s[2]) | ||||
|             # print "reserved", i8(s[3]) | ||||
|             # print "hotspot x", i16(s[4:]) | ||||
|             # print "hotspot y", i16(s[6:]) | ||||
|             # print "bytes", i32(s[8:]) | ||||
|             # print "offset", i32(s[12:]) | ||||
|             # print("width", i8(s[0])) | ||||
|             # print("height", i8(s[1])) | ||||
|             # print("colors", i8(s[2])) | ||||
|             # print("reserved", i8(s[3])) | ||||
|             # print("hotspot x", i16(s[4:])) | ||||
|             # 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") | ||||
| 
 | ||||
|  |  | |||
|  | @ -21,15 +21,14 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import Image, _binary | ||||
| from PIL.PcxImagePlugin import PcxImageFile | ||||
| from . import Image | ||||
| from ._binary import i32le as i32 | ||||
| from .PcxImagePlugin import PcxImageFile | ||||
| 
 | ||||
| __version__ = "0.2" | ||||
| 
 | ||||
| MAGIC = 0x3ADE68B1  # QUIZ: what's this value, then? | ||||
| 
 | ||||
| i32 = _binary.i32le | ||||
| 
 | ||||
| 
 | ||||
| def _accept(prefix): | ||||
|     return len(prefix) >= 4 and i32(prefix) == MAGIC | ||||
|  | @ -42,7 +41,8 @@ class DcxImageFile(PcxImageFile): | |||
| 
 | ||||
|     format = "DCX" | ||||
|     format_description = "Intel DCX" | ||||
| 
 | ||||
|     _close_exclusive_fp_after_loading = False | ||||
|      | ||||
|     def _open(self): | ||||
| 
 | ||||
|         # Header | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ Full text of the CC0 license: | |||
| 
 | ||||
| import struct | ||||
| from io import BytesIO | ||||
| from PIL import Image, ImageFile | ||||
| from . import Image, ImageFile | ||||
| 
 | ||||
| 
 | ||||
| # Magic ("DDS ") | ||||
|  |  | |||
|  | @ -22,17 +22,16 @@ | |||
| 
 | ||||
| import re | ||||
| import io | ||||
| import os | ||||
| import sys | ||||
| from PIL import Image, ImageFile, _binary | ||||
| from . import Image, ImageFile | ||||
| from ._binary import i32le as i32 | ||||
| 
 | ||||
| __version__ = "0.5" | ||||
| 
 | ||||
| # | ||||
| # -------------------------------------------------------------------- | ||||
| 
 | ||||
| i32 = _binary.i32le | ||||
| o32 = _binary.o32le | ||||
| 
 | ||||
| split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$") | ||||
| field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$") | ||||
| 
 | ||||
|  | @ -59,8 +58,8 @@ def has_ghostscript(): | |||
|     if not sys.platform.startswith('win'): | ||||
|         import subprocess | ||||
|         try: | ||||
|             gs = subprocess.Popen(['gs', '--version'], stdout=subprocess.PIPE) | ||||
|             gs.stdout.read() | ||||
|             with open(os.devnull, 'wb') as devnull: | ||||
|                 subprocess.check_call(['gs', '--version'], stdout=devnull) | ||||
|             return True | ||||
|         except OSError: | ||||
|             # no ghostscript | ||||
|  | @ -85,7 +84,6 @@ def Ghostscript(tile, size, fp, scale=1): | |||
|            float((72.0 * size[1]) / (bbox[3]-bbox[1]))) | ||||
|     # print("Ghostscript", scale, size, orig_size, bbox, orig_bbox, res) | ||||
| 
 | ||||
|     import os | ||||
|     import subprocess | ||||
|     import tempfile | ||||
| 
 | ||||
|  | @ -123,6 +121,7 @@ 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) | ||||
|                "-dBATCH",                    # exit after processing | ||||
|                "-dNOPAUSE",                  # don't pause between pages, | ||||
|                "-dSAFER",                    # safe mode | ||||
|                "-sDEVICE=ppmraw",            # ppm driver | ||||
|  | @ -139,12 +138,8 @@ def Ghostscript(tile, size, fp, scale=1): | |||
| 
 | ||||
|     # push data through ghostscript | ||||
|     try: | ||||
|         gs = subprocess.Popen(command, stdin=subprocess.PIPE, | ||||
|                               stdout=subprocess.PIPE) | ||||
|         gs.stdin.close() | ||||
|         status = gs.wait() | ||||
|         if status: | ||||
|             raise IOError("gs failed (status %d)" % status) | ||||
|         with open(os.devnull, 'w+b') as devnull: | ||||
|             subprocess.check_call(command, stdin=devnull, stdout=devnull) | ||||
|         im = Image.open(outfile) | ||||
|         im.load() | ||||
|     finally: | ||||
|  | @ -323,7 +318,7 @@ class EpsImageFile(ImageFile.ImageFile): | |||
|             # EPS can contain binary data | ||||
|             # or start directly with latin coding | ||||
|             # more info see: | ||||
|             # http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf | ||||
|             # https://web.archive.org/web/20160528181353/http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf | ||||
|             offset = i32(s[4:8]) | ||||
|             length = i32(s[8:12]) | ||||
|         else: | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import Image, ImageFile | ||||
| from . import Image, ImageFile | ||||
| 
 | ||||
| _handler = None | ||||
| 
 | ||||
|  |  | |||
|  | @ -16,15 +16,11 @@ | |||
| # | ||||
| 
 | ||||
| 
 | ||||
| from PIL import Image, ImageFile, ImagePalette, _binary | ||||
| from . import Image, ImageFile, ImagePalette | ||||
| from ._binary import i8, i16le as i16, i32le as i32, o8 | ||||
| 
 | ||||
| __version__ = "0.2" | ||||
| 
 | ||||
| i8 = _binary.i8 | ||||
| i16 = _binary.i16le | ||||
| i32 = _binary.i32le | ||||
| o8 = _binary.o8 | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # decoder | ||||
|  | @ -41,7 +37,8 @@ class FliImageFile(ImageFile.ImageFile): | |||
| 
 | ||||
|     format = "FLI" | ||||
|     format_description = "Autodesk FLI/FLC Animation" | ||||
| 
 | ||||
|     _close_exclusive_fp_after_loading = False | ||||
|      | ||||
|     def _open(self): | ||||
| 
 | ||||
|         # HEAD | ||||
|  |  | |||
|  | @ -14,8 +14,10 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from __future__ import print_function | ||||
| 
 | ||||
| import os | ||||
| from PIL import Image, _binary | ||||
| from . import Image, _binary | ||||
| 
 | ||||
| WIDTH = 800 | ||||
| 
 | ||||
|  | @ -88,7 +90,7 @@ class FontFile(object): | |||
|                     x = xx | ||||
|                 s = src[0] + x0, src[1] + y0, src[2] + x0, src[3] + y0 | ||||
|                 self.bitmap.paste(im.crop(src), s) | ||||
|                 # print chr(i), dst, s | ||||
|                 # print(chr(i), dst, s) | ||||
|                 self.metrics[i] = d, dst, s | ||||
| 
 | ||||
|     def save(self, filename): | ||||
|  |  | |||
|  | @ -15,13 +15,15 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from __future__ import print_function | ||||
| 
 | ||||
| from PIL import Image, ImageFile | ||||
| from PIL.OleFileIO import i8, i32, MAGIC, OleFileIO | ||||
| from . import Image, ImageFile | ||||
| from ._binary import i32le as i32, i8 | ||||
| 
 | ||||
| import olefile | ||||
| 
 | ||||
| __version__ = "0.1" | ||||
| 
 | ||||
| 
 | ||||
| # we map from colour field tuples to (mode, rawmode) descriptors | ||||
| MODES = { | ||||
|     # opacity | ||||
|  | @ -42,7 +44,7 @@ MODES = { | |||
| # -------------------------------------------------------------------- | ||||
| 
 | ||||
| def _accept(prefix): | ||||
|     return prefix[:8] == MAGIC | ||||
|     return prefix[:8] == olefile.MAGIC | ||||
| 
 | ||||
| 
 | ||||
| ## | ||||
|  | @ -59,7 +61,7 @@ class FpxImageFile(ImageFile.ImageFile): | |||
|         # to be a FlashPix file | ||||
| 
 | ||||
|         try: | ||||
|             self.ole = OleFileIO(self.fp) | ||||
|             self.ole = olefile.OleFileIO(self.fp) | ||||
|         except IOError: | ||||
|             raise SyntaxError("not an FPX file; invalid OLE file") | ||||
| 
 | ||||
|  | @ -112,7 +114,7 @@ class FpxImageFile(ImageFile.ImageFile): | |||
|             if id in prop: | ||||
|                 self.jpeg[i] = prop[id] | ||||
| 
 | ||||
|         # print len(self.jpeg), "tables loaded" | ||||
|         # print(len(self.jpeg), "tables loaded") | ||||
| 
 | ||||
|         self._open_subimage(1, self.maxid) | ||||
| 
 | ||||
|  | @ -141,7 +143,7 @@ class FpxImageFile(ImageFile.ImageFile): | |||
|         offset = i32(s, 28) | ||||
|         length = i32(s, 32) | ||||
| 
 | ||||
|         # print size, self.mode, self.rawmode | ||||
|         # print(size, self.mode, self.rawmode) | ||||
| 
 | ||||
|         if size != self.size: | ||||
|             raise IOError("subimage mismatch") | ||||
|  |  | |||
|  | @ -42,7 +42,7 @@ Note: All data is stored in little-Endian (Intel) byte order. | |||
| 
 | ||||
| import struct | ||||
| from io import BytesIO | ||||
| from PIL import Image, ImageFile | ||||
| from . import Image, ImageFile | ||||
| 
 | ||||
| 
 | ||||
| MAGIC = b"FTEX" | ||||
|  |  | |||
|  | @ -24,9 +24,8 @@ | |||
| # 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 | ||||
| 
 | ||||
| i32 = _binary.i32be | ||||
| from . import Image, ImageFile | ||||
| from ._binary import i32be as i32 | ||||
| 
 | ||||
| 
 | ||||
| def _accept(prefix): | ||||
|  |  | |||
|  | @ -23,8 +23,9 @@ | |||
| # purposes only. | ||||
| 
 | ||||
| 
 | ||||
| from PIL import ImageFile, ImagePalette, _binary | ||||
| from PIL._util import isPath | ||||
| from . import ImageFile, ImagePalette | ||||
| from ._binary import i16be as i16 | ||||
| from ._util import isPath | ||||
| 
 | ||||
| __version__ = "0.1" | ||||
| 
 | ||||
|  | @ -34,8 +35,6 @@ except ImportError: | |||
|     import __builtin__ | ||||
|     builtins = __builtin__ | ||||
| 
 | ||||
| i16 = _binary.i16be | ||||
| 
 | ||||
| 
 | ||||
| ## | ||||
| # Image plugin for the GD uncompressed format.  Note that this format | ||||
|  |  | |||
|  | @ -24,21 +24,14 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import Image, ImageFile, ImagePalette, \ | ||||
|                 ImageChops, ImageSequence, _binary | ||||
| from . import Image, ImageFile, ImagePalette, ImageChops, ImageSequence | ||||
| from ._binary import i8, i16le as i16, o8, o16le as o16 | ||||
| 
 | ||||
| import itertools | ||||
| 
 | ||||
| __version__ = "0.9" | ||||
| 
 | ||||
| 
 | ||||
| # -------------------------------------------------------------------- | ||||
| # Helpers | ||||
| 
 | ||||
| i8 = _binary.i8 | ||||
| i16 = _binary.i16le | ||||
| o8 = _binary.o8 | ||||
| o16 = _binary.o16le | ||||
| 
 | ||||
| 
 | ||||
| # -------------------------------------------------------------------- | ||||
| # Identify/read GIF files | ||||
| 
 | ||||
|  | @ -54,6 +47,8 @@ class GifImageFile(ImageFile.ImageFile): | |||
| 
 | ||||
|     format = "GIF" | ||||
|     format_description = "Compuserve GIF" | ||||
|     _close_exclusive_fp_after_loading = False | ||||
|      | ||||
|     global_palette = None | ||||
| 
 | ||||
|     def data(self): | ||||
|  | @ -262,7 +257,7 @@ class GifImageFile(ImageFile.ImageFile): | |||
| 
 | ||||
|             # only dispose the extent in this frame | ||||
|             if self.dispose: | ||||
|                 self.dispose = self.dispose.crop(self.dispose_extent) | ||||
|                 self.dispose = self._crop(self.dispose, self.dispose_extent) | ||||
|         except (AttributeError, KeyError): | ||||
|             pass | ||||
| 
 | ||||
|  | @ -285,7 +280,7 @@ class GifImageFile(ImageFile.ImageFile): | |||
|         if self._prev_im and self.disposal_method == 1: | ||||
|             # we do this by pasting the updated area onto the previous | ||||
|             # frame which we then use as the current image content | ||||
|             updated = self.im.crop(self.dispose_extent) | ||||
|             updated = self._crop(self.im, self.dispose_extent) | ||||
|             self._prev_im.paste(updated, self.dispose_extent, | ||||
|                                 updated.convert('RGBA')) | ||||
|             self.im = self._prev_im | ||||
|  | @ -294,52 +289,168 @@ class GifImageFile(ImageFile.ImageFile): | |||
| # -------------------------------------------------------------------- | ||||
| # Write GIF files | ||||
| 
 | ||||
| try: | ||||
|     import _imaging_gif | ||||
| except ImportError: | ||||
|     _imaging_gif = None | ||||
| 
 | ||||
| RAWMODE = { | ||||
|     "1": "L", | ||||
|     "L": "L", | ||||
|     "P": "P", | ||||
|     "P": "P" | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| def _convert_mode(im, initial_call=False): | ||||
|     # convert on the fly (EXPERIMENTAL -- I'm not sure PIL | ||||
|     # should automatically convert images on save...) | ||||
| def _normalize_mode(im, initial_call=False): | ||||
|     """ | ||||
|     Takes an image (or frame), returns an image in a mode that is appropriate | ||||
|     for saving in a Gif. | ||||
| 
 | ||||
|     It may return the original image, or it may return an image converted to | ||||
|     palette or 'L' mode. | ||||
| 
 | ||||
|     UNDONE: What is the point of mucking with the initial call palette, for | ||||
|     an image that shouldn't have a palette, or it would be a mode 'P' and | ||||
|     get returned in the RAWMODE clause. | ||||
| 
 | ||||
|     :param im: Image object | ||||
|     :param initial_call: Default false, set to true for a single frame. | ||||
|     :returns: Image object | ||||
|     """ | ||||
|     if im.mode in RAWMODE: | ||||
|         im.load() | ||||
|         return im | ||||
|     if Image.getmodebase(im.mode) == "RGB": | ||||
|         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) | ||||
|             return im.convert("P", palette=Image.ADAPTIVE, colors=palette_size) | ||||
|         else: | ||||
|             return im.convert("P") | ||||
|     return im.convert("L") | ||||
| 
 | ||||
| 
 | ||||
| def _normalize_palette(im, palette, info): | ||||
|     """ | ||||
|     Normalizes the palette for image. | ||||
|       - Sets the palette to the incoming palette, if provided. | ||||
|       - Ensures that there's a palette for L mode images | ||||
|       - Optimizes the palette if necessary/desired. | ||||
| 
 | ||||
|     :param im: Image object | ||||
|     :param palette: bytes object containing the source palette, or .... | ||||
|     :param info: encoderinfo | ||||
|     :returns: Image object | ||||
|     """ | ||||
|     source_palette = None | ||||
|     if palette: | ||||
|         # a bytes palette | ||||
|         if isinstance(palette, (bytes, bytearray, list)): | ||||
|             source_palette = bytearray(palette[:768]) | ||||
|         if isinstance(palette, ImagePalette.ImagePalette): | ||||
|             source_palette = bytearray(itertools.chain.from_iterable( | ||||
|                                 zip(palette.palette[:256], | ||||
|                                     palette.palette[256:512], | ||||
|                                     palette.palette[512:768]))) | ||||
| 
 | ||||
|     if im.mode == "P": | ||||
|         if not source_palette: | ||||
|             source_palette = im.im.getpalette("RGB")[:768] | ||||
|     else:  # L-mode | ||||
|         if not source_palette: | ||||
|             source_palette = bytearray(i//3 for i in range(768)) | ||||
|         im.palette = ImagePalette.ImagePalette("RGB", | ||||
|                                                palette=source_palette) | ||||
| 
 | ||||
|     used_palette_colors = _get_optimize(im, info) | ||||
|     if used_palette_colors is not None: | ||||
|         return im.remap_palette(used_palette_colors, source_palette) | ||||
| 
 | ||||
|     im.palette.palette = source_palette | ||||
|     return im | ||||
| 
 | ||||
| 
 | ||||
| def _write_single_frame(im, fp, palette): | ||||
|     im_out = _normalize_mode(im, True) | ||||
|     im_out = _normalize_palette(im_out, palette, im.encoderinfo) | ||||
| 
 | ||||
|     for s in _get_global_header(im_out, im.encoderinfo): | ||||
|         fp.write(s) | ||||
| 
 | ||||
|     # local image header | ||||
|     flags = 0 | ||||
|     if get_interlace(im): | ||||
|         flags = flags | 64 | ||||
|     _write_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 | ||||
| 
 | ||||
| 
 | ||||
| def _write_multiple_frames(im, fp, palette): | ||||
| 
 | ||||
|     duration = im.encoderinfo.get("duration", None) | ||||
| 
 | ||||
|     im_frames = [] | ||||
|     frame_count = 0 | ||||
|     for imSequence in [im]+im.encoderinfo.get("append_images", []): | ||||
|         for im_frame in ImageSequence.Iterator(imSequence): | ||||
|             # a copy is required here since seek can still mutate the image | ||||
|             im_frame = _normalize_mode(im_frame.copy()) | ||||
|             im_frame = _normalize_palette(im_frame, palette, im.encoderinfo) | ||||
| 
 | ||||
|             encoderinfo = im.encoderinfo.copy() | ||||
|             if isinstance(duration, (list, tuple)): | ||||
|                 encoderinfo['duration'] = duration[frame_count] | ||||
|             frame_count += 1 | ||||
| 
 | ||||
|             if im_frames: | ||||
|                 # delta frame | ||||
|                 previous = im_frames[-1] | ||||
|                 if _get_palette_bytes(im_frame) == _get_palette_bytes(previous['im']): | ||||
|                     delta = ImageChops.subtract_modulo(im_frame, | ||||
|                                                        previous['im']) | ||||
|                 else: | ||||
|                     delta = ImageChops.subtract_modulo(im_frame.convert('RGB'), | ||||
|                                                        previous['im'].convert('RGB')) | ||||
|                 bbox = delta.getbbox() | ||||
|                 if not bbox: | ||||
|                     # This frame is identical to the previous frame | ||||
|                     if duration: | ||||
|                         previous['encoderinfo']['duration'] += encoderinfo['duration'] | ||||
|                     continue | ||||
|             else: | ||||
|                 bbox = None | ||||
|             im_frames.append({ | ||||
|                 'im': im_frame, | ||||
|                 'bbox': bbox, | ||||
|                 'encoderinfo': encoderinfo | ||||
|             }) | ||||
| 
 | ||||
|     if len(im_frames) > 1: | ||||
|         for frame_data in im_frames: | ||||
|             im_frame = frame_data['im'] | ||||
|             if not frame_data['bbox']: | ||||
|                 # global header | ||||
|                 for s in _get_global_header(im_frame, | ||||
|                                             frame_data['encoderinfo']): | ||||
|                     fp.write(s) | ||||
|                 offset = (0, 0) | ||||
|             else: | ||||
|                 # compress difference | ||||
|                 frame_data['encoderinfo']['include_color_table'] = True | ||||
| 
 | ||||
|                 im_frame = im_frame.crop(frame_data['bbox']) | ||||
|                 offset = frame_data['bbox'][:2] | ||||
|             _write_frame_data(fp, im_frame, offset, frame_data['encoderinfo']) | ||||
|         return True | ||||
| 
 | ||||
| 
 | ||||
| def _save_all(im, fp, filename): | ||||
|     _save(im, fp, filename, save_all=True) | ||||
| 
 | ||||
| 
 | ||||
| def _save(im, fp, filename, save_all=False): | ||||
| 
 | ||||
|     im.encoderinfo.update(im.info) | ||||
|     if _imaging_gif: | ||||
|         # call external driver | ||||
|         try: | ||||
|             _imaging_gif.save(im, fp, filename) | ||||
|             return | ||||
|         except IOError: | ||||
|             pass  # write uncompressed file | ||||
| 
 | ||||
|     if im.mode in RAWMODE: | ||||
|         im_out = im.copy() | ||||
|     else: | ||||
|         im_out = _convert_mode(im, True) | ||||
| 
 | ||||
|     # header | ||||
|     try: | ||||
|         palette = im.encoderinfo["palette"] | ||||
|  | @ -347,62 +458,8 @@ def _save(im, fp, filename, save_all=False): | |||
|         palette = None | ||||
|         im.encoderinfo["optimize"] = im.encoderinfo.get("optimize", True) | ||||
| 
 | ||||
|     if save_all: | ||||
|         previous = None | ||||
| 
 | ||||
|         first_frame = None | ||||
|         append_images = im.encoderinfo.get("append_images", []) | ||||
|         for imSequence in [im]+append_images: | ||||
|             for im_frame in ImageSequence.Iterator(imSequence): | ||||
|                 encoderinfo = im.encoderinfo.copy() | ||||
|                 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: | ||||
|                     # global header | ||||
|                     first_frame = getheader(im_frame, palette, encoderinfo)[0] | ||||
|                     first_frame += getdata(im_frame, (0, 0), **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() | ||||
| 
 | ||||
|                     if bbox: | ||||
|                         # compress difference | ||||
|                         encoderinfo['include_color_table'] = True | ||||
|                         for s in getdata(im_frame.crop(bbox), | ||||
|                                          bbox[:2], **encoderinfo): | ||||
|                             fp.write(s) | ||||
|                     else: | ||||
|                         # FIXME: what should we do in this case? | ||||
|                         pass | ||||
|                 previous = im_frame | ||||
|         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) | ||||
| 
 | ||||
|         flags = 0 | ||||
| 
 | ||||
|         if get_interlace(im): | ||||
|             flags = flags | 64 | ||||
| 
 | ||||
|         # 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 | ||||
|     if not save_all or not _write_multiple_frames(im, fp, palette): | ||||
|         _write_single_frame(im, fp, palette) | ||||
| 
 | ||||
|     fp.write(b";")  # end of file | ||||
| 
 | ||||
|  | @ -411,10 +468,7 @@ def _save(im, fp, filename, save_all=False): | |||
| 
 | ||||
| 
 | ||||
| def get_interlace(im): | ||||
|     try: | ||||
|         interlace = im.encoderinfo["interlace"] | ||||
|     except KeyError: | ||||
|         interlace = 1 | ||||
|     interlace = im.encoderinfo.get("interlace", 1) | ||||
| 
 | ||||
|     # workaround for @PIL153 | ||||
|     if min(im.size) < 16: | ||||
|  | @ -423,7 +477,7 @@ def get_interlace(im): | |||
|     return interlace | ||||
| 
 | ||||
| 
 | ||||
| def _get_local_header(fp, im, offset, flags): | ||||
| def _write_local_header(fp, im, offset, flags): | ||||
|     transparent_color_exists = False | ||||
|     try: | ||||
|         transparency = im.encoderinfo["transparency"] | ||||
|  | @ -434,18 +488,13 @@ def _get_local_header(fp, im, offset, flags): | |||
|         # optimize the block away if transparent color is not used | ||||
|         transparent_color_exists = True | ||||
| 
 | ||||
|         if _get_optimize(im, im.encoderinfo): | ||||
|             used_palette_colors = _get_used_palette_colors(im) | ||||
| 
 | ||||
|         used_palette_colors = _get_optimize(im, im.encoderinfo) | ||||
|         if used_palette_colors is not None: | ||||
|             # 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 | ||||
|             try: | ||||
|                 transparency = used_palette_colors.index(transparency) | ||||
|             except ValueError: | ||||
|                 transparent_color_exists = False | ||||
| 
 | ||||
|     if "duration" in im.encoderinfo: | ||||
|         duration = int(im.encoderinfo["duration"] / 10) | ||||
|  | @ -482,11 +531,8 @@ def _get_local_header(fp, im, offset, flags): | |||
|                  o8(0)) | ||||
|     include_color_table = im.encoderinfo.get('include_color_table') | ||||
|     if include_color_table: | ||||
|         try: | ||||
|             palette = im.encoderinfo["palette"] | ||||
|         except KeyError: | ||||
|             palette = None | ||||
|         palette_bytes = _get_palette_bytes(im, palette, im.encoderinfo)[0] | ||||
|         palette = im.encoderinfo.get("palette", None) | ||||
|         palette_bytes = _get_palette_bytes(im) | ||||
|         color_table_size = _get_color_table_size(palette_bytes) | ||||
|         if color_table_size: | ||||
|             flags = flags | 128               # local color table flag | ||||
|  | @ -505,6 +551,8 @@ def _get_local_header(fp, im, offset, flags): | |||
| 
 | ||||
| def _save_netpbm(im, fp, filename): | ||||
| 
 | ||||
|     # Unused by default. | ||||
|     # To use, uncomment the register_save call at the end of the file. | ||||
|     # | ||||
|     # If you need real GIF compression and/or RGB quantization, you | ||||
|     # can use the external NETPBM/PBMPLUS utilities.  See comments | ||||
|  | @ -512,25 +560,21 @@ def _save_netpbm(im, fp, filename): | |||
| 
 | ||||
|     import os | ||||
|     from subprocess import Popen, check_call, PIPE, CalledProcessError | ||||
|     import tempfile | ||||
|     file = im._dump() | ||||
| 
 | ||||
|     if im.mode != "RGB": | ||||
|         with open(filename, 'wb') as f: | ||||
|             stderr = tempfile.TemporaryFile() | ||||
|             check_call(["ppmtogif", file], stdout=f, stderr=stderr) | ||||
|     else: | ||||
|         with open(filename, 'wb') as f: | ||||
| 
 | ||||
|     with open(filename, 'wb') as f: | ||||
|         if im.mode != "RGB": | ||||
|             with open(os.devnull, 'wb') as devnull: | ||||
|                 check_call(["ppmtogif", file], stdout=f, stderr=devnull) | ||||
|         else: | ||||
|             # Pipe ppmquant output into ppmtogif | ||||
|             # "ppmquant 256 %s | ppmtogif > %s" % (file, filename) | ||||
|             quant_cmd = ["ppmquant", "256", file] | ||||
|             togif_cmd = ["ppmtogif"] | ||||
|             stderr = tempfile.TemporaryFile() | ||||
|             quant_proc = Popen(quant_cmd, stdout=PIPE, stderr=stderr) | ||||
|             stderr = tempfile.TemporaryFile() | ||||
|             togif_proc = Popen(togif_cmd, stdin=quant_proc.stdout, stdout=f, | ||||
|                                stderr=stderr) | ||||
|             with open(os.devnull, 'wb') as devnull: | ||||
|                 quant_proc = Popen(quant_cmd, stdout=PIPE, stderr=devnull) | ||||
|                 togif_proc = Popen(togif_cmd, stdin=quant_proc.stdout, | ||||
|                                    stdout=f, stderr=devnull) | ||||
| 
 | ||||
|             # Allow ppmquant to receive SIGPIPE if ppmtogif exits | ||||
|             quant_proc.stdout.close() | ||||
|  | @ -549,24 +593,45 @@ def _save_netpbm(im, fp, filename): | |||
|         pass | ||||
| 
 | ||||
| 
 | ||||
| # -------------------------------------------------------------------- | ||||
| # GIF utilities | ||||
| # Force optimization so that we can test performance against | ||||
| # cases where it took lots of memory and time previously. | ||||
| _FORCE_OPTIMIZE = False | ||||
| 
 | ||||
| 
 | ||||
| def _get_optimize(im, info): | ||||
|     return im.mode in ("P", "L") and info and info.get("optimize", 0) | ||||
|     """ | ||||
|     Palette optimization is a potentially expensive operation. | ||||
| 
 | ||||
|     This function determines if the palette should be optimized using | ||||
|     some heuristics, then returns the list of palette entries in use. | ||||
| 
 | ||||
| def _get_used_palette_colors(im): | ||||
|     used_palette_colors = [] | ||||
|     :param im: Image object | ||||
|     :param info: encoderinfo | ||||
|     :returns: list of indexes of palette entries in use, or None | ||||
|     """ | ||||
|     if im.mode in ("P", "L") and info and info.get("optimize", 0): | ||||
|         # Potentially expensive operation. | ||||
| 
 | ||||
|     # check which colors are used | ||||
|     i = 0 | ||||
|     for count in im.histogram(): | ||||
|         if count: | ||||
|             used_palette_colors.append(i) | ||||
|         i += 1 | ||||
|         # The palette saves 3 bytes per color not used, but palette | ||||
|         # lengths are restricted to 3*(2**N) bytes. Max saving would | ||||
|         # be 768 -> 6 bytes if we went all the way down to 2 colors. | ||||
|         # * If we're over 128 colors, we can't save any space. | ||||
|         # * If there aren't any holes, it's not worth collapsing. | ||||
|         # * If we have a 'large' image, the palette is in the noise. | ||||
| 
 | ||||
|         # create the new palette if not every color is used | ||||
|         optimise = _FORCE_OPTIMIZE or im.mode == 'L' | ||||
|         if optimise or im.width * im.height < 512 * 512: | ||||
|             # check which colors are used | ||||
|             used_palette_colors = [] | ||||
|             for i, count in enumerate(im.histogram()): | ||||
|                 if count: | ||||
|                     used_palette_colors.append(i) | ||||
| 
 | ||||
|             if optimise or (len(used_palette_colors) <= 128 and | ||||
|                max(used_palette_colors) > len(used_palette_colors)): | ||||
|                 return used_palette_colors | ||||
| 
 | ||||
|     return used_palette_colors | ||||
| 
 | ||||
| def _get_color_table_size(palette_bytes): | ||||
|     # calculate the palette size for the header | ||||
|  | @ -576,7 +641,15 @@ def _get_color_table_size(palette_bytes): | |||
|         color_table_size = 0 | ||||
|     return color_table_size | ||||
| 
 | ||||
| 
 | ||||
| def _get_header_palette(palette_bytes): | ||||
|     """ | ||||
|     Returns the palette, null padded to the next power of 2 (*3) bytes | ||||
|     suitable for direct inclusion in the GIF header | ||||
| 
 | ||||
|     :param palette_bytes: Unpadded palette bytes, in RGBRGB form | ||||
|     :returns: Null padded palette | ||||
|     """ | ||||
|     color_table_size = _get_color_table_size(palette_bytes) | ||||
| 
 | ||||
|     # add the missing amount of bytes | ||||
|  | @ -586,102 +659,18 @@ def _get_header_palette(palette_bytes): | |||
|         palette_bytes += o8(0) * 3 * actual_target_size_diff | ||||
|     return palette_bytes | ||||
| 
 | ||||
| # Force optimization so that we can test performance against | ||||
| # cases where it took lots of memory and time previously.  | ||||
| _FORCE_OPTIMIZE = False | ||||
| 
 | ||||
| def _get_palette_bytes(im, palette, info): | ||||
|     if im.mode == "P": | ||||
|         if palette and isinstance(palette, bytes): | ||||
|             source_palette = palette[:768] | ||||
|         else: | ||||
|             source_palette = im.im.getpalette("RGB")[:768] | ||||
|     else:  # L-mode | ||||
|         if palette and isinstance(palette, bytes): | ||||
|             source_palette = palette[:768] | ||||
|         else: | ||||
|             source_palette = bytearray(i//3 for i in range(768)) | ||||
| def _get_palette_bytes(im): | ||||
|     """ | ||||
|     Gets the palette for inclusion in the gif header | ||||
| 
 | ||||
|     used_palette_colors = palette_bytes = None | ||||
|     :param im: Image object | ||||
|     :returns: Bytes, len<=768 suitable for inclusion in gif header | ||||
|     """ | ||||
|     return im.palette.palette | ||||
| 
 | ||||
|     if _get_optimize(im, info): | ||||
|         used_palette_colors = _get_used_palette_colors(im) | ||||
| 
 | ||||
|         # Potentially expensive operation. | ||||
| 
 | ||||
|         # The palette saves 3 bytes per color not used, but palette | ||||
|         # lengths are restricted to 3*(2**N) bytes. Max saving would | ||||
|         # be 768 -> 6 bytes if we went all the way down to 2 colors. | ||||
|         # * If we're over 128 colors, we can't save any space. | ||||
|         # * If there aren't any holes, it's not worth collapsing.  | ||||
|         # * If we have a 'large' image, the palette is in the noise. | ||||
| 
 | ||||
|         # create the new palette if not every color is used | ||||
|         if _FORCE_OPTIMIZE or im.mode == 'L' or \ | ||||
|                (len(used_palette_colors) <= 128 and   | ||||
|                 max(used_palette_colors) > len(used_palette_colors) and | ||||
|                 im.width * im.height < 512 * 512): | ||||
|             palette_bytes = b"" | ||||
|             new_positions = [0]*256 | ||||
| 
 | ||||
|             # pick only the used colors from the palette | ||||
|             for i, oldPosition in enumerate(used_palette_colors): | ||||
|                 palette_bytes += source_palette[oldPosition*3:oldPosition*3+3] | ||||
|                 new_positions[oldPosition] = i | ||||
| 
 | ||||
|             # replace the palette color id of all pixel with the new id | ||||
| 
 | ||||
|             # Palette images are [0..255], mapped through a 1 or 3 | ||||
|             # byte/color map.  We need to remap the whole image | ||||
|             # from palette 1 to palette 2. New_positions is | ||||
|             # an array of indexes into palette 1.  Palette 2 is | ||||
|             # palette 1 with any holes removed. | ||||
| 
 | ||||
|             # We're going to leverage the convert mechanism to use the | ||||
|             # C code to remap the image from palette 1 to palette 2, | ||||
|             # by forcing the source image into 'L' mode and adding a | ||||
|             # mapping 'L' mode palette, then converting back to 'L' | ||||
|             # sans palette thus converting the image bytes, then | ||||
|             # assigning the optimized RGB palette. | ||||
| 
 | ||||
|             # perf reference, 9500x4000 gif, w/~135 colors | ||||
|             # 14 sec prepatch, 1 sec postpatch with optimization forced. | ||||
| 
 | ||||
|             mapping_palette = bytearray(new_positions) | ||||
| 
 | ||||
|             m_im = im.copy() | ||||
|             m_im.mode = 'P' | ||||
| 
 | ||||
|             m_im.palette = ImagePalette.ImagePalette("RGB", | ||||
|                                                    palette=mapping_palette*3, | ||||
|                                                    size=768) | ||||
|             #possibly set palette dirty, then  | ||||
|             #m_im.putpalette(mapping_palette, 'L')  # converts to 'P' | ||||
|             # or just force it. | ||||
|             # UNDONE -- this is part of the general issue with palettes | ||||
|             m_im.im.putpalette(*m_im.palette.getdata()) | ||||
|              | ||||
|             m_im = m_im.convert('L') | ||||
|           | ||||
|             # Internally, we require 768 bytes for a palette.  | ||||
|             new_palette_bytes = (palette_bytes + | ||||
|                                  (768 - len(palette_bytes)) * b'\x00') | ||||
|             m_im.putpalette(new_palette_bytes) | ||||
|             m_im.palette = ImagePalette.ImagePalette("RGB", | ||||
|                                                    palette=palette_bytes, | ||||
|                                                    size=len(palette_bytes)) | ||||
| 
 | ||||
|             # oh gawd, this is modifying the image in place so I can pass by ref. | ||||
|             # REFACTOR SOONEST  | ||||
|             im.frombytes(m_im.tobytes()) | ||||
|              | ||||
|     if not palette_bytes: | ||||
|         palette_bytes = source_palette | ||||
| 
 | ||||
|     # returning palette, _not_ padded to 768 bytes like our internal ones. | ||||
|     return palette_bytes, used_palette_colors | ||||
| 
 | ||||
| def getheader(im, palette=None, info=None): | ||||
| def _get_global_header(im, info): | ||||
|     """Return a list of strings representing a GIF header""" | ||||
| 
 | ||||
|     # Header Block | ||||
|  | @ -691,7 +680,7 @@ def getheader(im, palette=None, info=None): | |||
|     for extensionKey in ["transparency", "duration", "loop", "comment"]: | ||||
|         if info and extensionKey in info: | ||||
|             if ((extensionKey == "duration" and info[extensionKey] == 0) or | ||||
|                 (extensionKey == "comment" and not (1 <= len(info[extensionKey]) <= 255))): | ||||
|                (extensionKey == "comment" and not (1 <= len(info[extensionKey]) <= 255))): | ||||
|                 continue | ||||
|             version = b"89a" | ||||
|             break | ||||
|  | @ -699,42 +688,89 @@ def getheader(im, palette=None, info=None): | |||
|         if im.info.get("version") == b"89a": | ||||
|             version = b"89a" | ||||
| 
 | ||||
|     header = [ | ||||
|         b"GIF"+version +        # signature + version | ||||
|         o16(im.size[0]) +       # canvas width | ||||
|         o16(im.size[1])         # canvas height | ||||
|     palette_bytes = _get_palette_bytes(im) | ||||
|     color_table_size = _get_color_table_size(palette_bytes) | ||||
| 
 | ||||
|     background = info["background"] if "background" in info else 0 | ||||
| 
 | ||||
|     return [ | ||||
|         b"GIF"+version +               # signature + version | ||||
|         o16(im.size[0]) +              # canvas width | ||||
|         o16(im.size[1]),               # canvas height | ||||
| 
 | ||||
|         # Logical Screen Descriptor | ||||
|         # size of global color table + global color table flag | ||||
|         o8(color_table_size + 128),   # packed fields | ||||
|         # background + reserved/aspect | ||||
|         o8(background) + o8(0), | ||||
| 
 | ||||
|         # Global Color Table | ||||
|         _get_header_palette(palette_bytes) | ||||
|     ] | ||||
| 
 | ||||
|     palette_bytes, used_palette_colors = _get_palette_bytes(im, palette, info) | ||||
| 
 | ||||
|     # Logical Screen Descriptor | ||||
|     color_table_size = _get_color_table_size(palette_bytes) | ||||
|     # size of global color table + global color table flag | ||||
|     header.append(o8(color_table_size + 128))  # packed fields | ||||
|     # background + reserved/aspect | ||||
|     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 | ||||
| def _write_frame_data(fp, im_frame, offset, params): | ||||
|     try: | ||||
|         im_frame.encoderinfo = params | ||||
| 
 | ||||
|         # local image header | ||||
|         _write_local_header(fp, im_frame, offset, 0) | ||||
| 
 | ||||
|         ImageFile._save(im_frame, fp, [("gif", (0, 0)+im_frame.size, 0, | ||||
|                                         RAWMODE[im_frame.mode])]) | ||||
| 
 | ||||
|         fp.write(b"\0")  # end of image data | ||||
|     finally: | ||||
|         del im_frame.encoderinfo | ||||
| 
 | ||||
| # -------------------------------------------------------------------- | ||||
| # Legacy GIF utilities | ||||
| 
 | ||||
| 
 | ||||
| def getheader(im, palette=None, info=None): | ||||
|     """ | ||||
|     Legacy Method to get Gif data from image. | ||||
| 
 | ||||
|     Warning:: May modify image data. | ||||
| 
 | ||||
|     :param im: Image object | ||||
|     :param palette: bytes object containing the source palette, or .... | ||||
|     :param info: encoderinfo | ||||
|     :returns: tuple of(list of header items, optimized palette) | ||||
| 
 | ||||
|     """ | ||||
|     used_palette_colors = _get_optimize(im, info) | ||||
| 
 | ||||
|     if info is None: | ||||
|         info = {} | ||||
| 
 | ||||
|     if "background" not in info and "background" in im.info: | ||||
|         info["background"] = im.info["background"] | ||||
| 
 | ||||
|     im_mod = _normalize_palette(im, palette, info) | ||||
|     im.palette = im_mod.palette | ||||
|     im.im = im_mod.im | ||||
|     header = _get_global_header(im, info) | ||||
| 
 | ||||
|     # Header + Logical Screen Descriptor + Global Color Table | ||||
|     header.append(_get_header_palette(palette_bytes)) | ||||
|     return header, used_palette_colors | ||||
| 
 | ||||
| 
 | ||||
| # To specify duration, add the time in milliseconds to getdata(), | ||||
| # e.g. getdata(im_frame, duration=1000) | ||||
| def getdata(im, offset=(0, 0), **params): | ||||
|     """Return a list of strings representing this image. | ||||
|        The first string is a local image header, the rest contains | ||||
|        encoded image data.""" | ||||
|     """ | ||||
|     Legacy Method | ||||
| 
 | ||||
|     Return a list of strings representing this image. | ||||
|     The first string is a local image header, the rest contains | ||||
|     encoded image data. | ||||
| 
 | ||||
|     :param im: Image object | ||||
|     :param offset: Tuple of (x, y) pixels. Defaults to (0,0) | ||||
|     :param **params: E.g. duration or other encoder info parameters | ||||
|     :returns: List of Bytes containing gif encoded frame data | ||||
| 
 | ||||
|     """ | ||||
|     class Collector(object): | ||||
|         data = [] | ||||
| 
 | ||||
|  | @ -745,18 +781,7 @@ def getdata(im, offset=(0, 0), **params): | |||
| 
 | ||||
|     fp = Collector() | ||||
| 
 | ||||
|     try: | ||||
|         im.encoderinfo = params | ||||
| 
 | ||||
|         # local image header | ||||
|         _get_local_header(fp, im, offset, 0) | ||||
| 
 | ||||
|         ImageFile._save(im, fp, [("gif", (0, 0)+im.size, 0, RAWMODE[im.mode])]) | ||||
| 
 | ||||
|         fp.write(b"\0")  # end of image data | ||||
| 
 | ||||
|     finally: | ||||
|         del im.encoderinfo | ||||
|     _write_frame_data(fp, im, offset, params) | ||||
| 
 | ||||
|     return fp.data | ||||
| 
 | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ | |||
| # | ||||
| 
 | ||||
| from math import pi, log, sin, sqrt | ||||
| from PIL._binary import o8 | ||||
| from ._binary import o8 | ||||
| 
 | ||||
| # -------------------------------------------------------------------- | ||||
| # Stuff to translate curve segments to palette values (derived from | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ | |||
| # | ||||
| 
 | ||||
| import re | ||||
| from PIL._binary import o8 | ||||
| from ._binary import o8 | ||||
| 
 | ||||
| 
 | ||||
| ## | ||||
|  |  | |||
|  | @ -9,7 +9,8 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import Image, ImageFile | ||||
| from . import Image, ImageFile | ||||
| from ._binary import i8 | ||||
| 
 | ||||
| _handler = None | ||||
| 
 | ||||
|  | @ -28,7 +29,7 @@ def register_handler(handler): | |||
| # Image adapter | ||||
| 
 | ||||
| def _accept(prefix): | ||||
|     return prefix[0:4] == b"GRIB" and prefix[7] == b'\x01' | ||||
|     return prefix[0:4] == b"GRIB" and i8(prefix[7]) == 1 | ||||
| 
 | ||||
| 
 | ||||
| class GribStubImageFile(ImageFile.StubImageFile): | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import Image, ImageFile | ||||
| from . import Image, ImageFile | ||||
| 
 | ||||
| _handler = None | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,7 +15,8 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import Image, ImageFile, PngImagePlugin, _binary | ||||
| from PIL import Image, ImageFile, PngImagePlugin | ||||
| from PIL._binary import i8 | ||||
| import io | ||||
| import os | ||||
| import shutil | ||||
|  | @ -27,8 +28,6 @@ enable_jpeg2k = hasattr(Image.core, 'jp2klib_version') | |||
| if enable_jpeg2k: | ||||
|     from PIL import Jpeg2KImagePlugin | ||||
| 
 | ||||
| i8 = _binary.i8 | ||||
| 
 | ||||
| HEADERSIZE = 8 | ||||
| 
 | ||||
| 
 | ||||
|  | @ -330,8 +329,8 @@ def _save(im, fp, filename): | |||
|     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) | ||||
|     with open(os.devnull, 'wb') as devnull: | ||||
|         convert_proc = Popen(convert_cmd, stdout=PIPE, stderr=devnull) | ||||
| 
 | ||||
|     convert_proc.stdout.close() | ||||
| 
 | ||||
|  |  | |||
|  | @ -25,7 +25,8 @@ | |||
| import struct | ||||
| from io import BytesIO | ||||
| 
 | ||||
| from PIL import Image, ImageFile, BmpImagePlugin, PngImagePlugin, _binary | ||||
| from . import Image, ImageFile, BmpImagePlugin, PngImagePlugin | ||||
| from ._binary import i8, i16le as i16, i32le as i32 | ||||
| from math import log, ceil | ||||
| 
 | ||||
| __version__ = "0.1" | ||||
|  | @ -33,10 +34,6 @@ __version__ = "0.1" | |||
| # | ||||
| # -------------------------------------------------------------------- | ||||
| 
 | ||||
| i8 = _binary.i8 | ||||
| i16 = _binary.i16le | ||||
| i32 = _binary.i32le | ||||
| 
 | ||||
| _MAGIC = b"\0\0\1\0" | ||||
| 
 | ||||
| 
 | ||||
|  | @ -44,16 +41,19 @@ def _save(im, fp, filename): | |||
|     fp.write(_MAGIC)  # (2+2) | ||||
|     sizes = im.encoderinfo.get("sizes", | ||||
|                                [(16, 16), (24, 24), (32, 32), (48, 48), | ||||
|                                 (64, 64), (128, 128), (255, 255)]) | ||||
|                                 (64, 64), (128, 128), (256, 256)]) | ||||
|     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 = filter(lambda x: False if (x[0] > width or x[1] > height or | ||||
|                                        x[0] > 256 or x[1] > 256) else True, | ||||
|                    sizes) | ||||
|     sizes = list(sizes) | ||||
|     fp.write(struct.pack("<H", len(sizes)))  # idCount(2) | ||||
|     offset = fp.tell() + len(sizes)*16 | ||||
|     for size in sizes: | ||||
|         width, height = size | ||||
|         fp.write(struct.pack("B", width))  # bWidth(1) | ||||
|         fp.write(struct.pack("B", height))  # bHeight(1) | ||||
|         # 0 means 256 | ||||
|         fp.write(struct.pack("B", width if width < 256 else 0))  # bWidth(1) | ||||
|         fp.write(struct.pack("B", height if height < 256 else 0))  # bHeight(1) | ||||
|         fp.write(b"\0")  # bColorCount(1) | ||||
|         fp.write(b"\0")  # bReserved(1) | ||||
|         fp.write(b"\0\0")  # wPlanes(2) | ||||
|  | @ -176,8 +176,8 @@ class IcoFile(object): | |||
|             # figure out where AND mask image starts | ||||
|             mode = a[0] | ||||
|             bpp = 8 | ||||
|             for k in BmpImagePlugin.BIT2MODE.keys(): | ||||
|                 if mode == BmpImagePlugin.BIT2MODE[k][1]: | ||||
|             for k, v in BmpImagePlugin.BIT2MODE.items(): | ||||
|                 if mode == v[1]: | ||||
|                     bpp = k | ||||
|                     break | ||||
| 
 | ||||
|  | @ -215,13 +215,13 @@ class IcoFile(object): | |||
|                 total_bytes = int((w * im.size[1]) / 8) | ||||
| 
 | ||||
|                 self.buf.seek(and_mask_offset) | ||||
|                 maskData = self.buf.read(total_bytes) | ||||
|                 mask_data = self.buf.read(total_bytes) | ||||
| 
 | ||||
|                 # convert raw data to image | ||||
|                 mask = Image.frombuffer( | ||||
|                     '1',            # 1 bpp | ||||
|                     im.size,        # (w, h) | ||||
|                     maskData,       # source chars | ||||
|                     mask_data,      # source chars | ||||
|                     'raw',          # raw decoder | ||||
|                     ('1;I', int(w/8), -1)  # 1bpp inverted, padded, reversed | ||||
|                 ) | ||||
|  | @ -278,6 +278,7 @@ class IcoImageFile(ImageFile.ImageFile): | |||
| # | ||||
| # -------------------------------------------------------------------- | ||||
| 
 | ||||
| 
 | ||||
| Image.register_open(IcoImageFile.format, IcoImageFile, _accept) | ||||
| Image.register_save(IcoImageFile.format, _save) | ||||
| Image.register_extension(IcoImageFile.format, ".ico") | ||||
|  |  | |||
|  | @ -27,8 +27,8 @@ | |||
| 
 | ||||
| 
 | ||||
| import re | ||||
| from PIL import Image, ImageFile, ImagePalette | ||||
| from PIL._binary import i8 | ||||
| from . import Image, ImageFile, ImagePalette | ||||
| from ._binary import i8 | ||||
| 
 | ||||
| __version__ = "0.7" | ||||
| 
 | ||||
|  | @ -109,6 +109,7 @@ class ImImageFile(ImageFile.ImageFile): | |||
| 
 | ||||
|     format = "IM" | ||||
|     format_description = "IFUNC Image Memory" | ||||
|     _close_exclusive_fp_after_loading = False | ||||
| 
 | ||||
|     def _open(self): | ||||
| 
 | ||||
|  | @ -325,10 +326,7 @@ def _save(im, fp, filename, check=0): | |||
|     except KeyError: | ||||
|         raise ValueError("Cannot save %s images as IM" % im.mode) | ||||
| 
 | ||||
|     try: | ||||
|         frames = im.encoderinfo["frames"] | ||||
|     except KeyError: | ||||
|         frames = 1 | ||||
|     frames = im.encoderinfo.get("frames", 1) | ||||
| 
 | ||||
|     if check: | ||||
|         return check | ||||
|  |  | |||
							
								
								
									
										421
									
								
								PIL/Image.py
									
									
									
									
									
								
							
							
						
						
									
										421
									
								
								PIL/Image.py
									
									
									
									
									
								
							|  | @ -24,9 +24,7 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from __future__ import print_function | ||||
| 
 | ||||
| from PIL import VERSION, PILLOW_VERSION, _plugins | ||||
| from . import VERSION, PILLOW_VERSION, _plugins | ||||
| 
 | ||||
| import logging | ||||
| import warnings | ||||
|  | @ -48,15 +46,6 @@ class _imaging_not_installed(object): | |||
| # Limit to around a quarter gigabyte for a 24 bit (3 bpp) image | ||||
| MAX_IMAGE_PIXELS = int(1024 * 1024 * 1024 // 4 // 3) | ||||
| 
 | ||||
| try: | ||||
|     # give Tk a chance to set up the environment, in case we're | ||||
|     # using an _imaging module linked against libtcl/libtk (use | ||||
|     # __import__ to hide this from naive packagers; we don't really | ||||
|     # depend on Tk unless ImageTk is used, and that module already | ||||
|     # imports Tkinter) | ||||
|     __import__("FixTk") | ||||
| except ImportError: | ||||
|     pass | ||||
| 
 | ||||
| try: | ||||
|     # If the _imaging C module is not present, Pillow will not load. | ||||
|  | @ -64,10 +53,13 @@ try: | |||
|     # import Image and use the Image.core variable instead. | ||||
|     # Also note that Image.core is not a publicly documented interface, | ||||
|     # and should be considered private and subject to change. | ||||
|     from PIL import _imaging as core | ||||
|     from . import _imaging as core | ||||
|     if PILLOW_VERSION != getattr(core, 'PILLOW_VERSION', None): | ||||
|         raise ImportError("The _imaging extension was built for another " | ||||
|                           "version of Pillow or PIL") | ||||
|                           "version of Pillow or PIL: Core Version: %s" | ||||
|                           "Pillow Version:  %s" % | ||||
|                           (getattr(core, 'PILLOW_VERSION', None), | ||||
|                            PILLOW_VERSION)) | ||||
| 
 | ||||
| except ImportError as v: | ||||
|     core = _imaging_not_installed() | ||||
|  | @ -109,11 +101,9 @@ except ImportError: | |||
|     import __builtin__ | ||||
|     builtins = __builtin__ | ||||
| 
 | ||||
| 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 | ||||
| from . import ImageMode | ||||
| from ._binary import i8 | ||||
| from ._util import isPath, isStringType, deferred_error | ||||
| 
 | ||||
| import os | ||||
| import sys | ||||
|  | @ -146,6 +136,7 @@ def isImageType(t): | |||
|     """ | ||||
|     return hasattr(t, "im") | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # Constants (also defined in _imagingmodule.c!) | ||||
| 
 | ||||
|  | @ -211,6 +202,8 @@ MIME = {} | |||
| SAVE = {} | ||||
| SAVE_ALL = {} | ||||
| EXTENSION = {} | ||||
| DECODERS = {} | ||||
| ENCODERS = {} | ||||
| 
 | ||||
| # -------------------------------------------------------------------- | ||||
| # Modes supported by this version | ||||
|  | @ -284,7 +277,7 @@ def _conv_type_shape(im): | |||
|         return shape+(extra,), typ | ||||
| 
 | ||||
| 
 | ||||
| MODES = sorted(_MODEINFO.keys()) | ||||
| MODES = sorted(_MODEINFO) | ||||
| 
 | ||||
| # raw modes that may be memory mapped.  NOTE: if you change this, you | ||||
| # may have to modify the stride calculation in map.c too! | ||||
|  | @ -341,6 +334,7 @@ def getmodebands(mode): | |||
|     """ | ||||
|     return len(ImageMode.getmode(mode).bands) | ||||
| 
 | ||||
| 
 | ||||
| # -------------------------------------------------------------------- | ||||
| # Helpers | ||||
| 
 | ||||
|  | @ -355,23 +349,23 @@ def preinit(): | |||
|         return | ||||
| 
 | ||||
|     try: | ||||
|         from PIL import BmpImagePlugin | ||||
|         from . import BmpImagePlugin | ||||
|     except ImportError: | ||||
|         pass | ||||
|     try: | ||||
|         from PIL import GifImagePlugin | ||||
|         from . import GifImagePlugin | ||||
|     except ImportError: | ||||
|         pass | ||||
|     try: | ||||
|         from PIL import JpegImagePlugin | ||||
|         from . import JpegImagePlugin | ||||
|     except ImportError: | ||||
|         pass | ||||
|     try: | ||||
|         from PIL import PpmImagePlugin | ||||
|         from . import PpmImagePlugin | ||||
|     except ImportError: | ||||
|         pass | ||||
|     try: | ||||
|         from PIL import PngImagePlugin | ||||
|         from . import PngImagePlugin | ||||
|     except ImportError: | ||||
|         pass | ||||
| #   try: | ||||
|  | @ -415,6 +409,11 @@ def _getdecoder(mode, decoder_name, args, extra=()): | |||
|     elif not isinstance(args, tuple): | ||||
|         args = (args,) | ||||
| 
 | ||||
|     try: | ||||
|         decoder = DECODERS[decoder_name] | ||||
|         return decoder(mode, *args + extra) | ||||
|     except KeyError: | ||||
|         pass | ||||
|     try: | ||||
|         # get decoder | ||||
|         decoder = getattr(core, decoder_name + "_decoder") | ||||
|  | @ -432,6 +431,11 @@ def _getencoder(mode, encoder_name, args, extra=()): | |||
|     elif not isinstance(args, tuple): | ||||
|         args = (args,) | ||||
| 
 | ||||
|     try: | ||||
|         encoder = ENCODERS[encoder_name] | ||||
|         return encoder(mode, *args + extra) | ||||
|     except KeyError: | ||||
|         pass | ||||
|     try: | ||||
|         # get encoder | ||||
|         encoder = getattr(core, encoder_name + "_encoder") | ||||
|  | @ -496,6 +500,7 @@ class Image(object): | |||
|     """ | ||||
|     format = None | ||||
|     format_description = None | ||||
|     _close_exclusive_fp_after_loading = True | ||||
| 
 | ||||
|     def __init__(self): | ||||
|         # FIXME: take "new" parameters / other image? | ||||
|  | @ -525,13 +530,11 @@ class Image(object): | |||
|         if self.palette: | ||||
|             new.palette = self.palette.copy() | ||||
|         if im.mode == "P" and not new.palette: | ||||
|             from PIL import ImagePalette | ||||
|             from . import ImagePalette | ||||
|             new.palette = ImagePalette.ImagePalette() | ||||
|         new.info = self.info.copy() | ||||
|         return new | ||||
| 
 | ||||
|     _makeself = _new  # compatibility | ||||
| 
 | ||||
|     # Context Manager Support | ||||
|     def __enter__(self): | ||||
|         return self | ||||
|  | @ -552,21 +555,32 @@ class Image(object): | |||
|         """ | ||||
|         try: | ||||
|             self.fp.close() | ||||
|             self.fp = None | ||||
|         except Exception as msg: | ||||
|             logger.debug("Error closing: %s", msg) | ||||
| 
 | ||||
|         if getattr(self, 'map', None): | ||||
|             self.map = None | ||||
| 
 | ||||
|         # Instead of simply setting to None, we're setting up a | ||||
|         # deferred error that will better explain that the core image | ||||
|         # object is gone. | ||||
|         self.im = deferred_error(ValueError("Operation on closed image")) | ||||
| 
 | ||||
|     if sys.version_info >= (3, 4, 0): | ||||
|         def __del__(self): | ||||
|             if (hasattr(self, 'fp') and hasattr(self, '_exclusive_fp') | ||||
|                and self.fp and self._exclusive_fp): | ||||
|                 self.fp.close() | ||||
|             self.fp = None | ||||
| 
 | ||||
|     def _copy(self): | ||||
|         self.load() | ||||
|         self.im = self.im.copy() | ||||
|         self.pyaccess = None | ||||
|         self.readonly = 0 | ||||
| 
 | ||||
|     def _dump(self, file=None, format=None): | ||||
|     def _dump(self, file=None, format=None, **options): | ||||
|         import tempfile | ||||
|         suffix = '' | ||||
|         if format: | ||||
|  | @ -581,7 +595,7 @@ class Image(object): | |||
|         else: | ||||
|             if not file.endswith(format): | ||||
|                 file = file + "." + format | ||||
|             self.save(file, format) | ||||
|             self.save(file, format, **options) | ||||
|         return file | ||||
| 
 | ||||
|     def __eq__(self, other): | ||||
|  | @ -695,8 +709,8 @@ class Image(object): | |||
|         return b"".join(data) | ||||
| 
 | ||||
|     def tostring(self, *args, **kw): | ||||
|         raise NotImplementedError("tostring() has been removed. " + | ||||
|                         "Please call tobytes() instead.") | ||||
|         raise NotImplementedError("tostring() has been removed. " | ||||
|                                   "Please call tobytes() instead.") | ||||
| 
 | ||||
|     def tobitmap(self, name="image"): | ||||
|         """ | ||||
|  | @ -746,8 +760,8 @@ class Image(object): | |||
|             raise ValueError("cannot decode image data") | ||||
| 
 | ||||
|     def fromstring(self, *args, **kw): | ||||
|         raise NotImplementedError("fromstring() has been removed. " + | ||||
|                         "Please call frombytes() instead.") | ||||
|         raise NotImplementedError("fromstring() has been removed. " | ||||
|                                   "Please call frombytes() instead.") | ||||
| 
 | ||||
|     def load(self): | ||||
|         """ | ||||
|  | @ -777,7 +791,7 @@ class Image(object): | |||
|             if HAS_CFFI and USE_CFFI_ACCESS: | ||||
|                 if self.pyaccess: | ||||
|                     return self.pyaccess | ||||
|                 from PIL import PyAccess | ||||
|                 from . import PyAccess | ||||
|                 self.pyaccess = PyAccess.new(self, self.readonly) | ||||
|                 if self.pyaccess: | ||||
|                     return self.pyaccess | ||||
|  | @ -883,7 +897,7 @@ class Image(object): | |||
|                             try: | ||||
|                                 t = trns_im.palette.getcolor(t) | ||||
|                             except: | ||||
|                                 raise ValueError("Couldn't allocate a palette " + | ||||
|                                 raise ValueError("Couldn't allocate a palette " | ||||
|                                                  "color for transparency") | ||||
|                     trns_im.putpixel((0, 0), t) | ||||
| 
 | ||||
|  | @ -910,7 +924,7 @@ class Image(object): | |||
|         if mode == "P" and palette == ADAPTIVE: | ||||
|             im = self.im.quantize(colors) | ||||
|             new = self._new(im) | ||||
|             from PIL import ImagePalette | ||||
|             from . import ImagePalette | ||||
|             new.palette = ImagePalette.raw("RGB", new.im.getpalette("RGB")) | ||||
|             if delete_trns: | ||||
|                 # This could possibly happen if we requantize to fewer colors. | ||||
|  | @ -997,7 +1011,7 @@ class Image(object): | |||
|                     "only RGB or L mode images can be quantized to a palette" | ||||
|                     ) | ||||
|             im = self.im.convert("P", 1, palette.im) | ||||
|             return self._makeself(im) | ||||
|             return self._new(im) | ||||
| 
 | ||||
|         return self._new(self.im.quantize(colors, method, kmeans)) | ||||
| 
 | ||||
|  | @ -1031,6 +1045,20 @@ class Image(object): | |||
|         if box is None: | ||||
|             return self.copy() | ||||
| 
 | ||||
|         return self._new(self._crop(self.im, box)) | ||||
| 
 | ||||
|     def _crop(self, im, box): | ||||
|         """ | ||||
|         Returns a rectangular region from the core image object im. | ||||
| 
 | ||||
|         This is equivalent to calling im.crop((x0, y0, x1, y1)), but | ||||
|         includes additional sanity checks. | ||||
| 
 | ||||
|         :param im: a core image object | ||||
|         :param box: The crop rectangle, as a (left, upper, right, lower)-tuple. | ||||
|         :returns: A core image object. | ||||
|         """ | ||||
| 
 | ||||
|         x0, y0, x1, y1 = map(int, map(round, box)) | ||||
| 
 | ||||
|         if x1 < x0: | ||||
|  | @ -1038,8 +1066,9 @@ class Image(object): | |||
|         if y1 < y0: | ||||
|             y1 = y0 | ||||
| 
 | ||||
|         return self._new(self.im.crop(( x0, y0, x1, y1))) | ||||
|         _decompression_bomb_check((x1, y1)) | ||||
| 
 | ||||
|         return im.crop((x0, y0, x1, y1)) | ||||
| 
 | ||||
|     def draft(self, mode, size): | ||||
|         """ | ||||
|  | @ -1053,6 +1082,9 @@ class Image(object): | |||
|         in place.  If the image has already been loaded, this method has no | ||||
|         effect. | ||||
| 
 | ||||
|         Note: This method is not implemented for most images. It is | ||||
|         currently implemented only for JPEG and PCD images. | ||||
| 
 | ||||
|         :param mode: The requested mode. | ||||
|         :param size: The requested size. | ||||
|         """ | ||||
|  | @ -1258,8 +1290,8 @@ class Image(object): | |||
|         return self.im.histogram() | ||||
| 
 | ||||
|     def offset(self, xoffset, yoffset=None): | ||||
|         raise NotImplementedError("offset() has been removed. " + | ||||
|                         "Please call ImageChops.offset() instead.") | ||||
|         raise NotImplementedError("offset() has been removed. " | ||||
|                                   "Please call ImageChops.offset() instead.") | ||||
| 
 | ||||
|     def paste(self, im, box=None, mask=None): | ||||
|         """ | ||||
|  | @ -1323,7 +1355,7 @@ class Image(object): | |||
|             box += (box[0]+size[0], box[1]+size[1]) | ||||
| 
 | ||||
|         if isStringType(im): | ||||
|             from PIL import ImageColor | ||||
|             from . import ImageColor | ||||
|             im = ImageColor.getcolor(im, self.mode) | ||||
| 
 | ||||
|         elif isImageType(im): | ||||
|  | @ -1344,6 +1376,54 @@ class Image(object): | |||
|         else: | ||||
|             self.im.paste(im, box) | ||||
| 
 | ||||
|     def alpha_composite(self, im, dest=(0,0), source=(0,0)): | ||||
|         """ 'In-place' analog of Image.alpha_composite. Composites an image | ||||
|         onto this image. | ||||
| 
 | ||||
|         :param im: image to composite over this one | ||||
|         :param dest: Optional 2 tuple (left, top) specifying the upper | ||||
|           left corner in this (destination) image. | ||||
|         :param source: Optional 2 (left, top) tuple for the upper left | ||||
|           corner in the overlay source image, or 4 tuple (left, top, right, | ||||
|           bottom) for the bounds of the source rectangle | ||||
| 
 | ||||
|         Performance Note: Not currently implemented in-place in the core layer. | ||||
|         """ | ||||
| 
 | ||||
|         if not isinstance(source, tuple): | ||||
|             raise ValueError("Source must be a tuple") | ||||
|         if not isinstance(dest, tuple): | ||||
|             raise ValueError("Destination must be a tuple") | ||||
|         if not len(source) in (2, 4): | ||||
|             raise ValueError("Source must be a 2 or 4-tuple") | ||||
|         if not len(dest) == 2: | ||||
|             raise ValueError("Destination must be a 2-tuple") | ||||
|         if min(source) < 0: | ||||
|             raise ValueError("Source must be non-negative") | ||||
|         if min(dest) < 0: | ||||
|             raise ValueError("Destination must be non-negative") | ||||
| 
 | ||||
|         if len(source) == 2: | ||||
|             source = source + im.size | ||||
| 
 | ||||
|         # over image, crop if it's not the whole thing. | ||||
|         if source == (0,0) + im.size: | ||||
|             overlay = im | ||||
|         else: | ||||
|             overlay = im.crop(source) | ||||
| 
 | ||||
|         # target for the paste | ||||
|         box = dest + (dest[0] + overlay.width, dest[1] + overlay.height) | ||||
| 
 | ||||
|         # destination image. don't copy if we're using the whole image. | ||||
|         if dest == (0,0) + self.size: | ||||
|             background = self | ||||
|         else: | ||||
|             background = self.crop(box) | ||||
| 
 | ||||
|         result = alpha_composite(background, overlay) | ||||
|         self.paste(result, box) | ||||
| 
 | ||||
|     def point(self, lut, mode=None): | ||||
|         """ | ||||
|         Maps this image through a lookup table or function. | ||||
|  | @ -1470,7 +1550,7 @@ class Image(object): | |||
| 
 | ||||
|         :param data: A palette sequence (either a list or a string). | ||||
|         """ | ||||
|         from PIL import ImagePalette | ||||
|         from . import ImagePalette | ||||
| 
 | ||||
|         if self.mode not in ("L", "P"): | ||||
|             raise ValueError("illegal image mode") | ||||
|  | @ -1519,6 +1599,80 @@ class Image(object): | |||
|             return self.pyaccess.putpixel(xy, value) | ||||
|         return self.im.putpixel(xy, value) | ||||
| 
 | ||||
|     def remap_palette(self, dest_map, source_palette=None): | ||||
|         """ | ||||
|         Rewrites the image to reorder the palette. | ||||
| 
 | ||||
|         :param dest_map: A list of indexes into the original palette. | ||||
|            e.g. [1,0] would swap a two item palette, and list(range(255)) | ||||
|            is the identity transform. | ||||
|         :param source_palette: Bytes or None. | ||||
|         :returns:  An :py:class:`~PIL.Image.Image` object. | ||||
| 
 | ||||
|         """ | ||||
|         from . import ImagePalette | ||||
| 
 | ||||
|         if self.mode not in ("L", "P"): | ||||
|             raise ValueError("illegal image mode") | ||||
| 
 | ||||
|         if source_palette is None: | ||||
|             if self.mode == "P": | ||||
|                 source_palette = self.im.getpalette("RGB")[:768] | ||||
|             else:  # L-mode | ||||
|                 source_palette = bytearray(i//3 for i in range(768)) | ||||
| 
 | ||||
|         palette_bytes = b"" | ||||
|         new_positions = [0]*256 | ||||
| 
 | ||||
|         # pick only the used colors from the palette | ||||
|         for i, oldPosition in enumerate(dest_map): | ||||
|             palette_bytes += source_palette[oldPosition*3:oldPosition*3+3] | ||||
|             new_positions[oldPosition] = i | ||||
| 
 | ||||
|         # replace the palette color id of all pixel with the new id | ||||
| 
 | ||||
|         # Palette images are [0..255], mapped through a 1 or 3 | ||||
|         # byte/color map.  We need to remap the whole image | ||||
|         # from palette 1 to palette 2. New_positions is | ||||
|         # an array of indexes into palette 1.  Palette 2 is | ||||
|         # palette 1 with any holes removed. | ||||
| 
 | ||||
|         # We're going to leverage the convert mechanism to use the | ||||
|         # C code to remap the image from palette 1 to palette 2, | ||||
|         # by forcing the source image into 'L' mode and adding a | ||||
|         # mapping 'L' mode palette, then converting back to 'L' | ||||
|         # sans palette thus converting the image bytes, then | ||||
|         # assigning the optimized RGB palette. | ||||
| 
 | ||||
|         # perf reference, 9500x4000 gif, w/~135 colors | ||||
|         # 14 sec prepatch, 1 sec postpatch with optimization forced. | ||||
| 
 | ||||
|         mapping_palette = bytearray(new_positions) | ||||
| 
 | ||||
|         m_im = self.copy() | ||||
|         m_im.mode = 'P' | ||||
| 
 | ||||
|         m_im.palette = ImagePalette.ImagePalette("RGB", | ||||
|                                                  palette=mapping_palette*3, | ||||
|                                                  size=768) | ||||
|         # possibly set palette dirty, then | ||||
|         # m_im.putpalette(mapping_palette, 'L')  # converts to 'P' | ||||
|         # or just force it. | ||||
|         # UNDONE -- this is part of the general issue with palettes | ||||
|         m_im.im.putpalette(*m_im.palette.getdata()) | ||||
| 
 | ||||
|         m_im = m_im.convert('L') | ||||
| 
 | ||||
|         # Internally, we require 768 bytes for a palette. | ||||
|         new_palette_bytes = (palette_bytes + | ||||
|                              (768 - len(palette_bytes)) * b'\x00') | ||||
|         m_im.putpalette(new_palette_bytes) | ||||
|         m_im.palette = ImagePalette.ImagePalette("RGB", | ||||
|                                                  palette=palette_bytes, | ||||
|                                                  size=len(palette_bytes)) | ||||
| 
 | ||||
|         return m_im | ||||
| 
 | ||||
|     def resize(self, size, resample=NEAREST, box=None): | ||||
|         """ | ||||
|         Returns a resized copy of this image. | ||||
|  | @ -1567,7 +1721,8 @@ class Image(object): | |||
| 
 | ||||
|         return self._new(self.im.resize(size, resample, box)) | ||||
| 
 | ||||
|     def rotate(self, angle, resample=NEAREST, expand=0): | ||||
|     def rotate(self, angle, resample=NEAREST, expand=0, center=None, | ||||
|                translate=None): | ||||
|         """ | ||||
|         Returns a rotated copy of this image.  This method returns a | ||||
|         copy of this image, rotated the given number of degrees counter | ||||
|  | @ -1584,48 +1739,86 @@ class Image(object): | |||
|         :param expand: Optional expansion flag.  If true, expands the output | ||||
|            image to make it large enough to hold the entire rotated image. | ||||
|            If false or omitted, make the output image the same size as the | ||||
|            input image. | ||||
|            input image.  Note that the expand flag assumes rotation around | ||||
|            the center and no translation. | ||||
|         :param center: Optional center of rotation (a 2-tuple).  Origin is | ||||
|            the upper left corner.  Default is the center of the image. | ||||
|         :param translate: An optional post-rotate translation (a 2-tuple). | ||||
|         :returns: An :py:class:`~PIL.Image.Image` object. | ||||
|         """ | ||||
| 
 | ||||
|         angle = angle % 360.0 | ||||
| 
 | ||||
|         # Fast paths regardless of filter | ||||
|         if angle == 0: | ||||
|             return self.copy() | ||||
|         if angle == 180: | ||||
|             return self.transpose(ROTATE_180) | ||||
|         if angle == 90 and expand: | ||||
|             return self.transpose(ROTATE_90) | ||||
|         if angle == 270 and expand: | ||||
|             return self.transpose(ROTATE_270) | ||||
|         # Fast paths regardless of filter, as long as we're not | ||||
|         # translating or changing the center. | ||||
|         if not (center or translate): | ||||
|             if angle == 0: | ||||
|                 return self.copy() | ||||
|             if angle == 180: | ||||
|                 return self.transpose(ROTATE_180) | ||||
|             if angle == 90 and expand: | ||||
|                 return self.transpose(ROTATE_90) | ||||
|             if angle == 270 and expand: | ||||
|                 return self.transpose(ROTATE_270) | ||||
| 
 | ||||
|         # Calculate the affine matrix.  Note that this is the reverse | ||||
|         # transformation (from destination image to source) because we | ||||
|         # want to interpolate the (discrete) destination pixel from | ||||
|         # the local area around the (floating) source pixel. | ||||
| 
 | ||||
|         # The matrix we actually want (note that it operates from the right): | ||||
|         # (1, 0, tx)   (1, 0, cx)   ( cos a, sin a, 0)   (1, 0, -cx) | ||||
|         # (0, 1, ty) * (0, 1, cy) * (-sin a, cos a, 0) * (0, 1, -cy) | ||||
|         # (0, 0,  1)   (0, 0,  1)   (     0,     0, 1)   (0, 0,   1) | ||||
| 
 | ||||
|         # The reverse matrix is thus: | ||||
|         # (1, 0, cx)   ( cos -a, sin -a, 0)   (1, 0, -cx)   (1, 0, -tx) | ||||
|         # (0, 1, cy) * (-sin -a, cos -a, 0) * (0, 1, -cy) * (0, 1, -ty) | ||||
|         # (0, 0,  1)   (      0,      0, 1)   (0, 0,   1)   (0, 0,   1) | ||||
| 
 | ||||
|         # In any case, the final translation may be updated at the end to | ||||
|         # compensate for the expand flag. | ||||
| 
 | ||||
|         w, h = self.size | ||||
| 
 | ||||
|         if translate is None: | ||||
|             translate = [0, 0] | ||||
|         if center is None: | ||||
|             center = [w / 2.0, h / 2.0] | ||||
| 
 | ||||
|         angle = - math.radians(angle) | ||||
|         matrix = [ | ||||
|             round(math.cos(angle), 15), round(math.sin(angle), 15), 0.0, | ||||
|             round(-math.sin(angle), 15), round(math.cos(angle), 15), 0.0 | ||||
|             ] | ||||
|         ] | ||||
| 
 | ||||
|         def transform(x, y, matrix=matrix): | ||||
|         def transform(x, y, matrix): | ||||
|             (a, b, c, d, e, f) = matrix | ||||
|             return a*x + b*y + c, d*x + e*y + f | ||||
| 
 | ||||
|         w, h = self.size | ||||
|         matrix[2], matrix[5] = transform(-center[0] - translate[0], | ||||
|                                          -center[1] - translate[1], matrix) | ||||
|         matrix[2] += center[0] | ||||
|         matrix[5] += center[1] | ||||
| 
 | ||||
|         if expand: | ||||
|             # calculate output size | ||||
|             xx = [] | ||||
|             yy = [] | ||||
|             for x, y in ((0, 0), (w, 0), (w, h), (0, h)): | ||||
|                 x, y = transform(x, y) | ||||
|                 x, y = transform(x, y, matrix) | ||||
|                 xx.append(x) | ||||
|                 yy.append(y) | ||||
|             w = int(math.ceil(max(xx)) - math.floor(min(xx))) | ||||
|             h = int(math.ceil(max(yy)) - math.floor(min(yy))) | ||||
|             nw = int(math.ceil(max(xx)) - math.floor(min(xx))) | ||||
|             nh = int(math.ceil(max(yy)) - math.floor(min(yy))) | ||||
| 
 | ||||
|         # adjust center | ||||
|         x, y = transform(w / 2.0, h / 2.0) | ||||
|         matrix[2] = self.size[0] / 2.0 - x | ||||
|         matrix[5] = self.size[1] / 2.0 - y | ||||
|             # We multiply a translation matrix from the right.  Because of its | ||||
|             # special form, this is the same as taking the image of the | ||||
|             # translation vector as new translation vector. | ||||
|             matrix[2], matrix[5] = transform(-(nw - w) / 2.0, | ||||
|                                              -(nh - h) / 2.0, | ||||
|                                              matrix) | ||||
|             w, h = nw, nh | ||||
| 
 | ||||
|         return self.transform((w, h), AFFINE, matrix, resample) | ||||
| 
 | ||||
|  | @ -1689,7 +1882,10 @@ class Image(object): | |||
|         if not format: | ||||
|             if ext not in EXTENSION: | ||||
|                 init() | ||||
|             format = EXTENSION[ext] | ||||
|             try: | ||||
|                 format = EXTENSION[ext] | ||||
|             except KeyError: | ||||
|                 raise ValueError('unknown file extension: {}'.format(ext)) | ||||
| 
 | ||||
|         if format.upper() not in SAVE: | ||||
|             init() | ||||
|  | @ -1740,8 +1936,8 @@ class Image(object): | |||
|         PPM file, and calls either the **xv** utility or the **display** | ||||
|         utility, depending on which one can be found. | ||||
| 
 | ||||
|         On macOS, this method saves the image to a temporary BMP file, and opens | ||||
|         it with the native Preview application. | ||||
|         On macOS, this method saves the image to a temporary BMP file, and | ||||
|         opens it with the native Preview application. | ||||
| 
 | ||||
|         On Windows, it saves the image to a temporary BMP file, and uses | ||||
|         the standard BMP display utility to show it (usually Paint). | ||||
|  | @ -1956,20 +2152,19 @@ class Image(object): | |||
| 
 | ||||
|     def toqimage(self): | ||||
|         """Returns a QImage copy of this image""" | ||||
|         from PIL import ImageQt | ||||
|         from . 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 | ||||
|         from . import ImageQt | ||||
|         if not ImageQt.qt_is_installed: | ||||
|             raise ImportError("Qt bindings are not installed") | ||||
|         return ImageQt.toqpixmap(self) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| # -------------------------------------------------------------------- | ||||
| # Abstract handlers. | ||||
| 
 | ||||
|  | @ -1994,6 +2189,7 @@ def _wedge(): | |||
| 
 | ||||
|     return Image()._new(core.wedge("L")) | ||||
| 
 | ||||
| 
 | ||||
| def _check_size(size): | ||||
|     """ | ||||
|     Common check to enforce type and sanity check on size tuples | ||||
|  | @ -2006,11 +2202,12 @@ def _check_size(size): | |||
|         raise ValueError("Size must be a tuple") | ||||
|     if len(size) != 2: | ||||
|         raise ValueError("Size must be a tuple of length 2") | ||||
|     if size[0] <= 0 or size[1] <= 0: | ||||
|         raise ValueError("Width and Height must be > 0") | ||||
|     if size[0] < 0 or size[1] < 0: | ||||
|         raise ValueError("Width and height must be >= 0") | ||||
| 
 | ||||
|     return True | ||||
| 
 | ||||
| 
 | ||||
| def new(mode, size, color=0): | ||||
|     """ | ||||
|     Creates a new image with the given mode and size. | ||||
|  | @ -2036,7 +2233,7 @@ def new(mode, size, color=0): | |||
|     if isStringType(color): | ||||
|         # css3-style specifier | ||||
| 
 | ||||
|         from PIL import ImageColor | ||||
|         from . import ImageColor | ||||
|         color = ImageColor.getcolor(color, mode) | ||||
| 
 | ||||
|     return Image()._new(core.fill(mode, size, color)) | ||||
|  | @ -2082,7 +2279,7 @@ def frombytes(mode, size, data, decoder_name="raw", *args): | |||
| 
 | ||||
| def fromstring(*args, **kw): | ||||
|     raise NotImplementedError("fromstring() has been removed. " + | ||||
|                     "Please call frombytes() instead.") | ||||
|                               "Please call frombytes() instead.") | ||||
| 
 | ||||
| 
 | ||||
| def frombuffer(mode, size, data, decoder_name="raw", *args): | ||||
|  | @ -2164,16 +2361,13 @@ def fromarray(obj, mode=None): | |||
|     arr = obj.__array_interface__ | ||||
|     shape = arr['shape'] | ||||
|     ndim = len(shape) | ||||
|     try: | ||||
|         strides = arr['strides'] | ||||
|     except KeyError: | ||||
|         strides = None | ||||
|     strides = arr.get('strides', None) | ||||
|     if mode is None: | ||||
|         try: | ||||
|             typekey = (1, 1) + shape[2:], arr['typestr'] | ||||
|             mode, rawmode = _fromarray_typemap[typekey] | ||||
|         except KeyError: | ||||
|             # print typekey | ||||
|             # print(typekey) | ||||
|             raise TypeError("Cannot handle this data type") | ||||
|     else: | ||||
|         rawmode = mode | ||||
|  | @ -2198,7 +2392,7 @@ def fromarray(obj, mode=None): | |||
| 
 | ||||
| def fromqimage(im): | ||||
|     """Creates an image instance from a QImage image""" | ||||
|     from PIL import ImageQt | ||||
|     from . import ImageQt | ||||
|     if not ImageQt.qt_is_installed: | ||||
|         raise ImportError("Qt bindings are not installed") | ||||
|     return ImageQt.fromqimage(im) | ||||
|  | @ -2206,11 +2400,12 @@ def fromqimage(im): | |||
| 
 | ||||
| def fromqpixmap(im): | ||||
|     """Creates an image instance from a QPixmap image""" | ||||
|     from PIL import ImageQt | ||||
|     from . 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 | ||||
|  | @ -2276,6 +2471,7 @@ def open(fp, mode="r"): | |||
|     if mode != "r": | ||||
|         raise ValueError("bad mode %r" % mode) | ||||
| 
 | ||||
|     exclusive_fp = False | ||||
|     filename = "" | ||||
|     if isPath(fp): | ||||
|         filename = fp | ||||
|  | @ -2289,11 +2485,13 @@ def open(fp, mode="r"): | |||
| 
 | ||||
|     if filename: | ||||
|         fp = builtins.open(filename, "rb") | ||||
|         exclusive_fp = True | ||||
| 
 | ||||
|     try: | ||||
|         fp.seek(0) | ||||
|     except (AttributeError, io.UnsupportedOperation): | ||||
|         fp = io.BytesIO(fp.read()) | ||||
|         exclusive_fp = True | ||||
| 
 | ||||
|     prefix = fp.read(16) | ||||
| 
 | ||||
|  | @ -2322,8 +2520,11 @@ def open(fp, mode="r"): | |||
|             im = _open_core(fp, filename, prefix) | ||||
| 
 | ||||
|     if im: | ||||
|         im._exclusive_fp = exclusive_fp | ||||
|         return im | ||||
| 
 | ||||
|     if exclusive_fp: | ||||
|         fp.close() | ||||
|     raise IOError("cannot identify image file %r" | ||||
|                   % (filename if filename else fp)) | ||||
| 
 | ||||
|  | @ -2491,6 +2692,44 @@ def register_extension(id, extension): | |||
|     EXTENSION[extension.lower()] = id.upper() | ||||
| 
 | ||||
| 
 | ||||
| def registered_extensions(): | ||||
|     """ | ||||
|     Returns a dictionary containing all file extensions belonging | ||||
|     to registered plugins | ||||
|     """ | ||||
|     if not bool(EXTENSION): | ||||
|         init() | ||||
|     return EXTENSION | ||||
| 
 | ||||
| 
 | ||||
| def register_decoder(name, decoder): | ||||
|     """ | ||||
|     Registers an image decoder.  This function should not be | ||||
|     used in application code. | ||||
| 
 | ||||
|     :param name: The name of the decoder | ||||
|     :param decoder: A callable(mode, args) that returns an | ||||
|                     ImageFile.PyDecoder object | ||||
| 
 | ||||
|     .. versionadded:: 4.1.0 | ||||
|     """ | ||||
|     DECODERS[name] = decoder | ||||
| 
 | ||||
| 
 | ||||
| def register_encoder(name, encoder): | ||||
|     """ | ||||
|     Registers an image encoder.  This function should not be | ||||
|     used in application code. | ||||
| 
 | ||||
|     :param name: The name of the encoder | ||||
|     :param encoder: A callable(mode, args) that returns an | ||||
|                     ImageFile.PyEncoder object | ||||
| 
 | ||||
|     .. versionadded:: 4.1.0 | ||||
|     """ | ||||
|     ENCODERS[name] = encoder | ||||
| 
 | ||||
| 
 | ||||
| # -------------------------------------------------------------------- | ||||
| # Simple display support.  User code may override this. | ||||
| 
 | ||||
|  | @ -2500,7 +2739,7 @@ def _show(image, **options): | |||
| 
 | ||||
| 
 | ||||
| def _showxv(image, title=None, **options): | ||||
|     from PIL import ImageShow | ||||
|     from . import ImageShow | ||||
|     ImageShow.show(image, title, **options) | ||||
| 
 | ||||
| 
 | ||||
|  | @ -2529,3 +2768,21 @@ def effect_noise(size, sigma): | |||
|     :param sigma: Standard deviation of noise. | ||||
|     """ | ||||
|     return Image()._new(core.effect_noise(size, sigma)) | ||||
| 
 | ||||
| 
 | ||||
| def linear_gradient(mode): | ||||
|     """ | ||||
|     Generate 256x256 linear gradient from black to white, top to bottom. | ||||
| 
 | ||||
|     :param mode: Input mode. | ||||
|     """ | ||||
|     return Image()._new(core.linear_gradient(mode)) | ||||
| 
 | ||||
| 
 | ||||
| def radial_gradient(mode): | ||||
|     """ | ||||
|     Generate 256x256 radial gradient from black to white, centre to edge. | ||||
| 
 | ||||
|     :param mode: Input mode. | ||||
|     """ | ||||
|     return Image()._new(core.radial_gradient(mode)) | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import Image | ||||
| from . import Image | ||||
| 
 | ||||
| 
 | ||||
| def constant(image, value): | ||||
|  |  | |||
|  | @ -166,7 +166,6 @@ class ImageCmsProfile(object): | |||
|             self._set(profile) | ||||
|         else: | ||||
|             raise TypeError("Invalid type for Profile") | ||||
|          | ||||
| 
 | ||||
|     def _set(self, profile, filename=None): | ||||
|         self.profile = profile | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import Image | ||||
| from . import Image | ||||
| import re | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -31,10 +31,9 @@ | |||
| # | ||||
| 
 | ||||
| import numbers | ||||
| import warnings | ||||
| 
 | ||||
| from PIL import Image, ImageColor | ||||
| from PIL._util import isStringType | ||||
| from . import Image, ImageColor | ||||
| from ._util import isStringType | ||||
| 
 | ||||
| """ | ||||
| A simple 2D drawing interface for PIL images. | ||||
|  | @ -87,25 +86,14 @@ class ImageDraw(object): | |||
|         self.fill = 0 | ||||
|         self.font = None | ||||
| 
 | ||||
|     def setink(self, ink): | ||||
|         raise NotImplementedError("setink() has been removed. " + | ||||
|                                   "Please use keyword arguments instead.") | ||||
| 
 | ||||
|     def setfill(self, onoff): | ||||
|         raise NotImplementedError("setfill() has been removed. " + | ||||
|                                   "Please use keyword arguments instead.") | ||||
| 
 | ||||
|     def setfont(self, font): | ||||
|         warnings.warn("setfont() is deprecated. " + | ||||
|                       "Please set the attribute directly instead.") | ||||
|         # compatibility | ||||
|         self.font = font | ||||
| 
 | ||||
|     def getfont(self): | ||||
|         """Get the current default font.""" | ||||
|         """ | ||||
|         Get the current default font. | ||||
| 
 | ||||
|         :returns: An image font.""" | ||||
|         if not self.font: | ||||
|             # FIXME: should add a font repository | ||||
|             from PIL import ImageFont | ||||
|             from . import ImageFont | ||||
|             self.font = ImageFont.load_default() | ||||
|         return self.font | ||||
| 
 | ||||
|  | @ -222,7 +210,6 @@ class ImageDraw(object): | |||
|         if self._multiline_check(text): | ||||
|             return self.multiline_text(xy, text, fill, font, anchor, | ||||
|                                        *args, **kwargs) | ||||
| 
 | ||||
|         ink, fill = self._getink(fill) | ||||
|         if font is None: | ||||
|             font = self.getfont() | ||||
|  | @ -230,17 +217,17 @@ class ImageDraw(object): | |||
|             ink = fill | ||||
|         if ink is not None: | ||||
|             try: | ||||
|                 mask, offset = font.getmask2(text, self.fontmode) | ||||
|                 mask, offset = font.getmask2(text, self.fontmode, *args, **kwargs) | ||||
|                 xy = xy[0] + offset[0], xy[1] + offset[1] | ||||
|             except AttributeError: | ||||
|                 try: | ||||
|                     mask = font.getmask(text, self.fontmode) | ||||
|                     mask = font.getmask(text, self.fontmode, *args, **kwargs) | ||||
|                 except TypeError: | ||||
|                     mask = font.getmask(text) | ||||
|             self.draw.draw_bitmap(xy, mask, ink) | ||||
| 
 | ||||
|     def multiline_text(self, xy, text, fill=None, font=None, anchor=None, | ||||
|                        spacing=4, align="left"): | ||||
|                        spacing=4, align="left", direction=None, features=None): | ||||
|         widths = [] | ||||
|         max_width = 0 | ||||
|         lines = self._multiline_split(text) | ||||
|  | @ -259,25 +246,30 @@ class ImageDraw(object): | |||
|                 left += (max_width - widths[idx]) | ||||
|             else: | ||||
|                 assert False, 'align must be "left", "center" or "right"' | ||||
|             self.text((left, top), line, fill, font, anchor) | ||||
|             self.text((left, top), line, fill, font, anchor, | ||||
|                       direction=direction, features=features) | ||||
|             top += line_spacing | ||||
|             left = xy[0] | ||||
| 
 | ||||
|     def textsize(self, text, font=None, *args, **kwargs): | ||||
|     def textsize(self, text, font=None, spacing=4, direction=None, | ||||
|                  features=None): | ||||
|         """Get the size of a given string, in pixels.""" | ||||
|         if self._multiline_check(text): | ||||
|             return self.multiline_textsize(text, font, *args, **kwargs) | ||||
|             return self.multiline_textsize(text, font, spacing, | ||||
|                                            direction, features) | ||||
| 
 | ||||
|         if font is None: | ||||
|             font = self.getfont() | ||||
|         return font.getsize(text) | ||||
|         return font.getsize(text, direction, features) | ||||
| 
 | ||||
|     def multiline_textsize(self, text, font=None, spacing=4): | ||||
|     def multiline_textsize(self, text, font=None, spacing=4, direction=None, | ||||
|                            features=None): | ||||
|         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) | ||||
|             line_width, line_height = self.textsize(line, font, spacing, | ||||
|                                                     direction, features) | ||||
|             max_width = max(max_width, line_width) | ||||
|         return max_width, len(lines)*line_spacing | ||||
| 
 | ||||
|  | @ -298,6 +290,7 @@ def Draw(im, mode=None): | |||
|     except AttributeError: | ||||
|         return ImageDraw(im, mode) | ||||
| 
 | ||||
| 
 | ||||
| # experimental access to the outline API | ||||
| try: | ||||
|     Outline = Image.core.outline | ||||
|  | @ -319,17 +312,17 @@ def getdraw(im=None, hints=None): | |||
|     handler = None | ||||
|     if not hints or "nicest" in hints: | ||||
|         try: | ||||
|             from PIL import _imagingagg as handler | ||||
|             from . import _imagingagg as handler | ||||
|         except ImportError: | ||||
|             pass | ||||
|     if handler is None: | ||||
|         from PIL import ImageDraw2 as handler | ||||
|         from . import ImageDraw2 as handler | ||||
|     if im: | ||||
|         im = handler.Draw(im) | ||||
|     return im, handler | ||||
| 
 | ||||
| 
 | ||||
| def floodfill(image, xy, value, border=None): | ||||
| def floodfill(image, xy, value, border=None, thresh=0): | ||||
|     """ | ||||
|     (experimental) Fills a bounded region with a given color. | ||||
| 
 | ||||
|  | @ -340,16 +333,20 @@ def floodfill(image, xy, value, border=None): | |||
|         pixels with a color different from the border color.  If not given, | ||||
|         the region consists of pixels having the same color as the seed | ||||
|         pixel. | ||||
|     :param thresh: Optional threshold value which specifies a maximum | ||||
|         tolerable difference of a pixel value from the 'background' in | ||||
|         order for it to be replaced. Useful for filling regions of non- | ||||
|         homogeneous, but similar, colors. | ||||
|     """ | ||||
|     # based on an implementation by Eric S. Raymond | ||||
|     pixel = image.load() | ||||
|     x, y = xy | ||||
|     try: | ||||
|         background = pixel[x, y] | ||||
|         if background == value: | ||||
|         if _color_diff(value, background) <= thresh: | ||||
|             return  # seed point already has fill color | ||||
|         pixel[x, y] = value | ||||
|     except IndexError: | ||||
|     except (ValueError, IndexError): | ||||
|         return  # seed point outside image | ||||
|     edge = [(x, y)] | ||||
|     if border is None: | ||||
|  | @ -362,7 +359,7 @@ def floodfill(image, xy, value, border=None): | |||
|                     except IndexError: | ||||
|                         pass | ||||
|                     else: | ||||
|                         if p == background: | ||||
|                         if _color_diff(p, background) <= thresh: | ||||
|                             pixel[s, t] = value | ||||
|                             newedge.append((s, t)) | ||||
|             edge = newedge | ||||
|  | @ -380,3 +377,10 @@ def floodfill(image, xy, value, border=None): | |||
|                             pixel[s, t] = value | ||||
|                             newedge.append((s, t)) | ||||
|             edge = newedge | ||||
| 
 | ||||
| 
 | ||||
| def _color_diff(rgb1, rgb2): | ||||
|     """ | ||||
|     Uses 1-norm distance to calculate difference between two rgb values. | ||||
|     """ | ||||
|     return abs(rgb1[0]-rgb2[0]) +  abs(rgb1[1]-rgb2[1]) +  abs(rgb1[2]-rgb2[2]) | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import Image, ImageColor, ImageDraw, ImageFont, ImagePath | ||||
| from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath | ||||
| 
 | ||||
| 
 | ||||
| class Pen(object): | ||||
|  |  | |||
|  | @ -18,7 +18,7 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import Image, ImageFilter, ImageStat | ||||
| from . import Image, ImageFilter, ImageStat | ||||
| 
 | ||||
| 
 | ||||
| class _Enhance(object): | ||||
|  |  | |||
							
								
								
									
										180
									
								
								PIL/ImageFile.py
									
									
									
									
									
								
							
							
						
						
									
										180
									
								
								PIL/ImageFile.py
									
									
									
									
									
								
							|  | @ -27,8 +27,8 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import Image | ||||
| from PIL._util import isPath | ||||
| from . import Image | ||||
| from ._util import isPath | ||||
| import io | ||||
| import os | ||||
| import sys | ||||
|  | @ -88,10 +88,13 @@ class ImageFile(Image.Image): | |||
|             # filename | ||||
|             self.fp = open(fp, "rb") | ||||
|             self.filename = fp | ||||
|             self._exclusive_fp = True | ||||
|         else: | ||||
|             # stream | ||||
|             self.fp = fp | ||||
|             self.filename = filename | ||||
|             # can be overridden | ||||
|             self._exclusive_fp = None | ||||
| 
 | ||||
|         try: | ||||
|             self._open() | ||||
|  | @ -100,6 +103,9 @@ class ImageFile(Image.Image): | |||
|                 KeyError,  # unsupported mode | ||||
|                 EOFError,  # got header but not the first frame | ||||
|                 struct.error) as v: | ||||
|             # close the file only if we have opened it this constructor | ||||
|             if self._exclusive_fp: | ||||
|                 self.fp.close() | ||||
|             raise SyntaxError(v) | ||||
| 
 | ||||
|         if not self.mode or self.size[0] <= 0: | ||||
|  | @ -115,6 +121,8 @@ class ImageFile(Image.Image): | |||
| 
 | ||||
|         # raise exception if something's wrong.  must be called | ||||
|         # directly after open, and closes file when finished. | ||||
|         if self._exclusive_fp: | ||||
|             self.fp.close() | ||||
|         self.fp = None | ||||
| 
 | ||||
|     def load(self): | ||||
|  | @ -152,7 +160,7 @@ class ImageFile(Image.Image): | |||
|             # try memory mapping | ||||
|             decoder_name, extents, offset, args = self.tile[0] | ||||
|             if decoder_name == "raw" and len(args) >= 3 and args[0] == self.mode \ | ||||
|                    and args[0] in Image._MAPMODES: | ||||
|                and args[0] in Image._MAPMODES: | ||||
|                 try: | ||||
|                     if hasattr(Image.core, "map"): | ||||
|                         # use built-in mapper  WIN32 only | ||||
|  | @ -178,7 +186,7 @@ class ImageFile(Image.Image): | |||
|                     self.map = None | ||||
| 
 | ||||
|         self.load_prepare() | ||||
| 
 | ||||
|         err_code = -3 # initialize to unknown error | ||||
|         if not self.map: | ||||
|             # sort tiles in file order | ||||
|             self.tile.sort(key=_tilesort) | ||||
|  | @ -191,12 +199,9 @@ class ImageFile(Image.Image): | |||
| 
 | ||||
|             for decoder_name, extents, offset, args in self.tile: | ||||
|                 decoder = Image._getdecoder(self.mode, decoder_name, | ||||
|                                       args, self.decoderconfig) | ||||
|                                             args, self.decoderconfig) | ||||
|                 seek(offset) | ||||
|                 try: | ||||
|                     decoder.setimage(self.im, extents) | ||||
|                 except ValueError: | ||||
|                     continue | ||||
|                 decoder.setimage(self.im, extents) | ||||
|                 if decoder.pulls_fd: | ||||
|                     decoder.setfd(self.fp) | ||||
|                     status, err_code = decoder.decode(b"") | ||||
|  | @ -211,7 +216,7 @@ class ImageFile(Image.Image): | |||
|                             else: | ||||
|                                 raise IOError("image file is truncated") | ||||
| 
 | ||||
|                         if not s and not decoder.handles_eof:  # truncated jpeg | ||||
|                         if not s:  # truncated jpeg | ||||
|                             self.tile = [] | ||||
| 
 | ||||
|                             # JpegDecode needs to clean things up here either way | ||||
|  | @ -237,20 +242,16 @@ class ImageFile(Image.Image): | |||
|         self.tile = [] | ||||
|         self.readonly = readonly | ||||
| 
 | ||||
|         self.fp = None  # might be shared | ||||
|         self.load_end() | ||||
| 
 | ||||
|         if self._exclusive_fp and self._close_exclusive_fp_after_loading: | ||||
|             self.fp.close() | ||||
|         self.fp = None | ||||
| 
 | ||||
|         if not self.map and not LOAD_TRUNCATED_IMAGES and err_code < 0: | ||||
|             # still raised if decoder fails to return anything | ||||
|             raise_ioerror(err_code) | ||||
| 
 | ||||
|         # post processing | ||||
|         if hasattr(self, "tile_post_rotate"): | ||||
|             # FIXME: This is a hack to handle rotated PCD's | ||||
|             self.im = self.im.rotate(self.tile_post_rotate) | ||||
|             self.size = self.im.size | ||||
| 
 | ||||
|         self.load_end() | ||||
| 
 | ||||
|         return Image.Image.load(self) | ||||
| 
 | ||||
|     def load_prepare(self): | ||||
|  | @ -379,11 +380,8 @@ class Parser(object): | |||
| 
 | ||||
|             # attempt to open this file | ||||
|             try: | ||||
|                 try: | ||||
|                     fp = io.BytesIO(self.data) | ||||
|                 with io.BytesIO(self.data) as fp: | ||||
|                     im = Image.open(fp) | ||||
|                 finally: | ||||
|                     fp.close()  # explicitly close the virtual file | ||||
|             except IOError: | ||||
|                 # traceback.print_exc() | ||||
|                 pass  # not enough data | ||||
|  | @ -431,12 +429,11 @@ class Parser(object): | |||
|         if self.data: | ||||
|             # incremental parsing not possible; reopen the file | ||||
|             # not that we have all data | ||||
|             try: | ||||
|                 fp = io.BytesIO(self.data) | ||||
|                 self.image = Image.open(fp) | ||||
|             finally: | ||||
|                 self.image.load() | ||||
|                 fp.close()  # explicitly close the virtual file | ||||
|             with io.BytesIO(self.data) as fp: | ||||
|                 try: | ||||
|                     self.image = Image.open(fp) | ||||
|                 finally: | ||||
|                     self.image.load() | ||||
|         return self.image | ||||
| 
 | ||||
| 
 | ||||
|  | @ -526,3 +523,128 @@ def _safe_read(fp, size): | |||
|         data.append(block) | ||||
|         size -= len(block) | ||||
|     return b"".join(data) | ||||
| 
 | ||||
| 
 | ||||
| class PyCodecState(object): | ||||
|     def __init__(self): | ||||
|         self.xsize = 0 | ||||
|         self.ysize = 0 | ||||
|         self.xoff = 0 | ||||
|         self.yoff = 0 | ||||
| 
 | ||||
|     def extents(self): | ||||
|         return (self.xoff, self.yoff, | ||||
|                 self.xoff+self.xsize, self.yoff+self.ysize) | ||||
| 
 | ||||
| 
 | ||||
| class PyDecoder(object): | ||||
|     """ | ||||
|     Python implementation of a format decoder. Override this class and | ||||
|     add the decoding logic in the `decode` method. | ||||
| 
 | ||||
|     See :ref:`Writing Your Own File Decoder in Python<file-decoders-py>` | ||||
|     """ | ||||
| 
 | ||||
|     _pulls_fd = False | ||||
| 
 | ||||
|     def __init__(self, mode, *args): | ||||
|         self.im = None | ||||
|         self.state = PyCodecState() | ||||
|         self.fd = None | ||||
|         self.mode = mode | ||||
|         self.init(args) | ||||
| 
 | ||||
|     def init(self, args): | ||||
|         """ | ||||
|         Override to perform decoder specific initialization | ||||
| 
 | ||||
|         :param args: Array of args items from the tile entry | ||||
|         :returns: None | ||||
|         """ | ||||
|         self.args = args | ||||
| 
 | ||||
|     @property | ||||
|     def pulls_fd(self): | ||||
|         return self._pulls_fd | ||||
| 
 | ||||
|     def decode(self, buffer): | ||||
|         """ | ||||
|         Override to perform the decoding process. | ||||
| 
 | ||||
|         :param buffer: A bytes object with the data to be decoded.  If `handles_eof` | ||||
|              is set, then `buffer` will be empty and `self.fd` will be set. | ||||
|         :returns: A tuple of (bytes consumed, errcode). If finished with decoding | ||||
|              return <0 for the bytes consumed. Err codes are from `ERRORS` | ||||
|         """ | ||||
|         raise NotImplementedError() | ||||
| 
 | ||||
|     def cleanup(self): | ||||
|         """ | ||||
|         Override to perform decoder specific cleanup | ||||
| 
 | ||||
|         :returns: None | ||||
|         """ | ||||
|         pass | ||||
| 
 | ||||
|     def setfd(self, fd): | ||||
|         """ | ||||
|         Called from ImageFile to set the python file-like object | ||||
| 
 | ||||
|         :param fd: A python file-like object | ||||
|         :returns: None | ||||
|         """ | ||||
|         self.fd = fd | ||||
| 
 | ||||
|     def setimage(self, im, extents=None): | ||||
|         """ | ||||
|         Called from ImageFile to set the core output image for the decoder | ||||
| 
 | ||||
|         :param im: A core image object | ||||
|         :param extents: a 4 tuple of (x0, y0, x1, y1) defining the rectangle | ||||
|             for this tile | ||||
|         :returns: None | ||||
|         """ | ||||
| 
 | ||||
|         # following c code | ||||
|         self.im = im | ||||
| 
 | ||||
|         if extents: | ||||
|             (x0, y0, x1, y1) = extents | ||||
|         else: | ||||
|             (x0, y0, x1, y1) = (0, 0, 0, 0) | ||||
| 
 | ||||
|         if x0 == 0 and x1 == 0: | ||||
|             self.state.xsize, self.state.ysize = self.im.size | ||||
|         else: | ||||
|             self.state.xoff = x0 | ||||
|             self.state.yoff = y0 | ||||
|             self.state.xsize = x1 - x0 | ||||
|             self.state.ysize = y1 - y0 | ||||
| 
 | ||||
|         if self.state.xsize <= 0 or self.state.ysize <= 0: | ||||
|             raise ValueError("Size cannot be negative") | ||||
| 
 | ||||
|         if (self.state.xsize + self.state.xoff > self.im.size[0] or | ||||
|            self.state.ysize + self.state.yoff > self.im.size[1]): | ||||
|             raise ValueError("Tile cannot extend outside image") | ||||
| 
 | ||||
|     def set_as_raw(self, data, rawmode=None): | ||||
|         """ | ||||
|         Convenience method to set the internal image from a stream of raw data | ||||
| 
 | ||||
|         :param data: Bytes to be set | ||||
|         :param rawmode: The rawmode to be used for the decoder. If not specified, | ||||
|              it will default to the mode of the image | ||||
|         :returns: None | ||||
|         """ | ||||
| 
 | ||||
|         if not rawmode: | ||||
|             rawmode = self.mode | ||||
|         d = Image._getdecoder(self.mode, 'raw', (rawmode)) | ||||
|         d.setimage(self.im, self.state.extents()) | ||||
|         s = d.decode(data) | ||||
| 
 | ||||
|         if s[0] >= 0: | ||||
|             raise ValueError("not enough image data") | ||||
|         if s[1] != 0: | ||||
|             raise ValueError("cannot decode image data") | ||||
|  |  | |||
|  | @ -25,8 +25,8 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import Image | ||||
| from PIL._util import isDirectory, isPath | ||||
| from . import Image | ||||
| from ._util import isDirectory, isPath | ||||
| import os | ||||
| import sys | ||||
| 
 | ||||
|  | @ -37,10 +37,13 @@ class _imagingft_not_installed(object): | |||
|         raise ImportError("The _imagingft C module is not installed") | ||||
| 
 | ||||
| try: | ||||
|     from PIL import _imagingft as core | ||||
|     from . import _imagingft as core | ||||
| except ImportError: | ||||
|     core = _imagingft_not_installed() | ||||
| 
 | ||||
| LAYOUT_BASIC = 0 | ||||
| LAYOUT_RAQM = 1 | ||||
| 
 | ||||
| # FIXME: add support for pilfont2 format (see FontFile.py) | ||||
| 
 | ||||
| # -------------------------------------------------------------------- | ||||
|  | @ -103,9 +106,12 @@ class ImageFont(object): | |||
| 
 | ||||
|         self.font = Image.core.font(image.im, data) | ||||
| 
 | ||||
|         # delegate critical operations to internal type | ||||
|         self.getsize = self.font.getsize | ||||
|         self.getmask = self.font.getmask | ||||
|     def getsize(self, text, *args, **kwargs): | ||||
|         return self.font.getsize(text) | ||||
| 
 | ||||
|     def getmask(self, text, mode="", *args, **kwargs): | ||||
|         return self.font.getmask(text, mode) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ## | ||||
|  | @ -115,7 +121,8 @@ class ImageFont(object): | |||
| class FreeTypeFont(object): | ||||
|     "FreeType font wrapper (requires _imagingft service)" | ||||
| 
 | ||||
|     def __init__(self, font=None, size=10, index=0, encoding=""): | ||||
|     def __init__(self, font=None, size=10, index=0, encoding="", | ||||
|                  layout_engine=None): | ||||
|         # FIXME: use service provider instead | ||||
| 
 | ||||
|         self.path = font | ||||
|  | @ -123,12 +130,21 @@ class FreeTypeFont(object): | |||
|         self.index = index | ||||
|         self.encoding = encoding | ||||
| 
 | ||||
|         if layout_engine not in (LAYOUT_BASIC, LAYOUT_RAQM): | ||||
|             layout_engine = LAYOUT_BASIC | ||||
|             if core.HAVE_RAQM: | ||||
|                 layout_engine = LAYOUT_RAQM | ||||
|         if layout_engine == LAYOUT_RAQM and not core.HAVE_RAQM: | ||||
|             layout_engine = LAYOUT_BASIC | ||||
| 
 | ||||
|         self.layout_engine = layout_engine | ||||
| 
 | ||||
|         if isPath(font): | ||||
|             self.font = core.getfont(font, size, index, encoding) | ||||
|             self.font = core.getfont(font, size, index, encoding, layout_engine=layout_engine) | ||||
|         else: | ||||
|             self.font_bytes = font.read() | ||||
|             self.font = core.getfont( | ||||
|                 "", size, index, encoding, self.font_bytes) | ||||
|                 "", size, index, encoding, self.font_bytes, layout_engine) | ||||
| 
 | ||||
|     def getname(self): | ||||
|         return self.font.family, self.font.style | ||||
|  | @ -136,23 +152,24 @@ class FreeTypeFont(object): | |||
|     def getmetrics(self): | ||||
|         return self.font.ascent, self.font.descent | ||||
| 
 | ||||
|     def getsize(self, text): | ||||
|         size, offset = self.font.getsize(text) | ||||
|     def getsize(self, text, direction=None, features=None): | ||||
|         size, offset = self.font.getsize(text, direction, features) | ||||
|         return (size[0] + offset[0], size[1] + offset[1]) | ||||
| 
 | ||||
|     def getoffset(self, text): | ||||
|         return self.font.getsize(text)[1] | ||||
| 
 | ||||
|     def getmask(self, text, mode=""): | ||||
|         return self.getmask2(text, mode)[0] | ||||
|     def getmask(self, text, mode="", direction=None, features=None): | ||||
|         return self.getmask2(text, mode, direction=direction, features=features)[0] | ||||
| 
 | ||||
|     def getmask2(self, text, mode="", fill=Image.core.fill): | ||||
|         size, offset = self.font.getsize(text) | ||||
|     def getmask2(self, text, mode="", fill=Image.core.fill, direction=None, features=None): | ||||
|         size, offset = self.font.getsize(text, direction, features) | ||||
|         im = fill("L", size, 0) | ||||
|         self.font.render(text, im.id, mode == "1") | ||||
|         self.font.render(text, im.id, mode == "1", direction, features) | ||||
|         return im, offset | ||||
| 
 | ||||
|     def font_variant(self, font=None, size=None, index=None, encoding=None): | ||||
|     def font_variant(self, font=None, size=None, index=None, encoding=None, | ||||
|                      layout_engine=None): | ||||
|         """ | ||||
|         Create a copy of this FreeTypeFont object, | ||||
|         using any specified arguments to override the settings. | ||||
|  | @ -165,8 +182,9 @@ class FreeTypeFont(object): | |||
|         return FreeTypeFont(font=self.path if font is None else font, | ||||
|                             size=self.size if size is None else size, | ||||
|                             index=self.index if index is None else index, | ||||
|                             encoding=self.encoding if encoding is None else | ||||
|                             encoding) | ||||
|                             encoding=self.encoding if encoding is None else encoding, | ||||
|                             layout_engine=self.layout_engine if layout_engine is None else layout_engine | ||||
|                             ) | ||||
| 
 | ||||
| 
 | ||||
| class TransposedFont(object): | ||||
|  | @ -185,14 +203,14 @@ class TransposedFont(object): | |||
|         self.font = font | ||||
|         self.orientation = orientation  # any 'transpose' argument, or None | ||||
| 
 | ||||
|     def getsize(self, text): | ||||
|     def getsize(self, text, *args, **kwargs): | ||||
|         w, h = self.font.getsize(text) | ||||
|         if self.orientation in (Image.ROTATE_90, Image.ROTATE_270): | ||||
|             return h, w | ||||
|         return w, h | ||||
| 
 | ||||
|     def getmask(self, text, mode=""): | ||||
|         im = self.font.getmask(text, mode) | ||||
|     def getmask(self, text, mode="", *args, **kwargs): | ||||
|         im = self.font.getmask(text, mode, *args, **kwargs) | ||||
|         if self.orientation is not None: | ||||
|             return im.transpose(self.orientation) | ||||
|         return im | ||||
|  | @ -212,7 +230,8 @@ def load(filename): | |||
|     return f | ||||
| 
 | ||||
| 
 | ||||
| def truetype(font=None, size=10, index=0, encoding=""): | ||||
| def truetype(font=None, size=10, index=0, encoding="", | ||||
|              layout_engine=None): | ||||
|     """ | ||||
|     Load a TrueType or OpenType font file, and create a font object. | ||||
|     This function loads a font object from the given file, and creates | ||||
|  | @ -230,12 +249,14 @@ def truetype(font=None, size=10, index=0, encoding=""): | |||
|                      Symbol), "ADOB" (Adobe Standard), "ADBE" (Adobe Expert), | ||||
|                      and "armn" (Apple Roman). See the FreeType documentation | ||||
|                      for more information. | ||||
|     :param layout_engine: Which layout engine to use, if available: | ||||
|                      `ImageFont.LAYOUT_BASIC` or `ImageFont.LAYOUT_RAQM`.                   | ||||
|     :return: A font object. | ||||
|     :exception IOError: If the file could not be read. | ||||
|     """ | ||||
| 
 | ||||
|     try: | ||||
|         return FreeTypeFont(font, size, index, encoding) | ||||
|         return FreeTypeFont(font, size, index, encoding, layout_engine) | ||||
|     except IOError: | ||||
|         ttf_filename = os.path.basename(font) | ||||
| 
 | ||||
|  | @ -266,16 +287,16 @@ def truetype(font=None, size=10, index=0, encoding=""): | |||
|                 for walkfilename in walkfilenames: | ||||
|                     if ext and walkfilename == ttf_filename: | ||||
|                         fontpath = os.path.join(walkroot, walkfilename) | ||||
|                         return FreeTypeFont(fontpath, size, index, encoding) | ||||
|                         return FreeTypeFont(fontpath, size, index, encoding, layout_engine) | ||||
|                     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) | ||||
|                             return FreeTypeFont(fontpath, size, index, encoding, layout_engine) | ||||
|                         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) | ||||
|                                 index, encoding, layout_engine) | ||||
|         raise | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import Image | ||||
| from . import Image | ||||
| 
 | ||||
| import sys | ||||
| if sys.platform not in ["win32", "darwin"]: | ||||
|  | @ -75,7 +75,7 @@ def grabclipboard(): | |||
|         debug = 0  # temporary interface | ||||
|         data = Image.core.grabclipboard(debug) | ||||
|         if isinstance(data, bytes): | ||||
|             from PIL import BmpImagePlugin | ||||
|             from . import BmpImagePlugin | ||||
|             import io | ||||
|             return BmpImagePlugin.DibImageFile(io.BytesIO(data)) | ||||
|         return data | ||||
|  |  | |||
|  | @ -15,8 +15,7 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import Image | ||||
| from PIL import _imagingmath | ||||
| from . import Image, _imagingmath | ||||
| 
 | ||||
| try: | ||||
|     import builtins | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ | |||
| # | ||||
| 
 | ||||
| # mode descriptor cache | ||||
| _modes = {} | ||||
| _modes = None | ||||
| 
 | ||||
| 
 | ||||
| class ModeDescriptor(object): | ||||
|  | @ -32,19 +32,24 @@ class ModeDescriptor(object): | |||
| 
 | ||||
| def getmode(mode): | ||||
|     """Gets a mode descriptor for the given mode.""" | ||||
|     global _modes | ||||
|     if not _modes: | ||||
|         # initialize mode cache | ||||
|         from PIL import Image | ||||
| 
 | ||||
|         from . import Image | ||||
|         modes = {} | ||||
|         # core modes | ||||
|         for m, (basemode, basetype, bands) in Image._MODEINFO.items(): | ||||
|             _modes[m] = ModeDescriptor(m, bands, basemode, basetype) | ||||
|             modes[m] = ModeDescriptor(m, bands, basemode, basetype) | ||||
|         # extra experimental modes | ||||
|         _modes["RGBa"] = ModeDescriptor("RGBa", ("R", "G", "B", "a"), "RGB", "L") | ||||
|         _modes["LA"] = ModeDescriptor("LA", ("L", "A"), "L", "L") | ||||
|         _modes["La"] = ModeDescriptor("La", ("L", "a"), "L", "L") | ||||
|         _modes["PA"] = ModeDescriptor("PA", ("P", "A"), "RGB", "L") | ||||
|         modes["RGBa"] = ModeDescriptor("RGBa", ("R", "G", "B", "a"), "RGB", "L") | ||||
|         modes["LA"] = ModeDescriptor("LA", ("L", "A"), "L", "L") | ||||
|         modes["La"] = ModeDescriptor("La", ("L", "a"), "L", "L") | ||||
|         modes["PA"] = ModeDescriptor("PA", ("P", "A"), "RGB", "L") | ||||
|         # mapping modes | ||||
|         _modes["I;16"] = ModeDescriptor("I;16", "I", "L", "L") | ||||
|         _modes["I;16L"] = ModeDescriptor("I;16L", "I", "L", "L") | ||||
|         _modes["I;16B"] = ModeDescriptor("I;16B", "I", "L", "L") | ||||
|         modes["I;16"] = ModeDescriptor("I;16", "I", "L", "L") | ||||
|         modes["I;16L"] = ModeDescriptor("I;16L", "I", "L", "L") | ||||
|         modes["I;16B"] = ModeDescriptor("I;16B", "I", "L", "L") | ||||
|         # set global mode cache atomically | ||||
|         _modes = modes | ||||
|     return _modes[mode] | ||||
|  |  | |||
|  | @ -5,8 +5,9 @@ | |||
| # | ||||
| # Copyright (c) 2014 Dov Grobgeld <dov.grobgeld@gmail.com> | ||||
| 
 | ||||
| from PIL import Image | ||||
| from PIL import _imagingmorph | ||||
| from __future__ import print_function | ||||
| 
 | ||||
| from . import Image, _imagingmorph | ||||
| import re | ||||
| 
 | ||||
| LUT_SIZE = 1 << 9 | ||||
|  | @ -122,7 +123,7 @@ class LutBuilder(object): | |||
|                            .replace('0', 'Z') | ||||
|                            .replace('1', '0') | ||||
|                            .replace('Z', '1')) | ||||
|                 res = '%d' % (1-int(res)) | ||||
|                 res = 1-int(res) | ||||
|                 patterns.append((pattern, res)) | ||||
| 
 | ||||
|         return patterns | ||||
|  | @ -151,9 +152,9 @@ class LutBuilder(object): | |||
|             patterns += self._pattern_permute(pattern, options, result) | ||||
| 
 | ||||
| #        # Debugging | ||||
| #        for p,r in patterns: | ||||
| #            print p,r | ||||
| #        print '--' | ||||
| #        for p, r in patterns: | ||||
| #            print(p, r) | ||||
| #        print('--') | ||||
| 
 | ||||
|         # compile the patterns into regular expressions for speed | ||||
|         for i, pattern in enumerate(patterns): | ||||
|  | @ -233,7 +234,7 @@ class MorphOp(object): | |||
|         with open(filename, 'rb') as f: | ||||
|             self.lut = bytearray(f.read()) | ||||
| 
 | ||||
|         if len(self.lut) != 8192: | ||||
|         if len(self.lut) != LUT_SIZE: | ||||
|             self.lut = None | ||||
|             raise Exception('Wrong size operator file!') | ||||
| 
 | ||||
|  |  | |||
|  | @ -17,8 +17,8 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import Image | ||||
| from PIL._util import isStringType | ||||
| from . import Image | ||||
| from ._util import isStringType | ||||
| import operator | ||||
| import functools | ||||
| 
 | ||||
|  | @ -39,7 +39,7 @@ def _border(border): | |||
| 
 | ||||
| def _color(color, mode): | ||||
|     if isStringType(color): | ||||
|         from PIL import ImageColor | ||||
|         from . import ImageColor | ||||
|         color = ImageColor.getcolor(color, mode) | ||||
|     return color | ||||
| 
 | ||||
|  | @ -206,7 +206,8 @@ def deform(image, deformer, resample=Image.BILINEAR): | |||
|     :param image: The image to deform. | ||||
|     :param deformer: A deformer object.  Any object that implements a | ||||
|                     **getmesh** method can be used. | ||||
|     :param resample: What resampling filter to use. | ||||
|     :param resample: An optional resampling filter. Same values possible as | ||||
|        in the PIL.Image.transform function. | ||||
|     :return: An image. | ||||
|     """ | ||||
|     return image.transform( | ||||
|  |  | |||
|  | @ -17,10 +17,7 @@ | |||
| # | ||||
| 
 | ||||
| import array | ||||
| from PIL import ImageColor | ||||
| from PIL import GimpPaletteFile | ||||
| from PIL import GimpGradientFile | ||||
| from PIL import PaletteFile | ||||
| from . import ImageColor, GimpPaletteFile, GimpGradientFile, PaletteFile | ||||
| 
 | ||||
| 
 | ||||
| class ImagePalette(object): | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import Image | ||||
| from . import Image | ||||
| 
 | ||||
| 
 | ||||
| # the Python class below is overridden by the C implementation. | ||||
|  |  | |||
|  | @ -16,8 +16,8 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import Image | ||||
| from PIL._util import isPath | ||||
| from . import Image | ||||
| from ._util import isPath | ||||
| from io import BytesIO | ||||
| 
 | ||||
| qt_is_installed = True | ||||
|  |  | |||
|  | @ -69,7 +69,7 @@ class Viewer(object): | |||
|             # FIXME: auto-contrast if max() > 255? | ||||
|         else: | ||||
|             base = Image.getmodebase(image.mode) | ||||
|         if base != image.mode and image.mode != "1": | ||||
|         if base != image.mode and image.mode != "1" and image.mode != "RGBA": | ||||
|             image = image.convert(base) | ||||
| 
 | ||||
|         return self.show_image(image, **options) | ||||
|  | @ -77,6 +77,7 @@ class Viewer(object): | |||
|     # hook methods | ||||
| 
 | ||||
|     format = None | ||||
|     options = {} | ||||
| 
 | ||||
|     def get_format(self, image): | ||||
|         """Return format name, or None to save as PGM/PPM""" | ||||
|  | @ -87,7 +88,7 @@ class Viewer(object): | |||
| 
 | ||||
|     def save_image(self, image): | ||||
|         """Save to temporary file, and return filename""" | ||||
|         return image._dump(format=self.get_format(image)) | ||||
|         return image._dump(format=self.get_format(image), **self.options) | ||||
| 
 | ||||
|     def show_image(self, image, **options): | ||||
|         """Display given image""" | ||||
|  | @ -115,7 +116,8 @@ if sys.platform == "win32": | |||
| elif sys.platform == "darwin": | ||||
| 
 | ||||
|     class MacViewer(Viewer): | ||||
|         format = "BMP" | ||||
|         format = "PNG" | ||||
|         options = {'compress_level': 1} | ||||
| 
 | ||||
|         def get_command(self, file, **options): | ||||
|             # on darwin open returns immediately resulting in the temp | ||||
|  | @ -142,6 +144,9 @@ else: | |||
|         return None | ||||
| 
 | ||||
|     class UnixViewer(Viewer): | ||||
|         format = "PNG" | ||||
|         options = {'compress_level': 1} | ||||
| 
 | ||||
|         def show_file(self, file, **options): | ||||
|             command, executable = self.get_command_ex(file, **options) | ||||
|             command = "(%s %s; rm -f %s)&" % (command, quote(file), | ||||
|  |  | |||
|  | @ -25,14 +25,21 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| try: | ||||
|     import tkinter | ||||
| except ImportError: | ||||
|     import Tkinter | ||||
|     tkinter = Tkinter | ||||
|     del Tkinter | ||||
| import sys | ||||
| 
 | ||||
| from PIL import Image | ||||
| if sys.version_info[0] > 2: | ||||
|     import tkinter | ||||
| else: | ||||
|     import Tkinter as tkinter | ||||
| 
 | ||||
| # required for pypy, which always has cffi installed | ||||
| try: | ||||
|     from cffi import FFI | ||||
|     ffi = FFI() | ||||
| except ImportError: | ||||
|     pass | ||||
| 
 | ||||
| from . import Image | ||||
| from io import BytesIO | ||||
| 
 | ||||
| 
 | ||||
|  | @ -182,9 +189,15 @@ class PhotoImage(object): | |||
|         except tkinter.TclError: | ||||
|             # activate Tkinter hook | ||||
|             try: | ||||
|                 from PIL import _imagingtk | ||||
|                 from . import _imagingtk | ||||
|                 try: | ||||
|                     _imagingtk.tkinit(tk.interpaddr(), 1) | ||||
|                     if hasattr(tk, 'interp'): | ||||
|                         # Pypy is using a ffi cdata element | ||||
|                         # (Pdb) self.tk.interp | ||||
|                         #  <cdata 'Tcl_Interp *' 0x3061b50> | ||||
|                         _imagingtk.tkinit(int(ffi.cast("uintptr_t", tk.interp)), 1) | ||||
|                     else: | ||||
|                         _imagingtk.tkinit(tk.interpaddr(), 1) | ||||
|                 except AttributeError: | ||||
|                     _imagingtk.tkinit(id(tk), 0) | ||||
|                 tk.call("PyImagingPhoto", self.__photo, block.id) | ||||
|  | @ -264,6 +277,8 @@ class BitmapImage(object): | |||
| 
 | ||||
| 
 | ||||
| def getimage(photo): | ||||
|     """ This function is unimplemented """ | ||||
|      | ||||
|     """Copies the contents of a PhotoImage to a PIL image memory.""" | ||||
|     photo.tk.call("PyImagingPhotoGet", photo) | ||||
| 
 | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import Image | ||||
| from . import Image | ||||
| 
 | ||||
| 
 | ||||
| class Transform(Image.ImageTransformHandler): | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import Image | ||||
| from . import Image | ||||
| 
 | ||||
| 
 | ||||
| class HDC(object): | ||||
|  | @ -182,14 +182,6 @@ class Dib(object): | |||
|         """ | ||||
|         return self.image.tobytes() | ||||
| 
 | ||||
|     def fromstring(self, *args, **kw): | ||||
|         raise NotImplementedError("fromstring() has been removed. " + | ||||
|                                   "Please use frombytes() instead.") | ||||
| 
 | ||||
|     def tostring(self, *args, **kw): | ||||
|         raise NotImplementedError("tostring() has been removed. " + | ||||
|                                   "Please use tobytes() instead.") | ||||
| 
 | ||||
| 
 | ||||
| class Window(object): | ||||
|     """Create a Window with the given title size.""" | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ | |||
| 
 | ||||
| import re | ||||
| 
 | ||||
| from PIL import Image, ImageFile | ||||
| from . import Image, ImageFile | ||||
| 
 | ||||
| __version__ = "0.2" | ||||
| 
 | ||||
|  |  | |||
|  | @ -17,17 +17,13 @@ | |||
| 
 | ||||
| from __future__ import print_function | ||||
| 
 | ||||
| from PIL import Image, ImageFile, _binary | ||||
| from . import Image, ImageFile | ||||
| from ._binary import i8, i16be as i16, i32be as i32, o8 | ||||
| import os | ||||
| import tempfile | ||||
| 
 | ||||
| __version__ = "0.3" | ||||
| 
 | ||||
| i8 = _binary.i8 | ||||
| i16 = _binary.i16be | ||||
| i32 = _binary.i32be | ||||
| o8 = _binary.o8 | ||||
| 
 | ||||
| COMPRESSION = { | ||||
|     1: "raw", | ||||
|     5: "jpeg" | ||||
|  | @ -99,7 +95,7 @@ class IptcImageFile(ImageFile.ImageFile): | |||
|                 tagdata = self.fp.read(size) | ||||
|             else: | ||||
|                 tagdata = None | ||||
|             if tag in list(self.info.keys()): | ||||
|             if tag in self.info: | ||||
|                 if isinstance(self.info[tag], list): | ||||
|                     self.info[tag].append(tagdata) | ||||
|                 else: | ||||
|  | @ -107,7 +103,7 @@ class IptcImageFile(ImageFile.ImageFile): | |||
|             else: | ||||
|                 self.info[tag] = tagdata | ||||
| 
 | ||||
|             # print tag, self.info[tag] | ||||
|             # print(tag, self.info[tag]) | ||||
| 
 | ||||
|         # mode | ||||
|         layers = i8(self.info[(3, 60)][0]) | ||||
|  | @ -191,7 +187,7 @@ def getiptcinfo(im): | |||
|     :returns: A dictionary containing IPTC information, or None if | ||||
|         no IPTC information block was found. | ||||
|     """ | ||||
|     from PIL import TiffImagePlugin, JpegImagePlugin | ||||
|     from . import TiffImagePlugin, JpegImagePlugin | ||||
|     import io | ||||
| 
 | ||||
|     data = None | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ | |||
| # | ||||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| from PIL import Image, ImageFile | ||||
| from . import Image, ImageFile | ||||
| import struct | ||||
| import os | ||||
| import io | ||||
|  |  | |||
|  | @ -32,19 +32,16 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from __future__ import print_function | ||||
| 
 | ||||
| import array | ||||
| import struct | ||||
| import io | ||||
| import warnings | ||||
| from struct import unpack_from | ||||
| from PIL import Image, ImageFile, TiffImagePlugin, _binary | ||||
| from PIL.JpegPresets import presets | ||||
| from PIL._util import isStringType | ||||
| 
 | ||||
| i8 = _binary.i8 | ||||
| o8 = _binary.o8 | ||||
| i16 = _binary.i16be | ||||
| i32 = _binary.i32be | ||||
| from . import Image, ImageFile, TiffImagePlugin | ||||
| from ._binary import i8, o8, i16be as i16 | ||||
| from .JpegPresets import presets | ||||
| from ._util import isStringType | ||||
| 
 | ||||
| __version__ = "0.6" | ||||
| 
 | ||||
|  | @ -120,6 +117,25 @@ def APP(self, marker): | |||
|         # plus constant header size | ||||
|         self.info["mpoffset"] = self.fp.tell() - n + 4 | ||||
| 
 | ||||
|     # If DPI isn't in JPEG header, fetch from EXIF | ||||
|     if "dpi" not in self.info and "exif" in self.info: | ||||
|         try: | ||||
|             exif = self._getexif() | ||||
|             resolution_unit = exif[0x0128] | ||||
|             x_resolution = exif[0x011A] | ||||
|             try: | ||||
|                 dpi = x_resolution[0] / x_resolution[1] | ||||
|             except TypeError: | ||||
|                 dpi = x_resolution | ||||
|             if resolution_unit == 3:  # cm | ||||
|                 # 1 dpcm = 2.54 dpi | ||||
|                 dpi *= 2.54 | ||||
|             self.info["dpi"] = dpi, dpi | ||||
|         except (KeyError, SyntaxError): | ||||
|             # SyntaxError for invalid/unreadable exif | ||||
|             # KeyError for dpi not included | ||||
|             self.info["dpi"] = 72, 72 | ||||
| 
 | ||||
| 
 | ||||
| def COM(self, marker): | ||||
|     # | ||||
|  | @ -316,7 +332,7 @@ class JpegImageFile(ImageFile.ImageFile): | |||
| 
 | ||||
|             if i in MARKER: | ||||
|                 name, description, handler = MARKER[i] | ||||
|                 # print hex(i), name, description | ||||
|                 # print(hex(i), name, description) | ||||
|                 if handler is not None: | ||||
|                     handler(self, i) | ||||
|                 if i == 0xFFDA:  # start of scan | ||||
|  | @ -409,7 +425,8 @@ def _fixup_dict(src_dict): | |||
|         try: | ||||
|             if len(value) == 1 and not isinstance(value, dict): | ||||
|                 return value[0] | ||||
|         except: pass | ||||
|         except: | ||||
|             pass | ||||
|         return value | ||||
| 
 | ||||
|     return {k: _fixup(v) for k, v in src_dict.items()} | ||||
|  | @ -491,7 +508,7 @@ def _getmp(self): | |||
|     try: | ||||
|         rawmpentries = mp[0xB002] | ||||
|         for entrynum in range(0, quant): | ||||
|             unpackedentry = unpack_from( | ||||
|             unpackedentry = struct.unpack_from( | ||||
|                 '{}LLLHH'.format(endianness), rawmpentries, entrynum * 16) | ||||
|             labels = ('Attribute', 'Size', 'DataOffset', 'EntryNo1', | ||||
|                       'EntryNo2') | ||||
|  | @ -540,7 +557,6 @@ RAWMODE = { | |||
|     "1": "L", | ||||
|     "L": "L", | ||||
|     "RGB": "RGB", | ||||
|     "RGBA": "RGB", | ||||
|     "RGBX": "RGB", | ||||
|     "CMYK": "CMYK;I",  # assume adobe conventions | ||||
|     "YCbCr": "YCbCr", | ||||
|  | @ -589,14 +605,6 @@ def _save(im, fp, filename): | |||
|     except KeyError: | ||||
|         raise IOError("cannot write mode %s as JPEG" % im.mode) | ||||
| 
 | ||||
|     if im.mode == 'RGBA': | ||||
|         warnings.warn( | ||||
|             'You are saving RGBA image as JPEG. The alpha channel will be ' | ||||
|             'discarded. This conversion is deprecated and will be disabled ' | ||||
|             'in Pillow 3.7. Please, convert the image to RGB explicitly.', | ||||
|             DeprecationWarning | ||||
|         ) | ||||
| 
 | ||||
|     info = im.encoderinfo | ||||
| 
 | ||||
|     dpi = [int(round(x)) for x in info.get("dpi", (0, 0))] | ||||
|  | @ -691,8 +699,8 @@ def _save(im, fp, filename): | |||
|     # "progressive" is the official name, but older documentation | ||||
|     # says "progression" | ||||
|     # FIXME: issue a warning if the wrong form is used (post-1.1.7) | ||||
|     progressive = info.get("progressive", False) or\ | ||||
|                   info.get("progression", False) | ||||
|     progressive = (info.get("progressive", False) or | ||||
|                    info.get("progression", False)) | ||||
| 
 | ||||
|     optimize = info.get("optimize", False) | ||||
| 
 | ||||
|  | @ -716,15 +724,19 @@ def _save(im, fp, filename): | |||
|     # https://github.com/matthewwithanm/django-imagekit/issues/50 | ||||
|     bufsize = 0 | ||||
|     if optimize or progressive: | ||||
|         # CMYK can be bigger | ||||
|         if im.mode == 'CMYK': | ||||
|             bufsize = 4 * im.size[0] * im.size[1] | ||||
|         # keep sets quality to 0, but the actual value may be high. | ||||
|         if quality >= 95 or quality == 0: | ||||
|         elif quality >= 95 or quality == 0: | ||||
|             bufsize = 2 * im.size[0] * im.size[1] | ||||
|         else: | ||||
|             bufsize = im.size[0] * im.size[1] | ||||
| 
 | ||||
|     # The exif info needs to be written as one block, + APP1, + one spare byte. | ||||
|     # Ensure that our buffer is big enough | ||||
|     bufsize = max(ImageFile.MAXBLOCK, bufsize, len(info.get("exif", b"")) + 5) | ||||
|     # Ensure that our buffer is big enough. Same with the icc_profile block. | ||||
|     bufsize = max(ImageFile.MAXBLOCK, bufsize, len(info.get("exif", b"")) + 5, | ||||
|                   len(extra) + 1) | ||||
| 
 | ||||
|     ImageFile._save(im, fp, [("jpeg", (0, 0)+im.size, 0, rawmode)], bufsize) | ||||
| 
 | ||||
|  |  | |||
|  | @ -62,7 +62,7 @@ The tables format between im.quantization and quantization in presets differ in | |||
| You can convert the dict format to the preset format with the | ||||
| `JpegImagePlugin.convert_dict_qtables(dict_qtables)` function. | ||||
| 
 | ||||
| Libjpeg ref.: http://web.archive.org/web/20120328125543/http://www.jpegcameras.com/libjpeg/libjpeg-3.html | ||||
| Libjpeg ref.: https://web.archive.org/web/20120328125543/http://www.jpegcameras.com/libjpeg/libjpeg-3.html | ||||
| 
 | ||||
| """ | ||||
| 
 | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ | |||
| # | ||||
| 
 | ||||
| import struct | ||||
| from PIL import Image, ImageFile | ||||
| from . import Image, ImageFile | ||||
| 
 | ||||
| __version__ = "0.2" | ||||
| 
 | ||||
|  | @ -66,6 +66,7 @@ class McIdasImageFile(ImageFile.ImageFile): | |||
| 
 | ||||
|         self.tile = [("raw", (0, 0) + self.size, offset, (rawmode, stride, 1))] | ||||
| 
 | ||||
| 
 | ||||
| # -------------------------------------------------------------------- | ||||
| # registry | ||||
| 
 | ||||
|  |  | |||
|  | @ -17,8 +17,9 @@ | |||
| # | ||||
| 
 | ||||
| 
 | ||||
| from PIL import Image, TiffImagePlugin | ||||
| from PIL.OleFileIO import MAGIC, OleFileIO | ||||
| from . import Image, TiffImagePlugin | ||||
| 
 | ||||
| import olefile | ||||
| 
 | ||||
| __version__ = "0.1" | ||||
| 
 | ||||
|  | @ -28,7 +29,7 @@ __version__ = "0.1" | |||
| 
 | ||||
| 
 | ||||
| def _accept(prefix): | ||||
|     return prefix[:8] == MAGIC | ||||
|     return prefix[:8] == olefile.MAGIC | ||||
| 
 | ||||
| 
 | ||||
| ## | ||||
|  | @ -38,6 +39,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): | |||
| 
 | ||||
|     format = "MIC" | ||||
|     format_description = "Microsoft Image Composer" | ||||
|     _close_exclusive_fp_after_loading = False | ||||
| 
 | ||||
|     def _open(self): | ||||
| 
 | ||||
|  | @ -45,7 +47,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): | |||
|         # to be a Microsoft Image Composer file | ||||
| 
 | ||||
|         try: | ||||
|             self.ole = OleFileIO(self.fp) | ||||
|             self.ole = olefile.OleFileIO(self.fp) | ||||
|         except IOError: | ||||
|             raise SyntaxError("not an MIC file; invalid OLE file") | ||||
| 
 | ||||
|  | @ -95,6 +97,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): | |||
| 
 | ||||
|         return self.frame | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # -------------------------------------------------------------------- | ||||
| 
 | ||||
|  |  | |||
|  | @ -14,8 +14,8 @@ | |||
| # | ||||
| 
 | ||||
| 
 | ||||
| from PIL import Image, ImageFile | ||||
| from PIL._binary import i8 | ||||
| from . import Image, ImageFile | ||||
| from ._binary import i8 | ||||
| 
 | ||||
| __version__ = "0.1" | ||||
| 
 | ||||
|  |  | |||
|  | @ -18,7 +18,7 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import Image, JpegImagePlugin | ||||
| from . import Image, JpegImagePlugin | ||||
| 
 | ||||
| __version__ = "0.1" | ||||
| 
 | ||||
|  | @ -39,7 +39,8 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile): | |||
| 
 | ||||
|     format = "MPO" | ||||
|     format_description = "MPO (CIPA DC-007)" | ||||
| 
 | ||||
|     _close_exclusive_fp_after_loading = False | ||||
|      | ||||
|     def _open(self): | ||||
|         self.fp.seek(0)  # prep the fp in order to pass the JPEG test | ||||
|         JpegImagePlugin.JpegImageFile._open(self) | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| # | ||||
| # The Python Imaging Library. | ||||
| # $Id$ | ||||
| # | ||||
| # MSP file handling | ||||
| # | ||||
|  | @ -9,15 +8,25 @@ | |||
| # History: | ||||
| #       95-09-05 fl     Created | ||||
| #       97-01-03 fl     Read/write MSP images | ||||
| #       17-02-21 es     Fixed RLE interpretation | ||||
| # | ||||
| # Copyright (c) Secret Labs AB 1997. | ||||
| # Copyright (c) Fredrik Lundh 1995-97. | ||||
| # Copyright (c) Eric Soroos 2017. | ||||
| # | ||||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| # More info on this format: https://archive.org/details/gg243631 | ||||
| # Page 313: | ||||
| # Figure 205. Windows Paint Version 1: "DanM" Format | ||||
| # Figure 206. Windows Paint Version 2: "LinS" Format. Used in Windows V2.03 | ||||
| # | ||||
| # See also: http://www.fileformat.info/format/mspaint/egff.htm | ||||
| 
 | ||||
| 
 | ||||
| from PIL import Image, ImageFile, _binary | ||||
| from . import Image, ImageFile | ||||
| from ._binary import i16le as i16, o16le as o16, i8 | ||||
| import struct | ||||
| import io | ||||
| 
 | ||||
| __version__ = "0.1" | ||||
| 
 | ||||
|  | @ -25,8 +34,6 @@ __version__ = "0.1" | |||
| # | ||||
| # read MSP files | ||||
| 
 | ||||
| i16 = _binary.i16le | ||||
| 
 | ||||
| 
 | ||||
| def _accept(prefix): | ||||
|     return prefix[:4] in [b"DanM", b"LinS"] | ||||
|  | @ -61,13 +68,93 @@ class MspImageFile(ImageFile.ImageFile): | |||
|         if s[:4] == b"DanM": | ||||
|             self.tile = [("raw", (0, 0)+self.size, 32, ("1", 0, 1))] | ||||
|         else: | ||||
|             self.tile = [("msp", (0, 0)+self.size, 32+2*self.size[1], None)] | ||||
|             self.tile = [("MSP", (0, 0)+self.size, 32, None)] | ||||
| 
 | ||||
| 
 | ||||
| class MspDecoder(ImageFile.PyDecoder): | ||||
|     # The algo for the MSP decoder is from | ||||
|     # http://www.fileformat.info/format/mspaint/egff.htm | ||||
|     # cc-by-attribution -- That page references is taken from the | ||||
|     # Encyclopedia of Graphics File Formats and is licensed by | ||||
|     # O'Reilly under the Creative Common/Attribution license | ||||
|     # | ||||
|     # For RLE encoded files, the 32byte header is followed by a scan | ||||
|     # line map, encoded as one 16bit word of encoded byte length per | ||||
|     # line. | ||||
|     # | ||||
|     # NOTE: the encoded length of the line can be 0. This was not | ||||
|     # handled in the previous version of this encoder, and there's no | ||||
|     # mention of how to handle it in the documentation. From the few | ||||
|     # examples I've seen, I've assumed that it is a fill of the | ||||
|     # background color, in this case, white. | ||||
|     # | ||||
|     # | ||||
|     # Pseudocode of the decoder: | ||||
|     # Read a BYTE value as the RunType | ||||
|     #  If the RunType value is zero | ||||
|     #   Read next byte as the RunCount | ||||
|     #   Read the next byte as the RunValue | ||||
|     #   Write the RunValue byte RunCount times | ||||
|     #  If the RunType value is non-zero | ||||
|     #   Use this value as the RunCount | ||||
|     #   Read and write the next RunCount bytes literally | ||||
|     # | ||||
|     #  e.g.: | ||||
|     #  0x00 03 ff 05 00 01 02 03 04 | ||||
|     #  would yield the bytes: | ||||
|     #  0xff ff ff 00 01 02 03 04 | ||||
|     # | ||||
|     # which are then interpreted as a bit packed mode '1' image | ||||
| 
 | ||||
|     _pulls_fd = True | ||||
| 
 | ||||
|     def decode(self, buffer): | ||||
| 
 | ||||
|         img = io.BytesIO() | ||||
|         blank_line = bytearray((0xff,)*((self.state.xsize+7)//8)) | ||||
|         try: | ||||
|             self.fd.seek(32) | ||||
|             rowmap = struct.unpack_from("<%dH" % (self.state.ysize), | ||||
|                                         self.fd.read(self.state.ysize*2)) | ||||
|         except struct.error: | ||||
|             raise IOError("Truncated MSP file in row map") | ||||
| 
 | ||||
|         for x, rowlen in enumerate(rowmap): | ||||
|             try: | ||||
|                 if rowlen == 0: | ||||
|                     img.write(blank_line) | ||||
|                     continue | ||||
|                 row = self.fd.read(rowlen) | ||||
|                 if len(row) != rowlen: | ||||
|                     raise IOError("Truncated MSP file, expected %d bytes on row %s", | ||||
|                                   (rowlen, x)) | ||||
|                 idx = 0 | ||||
|                 while idx < rowlen: | ||||
|                     runtype = i8(row[idx]) | ||||
|                     idx += 1 | ||||
|                     if runtype == 0: | ||||
|                         (runcount, runval) = struct.unpack("Bc", row[idx:idx+2]) | ||||
|                         img.write(runval * runcount) | ||||
|                         idx += 2 | ||||
|                     else: | ||||
|                         runcount = runtype | ||||
|                         img.write(row[idx:idx+runcount]) | ||||
|                         idx += runcount | ||||
| 
 | ||||
|             except struct.error: | ||||
|                 raise IOError("Corrupted MSP file in row %d" % x) | ||||
| 
 | ||||
|         self.set_as_raw(img.getvalue(), ("1", 0, 1)) | ||||
| 
 | ||||
|         return 0, 0 | ||||
| 
 | ||||
| 
 | ||||
| Image.register_decoder('MSP', MspDecoder) | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # write MSP files (uncompressed only) | ||||
| 
 | ||||
| o16 = _binary.o16le | ||||
| 
 | ||||
| 
 | ||||
| def _save(im, fp, filename): | ||||
| 
 | ||||
|  | @ -95,6 +182,7 @@ def _save(im, fp, filename): | |||
|     # image body | ||||
|     ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 32, ("1", 0, 1))]) | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # registry | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,180 +0,0 @@ | |||
| olefile (formerly OleFileIO_PL) | ||||
| =============================== | ||||
| 
 | ||||
| [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. | ||||
|    | ||||
| 
 | ||||
| **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) | ||||
| 
 | ||||
| 
 | ||||
| News | ||||
| ---- | ||||
| 
 | ||||
| Follow all updates and news on Twitter: <https://twitter.com/decalage2> | ||||
| 
 | ||||
| - **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 | ||||
| - 2010-01-24 v0.21: fixed support for big-endian CPUs, such as PowerPC Macs. | ||||
| - 2009-12-11 v0.20: small bugfix in OleFileIO.open when filename is not plain str. | ||||
| - 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/Install | ||||
| ---------------- | ||||
| 
 | ||||
| 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, 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-pillow.org), 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: | ||||
| ---------------------------------------------------------------- | ||||
| 
 | ||||
| - Compatible with Python 3.x and 2.6+ | ||||
| - Many bug fixes | ||||
| - Support for files larger than 6.8MB | ||||
| - Support for 64 bits platforms and big-endian CPUs | ||||
| - Robust: many checks to detect malformed files | ||||
| - Runtime option to choose if malformed files should be parsed or raise exceptions | ||||
| - Improved API | ||||
| - Metadata extraction, stream/storage timestamps (e.g. for document forensics) | ||||
| - 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. | ||||
| 
 | ||||
| 
 | ||||
| ## 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 olefile. | ||||
| 
 | ||||
| 
 | ||||
| License | ||||
| ------- | ||||
| 
 | ||||
| olefile (formerly OleFileIO_PL) is copyright (c) 2005-2015 Philippe Lagadec  | ||||
| ([http://www.decalage.info](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. | ||||
| 
 | ||||
| 
 | ||||
| ---------- | ||||
| 
 | ||||
| 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 © 1997-2011 by Secret Labs AB | ||||
|     Copyright © 1995-2011 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: | ||||
| 
 | ||||
| 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. | ||||
							
								
								
									
										2309
									
								
								PIL/OleFileIO.py
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										2309
									
								
								PIL/OleFileIO.py
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -15,7 +15,7 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import EpsImagePlugin | ||||
| from . import EpsImagePlugin | ||||
| import sys | ||||
| 
 | ||||
| ## | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL._binary import o8 | ||||
| from ._binary import o8 | ||||
| 
 | ||||
| 
 | ||||
| ## | ||||
|  |  | |||
|  | @ -7,7 +7,8 @@ | |||
| # Image plugin for Palm pixmap images (output only). | ||||
| ## | ||||
| 
 | ||||
| from PIL import Image, ImageFile, _binary | ||||
| from . import Image, ImageFile | ||||
| from ._binary import o8, o16be as o16b | ||||
| 
 | ||||
| __version__ = "1.0" | ||||
| 
 | ||||
|  | @ -108,9 +109,6 @@ _COMPRESSION_TYPES = { | |||
|     "scanline": 0x00, | ||||
|     } | ||||
| 
 | ||||
| o8 = _binary.o8 | ||||
| o16b = _binary.o16be | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # -------------------------------------------------------------------- | ||||
|  |  | |||
|  | @ -15,12 +15,11 @@ | |||
| # | ||||
| 
 | ||||
| 
 | ||||
| from PIL import Image, ImageFile, _binary | ||||
| from . import Image, ImageFile | ||||
| from ._binary import i8 | ||||
| 
 | ||||
| __version__ = "0.1" | ||||
| 
 | ||||
| i8 = _binary.i8 | ||||
| 
 | ||||
| 
 | ||||
| ## | ||||
| # Image plugin for PhotoCD images.  This plugin only reads the 768x512 | ||||
|  | @ -42,8 +41,9 @@ class PcdImageFile(ImageFile.ImageFile): | |||
|             raise SyntaxError("not a PCD file") | ||||
| 
 | ||||
|         orientation = i8(s[1538]) & 3 | ||||
|         self.tile_post_rotate = None | ||||
|         if orientation == 1: | ||||
|             self.tile_post_rotate = 90  # hack | ||||
|             self.tile_post_rotate = 90 | ||||
|         elif orientation == 3: | ||||
|             self.tile_post_rotate = -90 | ||||
| 
 | ||||
|  | @ -51,6 +51,13 @@ 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 load_end(self): | ||||
|         if self.tile_post_rotate: | ||||
|             # Handle rotated PCDs | ||||
|             self.im = self.im.rotate(self.tile_post_rotate) | ||||
|             self.size = self.im.size | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # registry | ||||
| 
 | ||||
|  |  | |||
|  | @ -16,9 +16,8 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import Image | ||||
| from PIL import FontFile | ||||
| from PIL import _binary | ||||
| from . import Image, FontFile | ||||
| from ._binary import i8, i16le as l16, i32le as l32, i16be as b16, i32be as b32 | ||||
| 
 | ||||
| # -------------------------------------------------------------------- | ||||
| # declarations | ||||
|  | @ -42,12 +41,6 @@ BYTES_PER_ROW = [ | |||
|     lambda bits: ((bits+63) >> 3) & ~7, | ||||
| ] | ||||
| 
 | ||||
| i8 = _binary.i8 | ||||
| l16 = _binary.i16le | ||||
| l32 = _binary.i32le | ||||
| b16 = _binary.i16be | ||||
| b32 = _binary.i32be | ||||
| 
 | ||||
| 
 | ||||
| def sz(s, o): | ||||
|     return s[o:s.index(b"\0", o)] | ||||
|  |  | |||
|  | @ -25,17 +25,12 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from __future__ import print_function | ||||
| 
 | ||||
| import logging | ||||
| from PIL import Image, ImageFile, ImagePalette, _binary | ||||
| from . import Image, ImageFile, ImagePalette | ||||
| from ._binary import i8, i16le as i16, o8, o16le as o16 | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| i8 = _binary.i8 | ||||
| i16 = _binary.i16le | ||||
| o8 = _binary.o8 | ||||
| 
 | ||||
| __version__ = "0.6" | ||||
| 
 | ||||
| 
 | ||||
|  | @ -123,8 +118,6 @@ SAVE = { | |||
|     "RGB": (5, 8, 3, "RGB;L"), | ||||
| } | ||||
| 
 | ||||
| o16 = _binary.o16le | ||||
| 
 | ||||
| 
 | ||||
| def _save(im, fp, filename, check=0): | ||||
| 
 | ||||
|  |  | |||
|  | @ -20,8 +20,8 @@ | |||
| # Image plugin for PDF images (output only). | ||||
| ## | ||||
| 
 | ||||
| from PIL import Image, ImageFile | ||||
| from PIL._binary import i8 | ||||
| from . import Image, ImageFile, ImageSequence | ||||
| from ._binary import i8 | ||||
| import io | ||||
| 
 | ||||
| __version__ = "0.4" | ||||
|  | @ -37,11 +37,11 @@ __version__ = "0.4" | |||
| #  4. page | ||||
| #  5. page contents | ||||
| 
 | ||||
| def _obj(fp, obj, **dict): | ||||
| def _obj(fp, obj, **dictionary): | ||||
|     fp.write("%d 0 obj\n" % obj) | ||||
|     if dict: | ||||
|     if dictionary: | ||||
|         fp.write("<<\n") | ||||
|         for k, v in dict.items(): | ||||
|         for k, v in dictionary.items(): | ||||
|             if v is not None: | ||||
|                 fp.write("/%s %s\n" % (k, v)) | ||||
|         fp.write(">>\n") | ||||
|  | @ -133,13 +133,24 @@ def _save(im, fp, filename, save_all=False): | |||
| 
 | ||||
|     # | ||||
|     # pages | ||||
|     numberOfPages = 1 | ||||
|     ims = [im] | ||||
|     if save_all: | ||||
|         try: | ||||
|             numberOfPages = im.n_frames | ||||
|         except AttributeError: | ||||
|             # Image format does not have n_frames. It is a single frame image | ||||
|             pass | ||||
|         append_images = im.encoderinfo.get("append_images", []) | ||||
|         for append_im in append_images: | ||||
|             if append_im.mode != im.mode: | ||||
|                 append_im = append_im.convert(im.mode) | ||||
|                 append_im.encoderinfo = im.encoderinfo.copy() | ||||
|             ims.append(append_im) | ||||
|     numberOfPages = 0 | ||||
|     for im in ims: | ||||
|         im_numberOfPages = 1 | ||||
|         if save_all: | ||||
|             try: | ||||
|                 im_numberOfPages = im.n_frames | ||||
|             except AttributeError: | ||||
|                 # Image format does not have n_frames. It is a single frame image | ||||
|                 pass | ||||
|         numberOfPages += im_numberOfPages | ||||
|     pages = [str(pageNumber*3+4)+" 0 R" | ||||
|              for pageNumber in range(0, numberOfPages)] | ||||
| 
 | ||||
|  | @ -151,90 +162,92 @@ def _save(im, fp, filename, save_all=False): | |||
|         Kids="["+"\n".join(pages)+"]") | ||||
|     _endobj(fp) | ||||
| 
 | ||||
|     for pageNumber in range(0, numberOfPages): | ||||
|         im.seek(pageNumber) | ||||
|     pageNumber = 0 | ||||
|     for imSequence in ims: | ||||
|         for im in ImageSequence.Iterator(imSequence): | ||||
|             # | ||||
|             # image | ||||
| 
 | ||||
|         # | ||||
|         # image | ||||
|             op = io.BytesIO() | ||||
| 
 | ||||
|         op = io.BytesIO() | ||||
|             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) | ||||
| 
 | ||||
|         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) | ||||
|             # | ||||
|             # Get image characteristics | ||||
| 
 | ||||
|         # | ||||
|         # Get image characteristics | ||||
|             width, height = im.size | ||||
| 
 | ||||
|         width, height = im.size | ||||
|             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.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) | ||||
|             fp.write("stream\n") | ||||
|             fp.fp.write(op.getvalue()) | ||||
|             fp.write("\nendstream\n") | ||||
| 
 | ||||
|         fp.write("stream\n") | ||||
|         fp.fp.write(op.getvalue()) | ||||
|         fp.write("\nendstream\n") | ||||
|             _endobj(fp) | ||||
| 
 | ||||
|         _endobj(fp) | ||||
|             # | ||||
|             # page | ||||
| 
 | ||||
|         # | ||||
|         # page | ||||
|             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.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) | ||||
|             # | ||||
|             # page contents | ||||
| 
 | ||||
|         # | ||||
|         # page contents | ||||
|             op = TextWriter(io.BytesIO()) | ||||
| 
 | ||||
|         op = TextWriter(io.BytesIO()) | ||||
|             op.write( | ||||
|                 "q %d 0 0 %d 0 0 cm /image Do Q\n" % ( | ||||
|                     int(width * 72.0 / resolution), | ||||
|                     int(height * 72.0 / resolution))) | ||||
| 
 | ||||
|         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())) | ||||
| 
 | ||||
|         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") | ||||
| 
 | ||||
|         fp.write("stream\n") | ||||
|         fp.fp.write(op.fp.getvalue()) | ||||
|         fp.write("\nendstream\n") | ||||
|             _endobj(fp) | ||||
| 
 | ||||
|         _endobj(fp) | ||||
|             pageNumber += 1 | ||||
| 
 | ||||
|     # | ||||
|     # trailer | ||||
|  |  | |||
|  | @ -19,16 +19,15 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import Image, ImageFile, _binary | ||||
| from . import Image, ImageFile | ||||
| from ._binary import i16le as i16 | ||||
| 
 | ||||
| __version__ = "0.1" | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # helpers | ||||
| 
 | ||||
| i16 = _binary.i16le | ||||
| 
 | ||||
| 
 | ||||
| def _accept(prefix): | ||||
|     return prefix[:4] == b"\200\350\000\000" | ||||
| 
 | ||||
|  | @ -63,6 +62,7 @@ class PixarImageFile(ImageFile.ImageFile): | |||
|         # create tile descriptor (assuming "dumped") | ||||
|         self.tile = [("raw", (0, 0)+self.size, 1024, (self.mode, 0, 1))] | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # -------------------------------------------------------------------- | ||||
| 
 | ||||
|  |  | |||
|  | @ -31,23 +31,18 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from __future__ import print_function | ||||
| 
 | ||||
| import logging | ||||
| import re | ||||
| import zlib | ||||
| import struct | ||||
| 
 | ||||
| from PIL import Image, ImageFile, ImagePalette, _binary | ||||
| from . import Image, ImageFile, ImagePalette | ||||
| from ._binary import i8, i16be as i16, i32be as i32, o16be as o16, o32be as o32 | ||||
| 
 | ||||
| __version__ = "0.9" | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| i8 = _binary.i8 | ||||
| i16 = _binary.i16be | ||||
| i32 = _binary.i32be | ||||
| 
 | ||||
| is_cid = re.compile(br"\w\w\w\w").match | ||||
| 
 | ||||
| 
 | ||||
|  | @ -118,7 +113,8 @@ class ChunkStream(object): | |||
|             length = i32(s) | ||||
| 
 | ||||
|         if not is_cid(cid): | ||||
|             raise SyntaxError("broken PNG file (chunk %s)" % repr(cid)) | ||||
|             if not ImageFile.LOAD_TRUNCATED_IMAGES: | ||||
|                 raise SyntaxError("broken PNG file (chunk %s)" % repr(cid)) | ||||
| 
 | ||||
|         return cid, pos, length | ||||
| 
 | ||||
|  | @ -621,10 +617,6 @@ class PngImageFile(ImageFile.ImageFile): | |||
| # -------------------------------------------------------------------- | ||||
| # PNG writer | ||||
| 
 | ||||
| o8 = _binary.o8 | ||||
| o16 = _binary.o16be | ||||
| o32 = _binary.o32be | ||||
| 
 | ||||
| _OUTMODES = { | ||||
|     # supported PIL modes, and corresponding rawmodes/bits/color combinations | ||||
|     "1":    ("1",       b'\x01\x00'), | ||||
|  | @ -722,6 +714,32 @@ def _save(im, fp, filename, chunk=putchunk, check=0): | |||
|           b'\0',                                # 11: filter category | ||||
|           b'\0')                                # 12: interlace flag | ||||
| 
 | ||||
|     chunks = [b"cHRM", b"gAMA", b"sBIT", b"sRGB", b"tIME"] | ||||
| 
 | ||||
|     icc = im.encoderinfo.get("icc_profile", im.info.get("icc_profile")) | ||||
|     if icc: | ||||
|         # ICC profile | ||||
|         # according to PNG spec, the iCCP chunk contains: | ||||
|         # Profile name  1-79 bytes (character string) | ||||
|         # Null separator        1 byte (null character) | ||||
|         # Compression method    1 byte (0) | ||||
|         # Compressed profile    n bytes (zlib with deflate compression) | ||||
|         name = b"ICC Profile" | ||||
|         data = name + b"\0\0" + zlib.compress(icc) | ||||
|         chunk(fp, b"iCCP", data) | ||||
|     else: | ||||
|         chunks.remove(b"sRGB") | ||||
| 
 | ||||
|     info = im.encoderinfo.get("pnginfo") | ||||
|     if info: | ||||
|         chunks_multiple_allowed = [b"sPLT", b"iTXt", b"tEXt", b"zTXt"] | ||||
|         for cid, data in info.chunks: | ||||
|             if cid in chunks: | ||||
|                 chunks.remove(cid) | ||||
|                 chunk(fp, cid, data) | ||||
|             elif cid in chunks_multiple_allowed: | ||||
|                 chunk(fp, cid, data) | ||||
| 
 | ||||
|     if im.mode == "P": | ||||
|         palette_byte_number = (2 ** bits) * 3 | ||||
|         palette_bytes = im.im.getpalette("RGB")[:palette_byte_number] | ||||
|  | @ -768,20 +786,11 @@ def _save(im, fp, filename, chunk=putchunk, check=0): | |||
| 
 | ||||
|     info = im.encoderinfo.get("pnginfo") | ||||
|     if info: | ||||
|         chunks = [b"bKGD", b"hIST"] | ||||
|         for cid, data in info.chunks: | ||||
|             chunk(fp, cid, data) | ||||
| 
 | ||||
|     icc = im.encoderinfo.get("icc_profile", im.info.get("icc_profile")) | ||||
|     if icc: | ||||
|         # ICC profile | ||||
|         # according to PNG spec, the iCCP chunk contains: | ||||
|         # Profile name  1-79 bytes (character string) | ||||
|         # Null separator        1 byte (null character) | ||||
|         # Compression method    1 byte (0) | ||||
|         # Compressed profile    n bytes (zlib with deflate compression) | ||||
|         name = b"ICC Profile" | ||||
|         data = name + b"\0\0" + zlib.compress(icc) | ||||
|         chunk(fp, b"iCCP", data) | ||||
|             if cid in chunks: | ||||
|                 chunks.remove(cid) | ||||
|                 chunk(fp, cid, data) | ||||
| 
 | ||||
|     ImageFile._save(im, _idat(fp, chunk), | ||||
|                     [("zip", (0, 0)+im.size, 0, rawmode)]) | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ | |||
| 
 | ||||
| import string | ||||
| 
 | ||||
| from PIL import Image, ImageFile | ||||
| from . import Image, ImageFile | ||||
| 
 | ||||
| __version__ = "0.2" | ||||
| 
 | ||||
|  |  | |||
|  | @ -18,7 +18,8 @@ | |||
| 
 | ||||
| __version__ = "0.4" | ||||
| 
 | ||||
| from PIL import Image, ImageFile, ImagePalette, _binary | ||||
| from . import Image, ImageFile, ImagePalette | ||||
| from ._binary import i8, i16be as i16, i32be as i32 | ||||
| 
 | ||||
| MODES = { | ||||
|     # (photoshop mode, bits) -> (pil mode, required channels) | ||||
|  | @ -33,13 +34,6 @@ MODES = { | |||
|     (9, 8): ("LAB", 3) | ||||
| } | ||||
| 
 | ||||
| # | ||||
| # helpers | ||||
| 
 | ||||
| i8 = _binary.i8 | ||||
| i16 = _binary.i16be | ||||
| i32 = _binary.i32be | ||||
| 
 | ||||
| 
 | ||||
| # --------------------------------------------------------------------. | ||||
| # read PSD images | ||||
|  |  | |||
|  | @ -20,8 +20,6 @@ | |||
| #      Access.c implementation. | ||||
| # | ||||
| 
 | ||||
| from __future__ import print_function | ||||
| 
 | ||||
| import logging | ||||
| import sys | ||||
| 
 | ||||
|  | @ -167,7 +165,7 @@ class _PyAccess8(PyAccess): | |||
|         try: | ||||
|             # integer | ||||
|             self.pixels[y][x] = min(color, 255) | ||||
|         except: | ||||
|         except TypeError: | ||||
|             # tuple | ||||
|             self.pixels[y][x] = min(color[0], 255) | ||||
| 
 | ||||
|  | @ -184,7 +182,7 @@ class _PyAccessI16_N(PyAccess): | |||
|         try: | ||||
|             # integer | ||||
|             self.pixels[y][x] = min(color, 65535) | ||||
|         except: | ||||
|         except TypeError: | ||||
|             # tuple | ||||
|             self.pixels[y][x] = min(color[0], 65535) | ||||
| 
 | ||||
|  | @ -272,7 +270,7 @@ class _PyAccessF(PyAccess): | |||
|         try: | ||||
|             # not a tuple | ||||
|             self.pixels[y][x] = color | ||||
|         except: | ||||
|         except TypeError: | ||||
|             # tuple | ||||
|             self.pixels[y][x] = color[0] | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,9 +7,12 @@ | |||
| # See "The SGI Image File Format (Draft version 0.97)", Paul Haeberli. | ||||
| # <ftp://ftp.sgi.com/graphics/SGIIMAGESPEC> | ||||
| # | ||||
| # | ||||
| # History: | ||||
| # 2016-16-10 mb   Add save method without compression | ||||
| # 1995-09-10 fl   Created | ||||
| # | ||||
| # Copyright (c) 2016 by Mickael Bonfill. | ||||
| # Copyright (c) 2008 by Karsten Hiddemann. | ||||
| # Copyright (c) 1997 by Secret Labs AB. | ||||
| # Copyright (c) 1995 by Fredrik Lundh. | ||||
|  | @ -18,12 +21,12 @@ | |||
| # | ||||
| 
 | ||||
| 
 | ||||
| from PIL import Image, ImageFile, _binary | ||||
| from . import Image, ImageFile | ||||
| from ._binary import i8, o8, i16be as i16 | ||||
| import struct | ||||
| import os | ||||
| 
 | ||||
| __version__ = "0.2" | ||||
| 
 | ||||
| i8 = _binary.i8 | ||||
| i16 = _binary.i16be | ||||
| __version__ = "0.3" | ||||
| 
 | ||||
| 
 | ||||
| def _accept(prefix): | ||||
|  | @ -76,12 +79,79 @@ class SgiImageFile(ImageFile.ImageFile): | |||
|         elif compression == 1: | ||||
|             raise ValueError("SGI RLE encoding not supported") | ||||
| 
 | ||||
| 
 | ||||
| def _save(im, fp, filename): | ||||
|     if im.mode != "RGB" and im.mode != "RGBA" and im.mode != "L": | ||||
|         raise ValueError("Unsupported SGI image mode") | ||||
| 
 | ||||
|     # Flip the image, since the origin of SGI file is the bottom-left corner | ||||
|     im = im.transpose(Image.FLIP_TOP_BOTTOM) | ||||
|     # Define the file as SGI File Format | ||||
|     magicNumber = 474 | ||||
|     # Run-Length Encoding Compression - Unsupported at this time | ||||
|     rle = 0 | ||||
|     # Byte-per-pixel precision, 1 = 8bits per pixel | ||||
|     bpc = 1 | ||||
|     # Number of dimensions (x,y,z) | ||||
|     dim = 3 | ||||
|     # X Dimension = width / Y Dimension = height | ||||
|     x, y = im.size | ||||
|     if im.mode == "L" and y == 1: | ||||
|         dim = 1 | ||||
|     elif im.mode == "L": | ||||
|         dim = 2 | ||||
|     # Z Dimension: Number of channels | ||||
|     z = len(im.mode) | ||||
|     if dim == 1 or dim == 2: | ||||
|         z = 1 | ||||
|     # Minimum Byte value | ||||
|     pinmin = 0 | ||||
|     # Maximum Byte value (255 = 8bits per pixel) | ||||
|     pinmax = 255 | ||||
|     # Image name (79 characters max, truncated below in write) | ||||
|     imgName = os.path.splitext(os.path.basename(filename))[0] | ||||
|     if str is not bytes: | ||||
|         imgName = imgName.encode('ascii', 'ignore') | ||||
|     # Standard representation of pixel in the file | ||||
|     colormap = 0 | ||||
|     fp.write(struct.pack('>h', magicNumber)) | ||||
|     fp.write(o8(rle)) | ||||
|     fp.write(o8(bpc)) | ||||
|     fp.write(struct.pack('>H', dim)) | ||||
|     fp.write(struct.pack('>H', x)) | ||||
|     fp.write(struct.pack('>H', y)) | ||||
|     fp.write(struct.pack('>H', z)) | ||||
|     fp.write(struct.pack('>l', pinmin)) | ||||
|     fp.write(struct.pack('>l', pinmax)) | ||||
| 
 | ||||
|     fp.write(struct.pack('4s', b''))  # dummy | ||||
|     fp.write(struct.pack('79s', imgName))  # truncates to 79 chars | ||||
|     fp.write(struct.pack('s', b''))  # force null byte after imgname | ||||
|     fp.write(struct.pack('>l', colormap)) | ||||
| 
 | ||||
|     fp.write(struct.pack('404s', b''))  # dummy | ||||
| 
 | ||||
|     # assert we've got the right number of bands. | ||||
|     if len(im.getbands()) != z: | ||||
|         raise ValueError("incorrect number of bands in SGI write: %s vs %s" % | ||||
|                          (z, len(im.getbands()))) | ||||
| 
 | ||||
|     for channel in im.split(): | ||||
|         fp.write(channel.tobytes()) | ||||
| 
 | ||||
|     fp.close() | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # registry | ||||
| 
 | ||||
| Image.register_open(SgiImageFile.format, SgiImageFile, _accept) | ||||
| 
 | ||||
| Image.register_save(SgiImageFile.format, _save) | ||||
| Image.register_mime(SgiImageFile.format, "image/sgi") | ||||
| Image.register_mime(SgiImageFile.format, "image/rgb") | ||||
| 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 | ||||
|  |  | |||
|  | @ -27,10 +27,10 @@ | |||
| # image data from electron microscopy and tomography. | ||||
| # | ||||
| # Spider home page: | ||||
| # http://spider.wadsworth.org/spider_doc/spider/docs/spider.html | ||||
| # https://spider.wadsworth.org/spider_doc/spider/docs/spider.html | ||||
| # | ||||
| # Details about the Spider image format: | ||||
| # http://spider.wadsworth.org/spider_doc/spider/docs/image_doc.html | ||||
| # https://spider.wadsworth.org/spider_doc/spider/docs/image_doc.html | ||||
| # | ||||
| 
 | ||||
| from __future__ import print_function | ||||
|  | @ -75,7 +75,7 @@ def isSpiderHeader(t): | |||
|     labrec = int(h[13])   # no. records in file header | ||||
|     labbyt = int(h[22])   # total no. of bytes in header | ||||
|     lenbyt = int(h[23])   # record length in bytes | ||||
|     # print "labrec = %d, labbyt = %d, lenbyt = %d" % (labrec,labbyt,lenbyt) | ||||
|     # print("labrec = %d, labbyt = %d, lenbyt = %d" % (labrec,labbyt,lenbyt)) | ||||
|     if labbyt != (labrec * lenbyt): | ||||
|         return 0 | ||||
|     # looks like a valid header | ||||
|  | @ -83,9 +83,8 @@ def isSpiderHeader(t): | |||
| 
 | ||||
| 
 | ||||
| def isSpiderImage(filename): | ||||
|     fp = open(filename, 'rb') | ||||
|     f = fp.read(92)   # read 23 * 4 bytes | ||||
|     fp.close() | ||||
|     with open(filename, 'rb') as fp: | ||||
|         f = fp.read(92)   # read 23 * 4 bytes | ||||
|     t = struct.unpack('>23f', f)  # try big-endian first | ||||
|     hdrlen = isSpiderHeader(t) | ||||
|     if hdrlen == 0: | ||||
|  | @ -98,6 +97,7 @@ class SpiderImageFile(ImageFile.ImageFile): | |||
| 
 | ||||
|     format = "SPIDER" | ||||
|     format_description = "Spider 2D image" | ||||
|     _close_exclusive_fp_after_loading = False | ||||
| 
 | ||||
|     def _open(self): | ||||
|         # check header | ||||
|  |  | |||
|  | @ -17,12 +17,11 @@ | |||
| # | ||||
| 
 | ||||
| 
 | ||||
| from PIL import Image, ImageFile, ImagePalette, _binary | ||||
| from . import Image, ImageFile, ImagePalette | ||||
| from ._binary import i32be as i32 | ||||
| 
 | ||||
| __version__ = "0.3" | ||||
| 
 | ||||
| i32 = _binary.i32be | ||||
| 
 | ||||
| 
 | ||||
| def _accept(prefix): | ||||
|     return len(prefix) >= 4 and i32(prefix) == 0x59a66a95 | ||||
|  | @ -38,7 +37,8 @@ class SunImageFile(ImageFile.ImageFile): | |||
| 
 | ||||
|     def _open(self): | ||||
| 
 | ||||
|         # The Sun Raster file header is 32 bytes in length and has the following format: | ||||
|         # The Sun Raster file header is 32 bytes in length | ||||
|         # and has the following format: | ||||
| 
 | ||||
|         #     typedef struct _SunRaster | ||||
|         #     { | ||||
|  | @ -52,7 +52,6 @@ class SunImageFile(ImageFile.ImageFile): | |||
|         #         DWORD ColorMapLength;   /* Size of the color map in bytes */ | ||||
|         #     } SUNRASTER; | ||||
| 
 | ||||
| 
 | ||||
|         # HEAD | ||||
|         s = self.fp.read(32) | ||||
|         if i32(s) != 0x59a66a95: | ||||
|  | @ -63,11 +62,11 @@ class SunImageFile(ImageFile.ImageFile): | |||
|         self.size = i32(s[4:8]), i32(s[8:12]) | ||||
| 
 | ||||
|         depth = i32(s[12:16]) | ||||
|         data_length = i32(s[16:20])  # unreliable, ignore.  | ||||
|         data_length = i32(s[16:20])   # unreliable, ignore. | ||||
|         file_type = i32(s[20:24]) | ||||
|         palette_type = i32(s[24:28]) # 0: None, 1: RGB, 2: Raw/arbitrary | ||||
|         palette_type = i32(s[24:28])  # 0: None, 1: RGB, 2: Raw/arbitrary | ||||
|         palette_length = i32(s[28:32]) | ||||
|          | ||||
| 
 | ||||
|         if depth == 1: | ||||
|             self.mode, rawmode = "1", "1;I" | ||||
|         elif depth == 4: | ||||
|  | @ -85,23 +84,23 @@ class SunImageFile(ImageFile.ImageFile): | |||
|             else: | ||||
|                 self.mode, rawmode = 'RGB', 'BGRX' | ||||
|         else: | ||||
|             raise SyntaxError("Unsupported Mode/Bit Depth")     | ||||
|          | ||||
|             raise SyntaxError("Unsupported Mode/Bit Depth") | ||||
| 
 | ||||
|         if palette_length: | ||||
|             if palette_length > 1024: | ||||
|                 raise SyntaxError("Unsupported Color Palette Length") | ||||
| 
 | ||||
|             if palette_type != 1: | ||||
|                 raise SyntaxError("Unsupported Palette Type") | ||||
|              | ||||
| 
 | ||||
|             offset = offset + palette_length | ||||
|             self.palette = ImagePalette.raw("RGB;L", self.fp.read(palette_length)) | ||||
|             if self.mode == "L": | ||||
|                 self.mode = "P" | ||||
|                 rawmode = rawmode.replace('L', 'P') | ||||
|              | ||||
| 
 | ||||
|         # 16 bit boundaries on stride | ||||
|         stride = ((self.size[0] * depth + 15) // 16) * 2   | ||||
|         stride = ((self.size[0] * depth + 15) // 16) * 2 | ||||
| 
 | ||||
|         # file type: Type is the version (or flavor) of the bitmap | ||||
|         # file. The following values are typically found in the Type | ||||
|  | @ -119,7 +118,7 @@ class SunImageFile(ImageFile.ImageFile): | |||
|         # RGB looks similar to standard, but RGB byte order | ||||
|         # TIFF and IFF mean that they were converted from T/IFF | ||||
|         # Experimental means that it's something else. | ||||
|         # (http://www.fileformat.info/format/sunraster/egff.htm) | ||||
|         # (https://www.fileformat.info/format/sunraster/egff.htm) | ||||
| 
 | ||||
|         if file_type in (0, 1, 3, 4, 5): | ||||
|             self.tile = [("raw", (0, 0)+self.size, offset, (rawmode, stride))] | ||||
|  | @ -127,7 +126,7 @@ class SunImageFile(ImageFile.ImageFile): | |||
|             self.tile = [("sun_rle", (0, 0)+self.size, offset, rawmode)] | ||||
|         else: | ||||
|             raise SyntaxError('Unsupported Sun Raster file type') | ||||
|          | ||||
| 
 | ||||
| # | ||||
| # registry | ||||
| 
 | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ | |||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| 
 | ||||
| from PIL import ContainerIO | ||||
| from . import ContainerIO | ||||
| 
 | ||||
| 
 | ||||
| ## | ||||
|  |  | |||
|  | @ -17,7 +17,8 @@ | |||
| # | ||||
| 
 | ||||
| 
 | ||||
| from PIL import Image, ImageFile, ImagePalette, _binary | ||||
| from . import Image, ImageFile, ImagePalette | ||||
| from ._binary import i8, i16le as i16, o8, o16le as o16 | ||||
| 
 | ||||
| __version__ = "0.3" | ||||
| 
 | ||||
|  | @ -26,9 +27,6 @@ __version__ = "0.3" | |||
| # -------------------------------------------------------------------- | ||||
| # Read RGA file | ||||
| 
 | ||||
| i8 = _binary.i8 | ||||
| i16 = _binary.i16le | ||||
| 
 | ||||
| 
 | ||||
| MODES = { | ||||
|     # map imagetype/depth to rawmode | ||||
|  | @ -132,10 +130,6 @@ class TgaImageFile(ImageFile.ImageFile): | |||
| # -------------------------------------------------------------------- | ||||
| # Write TGA file | ||||
| 
 | ||||
| o8 = _binary.o8 | ||||
| o16 = _binary.o16le | ||||
| o32 = _binary.o32le | ||||
| 
 | ||||
| SAVE = { | ||||
|     "1": ("1", 1, 0, 3), | ||||
|     "L": ("L", 8, 0, 3), | ||||
|  |  | |||
|  | @ -41,10 +41,8 @@ | |||
| 
 | ||||
| from __future__ import division, print_function | ||||
| 
 | ||||
| from PIL import Image, ImageFile | ||||
| from PIL import ImagePalette | ||||
| from PIL import _binary | ||||
| from PIL import TiffTags | ||||
| from . import Image, ImageFile, ImagePalette, TiffTags | ||||
| from ._binary import i8, o8 | ||||
| 
 | ||||
| import collections | ||||
| from fractions import Fraction | ||||
|  | @ -71,9 +69,6 @@ IFD_LEGACY_API = True | |||
| II = b"II"  # little-endian (Intel style) | ||||
| MM = b"MM"  # big-endian (Motorola style) | ||||
| 
 | ||||
| i8 = _binary.i8 | ||||
| o8 = _binary.o8 | ||||
| 
 | ||||
| # | ||||
| # -------------------------------------------------------------------- | ||||
| # Read TIFF files | ||||
|  | @ -247,6 +242,7 @@ def _limit_rational(val, max_val): | |||
|     n_d = IFDRational(1 / val if inv else val).limit_rational(max_val) | ||||
|     return n_d[::-1] if inv else n_d | ||||
| 
 | ||||
| 
 | ||||
| ## | ||||
| # Wrapper for TIFF IFDs. | ||||
| 
 | ||||
|  | @ -338,7 +334,7 @@ class IFDRational(Rational): | |||
|              'rfloordiv','mod','rmod', 'pow','rpow', 'pos', 'neg', | ||||
|              'abs', 'trunc', 'lt', 'gt', 'le', 'ge', 'nonzero', | ||||
|              'ceil', 'floor', 'round'] | ||||
|         print "\n".join("__%s__ = _delegate('__%s__')" % (s,s) for s in a) | ||||
|         print("\n".join("__%s__ = _delegate('__%s__')" % (s,s) for s in a)) | ||||
|         """ | ||||
| 
 | ||||
|     __add__ = _delegate('__add__') | ||||
|  | @ -467,15 +463,6 @@ class ImageFileDirectory_v2(collections.MutableMapping): | |||
|     def __str__(self): | ||||
|         return str(dict(self)) | ||||
| 
 | ||||
|     def as_dict(self): | ||||
|         """Return a dictionary of the image's tags. | ||||
| 
 | ||||
|         .. deprecated:: 3.0.0 | ||||
|         """ | ||||
|         warnings.warn("as_dict() is deprecated. " + | ||||
|                       "Please use dict(ifd) instead.", DeprecationWarning) | ||||
|         return dict(self) | ||||
| 
 | ||||
|     def named(self): | ||||
|         """ | ||||
|         :returns: dict of name|key: value | ||||
|  | @ -540,7 +527,8 @@ 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] | ||||
| 
 | ||||
|         values = tuple(info.cvt_enum(value) for value in values) | ||||
| 
 | ||||
|  | @ -569,7 +557,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): | |||
| 
 | ||||
|     def _register_loader(idx, size): | ||||
|         def decorator(func): | ||||
|             from PIL.TiffTags import TYPES | ||||
|             from .TiffTags import TYPES | ||||
|             if func.__name__.startswith("load_"): | ||||
|                 TYPES[idx] = func.__name__[5:].replace("_", " ") | ||||
|             _load_dispatch[idx] = size, func | ||||
|  | @ -583,7 +571,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): | |||
|         return decorator | ||||
| 
 | ||||
|     def _register_basic(idx_fmt_name): | ||||
|         from PIL.TiffTags import TYPES | ||||
|         from .TiffTags import TYPES | ||||
|         idx, fmt, name = idx_fmt_name | ||||
|         TYPES[idx] = name | ||||
|         size = struct.calcsize("=" + fmt) | ||||
|  | @ -593,9 +581,13 @@ class ImageFileDirectory_v2(collections.MutableMapping): | |||
|             b"".join(self._pack(fmt, value) for value in values)) | ||||
| 
 | ||||
|     list(map(_register_basic, | ||||
|              [(3, "H", "short"), (4, "L", "long"), | ||||
|               (6, "b", "signed byte"), (8, "h", "signed short"), | ||||
|               (9, "l", "signed long"), (11, "f", "float"), (12, "d", "double")])) | ||||
|              [(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, legacy_api=True): | ||||
|  | @ -621,7 +613,8 @@ class ImageFileDirectory_v2(collections.MutableMapping): | |||
|     @_register_loader(5, 8) | ||||
|     def load_rational(self, data, legacy_api=True): | ||||
|         vals = self._unpack("{}L".format(len(data) // 4), data) | ||||
|         combine = lambda a, b: (a, b) if legacy_api else IFDRational(a, b) | ||||
| 
 | ||||
|         def combine(a, b): return (a, b) if legacy_api else IFDRational(a, b) | ||||
|         return tuple(combine(num, denom) | ||||
|                      for num, denom in zip(vals[::2], vals[1::2])) | ||||
| 
 | ||||
|  | @ -641,7 +634,8 @@ class ImageFileDirectory_v2(collections.MutableMapping): | |||
|     @_register_loader(10, 8) | ||||
|     def load_signed_rational(self, data, legacy_api=True): | ||||
|         vals = self._unpack("{}l".format(len(data) // 4), data) | ||||
|         combine = lambda a, b: (a, b) if legacy_api else IFDRational(a, b) | ||||
| 
 | ||||
|         def combine(a, b): return (a, b) if legacy_api else IFDRational(a, b) | ||||
|         return tuple(combine(num, denom) | ||||
|                      for num, denom in zip(vals[::2], vals[1::2])) | ||||
| 
 | ||||
|  | @ -665,7 +659,8 @@ class ImageFileDirectory_v2(collections.MutableMapping): | |||
| 
 | ||||
|         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)) | ||||
|                 tag, typ, count, data = self._unpack("HHL4s", | ||||
|                                                      self._ensure_read(fp, 12)) | ||||
|                 if DEBUG: | ||||
|                     tagname = TiffTags.lookup(tag).name | ||||
|                     typname = TYPES.get(typ, "unknown") | ||||
|  | @ -693,8 +688,8 @@ class ImageFileDirectory_v2(collections.MutableMapping): | |||
| 
 | ||||
|                 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)) | ||||
|                                   "Expecting to read %d bytes but only got %d." | ||||
|                                   " Skipping tag %s" % (size, len(data), tag)) | ||||
|                     continue | ||||
| 
 | ||||
|                 if not data: | ||||
|  | @ -753,7 +748,8 @@ class ImageFileDirectory_v2(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 | ||||
|  | @ -782,6 +778,7 @@ class ImageFileDirectory_v2(collections.MutableMapping): | |||
| 
 | ||||
|         return offset | ||||
| 
 | ||||
| 
 | ||||
| ImageFileDirectory_v2._load_dispatch = _load_dispatch | ||||
| ImageFileDirectory_v2._write_dispatch = _write_dispatch | ||||
| for idx, name in TYPES.items(): | ||||
|  | @ -800,7 +797,7 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2): | |||
|         ifd = ImageFileDirectory_v1() | ||||
|         ifd[key] = 'Some Data' | ||||
|         ifd.tagtype[key] = 2 | ||||
|         print ifd[key] | ||||
|         print(ifd[key]) | ||||
|         ('Some Data',) | ||||
| 
 | ||||
|     Also contains a dictionary of tag types as read from the tiff image file, | ||||
|  | @ -889,6 +886,7 @@ class TiffImageFile(ImageFile.ImageFile): | |||
| 
 | ||||
|     format = "TIFF" | ||||
|     format_description = "Adobe TIFF" | ||||
|     _close_exclusive_fp_after_loading = False | ||||
| 
 | ||||
|     def _open(self): | ||||
|         "Open the first image in a TIFF file" | ||||
|  | @ -974,6 +972,7 @@ class TiffImageFile(ImageFile.ImageFile): | |||
|             self.__frame += 1 | ||||
|         self.fp.seek(self._frame_pos[frame]) | ||||
|         self.tag_v2.load(self.fp) | ||||
|         self.__next = self.tag_v2.next | ||||
|         # fill the legacy tag/ifd entries | ||||
|         self.tag = self.ifd = ImageFileDirectory_v1.from_v2(self.tag_v2) | ||||
|         self.__frame = frame | ||||
|  | @ -1006,9 +1005,6 @@ class TiffImageFile(ImageFile.ImageFile): | |||
|                 # Section 14: Differencing Predictor | ||||
|                 self.decoderconfig = (self.tag_v2[PREDICTOR],) | ||||
| 
 | ||||
|         if ICCPROFILE in self.tag_v2: | ||||
|             self.info['icc_profile'] = self.tag_v2[ICCPROFILE] | ||||
| 
 | ||||
|         return args | ||||
| 
 | ||||
|     def load(self): | ||||
|  | @ -1016,6 +1012,12 @@ class TiffImageFile(ImageFile.ImageFile): | |||
|             return self._load_libtiff() | ||||
|         return super(TiffImageFile, self).load() | ||||
| 
 | ||||
|     def load_end(self): | ||||
|         # allow closing if we're on the first frame, there's no next | ||||
|         # This is the ImageFile.load path only, libtiff specific below. | ||||
|         if self.__frame == 0 and not self.__next: | ||||
|             self._close_exclusive_fp_after_loading = True | ||||
| 
 | ||||
|     def _load_libtiff(self): | ||||
|         """ Overload method triggered when we detect a compressed tiff | ||||
|             Calls out to libtiff """ | ||||
|  | @ -1093,16 +1095,14 @@ class TiffImageFile(ImageFile.ImageFile): | |||
|         self.tile = [] | ||||
|         self.readonly = 0 | ||||
|         # libtiff closed the fp in a, we need to close self.fp, if possible | ||||
|         if hasattr(self.fp, 'close'): | ||||
|             if not self.__next: | ||||
|         if self._exclusive_fp: | ||||
|             if self.__frame == 0 and not self.__next: | ||||
|                 self.fp.close() | ||||
|         self.fp = None  # might be shared | ||||
|                 self.fp = None  # might be shared | ||||
| 
 | ||||
|         if err < 0: | ||||
|             raise IOError(err) | ||||
| 
 | ||||
|         self.load_end() | ||||
| 
 | ||||
|         return Image.Image.load(self) | ||||
| 
 | ||||
|     def _setup(self): | ||||
|  | @ -1138,7 +1138,7 @@ class TiffImageFile(ImageFile.ImageFile): | |||
| 
 | ||||
|         sampleFormat = self.tag_v2.get(SAMPLEFORMAT, (1,)) | ||||
|         if (len(sampleFormat) > 1 | ||||
|             and max(sampleFormat) == min(sampleFormat) == 1): | ||||
|            and max(sampleFormat) == min(sampleFormat) == 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 | ||||
|  | @ -1171,11 +1171,16 @@ class TiffImageFile(ImageFile.ImageFile): | |||
|         yres = self.tag_v2.get(Y_RESOLUTION, 1) | ||||
| 
 | ||||
|         if xres and yres: | ||||
|             resunit = self.tag_v2.get(RESOLUTION_UNIT, 1) | ||||
|             resunit = self.tag_v2.get(RESOLUTION_UNIT) | ||||
|             if resunit == 2:  # dots per inch | ||||
|                 self.info["dpi"] = xres, yres | ||||
|             elif resunit == 3:  # dots per centimeter. convert to dpi | ||||
|                 self.info["dpi"] = xres * 2.54, yres * 2.54 | ||||
|             elif resunit is None:  # used to default to 1, but now 2) | ||||
|                 self.info["dpi"] = xres, yres | ||||
|                 # For backward compatibility, | ||||
|                 # we also preserve the old behavior | ||||
|                 self.info["resolution"] = xres, yres | ||||
|             else:  # No absolute unit of measurement | ||||
|                 self.info["resolution"] = xres, yres | ||||
| 
 | ||||
|  | @ -1197,7 +1202,7 @@ class TiffImageFile(ImageFile.ImageFile): | |||
|                                                      "tiff_sgilog24", | ||||
|                                                      "tiff_raw_16"]: | ||||
|                 # if DEBUG: | ||||
|                 #     print "Activating g4 compression for whole file" | ||||
|                 #     print("Activating g4 compression for whole file") | ||||
| 
 | ||||
|                 # Decoder expects entire file as one tile. | ||||
|                 # There's a buffer size limit in load (64k) | ||||
|  | @ -1281,11 +1286,17 @@ class TiffImageFile(ImageFile.ImageFile): | |||
|                 print("- unsupported data organization") | ||||
|             raise SyntaxError("unknown data organization") | ||||
| 
 | ||||
|         # Fix up info. | ||||
|         if ICCPROFILE in self.tag_v2: | ||||
|             self.info['icc_profile'] = self.tag_v2[ICCPROFILE] | ||||
| 
 | ||||
|         # fixup palette descriptor | ||||
| 
 | ||||
|         if self.mode == "P": | ||||
|             palette = [o8(b // 256) for b in self.tag_v2[COLORMAP]] | ||||
|             self.palette = ImagePalette.raw("RGB;L", b"".join(palette)) | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # -------------------------------------------------------------------- | ||||
| # Write TIFF files | ||||
|  | @ -1362,10 +1373,10 @@ def _save(im, fp, filename): | |||
|                 ifd[key] = im.tag_v2[key] | ||||
|                 ifd.tagtype[key] = im.tag_v2.tagtype[key] | ||||
| 
 | ||||
|         # preserve ICC profile (should also work when saving other formats | ||||
|         # which support profiles as TIFF) -- 2008-06-06 Florian Hoech | ||||
|         if "icc_profile" in im.info: | ||||
|             ifd[ICCPROFILE] = im.info["icc_profile"] | ||||
|     # preserve ICC profile (should also work when saving other formats | ||||
|     # which support profiles as TIFF) -- 2008-06-06 Florian Hoech | ||||
|     if "icc_profile" in im.info: | ||||
|         ifd[ICCPROFILE] = im.info["icc_profile"] | ||||
| 
 | ||||
|     for key, name in [(IMAGEDESCRIPTION, "description"), | ||||
|                       (X_RESOLUTION, "resolution"), | ||||
|  | @ -1377,11 +1388,6 @@ def _save(im, fp, filename): | |||
|                       (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] = im.encoderinfo[name.replace("_", " ")] | ||||
|         if name in im.encoderinfo: | ||||
|             ifd[key] = im.encoderinfo[name] | ||||
| 
 | ||||
|  | @ -1491,6 +1497,7 @@ def _save(im, fp, filename): | |||
|         # just to access o32 and o16 (using correct byte order) | ||||
|         im._debug_multipage = ifd | ||||
| 
 | ||||
| 
 | ||||
| class AppendingTiffWriter: | ||||
|     fieldSizes = [ | ||||
|         0,  # None | ||||
|  | @ -1678,13 +1685,10 @@ class AppendingTiffWriter: | |||
| 
 | ||||
|     def fixIFD(self): | ||||
|         numTags = self.readShort() | ||||
|         #trace("fixing IFD at %X; number of tags: %u (0x%X)", self.f.tell()-2, | ||||
|         #      numTags, numTags) | ||||
| 
 | ||||
|         for i in range(numTags): | ||||
|             tag, fieldType, count = struct.unpack(self.tagFormat, self.f.read(8)) | ||||
|             #trace("  at %X: tag %u (0x%X), type %u, count %u", self.f.tell()-8, | ||||
|             #      tag, tag, fieldType, count) | ||||
|             tag, fieldType, count = struct.unpack(self.tagFormat, | ||||
|                                                   self.f.read(8)) | ||||
| 
 | ||||
|             fieldSize = self.fieldSizes[fieldType] | ||||
|             totalSize = fieldSize * count | ||||
|  | @ -1736,21 +1740,34 @@ class AppendingTiffWriter: | |||
|             else: | ||||
|                 self.rewriteLastLong(offset) | ||||
| 
 | ||||
| 
 | ||||
| def _save_all(im, fp, filename): | ||||
|     if not hasattr(im, "n_frames"): | ||||
|     encoderinfo = im.encoderinfo.copy() | ||||
|     encoderconfig = im.encoderconfig | ||||
|     append_images = encoderinfo.get("append_images", []) | ||||
|     if not hasattr(im, "n_frames") and not len(append_images): | ||||
|         return _save(im, fp, filename) | ||||
| 
 | ||||
|     cur_idx = im.tell() | ||||
|     try: | ||||
|         with AppendingTiffWriter(fp) as tf: | ||||
|             for idx in range(im.n_frames): | ||||
|                 im.seek(idx) | ||||
|                 im.load() | ||||
|                 _save(im, tf, filename) | ||||
|                 tf.newFrame() | ||||
|             for ims in [im]+append_images: | ||||
|                 ims.encoderinfo = encoderinfo | ||||
|                 ims.encoderconfig = encoderconfig | ||||
|                 if not hasattr(ims, "n_frames"): | ||||
|                     nfr = 1 | ||||
|                 else: | ||||
|                     nfr = ims.n_frames | ||||
| 
 | ||||
|                 for idx in range(nfr): | ||||
|                     ims.seek(idx) | ||||
|                     ims.load() | ||||
|                     _save(ims, tf, filename) | ||||
|                     tf.newFrame() | ||||
|     finally: | ||||
|         im.seek(cur_idx) | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # -------------------------------------------------------------------- | ||||
| # Register | ||||
|  |  | |||
|  | @ -18,12 +18,11 @@ | |||
| # the WalImageFile.open() function instead. | ||||
| 
 | ||||
| # This reader is based on the specification available from: | ||||
| #    http://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml | ||||
| #    https://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml | ||||
| # and has been tested with a few sample files found using google. | ||||
| 
 | ||||
| from __future__ import print_function | ||||
| 
 | ||||
| from PIL import Image, _binary | ||||
| from . import Image | ||||
| from ._binary import i32le as i32 | ||||
| 
 | ||||
| try: | ||||
|     import builtins | ||||
|  | @ -31,8 +30,6 @@ except ImportError: | |||
|     import __builtin__ | ||||
|     builtins = __builtin__ | ||||
| 
 | ||||
| i32 = _binary.i32le | ||||
| 
 | ||||
| 
 | ||||
| def open(filename): | ||||
|     """ | ||||
|  | @ -47,33 +44,35 @@ def open(filename): | |||
|     # FIXME: modify to return a WalImageFile instance instead of | ||||
|     # plain Image object ? | ||||
| 
 | ||||
|     def imopen(fp): | ||||
|         # read header fields | ||||
|         header = fp.read(32+24+32+12) | ||||
|         size = i32(header, 32), i32(header, 36) | ||||
|         offset = i32(header, 40) | ||||
| 
 | ||||
|         # load pixel data | ||||
|         fp.seek(offset) | ||||
| 
 | ||||
|         Image._decompression_bomb_check(size) | ||||
|         im = Image.frombytes("P", size, fp.read(size[0] * size[1])) | ||||
|         im.putpalette(quake2palette) | ||||
| 
 | ||||
|         im.format = "WAL" | ||||
|         im.format_description = "Quake2 Texture" | ||||
| 
 | ||||
|         # strings are null-terminated | ||||
|         im.info["name"] = header[:32].split(b"\0", 1)[0] | ||||
|         next_name = header[56:56+32].split(b"\0", 1)[0] | ||||
|         if next_name: | ||||
|             im.info["next_name"] = next_name | ||||
| 
 | ||||
|         return im | ||||
| 
 | ||||
|     if hasattr(filename, "read"): | ||||
|         fp = filename | ||||
|         return imopen(filename) | ||||
|     else: | ||||
|         fp = builtins.open(filename, "rb") | ||||
| 
 | ||||
|     # read header fields | ||||
|     header = fp.read(32+24+32+12) | ||||
|     size = i32(header, 32), i32(header, 36) | ||||
|     offset = i32(header, 40) | ||||
| 
 | ||||
|     # load pixel data | ||||
|     fp.seek(offset) | ||||
| 
 | ||||
|     im = Image.frombytes("P", size, fp.read(size[0] * size[1])) | ||||
|     im.putpalette(quake2palette) | ||||
| 
 | ||||
|     im.format = "WAL" | ||||
|     im.format_description = "Quake2 Texture" | ||||
| 
 | ||||
|     # strings are null-terminated | ||||
|     im.info["name"] = header[:32].split(b"\0", 1)[0] | ||||
|     next_name = header[56:56+32].split(b"\0", 1)[0] | ||||
|     if next_name: | ||||
|         im.info["next_name"] = next_name | ||||
| 
 | ||||
|     return im | ||||
| 
 | ||||
|         with builtins.open(filename, "rb") as fp: | ||||
|             return imopen(fp) | ||||
| 
 | ||||
| quake2palette = ( | ||||
|     # default palette taken from piffo 0.93 by Hans Häggström | ||||
|  |  | |||
|  | @ -1,7 +1,5 @@ | |||
| from PIL import Image | ||||
| from PIL import ImageFile | ||||
| from . import Image, ImageFile, _webp | ||||
| from io import BytesIO | ||||
| from PIL import _webp | ||||
| 
 | ||||
| 
 | ||||
| _VALID_WEBP_MODES = { | ||||
|  | @ -43,7 +41,7 @@ class WebPImageFile(ImageFile.ImageFile): | |||
|         self.tile = [("raw", (0, 0) + self.size, 0, self.mode)] | ||||
| 
 | ||||
|     def _getexif(self): | ||||
|         from PIL.JpegImagePlugin import _getexif | ||||
|         from .JpegImagePlugin import _getexif | ||||
|         return _getexif(self) | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -14,8 +14,16 @@ | |||
| # | ||||
| # See the README file for information on usage and redistribution. | ||||
| # | ||||
| # WMF/EMF reference documentation: | ||||
| # https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-WMF/[MS-WMF].pdf | ||||
| # http://wvware.sourceforge.net/caolan/index.html | ||||
| # http://wvware.sourceforge.net/caolan/ora-wmf.html | ||||
| 
 | ||||
| from __future__ import print_function | ||||
| 
 | ||||
| from . import Image, ImageFile | ||||
| from ._binary import i16le as word, si16le as short, i32le as dword, si32le as _long | ||||
| 
 | ||||
| from PIL import Image, ImageFile, _binary | ||||
| 
 | ||||
| __version__ = "0.2" | ||||
| 
 | ||||
|  | @ -53,24 +61,11 @@ if hasattr(Image.core, "drawwmf"): | |||
| 
 | ||||
|     register_handler(WmfHandler()) | ||||
| 
 | ||||
| # -------------------------------------------------------------------- | ||||
| 
 | ||||
| word = _binary.i16le | ||||
| 
 | ||||
| 
 | ||||
| def short(c, o=0): | ||||
|     v = word(c, o) | ||||
|     if v >= 32768: | ||||
|         v -= 65536 | ||||
|     return v | ||||
| 
 | ||||
| dword = _binary.i32le | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # -------------------------------------------------------------------- | ||||
| # Read WMF file | ||||
| 
 | ||||
| 
 | ||||
| def _accept(prefix): | ||||
|     return ( | ||||
|         prefix[:6] == b"\xd7\xcd\xc6\x9a\x00\x00" or | ||||
|  | @ -111,7 +106,7 @@ class WmfStubImageFile(ImageFile.StubImageFile): | |||
| 
 | ||||
|             self.info["dpi"] = 72 | ||||
| 
 | ||||
|             # print self.mode, self.size, self.info | ||||
|             # print(self.mode, self.size, self.info) | ||||
| 
 | ||||
|             # sanity check (standard metafile header) | ||||
|             if s[22:26] != b"\x01\x00\t\x00": | ||||
|  | @ -121,13 +116,13 @@ class WmfStubImageFile(ImageFile.StubImageFile): | |||
|             # enhanced metafile | ||||
| 
 | ||||
|             # get bounding box | ||||
|             x0 = dword(s, 8) | ||||
|             y0 = dword(s, 12) | ||||
|             x1 = dword(s, 16) | ||||
|             y1 = dword(s, 20) | ||||
|             x0 = _long(s, 8) | ||||
|             y0 = _long(s, 12) | ||||
|             x1 = _long(s, 16) | ||||
|             y1 = _long(s, 20) | ||||
| 
 | ||||
|             # get frame (in 0.01 millimeter units) | ||||
|             frame = dword(s, 24), dword(s, 28), dword(s, 32), dword(s, 36) | ||||
|             frame = _long(s, 24), _long(s, 28), _long(s, 32), _long(s, 36) | ||||
| 
 | ||||
|             # normalize size to 72 dots per inch | ||||
|             size = x1 - x0, y1 - y0 | ||||
|  |  | |||
|  | @ -17,12 +17,11 @@ | |||
| # FIXME: make save work (this requires quantization support) | ||||
| # | ||||
| 
 | ||||
| from PIL import Image, ImageFile, ImagePalette, _binary | ||||
| from . import Image, ImageFile, ImagePalette | ||||
| from ._binary import i8, o8 | ||||
| 
 | ||||
| __version__ = "0.1" | ||||
| 
 | ||||
| o8 = _binary.o8 | ||||
| 
 | ||||
| _MAGIC = b"P7 332" | ||||
| 
 | ||||
| # standard color palette for thumbnails (RGB332) | ||||
|  | @ -48,7 +47,7 @@ class XVThumbImageFile(ImageFile.ImageFile): | |||
|     def _open(self): | ||||
| 
 | ||||
|         # check magic | ||||
|         if self.fp.read(6) != _MAGIC: | ||||
|         if not _accept(self.fp.read(6)): | ||||
|             raise SyntaxError("not an XV thumbnail file") | ||||
| 
 | ||||
|         # Skip to beginning of next line | ||||
|  | @ -59,14 +58,14 @@ class XVThumbImageFile(ImageFile.ImageFile): | |||
|             s = self.fp.readline() | ||||
|             if not s: | ||||
|                 raise SyntaxError("Unexpected EOF reading XV thumbnail file") | ||||
|             if s[0] != b'#': | ||||
|             if i8(s[0]) != 35:  # ie. when not a comment: '#' | ||||
|                 break | ||||
| 
 | ||||
|         # parse header line (already read) | ||||
|         s = s.strip().split() | ||||
| 
 | ||||
|         self.mode = "P" | ||||
|         self.size = int(s[0:1]), int(s[1:2]) | ||||
|         self.size = int(s[0]), int(s[1]) | ||||
| 
 | ||||
|         self.palette = ImagePalette.raw("RGB", PALETTE) | ||||
| 
 | ||||
|  | @ -75,6 +74,7 @@ class XVThumbImageFile(ImageFile.ImageFile): | |||
|              self.fp.tell(), (self.mode, 0, 1) | ||||
|              )] | ||||
| 
 | ||||
| 
 | ||||
| # -------------------------------------------------------------------- | ||||
| 
 | ||||
| Image.register_open(XVThumbImageFile.format, XVThumbImageFile, _accept) | ||||
|  |  | |||
|  | @ -20,7 +20,7 @@ | |||
| # | ||||
| 
 | ||||
| import re | ||||
| from PIL import Image, ImageFile | ||||
| from . import Image, ImageFile | ||||
| 
 | ||||
| __version__ = "0.6" | ||||
| 
 | ||||
|  |  | |||
|  | @ -16,8 +16,8 @@ | |||
| 
 | ||||
| 
 | ||||
| import re | ||||
| from PIL import Image, ImageFile, ImagePalette | ||||
| from PIL._binary import i8, o8 | ||||
| from . import Image, ImageFile, ImagePalette | ||||
| from ._binary import i8, o8 | ||||
| 
 | ||||
| __version__ = "0.2" | ||||
| 
 | ||||
|  | @ -116,8 +116,6 @@ class XpmImageFile(ImageFile.ImageFile): | |||
|         for i in range(ysize): | ||||
|             s[i] = self.fp.readline()[1:xsize+1].ljust(xsize) | ||||
| 
 | ||||
|         self.fp = None | ||||
| 
 | ||||
|         return b"".join(s) | ||||
| 
 | ||||
| # | ||||
|  |  | |||
|  | @ -11,8 +11,10 @@ | |||
| 
 | ||||
| # ;-) | ||||
| 
 | ||||
| VERSION = '1.1.7'  # PIL version | ||||
| PILLOW_VERSION = '3.5.0.dev0'  # Pillow | ||||
| from . import version | ||||
| 
 | ||||
| VERSION = '1.1.7'  # PIL Version | ||||
| PILLOW_VERSION =  version.__version__ | ||||
| 
 | ||||
| __version__ = PILLOW_VERSION | ||||
| 
 | ||||
|  |  | |||
|  | @ -28,10 +28,9 @@ else: | |||
| 
 | ||||
| 
 | ||||
| # Input, le = little endian, be = big endian | ||||
| # TODO: replace with more readable struct.unpack equivalent | ||||
| def i16le(c, o=0): | ||||
|     """ | ||||
|     Converts a 2-bytes (16 bits) string to an integer. | ||||
|     Converts a 2-bytes (16 bits) string to an unsigned integer. | ||||
| 
 | ||||
|     c: string containing bytes to convert | ||||
|     o: offset of bytes to convert in string | ||||
|  | @ -39,9 +38,19 @@ def i16le(c, o=0): | |||
|     return unpack("<H", c[o:o+2])[0] | ||||
| 
 | ||||
| 
 | ||||
| def si16le(c, o=0): | ||||
|     """ | ||||
|     Converts a 2-bytes (16 bits) string to a signed integer. | ||||
| 
 | ||||
|     c: string containing bytes to convert | ||||
|     o: offset of bytes to convert in string | ||||
|     """ | ||||
|     return unpack("<h", c[o:o+2])[0] | ||||
| 
 | ||||
| 
 | ||||
| def i32le(c, o=0): | ||||
|     """ | ||||
|     Converts a 4-bytes (32 bits) string to an integer. | ||||
|     Converts a 4-bytes (32 bits) string to an unsigned integer. | ||||
| 
 | ||||
|     c: string containing bytes to convert | ||||
|     o: offset of bytes to convert in string | ||||
|  | @ -49,6 +58,16 @@ def i32le(c, o=0): | |||
|     return unpack("<I", c[o:o+4])[0] | ||||
| 
 | ||||
| 
 | ||||
| def si32le(c, o=0): | ||||
|     """ | ||||
|     Converts a 4-bytes (32 bits) string to a signed integer. | ||||
| 
 | ||||
|     c: string containing bytes to convert | ||||
|     o: offset of bytes to convert in string | ||||
|     """ | ||||
|     return unpack("<i", c[o:o+4])[0] | ||||
| 
 | ||||
| 
 | ||||
| def i16be(c, o=0): | ||||
|     return unpack(">H", c[o:o+2])[0] | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,46 +1,27 @@ | |||
| from PIL import Image | ||||
| from . import Image | ||||
| 
 | ||||
| modules = { | ||||
|     "pil": "PIL._imaging", | ||||
|     "tkinter": "PIL._imagingtk", | ||||
|     "tkinter": "PIL._tkinter_finder", | ||||
|     "freetype2": "PIL._imagingft", | ||||
|     "littlecms2": "PIL._imagingcms", | ||||
|     "webp": "PIL._webp", | ||||
|     "transp_webp": ("WEBP", "WebPDecoderBuggyAlpha") | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| def check_module(feature): | ||||
|     if feature not in modules: | ||||
|     if not (feature in modules): | ||||
|         raise ValueError("Unknown module %s" % feature) | ||||
| 
 | ||||
|     module = modules[feature] | ||||
| 
 | ||||
|     method_to_call = None | ||||
|     if isinstance(module, 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 | ||||
| 
 | ||||
|     except ImportError: | ||||
|         return False | ||||
| 
 | ||||
| def get_supported_modules(): | ||||
|     supported_modules = [] | ||||
|     for feature in modules: | ||||
|         if check_module(feature): | ||||
|             supported_modules.append(feature) | ||||
|     return supported_modules | ||||
|     return [f for f in modules if check_module(f)] | ||||
| 
 | ||||
| codecs = { | ||||
|     "jpg": "jpeg", | ||||
|  | @ -49,7 +30,6 @@ codecs = { | |||
|     "libtiff": "libtiff" | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| def check_codec(feature): | ||||
|     if feature not in codecs: | ||||
|         raise ValueError("Unknown codec %s" % feature) | ||||
|  | @ -60,8 +40,39 @@ def check_codec(feature): | |||
| 
 | ||||
| 
 | ||||
| def get_supported_codecs(): | ||||
|     supported_codecs = [] | ||||
|     for feature in codecs: | ||||
|         if check_codec(feature): | ||||
|             supported_codecs.append(feature) | ||||
|     return supported_codecs | ||||
|     return [f for f in codecs if check_codec(f)] | ||||
| 
 | ||||
| features = { | ||||
|     "webp_mux": ("PIL._webp", 'HAVE_WEBPMUX'), | ||||
|     "transp_webp": ("PIL._webp", "HAVE_TRANSPARENCY"), | ||||
|     "raqm": ("PIL._imagingft", "HAVE_RAQM") | ||||
| } | ||||
| 
 | ||||
| def check_feature(feature): | ||||
|     if feature not in features: | ||||
|         raise ValueError("Unknown feature %s" % feature) | ||||
| 
 | ||||
|     module, flag = features[feature] | ||||
|      | ||||
|     try: | ||||
|         imported_module = __import__(module, fromlist=['PIL']) | ||||
|         return getattr(imported_module, flag) | ||||
|     except ImportError: | ||||
|         return None | ||||
| 
 | ||||
| 
 | ||||
| def get_supported_features(): | ||||
|     return [f for f in features if check_feature(f)] | ||||
| 
 | ||||
| 
 | ||||
| def check(feature): | ||||
|     return (feature in modules and check_module(feature) or \ | ||||
|             feature in codecs and check_codec(feature) or \ | ||||
|             feature in features and check_feature(feature)) | ||||
| 
 | ||||
| def get_supported(): | ||||
|     ret = get_supported_modules() | ||||
|     ret.extend(get_supported_features()) | ||||
|     ret.extend(get_supported_codecs()) | ||||
|     return ret | ||||
|          | ||||
|  |  | |||
							
								
								
									
										2
									
								
								PIL/version.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								PIL/version.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| # Master version for Pillow | ||||
| __version__ = '4.3.0.dev0' | ||||
							
								
								
									
										12
									
								
								README.rst
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								README.rst
									
									
									
									
									
								
							|  | @ -14,9 +14,9 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors <https://github. | |||
|     * - docs | ||||
|       - |docs| | ||||
|     * - tests | ||||
|       - | |linux| |macos| |windows| |coverage| |health| | ||||
|       - | |linux| |macos| |windows| |coverage| | ||||
|     * - package | ||||
|       - |zenodo| |version| |downloads| | ||||
|       - |zenodo| |version| | ||||
| 
 | ||||
| .. |docs| image:: https://readthedocs.org/projects/pillow/badge/?version=latest | ||||
|    :target: https://pillow.readthedocs.io/?badge=latest | ||||
|  | @ -38,10 +38,6 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors <https://github. | |||
|    :target: https://coveralls.io/github/python-pillow/Pillow?branch=master | ||||
|    :alt: Code coverage | ||||
| 
 | ||||
| .. |health| image:: https://landscape.io/github/python-pillow/Pillow/master/landscape.svg | ||||
|    :target: https://landscape.io/github/python-pillow/Pillow/master | ||||
|    :alt: Code health | ||||
| 
 | ||||
| .. |zenodo| image:: https://zenodo.org/badge/17549/python-pillow/Pillow.svg | ||||
|    :target: https://zenodo.org/badge/latestdoi/17549/python-pillow/Pillow | ||||
| 
 | ||||
|  | @ -49,10 +45,6 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors <https://github. | |||
|    :target: https://pypi.python.org/pypi/Pillow/ | ||||
|    :alt: Latest PyPI version | ||||
| 
 | ||||
| .. |downloads| image:: https://img.shields.io/pypi/dm/pillow.svg | ||||
|    :target: https://pypi.python.org/pypi/Pillow/ | ||||
|    :alt: Number of PyPI downloads | ||||
| 
 | ||||
| .. end-badges | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										45
									
								
								RELEASING.md
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								RELEASING.md
									
									
									
									
									
								
							|  | @ -7,10 +7,8 @@ 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) 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 | ||||
| ``` | ||||
| * [ ] Check that all of the wheel builds [Pillow Wheel Builder](https://github.com/python-pillow/pillow-wheels) pass the tests in TravisCI. | ||||
| * [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in `PIL/version.py` | ||||
| * [ ] Update `CHANGES.rst`. | ||||
| * [ ] Run pre-release check via `make release-test` in a freshly cloned repo. | ||||
| * [ ] Create branch and tag for release e.g.: | ||||
|  | @ -20,12 +18,12 @@ Released quarterly on the first day of January, April, July, October. | |||
|     $ git push --all | ||||
|     $ git push --tags | ||||
| ``` | ||||
| * [ ] Create and upload source distributions e.g.: | ||||
| * [ ] Create source distributions e.g.: | ||||
| ``` | ||||
|     $ make sdist | ||||
|     $ make upload | ||||
| ``` | ||||
| * [ ] Create and upload [binary distributions](#binary-distributions) | ||||
| * [ ] Create binary distributions [binary distributions](#binary-distributions) | ||||
| * [ ] Upload all binaries and source distributions with ``twine upload dist/Pillow-4.1.0-*`` | ||||
| * [ ] 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 | ||||
|  | @ -40,24 +38,18 @@ Released as needed for security, installation or critical bug fixes. | |||
| ``` | ||||
|     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 | ||||
|     appveyor.yml | ||||
| ``` | ||||
| * [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in `PIL/version.py` | ||||
| * [ ] Run pre-release check via `make release-test`. | ||||
| * [ ] Create tag for release e.g.: | ||||
| ``` | ||||
|     $ git tag 2.9.1 | ||||
|     $ git push --tags | ||||
| ``` | ||||
| * [ ] Create and upload source distributions e.g.: | ||||
| * [ ] Create source distributions e.g.: | ||||
| ``` | ||||
|     $ make sdistup | ||||
|     $ make sdist | ||||
| ``` | ||||
| * [ ] Create and upload [binary distributions](#binary-distributions) | ||||
| * [ ] Create [binary distributions](#binary-distributions) | ||||
| 
 | ||||
| ## Embargoed Release | ||||
| 
 | ||||
|  | @ -76,11 +68,11 @@ Released as needed privately to individual vendors for critical security-related | |||
|     git push origin 2.5.x | ||||
|     git push origin --tags | ||||
| ``` | ||||
| * [ ] Create and upload source distributions e.g.: | ||||
| * [ ] Create source distributions e.g.: | ||||
| ``` | ||||
|     $ make sdistup | ||||
|     $ make sdist | ||||
| ``` | ||||
| * [ ] Create and upload [binary distributions](#binary-distributions) | ||||
| * [ ] Create [binary distributions](#binary-distributions) | ||||
| 
 | ||||
| ## Binary Distributions | ||||
| 
 | ||||
|  | @ -88,8 +80,8 @@ Released as needed privately to individual vendors for critical security-related | |||
| * [ ] 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 *``. | ||||
| 
 | ||||
| ### macOS | ||||
| * [ ] Use the [Pillow macOS Wheel Builder](https://github.com/python-pillow/pillow-wheels): | ||||
| ### Mac and Linux | ||||
| * [ ] Use the [Pillow Wheel Builder](https://github.com/python-pillow/pillow-wheels): | ||||
| ``` | ||||
|     $ git checkout https://github.com/python-pillow/pillow-wheels | ||||
|     $ cd pillow-wheels | ||||
|  | @ -97,12 +89,13 @@ Released as needed privately to individual vendors for critical security-related | |||
|     $ git submodule update | ||||
|     $ cd Pillow | ||||
|     $ git fetch --all | ||||
|     $ git commit -a -m "Pillow -> 2.9.0" | ||||
|     $ git push | ||||
|     $ git checkout [[release tag]] | ||||
|     $ cd .. | ||||
|     $ git commit -m "Pillow -> 2.9.0" Pillow | ||||
|     $ git push  | ||||
| ``` | ||||
| * [ ] Download distributions from the [Pillow macOS Wheel Builder container](http://cdf58691c5cf45771290-6a3b6a0f5f6ab91aadc447b2a897dd9a.r50.cf2.rackcdn.com/) and ``twine upload *``. | ||||
| * [ ] Download distributions from the [Pillow Wheel Builder container](http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com/). | ||||
| 
 | ||||
| ### Linux | ||||
| 
 | ||||
| ## Publicize Release | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										0
									
								
								Scripts/createfontdatachunk.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										0
									
								
								Scripts/createfontdatachunk.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										27
									
								
								Scripts/enhancer.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										27
									
								
								Scripts/enhancer.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							|  | @ -7,21 +7,22 @@ | |||
| # drag the slider to modify the image. | ||||
| # | ||||
| 
 | ||||
| try: | ||||
|     from tkinter import Tk, Toplevel, Frame, Label, Scale, HORIZONTAL | ||||
| except ImportError: | ||||
|     from Tkinter import Tk, Toplevel, Frame, Label, Scale, HORIZONTAL | ||||
| import sys | ||||
| 
 | ||||
| if sys.version_info[0] > 2: | ||||
|     import tkinter | ||||
| else: | ||||
|     import Tkinter as tkinter | ||||
| 
 | ||||
| from PIL import Image, ImageTk, ImageEnhance | ||||
| import sys | ||||
| 
 | ||||
| # | ||||
| # enhancer widget | ||||
| 
 | ||||
| 
 | ||||
| class Enhance(Frame): | ||||
| class Enhance(tkinter.Frame): | ||||
|     def __init__(self, master, image, name, enhancer, lo, hi): | ||||
|         Frame.__init__(self, master) | ||||
|         tkinter.Frame.__init__(self, master) | ||||
| 
 | ||||
|         # set up the image | ||||
|         self.tkim = ImageTk.PhotoImage(image.mode, image.size) | ||||
|  | @ -29,10 +30,10 @@ class Enhance(Frame): | |||
|         self.update("1.0")  # normalize | ||||
| 
 | ||||
|         # image window | ||||
|         Label(self, image=self.tkim).pack() | ||||
|         tkinter.Label(self, image=self.tkim).pack() | ||||
| 
 | ||||
|         # scale | ||||
|         s = Scale(self, label=name, orient=HORIZONTAL, | ||||
|         s = tkinter.Scale(self, label=name, orient=tkinter.HORIZONTAL, | ||||
|                   from_=lo, to=hi, resolution=0.01, | ||||
|                   command=self.update) | ||||
|         s.set(self.value) | ||||
|  | @ -49,15 +50,15 @@ if len(sys.argv) != 2: | |||
|     print("Usage: enhancer file") | ||||
|     sys.exit(1) | ||||
| 
 | ||||
| root = Tk() | ||||
| root = tkinter.Tk() | ||||
| 
 | ||||
| im = Image.open(sys.argv[1]) | ||||
| 
 | ||||
| im.thumbnail((200, 200)) | ||||
| 
 | ||||
| Enhance(root, im, "Color", ImageEnhance.Color, 0.0, 4.0).pack() | ||||
| Enhance(Toplevel(), im, "Sharpness", ImageEnhance.Sharpness, -2.0, 2.0).pack() | ||||
| Enhance(Toplevel(), im, "Brightness", ImageEnhance.Brightness, -1.0, 3.0).pack() | ||||
| Enhance(Toplevel(), im, "Contrast", ImageEnhance.Contrast, -1.0, 3.0).pack() | ||||
| Enhance(tkinter.Toplevel(), im, "Sharpness", ImageEnhance.Sharpness, -2.0, 2.0).pack() | ||||
| Enhance(tkinter.Toplevel(), im, "Brightness", ImageEnhance.Brightness, -1.0, 3.0).pack() | ||||
| Enhance(tkinter.Toplevel(), im, "Contrast", ImageEnhance.Contrast, -1.0, 3.0).pack() | ||||
| 
 | ||||
| root.mainloop() | ||||
|  |  | |||
							
								
								
									
										0
									
								
								Scripts/explode.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										0
									
								
								Scripts/explode.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										0
									
								
								Scripts/gifmaker.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										0
									
								
								Scripts/gifmaker.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue
	
	Block a user