Merge branch 'master' into license

This commit is contained in:
Hugo 2017-01-29 13:06:10 +02:00 committed by GitHub
commit 5d9c975c46
201 changed files with 3329 additions and 5149 deletions

View File

@ -6,104 +6,55 @@ notifications:
# Run slow PyPy* first, to give them a headstart and reduce waiting time. # 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. # Run latest 3.x and 2.x next, to get quick compatibility results.
# Then run the remainder. # Then run the remainder.
python:
- "pypy" matrix:
- "pypy3" fast_finish: true
- 3.5 allow_failures:
- 2.7 - python: nightly
- 2.6 include:
- "2.7_with_system_site_packages" # For PyQt4 - python: "pypy"
- 3.2 - python: "pypy3"
- 3.3 - python: '3.6'
- 3.4 - python: '2.7'
- nightly - env: DOCKER="alpine"
- env: DOCKER="ubuntu-trusty-x86"
- env: DOCKER="ubuntu-xenial-amd64"
- env: DOCKER="ubuntu-precise-amd64"
- python: "2.7_with_system_site_packages" # For PyQt4
- python: '3.5'
- python: '3.4'
- python: '3.3'
- python: 'nightly'
dist: trusty
sudo: required
services:
- docker
install: install:
- "travis_retry pip install pycparser!=2.14" # TEMPORARY, WORKAROUND FOR https://github.com/eliben/pycparser/issues/147 - if [ "$DOCKER" == "" ]; then .travis/install.sh; fi
- "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 and Python 2.6; skip for those
- if [ $TRAVIS_PYTHON_VERSION != "pypy" && $TRAVIS_PYTHON_VERSION != "2.6" ]; then travis_retry pip install pyroma; fi
- if [ "$TRAVIS_PYTHON_VERSION" == "2.6" ]; then travis_retry pip install unittest2; fi before_install:
- if [ "$DOCKER" ]; then docker pull pythonpillow/$DOCKER; fi
# Coverage 4.0 doesn't support Python 3.2 before_script:
- if [ "$TRAVIS_PYTHON_VERSION" == "3.2" ]; then travis_retry pip install coverage==3.7.1; fi # Qt needs a display for some of the tests, and it's only run on the system site packages install
- if [ "$TRAVIS_PYTHON_VERSION" != "3.2" ]; then travis_retry pip install coverage; fi - "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"
# 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
script: script:
- if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage erase; fi - |
- python setup.py clean if [ "$DOCKER" == "" ]; then
- CFLAGS="-coverage" python setup.py build_ext --inplace .travis/script.sh
else
- if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage run --append --include=PIL/* selftest.py; fi docker run -v $TRAVIS_BUILD_DIR:/Pillow pythonpillow/$DOCKER
- if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage run --append --include=PIL/* -m nose -vx Tests/test_*.py; fi fi
- 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
after_success: after_success:
# gather the coverage data - .travis/after_success.sh
- 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
after_failure: after_failure:
- | - |
if [ "$TRAVIS_REPO_SLUG" = "python-pillow/Pillow" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ]; then if [ "$TRAVIS_REPO_SLUG" = "python-pillow/Pillow" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ]; then
@ -125,11 +76,6 @@ after_script:
echo leader=$BUILD_LEADER status=$BUILD_AGGREGATE_STATUS echo leader=$BUILD_LEADER status=$BUILD_AGGREGATE_STATUS
fi fi
matrix:
fast_finish: true
allow_failures:
- python: nightly
env: env:
global: global:
# travis encrypt AUTH_TOKEN= # travis encrypt AUTH_TOKEN=

46
.travis/after_success.sh Executable file
View File

@ -0,0 +1,46 @@
#!/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 coveralls-merge
coveralls-merge coverage.c.json
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 succeded! Triggering macOS build..."
# Trigger a macOS build at the pillow-wheels repo
./build_children.sh
else
echo "Some jobs failed"
fi
fi

34
.travis/install.sh Executable file
View File

@ -0,0 +1,34 @@
#!/bin/bash
set -e
sudo apt-get update
sudo apt-get -qq install libfreetype6-dev liblcms2-dev\
python-qt4 ghostscript libffi-dev libjpeg-turbo-progs cmake imagemagick
pip install cffi
pip install nose
pip install check-manifest
pip install olefile
# Pyroma tests sometimes hang on PyPy; skip
if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then pip install pyroma; fi
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
View 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

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@ The Python Imaging Library (PIL) is
Pillow is the friendly PIL fork. It is Pillow is the friendly PIL fork. It is
Copyright © 2012-2016 by Alex Clark and contributors Copyright © 2010-2017 by Alex Clark and contributors
Like PIL, Pillow is licensed under the open source PIL Software License: Like PIL, Pillow is licensed under the open source PIL Software License:

View File

@ -58,6 +58,13 @@ install:
python setup.py install python setup.py install
python selftest.py --installed 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: install-req:
pip install -r requirements.txt pip install -r requirements.txt

View File

@ -17,8 +17,9 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import Image from __future__ import print_function
from PIL import FontFile
from . import Image, FontFile
# -------------------------------------------------------------------- # --------------------------------------------------------------------
@ -119,9 +120,9 @@ class BdfFontFile(FontFile.FontFile):
# fontname = ";".join(font[1:]) # fontname = ";".join(font[1:])
# print "#", fontname # print("#", fontname)
# for i in comments: # for i in comments:
# print "#", i # print("#", i)
while True: while True:
c = bdf_char(fp) c = bdf_char(fp)

View File

@ -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 import math
__version__ = "0.7" __version__ = "0.7"
i8 = _binary.i8
i16 = _binary.i16le
i32 = _binary.i32le
o8 = _binary.o8
o16 = _binary.o16le
o32 = _binary.o32le
# #
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# Read BMP file # Read BMP file
@ -73,7 +68,7 @@ class BmpImageFile(ImageFile.ImageFile):
read, seek = self.fp.read, self.fp.seek read, seek = self.fp.read, self.fp.seek
if header: if header:
seek(header) seek(header)
file_info = dict() file_info = {}
file_info['header_size'] = i32(read(4)) # read bmp header size @offset 14 (this is part of the header size) file_info['header_size'] = i32(read(4)) # read bmp header size @offset 14 (this is part of the header size)
file_info['direction'] = -1 file_info['direction'] = -1
# --------------------- If requested, read header at a specific position # --------------------- If requested, read header at a specific position
@ -136,12 +131,13 @@ class BmpImageFile(ImageFile.ImageFile):
# ----------------- Process BMP with Bitfields compression (not palette) # ----------------- Process BMP with Bitfields compression (not palette)
if file_info['compression'] == self.BITFIELDS: if file_info['compression'] == self.BITFIELDS:
SUPPORTED = { SUPPORTED = {
32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0)], 32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0), (0xff000000, 0xff0000, 0xff00, 0x0) ],
24: [(0xff0000, 0xff00, 0xff)], 24: [(0xff0000, 0xff00, 0xff)],
16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)] 16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]
} }
MASK_MODES = { MASK_MODES = {
(32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX", (32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX",
(32, (0xff000000, 0xff0000, 0xff00, 0x0)): "XBGR",
(32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA", (32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA",
(32, (0x0, 0x0, 0x0, 0x0)): "BGRA", (32, (0x0, 0x0, 0x0, 0x0)): "BGRA",
(24, (0xff0000, 0xff00, 0xff)): "BGR", (24, (0xff0000, 0xff00, 0xff)): "BGR",

View File

@ -9,7 +9,7 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import Image, ImageFile from . import Image, ImageFile
_handler = None _handler = None

View File

@ -16,18 +16,16 @@
# See the README file for information on usage and redistribution. # 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" __version__ = "0.1"
# #
# -------------------------------------------------------------------- # --------------------------------------------------------------------
i8 = _binary.i8
i16 = _binary.i16le
i32 = _binary.i32le
def _accept(prefix): def _accept(prefix):
return prefix[:4] == b"\0\0\2\0" return prefix[:4] == b"\0\0\2\0"
@ -58,14 +56,14 @@ class CurImageFile(BmpImagePlugin.BmpImageFile):
m = s m = s
elif i8(s[0]) > i8(m[0]) and i8(s[1]) > i8(m[1]): elif i8(s[0]) > i8(m[0]) and i8(s[1]) > i8(m[1]):
m = s m = s
# print "width", i8(s[0]) # print("width", i8(s[0]))
# print "height", i8(s[1]) # print("height", i8(s[1]))
# print "colors", i8(s[2]) # print("colors", i8(s[2]))
# print "reserved", i8(s[3]) # print("reserved", i8(s[3]))
# print "hotspot x", i16(s[4:]) # print("hotspot x", i16(s[4:]))
# print "hotspot y", i16(s[6:]) # print("hotspot y", i16(s[6:]))
# print "bytes", i32(s[8:]) # print("bytes", i32(s[8:]))
# print "offset", i32(s[12:]) # print("offset", i32(s[12:]))
if not m: if not m:
raise TypeError("No cursors were found") raise TypeError("No cursors were found")

View File

@ -21,15 +21,14 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import Image, _binary from . import Image
from PIL.PcxImagePlugin import PcxImageFile from ._binary import i32le as i32
from .PcxImagePlugin import PcxImageFile
__version__ = "0.2" __version__ = "0.2"
MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then? MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then?
i32 = _binary.i32le
def _accept(prefix): def _accept(prefix):
return len(prefix) >= 4 and i32(prefix) == MAGIC return len(prefix) >= 4 and i32(prefix) == MAGIC

View File

@ -12,7 +12,7 @@ Full text of the CC0 license:
import struct import struct
from io import BytesIO from io import BytesIO
from PIL import Image, ImageFile from . import Image, ImageFile
# Magic ("DDS ") # Magic ("DDS ")

View File

@ -23,16 +23,14 @@
import re import re
import io import io
import sys import sys
from PIL import Image, ImageFile, _binary from . import Image, ImageFile
from ._binary import i32le as i32, o32le as o32
__version__ = "0.5" __version__ = "0.5"
# #
# -------------------------------------------------------------------- # --------------------------------------------------------------------
i32 = _binary.i32le
o32 = _binary.o32le
split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$") split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$")
field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$") field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$")
@ -145,7 +143,8 @@ def Ghostscript(tile, size, fp, scale=1):
status = gs.wait() status = gs.wait()
if status: if status:
raise IOError("gs failed (status %d)" % status) raise IOError("gs failed (status %d)" % status)
im = Image.core.open_ppm(outfile) im = Image.open(outfile)
im.load()
finally: finally:
try: try:
os.unlink(outfile) os.unlink(outfile)
@ -154,7 +153,7 @@ def Ghostscript(tile, size, fp, scale=1):
except OSError: except OSError:
pass pass
return im return im.im.copy()
class PSFile(object): class PSFile(object):

View File

@ -9,7 +9,7 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import Image, ImageFile from . import Image, ImageFile
_handler = None _handler = None

View File

@ -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" __version__ = "0.2"
i8 = _binary.i8
i16 = _binary.i16le
i32 = _binary.i32le
o8 = _binary.o8
# #
# decoder # decoder

View File

@ -14,8 +14,10 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from __future__ import print_function
import os import os
from PIL import Image, _binary from . import Image, _binary
WIDTH = 800 WIDTH = 800
@ -88,7 +90,7 @@ class FontFile(object):
x = xx x = xx
s = src[0] + x0, src[1] + y0, src[2] + x0, src[3] + y0 s = src[0] + x0, src[1] + y0, src[2] + x0, src[3] + y0
self.bitmap.paste(im.crop(src), s) self.bitmap.paste(im.crop(src), s)
# print chr(i), dst, s # print(chr(i), dst, s)
self.metrics[i] = d, dst, s self.metrics[i] = d, dst, s
def save(self, filename): def save(self, filename):
@ -100,14 +102,13 @@ class FontFile(object):
self.bitmap.save(os.path.splitext(filename)[0] + ".pbm", "PNG") self.bitmap.save(os.path.splitext(filename)[0] + ".pbm", "PNG")
# font metrics # font metrics
fp = open(os.path.splitext(filename)[0] + ".pil", "wb") with open(os.path.splitext(filename)[0] + ".pil", "wb") as fp:
fp.write(b"PILfont\n") fp.write(b"PILfont\n")
fp.write((";;;;;;%d;\n" % self.ysize).encode('ascii')) # HACK!!! fp.write((";;;;;;%d;\n" % self.ysize).encode('ascii')) # HACK!!!
fp.write(b"DATA\n") fp.write(b"DATA\n")
for id in range(256): for id in range(256):
m = self.metrics[id] m = self.metrics[id]
if not m: if not m:
puti16(fp, [0] * 10) puti16(fp, [0] * 10)
else: else:
puti16(fp, m[0] + m[1] + m[2]) puti16(fp, m[0] + m[1] + m[2])
fp.close()

View File

@ -15,13 +15,15 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from __future__ import print_function
from PIL import Image, ImageFile from . import Image, ImageFile
from PIL.OleFileIO import i8, i32, MAGIC, OleFileIO from ._binary import i32le as i32, i8
import olefile
__version__ = "0.1" __version__ = "0.1"
# we map from colour field tuples to (mode, rawmode) descriptors # we map from colour field tuples to (mode, rawmode) descriptors
MODES = { MODES = {
# opacity # opacity
@ -42,7 +44,7 @@ MODES = {
# -------------------------------------------------------------------- # --------------------------------------------------------------------
def _accept(prefix): 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 # to be a FlashPix file
try: try:
self.ole = OleFileIO(self.fp) self.ole = olefile.OleFileIO(self.fp)
except IOError: except IOError:
raise SyntaxError("not an FPX file; invalid OLE file") raise SyntaxError("not an FPX file; invalid OLE file")
@ -112,7 +114,7 @@ class FpxImageFile(ImageFile.ImageFile):
if id in prop: if id in prop:
self.jpeg[i] = prop[id] self.jpeg[i] = prop[id]
# print len(self.jpeg), "tables loaded" # print(len(self.jpeg), "tables loaded")
self._open_subimage(1, self.maxid) self._open_subimage(1, self.maxid)
@ -141,7 +143,7 @@ class FpxImageFile(ImageFile.ImageFile):
offset = i32(s, 28) offset = i32(s, 28)
length = i32(s, 32) length = i32(s, 32)
# print size, self.mode, self.rawmode # print(size, self.mode, self.rawmode)
if size != self.size: if size != self.size:
raise IOError("subimage mismatch") raise IOError("subimage mismatch")

View File

@ -13,7 +13,7 @@ packed custom format called FTEX. This file format uses file extensions FTC and
* FTC files are compressed textures (using standard texture compression). * FTC files are compressed textures (using standard texture compression).
* FTU files are not compressed. * FTU files are not compressed.
Texture File Format Texture File Format
The FTC and FTU texture files both use the same format, called. This The FTC and FTU texture files both use the same format. This
has the following structure: has the following structure:
{header} {header}
{format_directory} {format_directory}
@ -42,7 +42,7 @@ Note: All data is stored in little-Endian (Intel) byte order.
import struct import struct
from io import BytesIO from io import BytesIO
from PIL import Image, ImageFile from . import Image, ImageFile
MAGIC = b"FTEX" MAGIC = b"FTEX"

View File

@ -24,9 +24,8 @@
# Version 3 files have a format specifier of 18 for 16bit floats in # Version 3 files have a format specifier of 18 for 16bit floats in
# the color depth field. This is currently unsupported by Pillow. # the color depth field. This is currently unsupported by Pillow.
from PIL import Image, ImageFile, _binary from . import Image, ImageFile
from ._binary import i32be as i32
i32 = _binary.i32be
def _accept(prefix): def _accept(prefix):

View File

@ -23,8 +23,9 @@
# purposes only. # purposes only.
from PIL import ImageFile, ImagePalette, _binary from . import ImageFile, ImagePalette
from PIL._util import isPath from ._binary import i16be as i16
from ._util import isPath
__version__ = "0.1" __version__ = "0.1"
@ -34,8 +35,6 @@ except ImportError:
import __builtin__ import __builtin__
builtins = __builtin__ builtins = __builtin__
i16 = _binary.i16be
## ##
# Image plugin for the GD uncompressed format. Note that this format # Image plugin for the GD uncompressed format. Note that this format

View File

@ -24,8 +24,9 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import Image, ImageFile, ImagePalette, \ from . import Image, ImageFile, ImagePalette, \
ImageChops, ImageSequence, _binary ImageChops, ImageSequence
from ._binary import i8, i16le as i16, o8, o16le as o16
__version__ = "0.9" __version__ = "0.9"
@ -33,11 +34,6 @@ __version__ = "0.9"
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# Helpers # Helpers
i8 = _binary.i8
i16 = _binary.i16le
o8 = _binary.o8
o16 = _binary.o16le
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# Identify/read GIF files # Identify/read GIF files
@ -352,10 +348,18 @@ def _save(im, fp, filename, save_all=False):
first_frame = None first_frame = None
append_images = im.encoderinfo.get("append_images", []) append_images = im.encoderinfo.get("append_images", [])
if "duration" in im.encoderinfo:
duration = im.encoderinfo["duration"]
else:
duration = None
frame_count = 0
for imSequence in [im]+append_images: for imSequence in [im]+append_images:
for im_frame in ImageSequence.Iterator(imSequence): for im_frame in ImageSequence.Iterator(imSequence):
encoderinfo = im.encoderinfo.copy() encoderinfo = im.encoderinfo.copy()
im_frame = _convert_mode(im_frame) im_frame = _convert_mode(im_frame)
if isinstance(duration, (list, tuple)):
encoderinfo["duration"] = duration[frame_count]
frame_count += 1
# To specify duration, add the time in milliseconds to getdata(), # To specify duration, add the time in milliseconds to getdata(),
# e.g. getdata(im_frame, duration=1000) # e.g. getdata(im_frame, duration=1000)
@ -434,18 +438,16 @@ def _get_local_header(fp, im, offset, flags):
# optimize the block away if transparent color is not used # optimize the block away if transparent color is not used
transparent_color_exists = True transparent_color_exists = True
if _get_optimize(im, im.encoderinfo): used_palette_colors = _get_optimize(im, im.encoderinfo)
used_palette_colors = _get_used_palette_colors(im) if used_palette_colors is not None:
# adjust the transparency index after optimize # adjust the transparency index after optimize
if len(used_palette_colors) < 256: for i, palette_color in enumerate(used_palette_colors):
for i in range(len(used_palette_colors)): if palette_color == transparency:
if used_palette_colors[i] == transparency: transparency = i
transparency = i transparent_color_exists = True
transparent_color_exists = True break
break else:
else: transparent_color_exists = False
transparent_color_exists = False
if "duration" in im.encoderinfo: if "duration" in im.encoderinfo:
duration = int(im.encoderinfo["duration"] / 10) duration = int(im.encoderinfo["duration"] / 10)
@ -515,22 +517,20 @@ def _save_netpbm(im, fp, filename):
import tempfile import tempfile
file = im._dump() file = im._dump()
if im.mode != "RGB": with open(filename, 'wb') as f:
with open(filename, 'wb') as f: if im.mode != "RGB":
stderr = tempfile.TemporaryFile() with tempfile.TemporaryFile() as stderr:
check_call(["ppmtogif", file], stdout=f, stderr=stderr) check_call(["ppmtogif", file], stdout=f, stderr=stderr)
else: else:
with open(filename, 'wb') as f:
# Pipe ppmquant output into ppmtogif # Pipe ppmquant output into ppmtogif
# "ppmquant 256 %s | ppmtogif > %s" % (file, filename) # "ppmquant 256 %s | ppmtogif > %s" % (file, filename)
quant_cmd = ["ppmquant", "256", file] quant_cmd = ["ppmquant", "256", file]
togif_cmd = ["ppmtogif"] togif_cmd = ["ppmtogif"]
stderr = tempfile.TemporaryFile() with tempfile.TemporaryFile() as stderr:
quant_proc = Popen(quant_cmd, stdout=PIPE, stderr=stderr) quant_proc = Popen(quant_cmd, stdout=PIPE, stderr=stderr)
stderr = tempfile.TemporaryFile() with tempfile.TemporaryFile() as stderr:
togif_proc = Popen(togif_cmd, stdin=quant_proc.stdout, stdout=f, togif_proc = Popen(togif_cmd, stdin=quant_proc.stdout,
stderr=stderr) stdout=f, stderr=stderr)
# Allow ppmquant to receive SIGPIPE if ppmtogif exits # Allow ppmquant to receive SIGPIPE if ppmtogif exits
quant_proc.stdout.close() quant_proc.stdout.close()
@ -552,9 +552,28 @@ def _save_netpbm(im, fp, filename):
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# GIF utilities # GIF utilities
def _get_optimize(im, info): # Force optimization so that we can test performance against
return im.mode in ("P", "L") and info and info.get("optimize", 0) # cases where it took lots of memory and time previously.
_FORCE_OPTIMIZE = False
def _get_optimize(im, info):
if im.mode in ("P", "L") and info and info.get("optimize", 0):
# 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
used_palette_colors = _get_used_palette_colors(im)
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):
return used_palette_colors
def _get_used_palette_colors(im): def _get_used_palette_colors(im):
used_palette_colors = [] used_palette_colors = []
@ -586,10 +605,6 @@ def _get_header_palette(palette_bytes):
palette_bytes += o8(0) * 3 * actual_target_size_diff palette_bytes += o8(0) * 3 * actual_target_size_diff
return palette_bytes 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): def _get_palette_bytes(im, palette, info):
if im.mode == "P": if im.mode == "P":
if palette and isinstance(palette, bytes): if palette and isinstance(palette, bytes):
@ -600,81 +615,66 @@ def _get_palette_bytes(im, palette, info):
if palette and isinstance(palette, bytes): if palette and isinstance(palette, bytes):
source_palette = palette[:768] source_palette = palette[:768]
else: else:
source_palette = bytearray([i//3 for i in range(768)]) source_palette = bytearray(i//3 for i in range(768))
used_palette_colors = palette_bytes = None palette_bytes = None
if _get_optimize(im, info): used_palette_colors = _get_optimize(im, info)
used_palette_colors = _get_used_palette_colors(im) if used_palette_colors is not None:
palette_bytes = b""
new_positions = [0]*256
# Potentially expensive operation. # 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
# The palette saves 3 bytes per color not used, but palette # replace the palette color id of all pixel with the new id
# 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 # Palette images are [0..255], mapped through a 1 or 3
if _FORCE_OPTIMIZE or im.mode == 'L' or \ # byte/color map. We need to remap the whole image
(len(used_palette_colors) <= 128 and # from palette 1 to palette 2. New_positions is
max(used_palette_colors) > len(used_palette_colors) and # an array of indexes into palette 1. Palette 2 is
im.width * im.height < 512 * 512): # palette 1 with any holes removed.
palette_bytes = b""
new_positions = [0]*256
# pick only the used colors from the palette # We're going to leverage the convert mechanism to use the
for i, oldPosition in enumerate(used_palette_colors): # C code to remap the image from palette 1 to palette 2,
palette_bytes += source_palette[oldPosition*3:oldPosition*3+3] # by forcing the source image into 'L' mode and adding a
new_positions[oldPosition] = i # mapping 'L' mode palette, then converting back to 'L'
# sans palette thus converting the image bytes, then
# assigning the optimized RGB palette.
# replace the palette color id of all pixel with the new id # perf reference, 9500x4000 gif, w/~135 colors
# 14 sec prepatch, 1 sec postpatch with optimization forced.
# Palette images are [0..255], mapped through a 1 or 3 mapping_palette = bytearray(new_positions)
# 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 m_im = im.copy()
# C code to remap the image from palette 1 to palette 2, m_im.mode = 'P'
# 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 m_im.palette = ImagePalette.ImagePalette("RGB",
# 14 sec prepatch, 1 sec postpatch with optimization forced. 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())
mapping_palette = bytearray(new_positions) m_im = m_im.convert('L')
m_im = im.copy() # Internally, we require 768 bytes for a palette.
m_im.mode = 'P' 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))
m_im.palette = ImagePalette.ImagePalette("RGB", # oh gawd, this is modifying the image in place so I can pass by ref.
palette=mapping_palette*3, # REFACTOR SOONEST
size=768) im.frombytes(m_im.tobytes())
#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: if not palette_bytes:
palette_bytes = source_palette palette_bytes = source_palette
@ -696,7 +696,7 @@ def getheader(im, palette=None, info=None):
version = b"89a" version = b"89a"
break break
else: else:
if im.info.get("version") == "89a": if im.info.get("version") == b"89a":
version = b"89a" version = b"89a"
header = [ header = [

View File

@ -14,7 +14,7 @@
# #
from math import pi, log, sin, sqrt 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 # Stuff to translate curve segments to palette values (derived from

View File

@ -15,7 +15,7 @@
# #
import re import re
from PIL._binary import o8 from ._binary import o8
## ##
@ -41,7 +41,7 @@ class GimpPaletteFile(object):
if not s: if not s:
break break
# skip fields and comment lines # skip fields and comment lines
if re.match(b"\w+:|#", s): if re.match(br"\w+:|#", s):
continue continue
if len(s) > 100: if len(s) > 100:
raise SyntaxError("bad palette file") raise SyntaxError("bad palette file")

View File

@ -9,7 +9,7 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import Image, ImageFile from . import Image, ImageFile
_handler = None _handler = None

View File

@ -9,7 +9,7 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import Image, ImageFile from . import Image, ImageFile
_handler = None _handler = None

View File

@ -15,7 +15,8 @@
# See the README file for information on usage and redistribution. # 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 io
import os import os
import shutil import shutil
@ -27,8 +28,6 @@ enable_jpeg2k = hasattr(Image.core, 'jp2klib_version')
if enable_jpeg2k: if enable_jpeg2k:
from PIL import Jpeg2KImagePlugin from PIL import Jpeg2KImagePlugin
i8 = _binary.i8
HEADERSIZE = 8 HEADERSIZE = 8
@ -330,8 +329,8 @@ def _save(im, fp, filename):
from subprocess import Popen, PIPE, CalledProcessError from subprocess import Popen, PIPE, CalledProcessError
convert_cmd = ["iconutil", "-c", "icns", "-o", filename, iconset] convert_cmd = ["iconutil", "-c", "icns", "-o", filename, iconset]
stderr = tempfile.TemporaryFile() with tempfile.TemporaryFile() as stderr:
convert_proc = Popen(convert_cmd, stdout=PIPE, stderr=stderr) convert_proc = Popen(convert_cmd, stdout=PIPE, stderr=stderr)
convert_proc.stdout.close() convert_proc.stdout.close()

View File

@ -25,7 +25,8 @@
import struct import struct
from io import BytesIO 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 from math import log, ceil
__version__ = "0.1" __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" _MAGIC = b"\0\0\1\0"
@ -44,16 +41,19 @@ def _save(im, fp, filename):
fp.write(_MAGIC) # (2+2) fp.write(_MAGIC) # (2+2)
sizes = im.encoderinfo.get("sizes", sizes = im.encoderinfo.get("sizes",
[(16, 16), (24, 24), (32, 32), (48, 48), [(16, 16), (24, 24), (32, 32), (48, 48),
(64, 64), (128, 128), (255, 255)]) (64, 64), (128, 128), (256, 256)])
width, height = im.size width, height = im.size
filter(lambda x: False if (x[0] > width or x[1] > height or sizes = filter(lambda x: False if (x[0] > width or x[1] > height or
x[0] > 255 or x[1] > 255) else True, sizes) x[0] > 256 or x[1] > 256) else True,
sizes)
sizes = list(sizes)
fp.write(struct.pack("<H", len(sizes))) # idCount(2) fp.write(struct.pack("<H", len(sizes))) # idCount(2)
offset = fp.tell() + len(sizes)*16 offset = fp.tell() + len(sizes)*16
for size in sizes: for size in sizes:
width, height = size width, height = size
fp.write(struct.pack("B", width)) # bWidth(1) # 0 means 256
fp.write(struct.pack("B", height)) # bHeight(1) 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") # bColorCount(1)
fp.write(b"\0") # bReserved(1) fp.write(b"\0") # bReserved(1)
fp.write(b"\0\0") # wPlanes(2) fp.write(b"\0\0") # wPlanes(2)
@ -139,7 +139,7 @@ class IcoFile(object):
""" """
Get a list of all available icon sizes and color depths. Get a list of all available icon sizes and color depths.
""" """
return set((h['width'], h['height']) for h in self.entry) return {(h['width'], h['height']) for h in self.entry}
def getimage(self, size, bpp=False): def getimage(self, size, bpp=False):
""" """
@ -215,13 +215,13 @@ class IcoFile(object):
total_bytes = int((w * im.size[1]) / 8) total_bytes = int((w * im.size[1]) / 8)
self.buf.seek(and_mask_offset) self.buf.seek(and_mask_offset)
maskData = self.buf.read(total_bytes) mask_data = self.buf.read(total_bytes)
# convert raw data to image # convert raw data to image
mask = Image.frombuffer( mask = Image.frombuffer(
'1', # 1 bpp '1', # 1 bpp
im.size, # (w, h) im.size, # (w, h)
maskData, # source chars mask_data, # source chars
'raw', # raw decoder 'raw', # raw decoder
('1;I', int(w/8), -1) # 1bpp inverted, padded, reversed ('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_open(IcoImageFile.format, IcoImageFile, _accept)
Image.register_save(IcoImageFile.format, _save) Image.register_save(IcoImageFile.format, _save)
Image.register_extension(IcoImageFile.format, ".ico") Image.register_extension(IcoImageFile.format, ".ico")

View File

@ -27,8 +27,8 @@
import re import re
from PIL import Image, ImageFile, ImagePalette from . import Image, ImageFile, ImagePalette
from PIL._binary import i8 from ._binary import i8
__version__ = "0.7" __version__ = "0.7"

View File

@ -26,7 +26,7 @@
from __future__ import print_function from __future__ import print_function
from PIL import VERSION, PILLOW_VERSION, _plugins from . import VERSION, PILLOW_VERSION, _plugins
import logging import logging
import warnings import warnings
@ -46,7 +46,7 @@ class _imaging_not_installed(object):
# Limit to around a quarter gigabyte for a 24 bit (3 bpp) image # Limit to around a quarter gigabyte for a 24 bit (3 bpp) image
MAX_IMAGE_PIXELS = int(1024 * 1024 * 1024 / 4 / 3) MAX_IMAGE_PIXELS = int(1024 * 1024 * 1024 // 4 // 3)
try: try:
# give Tk a chance to set up the environment, in case we're # give Tk a chance to set up the environment, in case we're
@ -64,10 +64,10 @@ try:
# import Image and use the Image.core variable instead. # import Image and use the Image.core variable instead.
# Also note that Image.core is not a publicly documented interface, # Also note that Image.core is not a publicly documented interface,
# and should be considered private and subject to change. # 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): if PILLOW_VERSION != getattr(core, 'PILLOW_VERSION', None):
raise ImportError("The _imaging extension was built for another " raise ImportError("The _imaging extension was built for another "
" version of Pillow or PIL") "version of Pillow or PIL")
except ImportError as v: except ImportError as v:
core = _imaging_not_installed() core = _imaging_not_installed()
@ -109,11 +109,9 @@ except ImportError:
import __builtin__ import __builtin__
builtins = __builtin__ builtins = __builtin__
from PIL import ImageMode from . import ImageMode
from PIL._binary import i8 from ._binary import i8
from PIL._util import isPath from ._util import isPath, isStringType, deferred_error
from PIL._util import isStringType
from PIL._util import deferred_error
import os import os
import sys import sys
@ -355,23 +353,23 @@ def preinit():
return return
try: try:
from PIL import BmpImagePlugin from . import BmpImagePlugin
except ImportError: except ImportError:
pass pass
try: try:
from PIL import GifImagePlugin from . import GifImagePlugin
except ImportError: except ImportError:
pass pass
try: try:
from PIL import JpegImagePlugin from . import JpegImagePlugin
except ImportError: except ImportError:
pass pass
try: try:
from PIL import PpmImagePlugin from . import PpmImagePlugin
except ImportError: except ImportError:
pass pass
try: try:
from PIL import PngImagePlugin from . import PngImagePlugin
except ImportError: except ImportError:
pass pass
# try: # try:
@ -525,13 +523,11 @@ class Image(object):
if self.palette: if self.palette:
new.palette = self.palette.copy() new.palette = self.palette.copy()
if im.mode == "P" and not new.palette: if im.mode == "P" and not new.palette:
from PIL import ImagePalette from . import ImagePalette
new.palette = ImagePalette.ImagePalette() new.palette = ImagePalette.ImagePalette()
new.info = self.info.copy() new.info = self.info.copy()
return new return new
_makeself = _new # compatibility
# Context Manager Support # Context Manager Support
def __enter__(self): def __enter__(self):
return self return self
@ -777,7 +773,7 @@ class Image(object):
if HAS_CFFI and USE_CFFI_ACCESS: if HAS_CFFI and USE_CFFI_ACCESS:
if self.pyaccess: if self.pyaccess:
return self.pyaccess return self.pyaccess
from PIL import PyAccess from . import PyAccess
self.pyaccess = PyAccess.new(self, self.readonly) self.pyaccess = PyAccess.new(self, self.readonly)
if self.pyaccess: if self.pyaccess:
return self.pyaccess return self.pyaccess
@ -879,7 +875,7 @@ class Image(object):
trns_im = Image()._new(core.new(self.mode, (1, 1))) trns_im = Image()._new(core.new(self.mode, (1, 1)))
if self.mode == 'P': if self.mode == 'P':
trns_im.putpalette(self.palette) trns_im.putpalette(self.palette)
if type(t) == tuple: if isinstance(t, tuple):
try: try:
t = trns_im.palette.getcolor(t) t = trns_im.palette.getcolor(t)
except: except:
@ -910,7 +906,7 @@ class Image(object):
if mode == "P" and palette == ADAPTIVE: if mode == "P" and palette == ADAPTIVE:
im = self.im.quantize(colors) im = self.im.quantize(colors)
new = self._new(im) new = self._new(im)
from PIL import ImagePalette from . import ImagePalette
new.palette = ImagePalette.raw("RGB", new.im.getpalette("RGB")) new.palette = ImagePalette.raw("RGB", new.im.getpalette("RGB"))
if delete_trns: if delete_trns:
# This could possibly happen if we requantize to fewer colors. # This could possibly happen if we requantize to fewer colors.
@ -997,7 +993,7 @@ class Image(object):
"only RGB or L mode images can be quantized to a palette" "only RGB or L mode images can be quantized to a palette"
) )
im = self.im.convert("P", 1, palette.im) 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)) return self._new(self.im.quantize(colors, method, kmeans))
@ -1020,7 +1016,7 @@ class Image(object):
4-tuple defining the left, upper, right, and lower pixel 4-tuple defining the left, upper, right, and lower pixel
coordinate. coordinate.
Note: Prior to Pillow 3.4.0, this was a lazy operation. Note: Prior to Pillow 3.4.0, this was a lazy operation.
:param box: The crop rectangle, as a (left, upper, right, lower)-tuple. :param box: The crop rectangle, as a (left, upper, right, lower)-tuple.
:rtype: :py:class:`~PIL.Image.Image` :rtype: :py:class:`~PIL.Image.Image`
@ -1307,8 +1303,7 @@ class Image(object):
box = None box = None
if box is None: if box is None:
# cover all of self box = (0, 0)
box = (0, 0) + self.size
if len(box) == 2: if len(box) == 2:
# upper left corner given; get size from image or mask # upper left corner given; get size from image or mask
@ -1321,10 +1316,10 @@ class Image(object):
raise ValueError( raise ValueError(
"cannot determine region size; use 4-item box" "cannot determine region size; use 4-item box"
) )
box = box + (box[0]+size[0], box[1]+size[1]) box += (box[0]+size[0], box[1]+size[1])
if isStringType(im): if isStringType(im):
from PIL import ImageColor from . import ImageColor
im = ImageColor.getcolor(im, self.mode) im = ImageColor.getcolor(im, self.mode)
elif isImageType(im): elif isImageType(im):
@ -1471,7 +1466,7 @@ class Image(object):
:param data: A palette sequence (either a list or a string). :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"): if self.mode not in ("L", "P"):
raise ValueError("illegal image mode") raise ValueError("illegal image mode")
@ -1545,7 +1540,7 @@ class Image(object):
size = tuple(size) size = tuple(size)
if self.size == size: if self.size == size:
return self._new(self.im) return self.copy()
if self.mode in ("1", "P"): if self.mode in ("1", "P"):
resample = NEAREST resample = NEAREST
@ -1558,7 +1553,7 @@ class Image(object):
return self._new(self.im.resize(size, resample)) return self._new(self.im.resize(size, resample))
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 Returns a rotated copy of this image. This method returns a
copy of this image, rotated the given number of degrees counter copy of this image, rotated the given number of degrees counter
@ -1575,48 +1570,81 @@ class Image(object):
:param expand: Optional expansion flag. If true, expands the output :param expand: Optional expansion flag. If true, expands the output
image to make it large enough to hold the entire rotated image. 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 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. :returns: An :py:class:`~PIL.Image.Image` object.
""" """
angle = angle % 360.0 angle = angle % 360.0
# Fast paths regardless of filter # Fast paths regardless of filter, as long as we're not
if angle == 0: # translating or changing the center.
return self.copy() if not (center or translate):
if angle == 180: if angle == 0:
return self.transpose(ROTATE_180) return self.copy()
if angle == 90 and expand: if angle == 180:
return self.transpose(ROTATE_90) return self.transpose(ROTATE_180)
if angle == 270 and expand: if angle == 90 and expand:
return self.transpose(ROTATE_270) 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) angle = - math.radians(angle)
matrix = [ matrix = [
round(math.cos(angle), 15), round(math.sin(angle), 15), 0.0, round(math.cos(angle), 15), round(math.sin(angle), 15), 0.0,
round(-math.sin(angle), 15), round(math.cos(angle), 15), 0.0 round(-math.sin(angle), 15), round(math.cos(angle), 15), 0.0
] ]
def transform(x, y, matrix):
def transform(x, y, matrix=matrix):
(a, b, c, d, e, f) = matrix (a, b, c, d, e, f) = matrix
return a*x + b*y + c, d*x + e*y + f return a*x + b*y + c, d*x + e*y + f
matrix[2], matrix[5] = transform(-center[0] - translate[0], -center[1] - translate[1], matrix)
matrix[2] += center[0]
matrix[5] += center[1]
w, h = self.size
if expand: if expand:
# calculate output size # calculate output size
xx = [] xx = []
yy = [] yy = []
for x, y in ((0, 0), (w, 0), (w, h), (0, h)): 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) xx.append(x)
yy.append(y) yy.append(y)
w = int(math.ceil(max(xx)) - math.floor(min(xx))) nw = int(math.ceil(max(xx)) - math.floor(min(xx)))
h = int(math.ceil(max(yy)) - math.floor(min(yy))) nh = int(math.ceil(max(yy)) - math.floor(min(yy)))
# adjust center # We multiply a translation matrix from the right. Because of its
x, y = transform(w / 2.0, h / 2.0) # special form, this is the same as taking the image of the translation vector
matrix[2] = self.size[0] / 2.0 - x # as new translation vector.
matrix[5] = self.size[1] / 2.0 - y 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) return self.transform((w, h), AFFINE, matrix, resample)
@ -1660,7 +1688,7 @@ class Image(object):
if isinstance(fp, Path): if isinstance(fp, Path):
filename = str(fp) filename = str(fp)
open_fp = True open_fp = True
elif hasattr(fp, "name") and isPath(fp.name): if not filename and hasattr(fp, "name") and isPath(fp.name):
# only set the name for metadata purposes # only set the name for metadata purposes
filename = fp.name filename = fp.name
@ -1947,14 +1975,14 @@ class Image(object):
def toqimage(self): def toqimage(self):
"""Returns a QImage copy of this image""" """Returns a QImage copy of this image"""
from PIL import ImageQt from . import ImageQt
if not ImageQt.qt_is_installed: if not ImageQt.qt_is_installed:
raise ImportError("Qt bindings are not installed") raise ImportError("Qt bindings are not installed")
return ImageQt.toqimage(self) return ImageQt.toqimage(self)
def toqpixmap(self): def toqpixmap(self):
"""Returns a QPixmap copy of this image""" """Returns a QPixmap copy of this image"""
from PIL import ImageQt from . import ImageQt
if not ImageQt.qt_is_installed: if not ImageQt.qt_is_installed:
raise ImportError("Qt bindings are not installed") raise ImportError("Qt bindings are not installed")
return ImageQt.toqpixmap(self) return ImageQt.toqpixmap(self)
@ -1985,6 +2013,22 @@ def _wedge():
return Image()._new(core.wedge("L")) return Image()._new(core.wedge("L"))
def _check_size(size):
"""
Common check to enforce type and sanity check on size tuples
:param size: Should be a 2 tuple of (width, height)
:returns: True, or raises a ValueError
"""
if not isinstance(size, (list, tuple)):
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")
return True
def new(mode, size, color=0): def new(mode, size, color=0):
""" """
@ -2002,6 +2046,8 @@ def new(mode, size, color=0):
:returns: An :py:class:`~PIL.Image.Image` object. :returns: An :py:class:`~PIL.Image.Image` object.
""" """
_check_size(size)
if color is None: if color is None:
# don't initialize # don't initialize
return Image()._new(core.new(mode, size)) return Image()._new(core.new(mode, size))
@ -2009,7 +2055,7 @@ def new(mode, size, color=0):
if isStringType(color): if isStringType(color):
# css3-style specifier # css3-style specifier
from PIL import ImageColor from . import ImageColor
color = ImageColor.getcolor(color, mode) color = ImageColor.getcolor(color, mode)
return Image()._new(core.fill(mode, size, color)) return Image()._new(core.fill(mode, size, color))
@ -2039,6 +2085,8 @@ def frombytes(mode, size, data, decoder_name="raw", *args):
:returns: An :py:class:`~PIL.Image.Image` object. :returns: An :py:class:`~PIL.Image.Image` object.
""" """
_check_size(size)
# may pass tuple instead of argument list # may pass tuple instead of argument list
if len(args) == 1 and isinstance(args[0], tuple): if len(args) == 1 and isinstance(args[0], tuple):
args = args[0] args = args[0]
@ -2091,6 +2139,8 @@ def frombuffer(mode, size, data, decoder_name="raw", *args):
.. versionadded:: 1.1.4 .. versionadded:: 1.1.4
""" """
_check_size(size)
# may pass tuple instead of argument list # may pass tuple instead of argument list
if len(args) == 1 and isinstance(args[0], tuple): if len(args) == 1 and isinstance(args[0], tuple):
args = args[0] args = args[0]
@ -2142,7 +2192,7 @@ def fromarray(obj, mode=None):
typekey = (1, 1) + shape[2:], arr['typestr'] typekey = (1, 1) + shape[2:], arr['typestr']
mode, rawmode = _fromarray_typemap[typekey] mode, rawmode = _fromarray_typemap[typekey]
except KeyError: except KeyError:
# print typekey # print(typekey)
raise TypeError("Cannot handle this data type") raise TypeError("Cannot handle this data type")
else: else:
rawmode = mode rawmode = mode
@ -2167,7 +2217,7 @@ def fromarray(obj, mode=None):
def fromqimage(im): def fromqimage(im):
"""Creates an image instance from a QImage image""" """Creates an image instance from a QImage image"""
from PIL import ImageQt from . import ImageQt
if not ImageQt.qt_is_installed: if not ImageQt.qt_is_installed:
raise ImportError("Qt bindings are not installed") raise ImportError("Qt bindings are not installed")
return ImageQt.fromqimage(im) return ImageQt.fromqimage(im)
@ -2175,7 +2225,7 @@ def fromqimage(im):
def fromqpixmap(im): def fromqpixmap(im):
"""Creates an image instance from a QPixmap image""" """Creates an image instance from a QPixmap image"""
from PIL import ImageQt from . import ImageQt
if not ImageQt.qt_is_installed: if not ImageQt.qt_is_installed:
raise ImportError("Qt bindings are not installed") raise ImportError("Qt bindings are not installed")
return ImageQt.fromqpixmap(im) return ImageQt.fromqpixmap(im)
@ -2460,6 +2510,16 @@ def register_extension(id, extension):
EXTENSION[extension.lower()] = id.upper() 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
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# Simple display support. User code may override this. # Simple display support. User code may override this.
@ -2469,7 +2529,7 @@ def _show(image, **options):
def _showxv(image, title=None, **options): def _showxv(image, title=None, **options):
from PIL import ImageShow from . import ImageShow
ImageShow.show(image, title, **options) ImageShow.show(image, title, **options)

View File

@ -15,7 +15,7 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import Image from . import Image
def constant(image, value): def constant(image, value):

View File

@ -17,7 +17,7 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import Image from . import Image
import re import re

View File

@ -33,8 +33,8 @@
import numbers import numbers
import warnings import warnings
from PIL import Image, ImageColor from . import Image, ImageColor
from PIL._util import isStringType from ._util import isStringType
""" """
A simple 2D drawing interface for PIL images. A simple 2D drawing interface for PIL images.
@ -105,7 +105,7 @@ class ImageDraw(object):
"""Get the current default font.""" """Get the current default font."""
if not self.font: if not self.font:
# FIXME: should add a font repository # FIXME: should add a font repository
from PIL import ImageFont from . import ImageFont
self.font = ImageFont.load_default() self.font = ImageFont.load_default()
return self.font return self.font
@ -208,12 +208,12 @@ class ImageDraw(object):
def _multiline_check(self, text): def _multiline_check(self, text):
"""Draw text.""" """Draw text."""
split_character = "\n" if isinstance(text, type("")) else b"\n" split_character = "\n" if isinstance(text, str) else b"\n"
return split_character in text return split_character in text
def _multiline_split(self, text): def _multiline_split(self, text):
split_character = "\n" if isinstance(text, type("")) else b"\n" split_character = "\n" if isinstance(text, str) else b"\n"
return text.split(split_character) return text.split(split_character)
@ -319,11 +319,11 @@ def getdraw(im=None, hints=None):
handler = None handler = None
if not hints or "nicest" in hints: if not hints or "nicest" in hints:
try: try:
from PIL import _imagingagg as handler from . import _imagingagg as handler
except ImportError: except ImportError:
pass pass
if handler is None: if handler is None:
from PIL import ImageDraw2 as handler from . import ImageDraw2 as handler
if im: if im:
im = handler.Draw(im) im = handler.Draw(im)
return im, handler return im, handler

View File

@ -16,7 +16,7 @@
# See the README file for information on usage and redistribution. # 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): class Pen(object):

View File

@ -18,7 +18,7 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import Image, ImageFilter, ImageStat from . import Image, ImageFilter, ImageStat
class _Enhance(object): class _Enhance(object):

View File

@ -27,8 +27,8 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import Image from . import Image
from PIL._util import isPath from ._util import isPath
import io import io
import os import os
import sys import sys
@ -150,15 +150,16 @@ class ImageFile(Image.Image):
if use_mmap: if use_mmap:
# try memory mapping # try memory mapping
d, e, o, a = self.tile[0] decoder_name, extents, offset, args = self.tile[0]
if d == "raw" and a[0] == self.mode and a[0] in Image._MAPMODES: if decoder_name == "raw" and len(args) >= 3 and args[0] == self.mode \
and args[0] in Image._MAPMODES:
try: try:
if hasattr(Image.core, "map"): if hasattr(Image.core, "map"):
# use built-in mapper # use built-in mapper WIN32 only
self.map = Image.core.map(self.filename) self.map = Image.core.map(self.filename)
self.map.seek(o) self.map.seek(offset)
self.im = self.map.readimage( self.im = self.map.readimage(
self.mode, self.size, a[1], a[2] self.mode, self.size, args[1], args[2]
) )
else: else:
# use mmap, if possible # use mmap, if possible
@ -167,7 +168,7 @@ class ImageFile(Image.Image):
size = os.path.getsize(self.filename) size = os.path.getsize(self.filename)
self.map = mmap.mmap(fp.fileno(), size, access=mmap.ACCESS_READ) self.map = mmap.mmap(fp.fileno(), size, access=mmap.ACCESS_READ)
self.im = Image.core.map_buffer( self.im = Image.core.map_buffer(
self.map, self.size, d, e, o, a self.map, self.size, decoder_name, extents, offset, args
) )
readonly = 1 readonly = 1
# After trashing self.im, we might need to reload the palette data. # After trashing self.im, we might need to reload the palette data.
@ -210,7 +211,7 @@ class ImageFile(Image.Image):
else: else:
raise IOError("image file is truncated") raise IOError("image file is truncated")
if not s and not decoder.handles_eof: # truncated jpeg if not s: # truncated jpeg
self.tile = [] self.tile = []
# JpegDecode needs to clean things up here either way # JpegDecode needs to clean things up here either way
@ -242,12 +243,6 @@ class ImageFile(Image.Image):
# still raised if decoder fails to return anything # still raised if decoder fails to return anything
raise_ioerror(err_code) 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() self.load_end()
return Image.Image.load(self) return Image.Image.load(self)

View File

@ -25,8 +25,8 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import Image from . import Image
from PIL._util import isDirectory, isPath from ._util import isDirectory, isPath
import os import os
import sys import sys
@ -37,7 +37,7 @@ class _imagingft_not_installed(object):
raise ImportError("The _imagingft C module is not installed") raise ImportError("The _imagingft C module is not installed")
try: try:
from PIL import _imagingft as core from . import _imagingft as core
except ImportError: except ImportError:
core = _imagingft_not_installed() core = _imagingft_not_installed()
@ -62,23 +62,22 @@ class ImageFont(object):
def _load_pilfont(self, filename): def _load_pilfont(self, filename):
fp = open(filename, "rb") with open(filename, "rb") as fp:
for ext in (".png", ".gif", ".pbm"):
for ext in (".png", ".gif", ".pbm"): try:
try: fullname = os.path.splitext(filename)[0] + ext
fullname = os.path.splitext(filename)[0] + ext image = Image.open(fullname)
image = Image.open(fullname) except:
except: pass
pass else:
if image and image.mode in ("1", "L"):
break
else: else:
if image and image.mode in ("1", "L"): raise IOError("cannot find glyph data file")
break
else:
raise IOError("cannot find glyph data file")
self.file = fullname self.file = fullname
return self._load_pilfont_data(fp, image) return self._load_pilfont_data(fp, image)
def _load_pilfont_data(self, file, image): def _load_pilfont_data(self, file, image):

View File

@ -15,7 +15,7 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import Image from . import Image
import sys import sys
if sys.platform not in ["win32", "darwin"]: if sys.platform not in ["win32", "darwin"]:
@ -41,7 +41,7 @@ def grab(bbox=None):
size, data = grabber() size, data = grabber()
im = Image.frombytes( im = Image.frombytes(
"RGB", size, data, "RGB", size, data,
# RGB, 32-bit line padding, origo in lower left corner # RGB, 32-bit line padding, origin lower left corner
"raw", "BGR", (size[0]*3 + 3) & -4, -1 "raw", "BGR", (size[0]*3 + 3) & -4, -1
) )
if bbox: if bbox:
@ -75,7 +75,7 @@ def grabclipboard():
debug = 0 # temporary interface debug = 0 # temporary interface
data = Image.core.grabclipboard(debug) data = Image.core.grabclipboard(debug)
if isinstance(data, bytes): if isinstance(data, bytes):
from PIL import BmpImagePlugin from . import BmpImagePlugin
import io import io
return BmpImagePlugin.DibImageFile(io.BytesIO(data)) return BmpImagePlugin.DibImageFile(io.BytesIO(data))
return data return data

View File

@ -15,8 +15,7 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import Image from . import Image, _imagingmath
from PIL import _imagingmath
try: try:
import builtins import builtins

View File

@ -14,7 +14,7 @@
# #
# mode descriptor cache # mode descriptor cache
_modes = {} _modes = None
class ModeDescriptor(object): class ModeDescriptor(object):
@ -32,19 +32,24 @@ class ModeDescriptor(object):
def getmode(mode): def getmode(mode):
"""Gets a mode descriptor for the given mode.""" """Gets a mode descriptor for the given mode."""
global _modes
if not _modes: if not _modes:
# initialize mode cache # initialize mode cache
from PIL import Image
from . import Image
modes = {}
# core modes # core modes
for m, (basemode, basetype, bands) in Image._MODEINFO.items(): 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 # extra experimental modes
_modes["RGBa"] = ModeDescriptor("RGBa", ("R", "G", "B", "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["La"] = ModeDescriptor("La", ("L", "a"), "L", "L") modes["La"] = ModeDescriptor("La", ("L", "a"), "L", "L")
_modes["PA"] = ModeDescriptor("PA", ("P", "A"), "RGB", "L") modes["PA"] = ModeDescriptor("PA", ("P", "A"), "RGB", "L")
# mapping modes # mapping modes
_modes["I;16"] = ModeDescriptor("I;16", "I", "L", "L") modes["I;16"] = ModeDescriptor("I;16", "I", "L", "L")
_modes["I;16L"] = ModeDescriptor("I;16L", "I", "L", "L") modes["I;16L"] = ModeDescriptor("I;16L", "I", "L", "L")
_modes["I;16B"] = ModeDescriptor("I;16B", "I", "L", "L") modes["I;16B"] = ModeDescriptor("I;16B", "I", "L", "L")
# set global mode cache atomically
_modes = modes
return _modes[mode] return _modes[mode]

View File

@ -5,8 +5,9 @@
# #
# Copyright (c) 2014 Dov Grobgeld <dov.grobgeld@gmail.com> # Copyright (c) 2014 Dov Grobgeld <dov.grobgeld@gmail.com>
from PIL import Image from __future__ import print_function
from PIL import _imagingmorph
from . import Image, _imagingmorph
import re import re
LUT_SIZE = 1 << 9 LUT_SIZE = 1 << 9
@ -78,7 +79,7 @@ class LutBuilder(object):
def build_default_lut(self): def build_default_lut(self):
symbols = [0, 1] symbols = [0, 1]
m = 1 << 4 # pos of current pixel m = 1 << 4 # pos of current pixel
self.lut = bytearray([symbols[(i & m) > 0] for i in range(LUT_SIZE)]) self.lut = bytearray(symbols[(i & m) > 0] for i in range(LUT_SIZE))
def get_lut(self): def get_lut(self):
return self.lut return self.lut
@ -88,7 +89,7 @@ class LutBuilder(object):
string permuted according to the permutation list. string permuted according to the permutation list.
""" """
assert(len(permutation) == 9) assert(len(permutation) == 9)
return ''.join([pattern[p] for p in permutation]) return ''.join(pattern[p] for p in permutation)
def _pattern_permute(self, basic_pattern, options, basic_result): def _pattern_permute(self, basic_pattern, options, basic_result):
"""pattern_permute takes a basic pattern and its result and clones """pattern_permute takes a basic pattern and its result and clones
@ -152,14 +153,14 @@ class LutBuilder(object):
# # Debugging # # Debugging
# for p,r in patterns: # for p,r in patterns:
# print p,r # print(p,r)
# print '--' # print('--')
# compile the patterns into regular expressions for speed # compile the patterns into regular expressions for speed
for i in range(len(patterns)): for i, pattern in enumerate(patterns):
p = patterns[i][0].replace('.', 'X').replace('X', '[01]') p = pattern[0].replace('.', 'X').replace('X', '[01]')
p = re.compile(p) p = re.compile(p)
patterns[i] = (p, patterns[i][1]) patterns[i] = (p, pattern[1])
# Step through table and find patterns that match. # Step through table and find patterns that match.
# Note that all the patterns are searched. The last one # Note that all the patterns are searched. The last one

View File

@ -17,8 +17,8 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import Image from . import Image
from PIL._util import isStringType from ._util import isStringType
import operator import operator
import functools import functools
@ -39,7 +39,7 @@ def _border(border):
def _color(color, mode): def _color(color, mode):
if isStringType(color): if isStringType(color):
from PIL import ImageColor from . import ImageColor
color = ImageColor.getcolor(color, mode) color = ImageColor.getcolor(color, mode)
return color return color
@ -206,7 +206,8 @@ def deform(image, deformer, resample=Image.BILINEAR):
:param image: The image to deform. :param image: The image to deform.
:param deformer: A deformer object. Any object that implements a :param deformer: A deformer object. Any object that implements a
**getmesh** method can be used. **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: An image.
""" """
return image.transform( return image.transform(

View File

@ -17,10 +17,7 @@
# #
import array import array
from PIL import ImageColor from . import ImageColor, GimpPaletteFile, GimpGradientFile, PaletteFile
from PIL import GimpPaletteFile
from PIL import GimpGradientFile
from PIL import PaletteFile
class ImagePalette(object): class ImagePalette(object):
@ -197,23 +194,23 @@ def load(filename):
# FIXME: supports GIMP gradients only # FIXME: supports GIMP gradients only
fp = open(filename, "rb") with open(filename, "rb") as fp:
for paletteHandler in [ for paletteHandler in [
GimpPaletteFile.GimpPaletteFile, GimpPaletteFile.GimpPaletteFile,
GimpGradientFile.GimpGradientFile, GimpGradientFile.GimpGradientFile,
PaletteFile.PaletteFile PaletteFile.PaletteFile
]: ]:
try: try:
fp.seek(0) fp.seek(0)
lut = paletteHandler(fp).getpalette() lut = paletteHandler(fp).getpalette()
if lut: if lut:
break break
except (SyntaxError, ValueError): except (SyntaxError, ValueError):
# import traceback # import traceback
# traceback.print_exc() # traceback.print_exc()
pass pass
else: else:
raise IOError("cannot load palette") raise IOError("cannot load palette")
return lut # data, rawmode return lut # data, rawmode

View File

@ -14,7 +14,7 @@
# See the README file for information on usage and redistribution. # 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. # the Python class below is overridden by the C implementation.

View File

@ -16,8 +16,8 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import Image from . import Image
from PIL._util import isPath from ._util import isPath
from io import BytesIO from io import BytesIO
qt_is_installed = True qt_is_installed = True
@ -157,7 +157,6 @@ def _toqclass_helper(im):
else: else:
raise ValueError("unsupported image mode %r" % im.mode) raise ValueError("unsupported image mode %r" % im.mode)
# must keep a reference, or Qt will crash!
__data = data or align8to32(im.tobytes(), im.size[0], im.mode) __data = data or align8to32(im.tobytes(), im.size[0], im.mode)
return { return {
'data': __data, 'im': im, 'format': format, 'colortable': colortable 'data': __data, 'im': im, 'format': format, 'colortable': colortable
@ -175,8 +174,13 @@ if qt_is_installed:
string or a PyQt string object). string or a PyQt string object).
""" """
im_data = _toqclass_helper(im) im_data = _toqclass_helper(im)
# must keep a reference, or Qt will crash!
# All QImage constructors that take data operate on an existing
# buffer, so this buffer has to hang on for the life of the image.
# Fixes https://github.com/python-pillow/Pillow/issues/1370
self.__data = im_data['data']
QImage.__init__(self, QImage.__init__(self,
im_data['data'], im_data['im'].size[0], self.__data, im_data['im'].size[0],
im_data['im'].size[1], im_data['format']) im_data['im'].size[1], im_data['format'])
if im_data['colortable']: if im_data['colortable']:
self.setColorTable(im_data['colortable']) self.setColorTable(im_data['colortable'])

View File

@ -32,7 +32,14 @@ except ImportError:
tkinter = Tkinter tkinter = Tkinter
del Tkinter del Tkinter
from PIL import Image # 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 from io import BytesIO
@ -182,9 +189,15 @@ class PhotoImage(object):
except tkinter.TclError: except tkinter.TclError:
# activate Tkinter hook # activate Tkinter hook
try: try:
from PIL import _imagingtk from . import _imagingtk
try: 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: except AttributeError:
_imagingtk.tkinit(id(tk), 0) _imagingtk.tkinit(id(tk), 0)
tk.call("PyImagingPhoto", self.__photo, block.id) tk.call("PyImagingPhoto", self.__photo, block.id)
@ -264,6 +277,8 @@ class BitmapImage(object):
def getimage(photo): def getimage(photo):
""" This function is unimplemented """
"""Copies the contents of a PhotoImage to a PIL image memory.""" """Copies the contents of a PhotoImage to a PIL image memory."""
photo.tk.call("PyImagingPhotoGet", photo) photo.tk.call("PyImagingPhotoGet", photo)

View File

@ -13,7 +13,7 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import Image from . import Image
class Transform(Image.ImageTransformHandler): class Transform(Image.ImageTransformHandler):

View File

@ -17,7 +17,7 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import Image from . import Image
class HDC(object): class HDC(object):

View File

@ -17,7 +17,7 @@
import re import re
from PIL import Image, ImageFile from . import Image, ImageFile
__version__ = "0.2" __version__ = "0.2"
@ -69,7 +69,7 @@ class ImtImageFile(ImageFile.ImageFile):
s = s + self.fp.readline() s = s + self.fp.readline()
if len(s) == 1 or len(s) > 100: if len(s) == 1 or len(s) > 100:
break break
if s[0] == b"*": if s[0] == ord(b"*"):
continue # comment continue # comment
m = field.match(s) m = field.match(s)

View File

@ -17,17 +17,13 @@
from __future__ import print_function 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 os
import tempfile import tempfile
__version__ = "0.3" __version__ = "0.3"
i8 = _binary.i8
i16 = _binary.i16be
i32 = _binary.i32be
o8 = _binary.o8
COMPRESSION = { COMPRESSION = {
1: "raw", 1: "raw",
5: "jpeg" 5: "jpeg"
@ -107,7 +103,7 @@ class IptcImageFile(ImageFile.ImageFile):
else: else:
self.info[tag] = tagdata self.info[tag] = tagdata
# print tag, self.info[tag] # print(tag, self.info[tag])
# mode # mode
layers = i8(self.info[(3, 60)][0]) layers = i8(self.info[(3, 60)][0])
@ -168,14 +164,9 @@ class IptcImageFile(ImageFile.ImageFile):
o.close() o.close()
try: try:
try: _im = Image.open(outfile)
# fast _im.load()
self.im = Image.core.open_ppm(outfile) self.im = _im.im
except:
# slightly slower
im = Image.open(outfile)
im.load()
self.im = im.im
finally: finally:
try: try:
os.unlink(outfile) os.unlink(outfile)
@ -196,7 +187,7 @@ def getiptcinfo(im):
:returns: A dictionary containing IPTC information, or None if :returns: A dictionary containing IPTC information, or None if
no IPTC information block was found. no IPTC information block was found.
""" """
from PIL import TiffImagePlugin, JpegImagePlugin from . import TiffImagePlugin, JpegImagePlugin
import io import io
data = None data = None

View File

@ -12,7 +12,7 @@
# #
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import Image, ImageFile from . import Image, ImageFile
import struct import struct
import os import os
import io import io

View File

@ -32,19 +32,16 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from __future__ import print_function
import array import array
import struct import struct
import io import io
import warnings import warnings
from struct import unpack_from from . import Image, ImageFile, TiffImagePlugin
from PIL import Image, ImageFile, TiffImagePlugin, _binary from ._binary import i8, o8, i16be as i16, i32be as i32
from PIL.JpegPresets import presets from .JpegPresets import presets
from PIL._util import isStringType from ._util import isStringType
i8 = _binary.i8
o8 = _binary.o8
i16 = _binary.i16be
i32 = _binary.i32be
__version__ = "0.6" __version__ = "0.6"
@ -316,7 +313,7 @@ class JpegImageFile(ImageFile.ImageFile):
if i in MARKER: if i in MARKER:
name, description, handler = MARKER[i] name, description, handler = MARKER[i]
# print hex(i), name, description # print(hex(i), name, description)
if handler is not None: if handler is not None:
handler(self, i) handler(self, i)
if i == 0xFFDA: # start of scan if i == 0xFFDA: # start of scan
@ -341,6 +338,10 @@ class JpegImageFile(ImageFile.ImageFile):
if len(self.tile) != 1: if len(self.tile) != 1:
return return
# Protect from second call
if self.decoderconfig:
return
d, e, o, a = self.tile[0] d, e, o, a = self.tile[0]
scale = 0 scale = 0
@ -349,7 +350,7 @@ class JpegImageFile(ImageFile.ImageFile):
a = mode, "" a = mode, ""
if size: if size:
scale = max(self.size[0] // size[0], self.size[1] // size[1]) scale = min(self.size[0] // size[0], self.size[1] // size[1])
for s in [8, 4, 2, 1]: for s in [8, 4, 2, 1]:
if scale >= s: if scale >= s:
break break
@ -377,7 +378,9 @@ class JpegImageFile(ImageFile.ImageFile):
raise ValueError("Invalid Filename") raise ValueError("Invalid Filename")
try: try:
self.im = Image.core.open_ppm(path) _im = Image.open(path)
_im.load()
self.im = _im.im
finally: finally:
try: try:
os.unlink(path) os.unlink(path)
@ -406,7 +409,7 @@ def _fixup_dict(src_dict):
except: pass except: pass
return value return value
return dict([(k, _fixup(v)) for k, v in src_dict.items()]) return {k: _fixup(v) for k, v in src_dict.items()}
def _getexif(self): def _getexif(self):
@ -485,8 +488,8 @@ def _getmp(self):
try: try:
rawmpentries = mp[0xB002] rawmpentries = mp[0xB002]
for entrynum in range(0, quant): for entrynum in range(0, quant):
unpackedentry = unpack_from( unpackedentry = struct.unpack_from(
'{0}LLLHH'.format(endianness), rawmpentries, entrynum * 16) '{}LLLHH'.format(endianness), rawmpentries, entrynum * 16)
labels = ('Attribute', 'Size', 'DataOffset', 'EntryNo1', labels = ('Attribute', 'Size', 'DataOffset', 'EntryNo1',
'EntryNo2') 'EntryNo2')
mpentry = dict(zip(labels, unpackedentry)) mpentry = dict(zip(labels, unpackedentry))
@ -710,8 +713,11 @@ def _save(im, fp, filename):
# https://github.com/matthewwithanm/django-imagekit/issues/50 # https://github.com/matthewwithanm/django-imagekit/issues/50
bufsize = 0 bufsize = 0
if optimize or progressive: 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. # 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] bufsize = 2 * im.size[0] * im.size[1]
else: else:
bufsize = im.size[0] * im.size[1] bufsize = im.size[0] * im.size[1]

View File

@ -17,7 +17,7 @@
# #
import struct import struct
from PIL import Image, ImageFile from . import Image, ImageFile
__version__ = "0.2" __version__ = "0.2"

View File

@ -17,8 +17,9 @@
# #
from PIL import Image, TiffImagePlugin from . import Image, TiffImagePlugin
from PIL.OleFileIO import MAGIC, OleFileIO
import olefile
__version__ = "0.1" __version__ = "0.1"
@ -28,7 +29,7 @@ __version__ = "0.1"
def _accept(prefix): def _accept(prefix):
return prefix[:8] == MAGIC return prefix[:8] == olefile.MAGIC
## ##
@ -45,7 +46,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
# to be a Microsoft Image Composer file # to be a Microsoft Image Composer file
try: try:
self.ole = OleFileIO(self.fp) self.ole = olefile.OleFileIO(self.fp)
except IOError: except IOError:
raise SyntaxError("not an MIC file; invalid OLE file") raise SyntaxError("not an MIC file; invalid OLE file")

View File

@ -14,8 +14,8 @@
# #
from PIL import Image, ImageFile from . import Image, ImageFile
from PIL._binary import i8 from ._binary import i8
__version__ = "0.1" __version__ = "0.1"

View File

@ -18,7 +18,7 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import Image, JpegImagePlugin from . import Image, JpegImagePlugin
__version__ = "0.1" __version__ = "0.1"

View File

@ -17,7 +17,8 @@
# #
from PIL import Image, ImageFile, _binary from . import Image, ImageFile
from ._binary import i16le as i16, o16le as o16
__version__ = "0.1" __version__ = "0.1"
@ -25,8 +26,6 @@ __version__ = "0.1"
# #
# read MSP files # read MSP files
i16 = _binary.i16le
def _accept(prefix): def _accept(prefix):
return prefix[:4] in [b"DanM", b"LinS"] return prefix[:4] in [b"DanM", b"LinS"]
@ -66,8 +65,6 @@ class MspImageFile(ImageFile.ImageFile):
# #
# write MSP files (uncompressed only) # write MSP files (uncompressed only)
o16 = _binary.o16le
def _save(im, fp, filename): def _save(im, fp, filename):

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,7 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import EpsImagePlugin from . import EpsImagePlugin
import sys import sys
## ##

View File

@ -13,7 +13,7 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL._binary import o8 from ._binary import o8
## ##

View File

@ -7,7 +7,8 @@
# Image plugin for Palm pixmap images (output only). # 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" __version__ = "1.0"
@ -80,13 +81,12 @@ _Palm8BitColormapValues = (
# so build a prototype image to be used for palette resampling # so build a prototype image to be used for palette resampling
def build_prototype_image(): def build_prototype_image():
image = Image.new("L", (1, len(_Palm8BitColormapValues),)) image = Image.new("L", (1, len(_Palm8BitColormapValues)))
image.putdata(list(range(len(_Palm8BitColormapValues)))) image.putdata(list(range(len(_Palm8BitColormapValues))))
palettedata = () palettedata = ()
for i in range(len(_Palm8BitColormapValues)): for colormapValue in _Palm8BitColormapValues:
palettedata = palettedata + _Palm8BitColormapValues[i] palettedata += colormapValue
for i in range(256 - len(_Palm8BitColormapValues)): palettedata += (0, 0, 0)*(256 - len(_Palm8BitColormapValues))
palettedata = palettedata + (0, 0, 0)
image.putpalette(palettedata) image.putpalette(palettedata)
return image return image
@ -109,9 +109,6 @@ _COMPRESSION_TYPES = {
"scanline": 0x00, "scanline": 0x00,
} }
o8 = _binary.o8
o16b = _binary.o16be
# #
# -------------------------------------------------------------------- # --------------------------------------------------------------------

View File

@ -15,12 +15,11 @@
# #
from PIL import Image, ImageFile, _binary from . import Image, ImageFile
from ._binary import i8
__version__ = "0.1" __version__ = "0.1"
i8 = _binary.i8
## ##
# Image plugin for PhotoCD images. This plugin only reads the 768x512 # 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") raise SyntaxError("not a PCD file")
orientation = i8(s[1538]) & 3 orientation = i8(s[1538]) & 3
self.tile_post_rotate = None
if orientation == 1: if orientation == 1:
self.tile_post_rotate = 90 # hack self.tile_post_rotate = 90
elif orientation == 3: elif orientation == 3:
self.tile_post_rotate = -90 self.tile_post_rotate = -90
@ -51,6 +51,13 @@ class PcdImageFile(ImageFile.ImageFile):
self.size = 768, 512 # FIXME: not correct for rotated images! self.size = 768, 512 # FIXME: not correct for rotated images!
self.tile = [("pcd", (0, 0)+self.size, 96*2048, None)] 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 # registry

View File

@ -16,9 +16,8 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import Image from . import Image, FontFile
from PIL import FontFile from ._binary import i8, i16le as l16, i32le as l32, i16be as b16, i32be as b32
from PIL import _binary
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# declarations # declarations
@ -42,12 +41,6 @@ BYTES_PER_ROW = [
lambda bits: ((bits+63) >> 3) & ~7, 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): def sz(s, o):
return s[o:s.index(b"\0", o)] return s[o:s.index(b"\0", o)]

View File

@ -28,14 +28,11 @@
from __future__ import print_function from __future__ import print_function
import logging 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__) logger = logging.getLogger(__name__)
i8 = _binary.i8
i16 = _binary.i16le
o8 = _binary.o8
__version__ = "0.6" __version__ = "0.6"
@ -123,8 +120,6 @@ SAVE = {
"RGB": (5, 8, 3, "RGB;L"), "RGB": (5, 8, 3, "RGB;L"),
} }
o16 = _binary.o16le
def _save(im, fp, filename, check=0): def _save(im, fp, filename, check=0):

View File

@ -20,8 +20,8 @@
# Image plugin for PDF images (output only). # Image plugin for PDF images (output only).
## ##
from PIL import Image, ImageFile from . import Image, ImageFile
from PIL._binary import i8 from ._binary import i8
import io import io
__version__ = "0.4" __version__ = "0.4"

View File

@ -19,16 +19,14 @@
# See the README file for information on usage and redistribution. # 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" __version__ = "0.1"
# #
# helpers # helpers
i16 = _binary.i16le
def _accept(prefix): def _accept(prefix):
return prefix[:4] == b"\200\350\000\000" return prefix[:4] == b"\200\350\000\000"

View File

@ -38,17 +38,14 @@ import re
import zlib import zlib
import struct import struct
from PIL import Image, ImageFile, ImagePalette, _binary from . import Image, ImageFile, ImagePalette
from ._binary import i8, i16be as i16, i32be as i32, o8, o16be as o16, o32be as o32
__version__ = "0.9" __version__ = "0.9"
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
i8 = _binary.i8 is_cid = re.compile(br"\w\w\w\w").match
i16 = _binary.i16be
i32 = _binary.i32be
is_cid = re.compile(b"\w\w\w\w").match
_MAGIC = b"\211PNG\r\n\032\n" _MAGIC = b"\211PNG\r\n\032\n"
@ -132,7 +129,7 @@ class ChunkStream(object):
def call(self, cid, pos, length): def call(self, cid, pos, length):
"Call the appropriate chunk handler" "Call the appropriate chunk handler"
logger.debug("STREAM %s %s %s", cid, pos, length) logger.debug("STREAM %r %s %s", cid, pos, length)
return getattr(self, "chunk_" + cid.decode('ascii'))(pos, length) return getattr(self, "chunk_" + cid.decode('ascii'))(pos, length)
def crc(self, cid, data): def crc(self, cid, data):
@ -148,10 +145,10 @@ class ChunkStream(object):
crc1 = Image.core.crc32(data, Image.core.crc32(cid)) crc1 = Image.core.crc32(data, Image.core.crc32(cid))
crc2 = i16(self.fp.read(2)), i16(self.fp.read(2)) crc2 = i16(self.fp.read(2)), i16(self.fp.read(2))
if crc1 != crc2: if crc1 != crc2:
raise SyntaxError("broken PNG file (bad header checksum in %s)" raise SyntaxError("broken PNG file (bad header checksum in %r)"
% cid) % cid)
except struct.error: except struct.error:
raise SyntaxError("broken PNG file (incomplete checksum in %s)" raise SyntaxError("broken PNG file (incomplete checksum in %r)"
% cid) % cid)
def crc_skip(self, cid, data): def crc_skip(self, cid, data):
@ -309,7 +306,7 @@ class PngStream(ChunkStream):
# Compression method 1 byte (0) # Compression method 1 byte (0)
# Compressed profile n bytes (zlib with deflate compression) # Compressed profile n bytes (zlib with deflate compression)
i = s.find(b"\0") i = s.find(b"\0")
logger.debug("iCCP profile name %s", s[:i]) logger.debug("iCCP profile name %r", s[:i])
logger.debug("Compression method %s", i8(s[i])) logger.debug("Compression method %s", i8(s[i]))
comp_method = i8(s[i]) comp_method = i8(s[i])
if comp_method != 0: if comp_method != 0:
@ -539,7 +536,7 @@ class PngImageFile(ImageFile.ImageFile):
except EOFError: except EOFError:
break break
except AttributeError: except AttributeError:
logger.debug("%s %s %s (unknown)", cid, pos, length) logger.debug("%r %s %s (unknown)", cid, pos, length)
s = ImageFile._safe_read(self.fp, length) s = ImageFile._safe_read(self.fp, length)
self.png.crc(cid, s) self.png.crc(cid, s)
@ -621,10 +618,6 @@ class PngImageFile(ImageFile.ImageFile):
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# PNG writer # PNG writer
o8 = _binary.o8
o16 = _binary.o16be
o32 = _binary.o32be
_OUTMODES = { _OUTMODES = {
# supported PIL modes, and corresponding rawmodes/bits/color combinations # supported PIL modes, and corresponding rawmodes/bits/color combinations
"1": ("1", b'\x01\x00'), "1": ("1", b'\x01\x00'),
@ -722,6 +715,32 @@ def _save(im, fp, filename, chunk=putchunk, check=0):
b'\0', # 11: filter category b'\0', # 11: filter category
b'\0') # 12: interlace flag 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": if im.mode == "P":
palette_byte_number = (2 ** bits) * 3 palette_byte_number = (2 ** bits) * 3
palette_bytes = im.im.getpalette("RGB")[:palette_byte_number] palette_bytes = im.im.getpalette("RGB")[:palette_byte_number]
@ -768,20 +787,11 @@ def _save(im, fp, filename, chunk=putchunk, check=0):
info = im.encoderinfo.get("pnginfo") info = im.encoderinfo.get("pnginfo")
if info: if info:
chunks = [b"bKGD", b"hIST"]
for cid, data in info.chunks: for cid, data in info.chunks:
chunk(fp, cid, data) if cid in chunks:
chunks.remove(cid)
icc = im.encoderinfo.get("icc_profile", im.info.get("icc_profile")) chunk(fp, cid, data)
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)
ImageFile._save(im, _idat(fp, chunk), ImageFile._save(im, _idat(fp, chunk),
[("zip", (0, 0)+im.size, 0, rawmode)]) [("zip", (0, 0)+im.size, 0, rawmode)])

View File

@ -17,7 +17,7 @@
import string import string
from PIL import Image, ImageFile from . import Image, ImageFile
__version__ = "0.2" __version__ = "0.2"
@ -123,11 +123,6 @@ class PpmImageFile(ImageFile.ImageFile):
self.fp.tell(), self.fp.tell(),
(rawmode, 0, 1))] (rawmode, 0, 1))]
# ALTERNATIVE: load via builtin debug function
# self.im = Image.core.open_ppm(self.filename)
# self.mode = self.im.mode
# self.size = self.im.size
# #
# -------------------------------------------------------------------- # --------------------------------------------------------------------

View File

@ -18,7 +18,8 @@
__version__ = "0.4" __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 = { MODES = {
# (photoshop mode, bits) -> (pil mode, required channels) # (photoshop mode, bits) -> (pil mode, required channels)
@ -33,13 +34,6 @@ MODES = {
(9, 8): ("LAB", 3) (9, 8): ("LAB", 3)
} }
#
# helpers
i8 = _binary.i8
i16 = _binary.i16be
i32 = _binary.i32be
# --------------------------------------------------------------------. # --------------------------------------------------------------------.
# read PSD images # read PSD images

View File

@ -7,9 +7,12 @@
# See "The SGI Image File Format (Draft version 0.97)", Paul Haeberli. # See "The SGI Image File Format (Draft version 0.97)", Paul Haeberli.
# <ftp://ftp.sgi.com/graphics/SGIIMAGESPEC> # <ftp://ftp.sgi.com/graphics/SGIIMAGESPEC>
# #
#
# History: # History:
# 2016-16-10 mb Add save method without compression
# 1995-09-10 fl Created # 1995-09-10 fl Created
# #
# Copyright (c) 2016 by Mickael Bonfill.
# Copyright (c) 2008 by Karsten Hiddemann. # Copyright (c) 2008 by Karsten Hiddemann.
# Copyright (c) 1997 by Secret Labs AB. # Copyright (c) 1997 by Secret Labs AB.
# Copyright (c) 1995 by Fredrik Lundh. # 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" __version__ = "0.3"
i8 = _binary.i8
i16 = _binary.i16be
def _accept(prefix): def _accept(prefix):
@ -76,12 +79,79 @@ class SgiImageFile(ImageFile.ImageFile):
elif compression == 1: elif compression == 1:
raise ValueError("SGI RLE encoding not supported") 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 # registry
Image.register_open(SgiImageFile.format, SgiImageFile, _accept) 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, ".bw")
Image.register_extension(SgiImageFile.format, ".rgb") Image.register_extension(SgiImageFile.format, ".rgb")
Image.register_extension(SgiImageFile.format, ".rgba") Image.register_extension(SgiImageFile.format, ".rgba")
Image.register_extension(SgiImageFile.format, ".sgi") Image.register_extension(SgiImageFile.format, ".sgi")
# End of file

View File

@ -58,7 +58,7 @@ iforms = [1, 3, -11, -12, -21, -22]
# There is no magic number to identify Spider files, so just check a # There is no magic number to identify Spider files, so just check a
# series of header locations to see if they have reasonable values. # series of header locations to see if they have reasonable values.
# Returns no.of bytes in the header, if it is a valid Spider header, # Returns no. of bytes in the header, if it is a valid Spider header,
# otherwise returns 0 # otherwise returns 0
def isSpiderHeader(t): def isSpiderHeader(t):
@ -75,7 +75,7 @@ def isSpiderHeader(t):
labrec = int(h[13]) # no. records in file header labrec = int(h[13]) # no. records in file header
labbyt = int(h[22]) # total no. of bytes in header labbyt = int(h[22]) # total no. of bytes in header
lenbyt = int(h[23]) # record length in bytes 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): if labbyt != (labrec * lenbyt):
return 0 return 0
# looks like a valid header # looks like a valid header
@ -83,9 +83,8 @@ def isSpiderHeader(t):
def isSpiderImage(filename): def isSpiderImage(filename):
fp = open(filename, 'rb') with open(filename, 'rb') as fp:
f = fp.read(92) # read 23 * 4 bytes f = fp.read(92) # read 23 * 4 bytes
fp.close()
t = struct.unpack('>23f', f) # try big-endian first t = struct.unpack('>23f', f) # try big-endian first
hdrlen = isSpiderHeader(t) hdrlen = isSpiderHeader(t)
if hdrlen == 0: if hdrlen == 0:

View File

@ -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" __version__ = "0.3"
i32 = _binary.i32be
def _accept(prefix): def _accept(prefix):
return len(prefix) >= 4 and i32(prefix) == 0x59a66a95 return len(prefix) >= 4 and i32(prefix) == 0x59a66a95
@ -38,6 +37,21 @@ class SunImageFile(ImageFile.ImageFile):
def _open(self): def _open(self):
# The Sun Raster file header is 32 bytes in length and has the following format:
# typedef struct _SunRaster
# {
# DWORD MagicNumber; /* Magic (identification) number */
# DWORD Width; /* Width of image in pixels */
# DWORD Height; /* Height of image in pixels */
# DWORD Depth; /* Number of bits per pixel */
# DWORD Length; /* Size of image data in bytes */
# DWORD Type; /* Type of raster file */
# DWORD ColorMapType; /* Type of color map */
# DWORD ColorMapLength; /* Size of the color map in bytes */
# } SUNRASTER;
# HEAD # HEAD
s = self.fp.read(32) s = self.fp.read(32)
if i32(s) != 0x59a66a95: if i32(s) != 0x59a66a95:
@ -48,30 +62,70 @@ class SunImageFile(ImageFile.ImageFile):
self.size = i32(s[4:8]), i32(s[8:12]) self.size = i32(s[4:8]), i32(s[8:12])
depth = i32(s[12:16]) depth = i32(s[12:16])
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_length = i32(s[28:32])
if depth == 1: if depth == 1:
self.mode, rawmode = "1", "1;I" self.mode, rawmode = "1", "1;I"
elif depth == 4:
self.mode, rawmode = "L", "L;4"
elif depth == 8: elif depth == 8:
self.mode = rawmode = "L" self.mode = rawmode = "L"
elif depth == 24: elif depth == 24:
self.mode, rawmode = "RGB", "BGR" if file_type == 3:
self.mode, rawmode = "RGB", "RGB"
else:
self.mode, rawmode = "RGB", "BGR"
elif depth == 32:
if file_type == 3:
self.mode, rawmode = 'RGB', 'RGBX'
else:
self.mode, rawmode = 'RGB', 'BGRX'
else: else:
raise SyntaxError("unsupported mode") raise SyntaxError("Unsupported Mode/Bit Depth")
compression = i32(s[20:24]) if palette_length:
if palette_length > 1024:
raise SyntaxError("Unsupported Color Palette Length")
if i32(s[24:28]) != 0: if palette_type != 1:
length = i32(s[28:32]) raise SyntaxError("Unsupported Palette Type")
offset = offset + length
self.palette = ImagePalette.raw("RGB;L", self.fp.read(length)) offset = offset + palette_length
self.palette = ImagePalette.raw("RGB;L", self.fp.read(palette_length))
if self.mode == "L": if self.mode == "L":
self.mode = rawmode = "P" self.mode = "P"
rawmode = rawmode.replace('L', 'P')
stride = (((self.size[0] * depth + 7) // 8) + 3) & (~3) # 16 bit boundaries on stride
stride = ((self.size[0] * depth + 15) // 16) * 2
if compression == 1: # file type: Type is the version (or flavor) of the bitmap
# file. The following values are typically found in the Type
# field:
# 0000h Old
# 0001h Standard
# 0002h Byte-encoded
# 0003h RGB format
# 0004h TIFF format
# 0005h IFF format
# FFFFh Experimental
# Old and standard are the same, except for the length tag.
# byte-encoded is run-length-encoded
# 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)
if file_type in (0, 1, 3, 4, 5):
self.tile = [("raw", (0, 0)+self.size, offset, (rawmode, stride))] self.tile = [("raw", (0, 0)+self.size, offset, (rawmode, stride))]
elif compression == 2: elif file_type == 2:
self.tile = [("sun_rle", (0, 0)+self.size, offset, rawmode)] self.tile = [("sun_rle", (0, 0)+self.size, offset, rawmode)]
else:
raise SyntaxError('Unsupported Sun Raster file type')
# #
# registry # registry

View File

@ -14,7 +14,7 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import ContainerIO from . import ContainerIO
## ##

View File

@ -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, o32le as o32
__version__ = "0.3" __version__ = "0.3"
@ -26,9 +27,6 @@ __version__ = "0.3"
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# Read RGA file # Read RGA file
i8 = _binary.i8
i16 = _binary.i16le
MODES = { MODES = {
# map imagetype/depth to rawmode # map imagetype/depth to rawmode
@ -132,10 +130,6 @@ class TgaImageFile(ImageFile.ImageFile):
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# Write TGA file # Write TGA file
o8 = _binary.o8
o16 = _binary.o16le
o32 = _binary.o32le
SAVE = { SAVE = {
"1": ("1", 1, 0, 3), "1": ("1", 1, 0, 3),
"L": ("L", 8, 0, 3), "L": ("L", 8, 0, 3),

View File

@ -41,10 +41,8 @@
from __future__ import division, print_function from __future__ import division, print_function
from PIL import Image, ImageFile from . import Image, ImageFile, ImagePalette, TiffTags
from PIL import ImagePalette from ._binary import i8, o8
from PIL import _binary
from PIL import TiffTags
import collections import collections
from fractions import Fraction from fractions import Fraction
@ -71,9 +69,6 @@ IFD_LEGACY_API = True
II = b"II" # little-endian (Intel style) II = b"II" # little-endian (Intel style)
MM = b"MM" # big-endian (Motorola style) MM = b"MM" # big-endian (Motorola style)
i8 = _binary.i8
o8 = _binary.o8
# #
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# Read TIFF files # Read TIFF files
@ -132,7 +127,7 @@ COMPRESSION_INFO = {
34677: "tiff_sgilog24", 34677: "tiff_sgilog24",
} }
COMPRESSION_INFO_REV = dict([(v, k) for (k, v) in COMPRESSION_INFO.items()]) COMPRESSION_INFO_REV = {v: k for k, v in COMPRESSION_INFO.items()}
OPEN_INFO = { OPEN_INFO = {
# (ByteOrder, PhotoInterpretation, SampleFormat, FillOrder, BitsPerSample, # (ByteOrder, PhotoInterpretation, SampleFormat, FillOrder, BitsPerSample,
@ -278,12 +273,12 @@ class IFDRational(Rational):
self._numerator = value self._numerator = value
self._val = float(1) self._val = float(1)
if type(value) == Fraction: if isinstance(value, Fraction):
self._numerator = value.numerator self._numerator = value.numerator
self._denominator = value.denominator self._denominator = value.denominator
self._val = value self._val = value
if type(value) == IFDRational: if isinstance(value, IFDRational):
self._denominator = value.denominator self._denominator = value.denominator
self._numerator = value.numerator self._numerator = value.numerator
self._val = value._val self._val = value._val
@ -294,11 +289,7 @@ class IFDRational(Rational):
return return
elif denominator == 1: elif denominator == 1:
if sys.hexversion < 0x2070000 and type(value) == float: self._val = Fraction(value)
# python 2.6 is different.
self._val = Fraction.from_float(value)
else:
self._val = Fraction(value)
else: else:
self._val = Fraction(value, denominator) self._val = Fraction(value, denominator)
@ -342,7 +333,7 @@ class IFDRational(Rational):
'rfloordiv','mod','rmod', 'pow','rpow', 'pos', 'neg', 'rfloordiv','mod','rmod', 'pow','rpow', 'pos', 'neg',
'abs', 'trunc', 'lt', 'gt', 'le', 'ge', 'nonzero', 'abs', 'trunc', 'lt', 'gt', 'le', 'ge', 'nonzero',
'ceil', 'floor', 'round'] '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__') __add__ = _delegate('__add__')
@ -573,7 +564,7 @@ class ImageFileDirectory_v2(collections.MutableMapping):
def _register_loader(idx, size): def _register_loader(idx, size):
def decorator(func): def decorator(func):
from PIL.TiffTags import TYPES from .TiffTags import TYPES
if func.__name__.startswith("load_"): if func.__name__.startswith("load_"):
TYPES[idx] = func.__name__[5:].replace("_", " ") TYPES[idx] = func.__name__[5:].replace("_", " ")
_load_dispatch[idx] = size, func _load_dispatch[idx] = size, func
@ -587,12 +578,12 @@ class ImageFileDirectory_v2(collections.MutableMapping):
return decorator return decorator
def _register_basic(idx_fmt_name): def _register_basic(idx_fmt_name):
from PIL.TiffTags import TYPES from .TiffTags import TYPES
idx, fmt, name = idx_fmt_name idx, fmt, name = idx_fmt_name
TYPES[idx] = name TYPES[idx] = name
size = struct.calcsize("=" + fmt) size = struct.calcsize("=" + fmt)
_load_dispatch[idx] = size, lambda self, data, legacy_api=True: ( _load_dispatch[idx] = size, lambda self, data, legacy_api=True: (
self._unpack("{0}{1}".format(len(data) // size, fmt), data)) self._unpack("{}{}".format(len(data) // size, fmt), data))
_write_dispatch[idx] = lambda self, *values: ( _write_dispatch[idx] = lambda self, *values: (
b"".join(self._pack(fmt, value) for value in values)) b"".join(self._pack(fmt, value) for value in values))
@ -624,7 +615,7 @@ class ImageFileDirectory_v2(collections.MutableMapping):
@_register_loader(5, 8) @_register_loader(5, 8)
def load_rational(self, data, legacy_api=True): def load_rational(self, data, legacy_api=True):
vals = self._unpack("{0}L".format(len(data) // 4), data) vals = self._unpack("{}L".format(len(data) // 4), data)
combine = lambda a, b: (a, b) if legacy_api else IFDRational(a, b) combine = lambda a, b: (a, b) if legacy_api else IFDRational(a, b)
return tuple(combine(num, denom) return tuple(combine(num, denom)
for num, denom in zip(vals[::2], vals[1::2])) for num, denom in zip(vals[::2], vals[1::2]))
@ -644,7 +635,7 @@ class ImageFileDirectory_v2(collections.MutableMapping):
@_register_loader(10, 8) @_register_loader(10, 8)
def load_signed_rational(self, data, legacy_api=True): def load_signed_rational(self, data, legacy_api=True):
vals = self._unpack("{0}l".format(len(data) // 4), data) vals = self._unpack("{}l".format(len(data) // 4), data)
combine = lambda a, b: (a, b) if legacy_api else IFDRational(a, b) combine = lambda a, b: (a, b) if legacy_api else IFDRational(a, b)
return tuple(combine(num, denom) return tuple(combine(num, denom)
for num, denom in zip(vals[::2], vals[1::2])) for num, denom in zip(vals[::2], vals[1::2]))
@ -804,7 +795,7 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2):
ifd = ImageFileDirectory_v1() ifd = ImageFileDirectory_v1()
ifd[key] = 'Some Data' ifd[key] = 'Some Data'
ifd.tagtype[key] = 2 ifd.tagtype[key] = 2
print ifd[key] print(ifd[key])
('Some Data',) ('Some Data',)
Also contains a dictionary of tag types as read from the tiff image file, Also contains a dictionary of tag types as read from the tiff image file,
@ -1010,9 +1001,6 @@ class TiffImageFile(ImageFile.ImageFile):
# Section 14: Differencing Predictor # Section 14: Differencing Predictor
self.decoderconfig = (self.tag_v2[PREDICTOR],) self.decoderconfig = (self.tag_v2[PREDICTOR],)
if ICCPROFILE in self.tag_v2:
self.info['icc_profile'] = self.tag_v2[ICCPROFILE]
return args return args
def load(self): def load(self):
@ -1056,7 +1044,7 @@ class TiffImageFile(ImageFile.ImageFile):
# io.BytesIO have a fileno, but returns an IOError if # io.BytesIO have a fileno, but returns an IOError if
# it doesn't use a file descriptor. # it doesn't use a file descriptor.
fp = False fp = False
if fp: if fp:
args[2] = fp args[2] = fp
@ -1175,11 +1163,15 @@ class TiffImageFile(ImageFile.ImageFile):
yres = self.tag_v2.get(Y_RESOLUTION, 1) yres = self.tag_v2.get(Y_RESOLUTION, 1)
if xres and yres: 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 if resunit == 2: # dots per inch
self.info["dpi"] = xres, yres self.info["dpi"] = xres, yres
elif resunit == 3: # dots per centimeter. convert to dpi elif resunit == 3: # dots per centimeter. convert to dpi
self.info["dpi"] = xres * 2.54, yres * 2.54 self.info["dpi"] = xres * 2.54, yres * 2.54
elif resunit == 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 else: # No absolute unit of measurement
self.info["resolution"] = xres, yres self.info["resolution"] = xres, yres
@ -1201,7 +1193,7 @@ class TiffImageFile(ImageFile.ImageFile):
"tiff_sgilog24", "tiff_sgilog24",
"tiff_raw_16"]: "tiff_raw_16"]:
# if DEBUG: # if DEBUG:
# print "Activating g4 compression for whole file" # print("Activating g4 compression for whole file")
# Decoder expects entire file as one tile. # Decoder expects entire file as one tile.
# There's a buffer size limit in load (64k) # There's a buffer size limit in load (64k)
@ -1246,12 +1238,12 @@ class TiffImageFile(ImageFile.ImageFile):
a = None a = None
else: else:
for i in range(len(offsets)): for i, offset in enumerate(offsets):
a = self._decoder(rawmode, l, i) a = self._decoder(rawmode, l, i)
self.tile.append( self.tile.append(
(self._compression, (self._compression,
(0, min(y, ysize), w, min(y+h, ysize)), (0, min(y, ysize), w, min(y+h, ysize)),
offsets[i], a)) offset, a))
if DEBUG: if DEBUG:
print("tiles: ", self.tile) print("tiles: ", self.tile)
y = y + h y = y + h
@ -1285,6 +1277,10 @@ class TiffImageFile(ImageFile.ImageFile):
print("- unsupported data organization") print("- unsupported data organization")
raise SyntaxError("unknown 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 # fixup palette descriptor
if self.mode == "P": if self.mode == "P":
@ -1366,10 +1362,10 @@ def _save(im, fp, filename):
ifd[key] = im.tag_v2[key] ifd[key] = im.tag_v2[key]
ifd.tagtype[key] = im.tag_v2.tagtype[key] ifd.tagtype[key] = im.tag_v2.tagtype[key]
# preserve ICC profile (should also work when saving other formats # preserve ICC profile (should also work when saving other formats
# which support profiles as TIFF) -- 2008-06-06 Florian Hoech # which support profiles as TIFF) -- 2008-06-06 Florian Hoech
if "icc_profile" in im.info: if "icc_profile" in im.info:
ifd[ICCPROFILE] = im.info["icc_profile"] ifd[ICCPROFILE] = im.info["icc_profile"]
for key, name in [(IMAGEDESCRIPTION, "description"), for key, name in [(IMAGEDESCRIPTION, "description"),
(X_RESOLUTION, "resolution"), (X_RESOLUTION, "resolution"),
@ -1518,7 +1514,7 @@ class AppendingTiffWriter:
# JPEGQTables = 519 # JPEGQTables = 519
# JPEGDCTables = 520 # JPEGDCTables = 520
# JPEGACTables = 521 # JPEGACTables = 521
Tags = set((273, 288, 324, 519, 520, 521)) Tags = {273, 288, 324, 519, 520, 521}
def __init__(self, fn, new=False): def __init__(self, fn, new=False):
if hasattr(fn, 'read'): if hasattr(fn, 'read'):

View File

@ -418,13 +418,13 @@ TYPES = {}
# some of these are not in our TAGS_V2 dict and were included from tiff.h # some of these are not in our TAGS_V2 dict and were included from tiff.h
LIBTIFF_CORE = set([255, 256, 257, 258, 259, 262, 263, 266, 274, 277, LIBTIFF_CORE = {255, 256, 257, 258, 259, 262, 263, 266, 274, 277,
278, 280, 281, 340, 341, 282, 283, 284, 286, 287, 278, 280, 281, 340, 341, 282, 283, 284, 286, 287,
296, 297, 321, 320, 338, 32995, 322, 323, 32998, 296, 297, 321, 320, 338, 32995, 322, 323, 32998,
32996, 339, 32997, 330, 531, 530, 301, 532, 333, 32996, 339, 32997, 330, 531, 530, 301, 532, 333,
# as above # as above
269 # this has been in our tests forever, and works 269 # this has been in our tests forever, and works
]) }
LIBTIFF_CORE.remove(320) # Array of short, crashes LIBTIFF_CORE.remove(320) # Array of short, crashes
LIBTIFF_CORE.remove(301) # Array of short, crashes LIBTIFF_CORE.remove(301) # Array of short, crashes

View File

@ -23,7 +23,8 @@
from __future__ import print_function from __future__ import print_function
from PIL import Image, _binary from . import Image
from ._binary import i32le as i32
try: try:
import builtins import builtins
@ -31,8 +32,6 @@ except ImportError:
import __builtin__ import __builtin__
builtins = __builtin__ builtins = __builtin__
i32 = _binary.i32le
def open(filename): def open(filename):
""" """
@ -47,33 +46,35 @@ def open(filename):
# FIXME: modify to return a WalImageFile instance instead of # FIXME: modify to return a WalImageFile instance instead of
# plain Image object ? # 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"): if hasattr(filename, "read"):
fp = filename return imopen(filename)
else: else:
fp = builtins.open(filename, "rb") with builtins.open(filename, "rb") as fp:
return 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)
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
quake2palette = ( quake2palette = (
# default palette taken from piffo 0.93 by Hans Häggström # default palette taken from piffo 0.93 by Hans Häggström

View File

@ -1,7 +1,5 @@
from PIL import Image from . import Image, ImageFile, _webp
from PIL import ImageFile
from io import BytesIO from io import BytesIO
from PIL import _webp
_VALID_WEBP_MODES = { _VALID_WEBP_MODES = {
@ -43,7 +41,7 @@ class WebPImageFile(ImageFile.ImageFile):
self.tile = [("raw", (0, 0) + self.size, 0, self.mode)] self.tile = [("raw", (0, 0) + self.size, 0, self.mode)]
def _getexif(self): def _getexif(self):
from PIL.JpegImagePlugin import _getexif from .JpegImagePlugin import _getexif
return _getexif(self) return _getexif(self)

View File

@ -14,8 +14,17 @@
# #
# See the README file for information on usage and redistribution. # 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" __version__ = "0.2"
@ -53,20 +62,6 @@ if hasattr(Image.core, "drawwmf"):
register_handler(WmfHandler()) 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 # Read WMF file
@ -111,7 +106,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
self.info["dpi"] = 72 self.info["dpi"] = 72
# print self.mode, self.size, self.info # print(self.mode, self.size, self.info)
# sanity check (standard metafile header) # sanity check (standard metafile header)
if s[22:26] != b"\x01\x00\t\x00": if s[22:26] != b"\x01\x00\t\x00":
@ -121,13 +116,13 @@ class WmfStubImageFile(ImageFile.StubImageFile):
# enhanced metafile # enhanced metafile
# get bounding box # get bounding box
x0 = dword(s, 8) x0 = _long(s, 8)
y0 = dword(s, 12) y0 = _long(s, 12)
x1 = dword(s, 16) x1 = _long(s, 16)
y1 = dword(s, 20) y1 = _long(s, 20)
# get frame (in 0.01 millimeter units) # 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 # normalize size to 72 dots per inch
size = x1 - x0, y1 - y0 size = x1 - x0, y1 - y0

View File

@ -17,12 +17,11 @@
# FIXME: make save work (this requires quantization support) # FIXME: make save work (this requires quantization support)
# #
from PIL import Image, ImageFile, ImagePalette, _binary from . import Image, ImageFile, ImagePalette
from ._binary import o8
__version__ = "0.1" __version__ = "0.1"
o8 = _binary.o8
_MAGIC = b"P7 332" _MAGIC = b"P7 332"
# standard color palette for thumbnails (RGB332) # standard color palette for thumbnails (RGB332)

View File

@ -20,13 +20,13 @@
# #
import re import re
from PIL import Image, ImageFile from . import Image, ImageFile
__version__ = "0.6" __version__ = "0.6"
# XBM header # XBM header
xbm_head = re.compile( xbm_head = re.compile(
b"\s*#define[ \t]+.*_width[ \t]+(?P<width>[0-9]+)[\r\n]+" br"\s*#define[ \t]+.*_width[ \t]+(?P<width>[0-9]+)[\r\n]+"
b"#define[ \t]+.*_height[ \t]+(?P<height>[0-9]+)[\r\n]+" b"#define[ \t]+.*_height[ \t]+(?P<height>[0-9]+)[\r\n]+"
b"(?P<hotspot>" b"(?P<hotspot>"
b"#define[ \t]+[^_]*_x_hot[ \t]+(?P<xhot>[0-9]+)[\r\n]+" b"#define[ \t]+[^_]*_x_hot[ \t]+(?P<xhot>[0-9]+)[\r\n]+"

View File

@ -16,8 +16,8 @@
import re import re
from PIL import Image, ImageFile, ImagePalette from . import Image, ImageFile, ImagePalette
from PIL._binary import i8, o8 from ._binary import i8, o8
__version__ = "0.2" __version__ = "0.2"

View File

@ -12,7 +12,7 @@
# ;-) # ;-)
VERSION = '1.1.7' # PIL version VERSION = '1.1.7' # PIL version
PILLOW_VERSION = '3.4.0.dev0' # Pillow PILLOW_VERSION = '4.1.0.dev0' # Pillow
__version__ = PILLOW_VERSION __version__ = PILLOW_VERSION

View File

@ -28,26 +28,43 @@ else:
# Input, le = little endian, be = big endian # Input, le = little endian, be = big endian
# TODO: replace with more readable struct.unpack equivalent
def i16le(c, o=0): 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 c: string containing bytes to convert
o: offset of bytes to convert in string o: offset of bytes to convert in string
""" """
return unpack("<H", c[o:o+2])[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): 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 c: string containing bytes to convert
o: offset of bytes to convert in string o: offset of bytes to convert in string
""" """
return unpack("<I", c[o:o+4])[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): def i16be(c, o=0):
return unpack(">H", c[o:o+2])[0] return unpack(">H", c[o:o+2])[0]

View File

@ -1,4 +1,4 @@
from PIL import Image from . import Image
modules = { modules = {
"pil": "PIL._imaging", "pil": "PIL._imaging",
@ -17,7 +17,7 @@ def check_module(feature):
module = modules[feature] module = modules[feature]
method_to_call = None method_to_call = None
if type(module) is tuple: if isinstance(module, tuple):
module, method_to_call = module module, method_to_call = module
try: try:

View File

@ -10,10 +10,6 @@ Install::
pip install coverage nose pip install coverage nose
If you're using Python 2.6, there's one additional dependency::
pip install unittest2
Execution Execution
--------- ---------

View File

@ -7,13 +7,8 @@ class TestJ2kEncodeOverflow(PillowTestCase):
im = Image.new('RGBA', (1024, 131584)) im = Image.new('RGBA', (1024, 131584))
target = self.tempfile('temp.jpc') target = self.tempfile('temp.jpc')
try: with self.assertRaises(IOError):
im.save(target) im.save(target)
self.assertTrue(False, "Expected IOError, save succeeded?")
except IOError as err:
self.assertTrue(True, "IOError is expected")
except Exception as err:
self.assertTrue(False, "Expected IOError, got %s" % type(err))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -10,13 +10,9 @@ class TestLibtiffSegfault(PillowTestCase):
libtiff >= 4.0.0 libtiff >= 4.0.0
""" """
try: with self.assertRaises(IOError):
im = Image.open(TEST_FILE) im = Image.open(TEST_FILE)
im.load() im.load()
except IOError:
self.assertTrue(True, "Got expected IOError")
except Exception:
self.fail("Should have returned IOError")
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -5,11 +5,22 @@ from __future__ import print_function
import sys import sys
import tempfile import tempfile
import os import os
import unittest
if sys.version_info[:2] <= (2, 6): from PIL import Image, ImageMath
import unittest2 as unittest
else:
import unittest def convert_to_comparable(a, b):
new_a, new_b = a, b
if a.mode == 'P':
new_a = Image.new('L', a.size)
new_b = Image.new('L', b.size)
new_a.putdata(a.getdata())
new_b.putdata(b.getdata())
elif a.mode == 'I;16':
new_a = a.convert('I')
new_b = b.convert('I')
return new_a, new_b
class PillowTestCase(unittest.TestCase): class PillowTestCase(unittest.TestCase):
@ -49,7 +60,7 @@ class PillowTestCase(unittest.TestCase):
len(a), len(b), len(a), len(b),
msg or "got length %s, expected %s" % (len(a), len(b))) msg or "got length %s, expected %s" % (len(a), len(b)))
self.assertTrue( self.assertTrue(
all([x == y for x, y in zip(a, b)]), all(x == y for x, y in zip(a, b)),
msg or "got %s, expected %s" % (a, b)) msg or "got %s, expected %s" % (a, b))
except: except:
self.assertEqual(a, b, msg) self.assertEqual(a, b, msg)
@ -84,14 +95,13 @@ class PillowTestCase(unittest.TestCase):
a.size, b.size, a.size, b.size,
msg or "got size %r, expected %r" % (a.size, b.size)) msg or "got size %r, expected %r" % (a.size, b.size))
a, b = convert_to_comparable(a, b)
diff = 0 diff = 0
try: for ach, bch in zip(a.split(), b.split()):
ord(b'0') chdiff = ImageMath.eval("abs(a - b)", a=ach, b=bch).convert('L')
for abyte, bbyte in zip(a.tobytes(), b.tobytes()): diff += sum(i * num for i, num in enumerate(chdiff.histogram()))
diff += abs(ord(abyte)-ord(bbyte))
except:
for abyte, bbyte in zip(a.tobytes(), b.tobytes()):
diff += abs(abyte-bbyte)
ave_diff = float(diff)/(a.size[0]*a.size[1]) ave_diff = float(diff)/(a.size[0]*a.size[1])
self.assertGreaterEqual( self.assertGreaterEqual(
epsilon, ave_diff, epsilon, ave_diff,
@ -139,9 +149,13 @@ class PillowTestCase(unittest.TestCase):
if skip: if skip:
self.skipTest(msg or "Known Bad Test") self.skipTest(msg or "Known Bad Test")
def shortDescription(self):
# Prevents `nose -v` printing docstrings
return None
def tempfile(self, template): def tempfile(self, template):
assert template[:5] in ("temp.", "temp_") assert template[:5] in ("temp.", "temp_")
(fd, path) = tempfile.mkstemp(template[4:], template[:4]) fd, path = tempfile.mkstemp(template[4:], template[:4])
os.close(fd) os.close(fd)
self.addCleanup(self.delete_tempfile, path) self.addCleanup(self.delete_tempfile, path)

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
Tests/images/drawing.emf Normal file

Binary file not shown.

BIN
Tests/images/drawing.wmf Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Tests/images/hopper.sgi Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

Some files were not shown because too many files have changed in this diff Show More