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 latest 3.x and 2.x next, to get quick compatibility results.
# Then run the remainder.
python:
- "pypy"
- "pypy3"
- 3.5
- 2.7
- 2.6
- "2.7_with_system_site_packages" # For PyQt4
- 3.2
- 3.3
- 3.4
- nightly
matrix:
fast_finish: true
allow_failures:
- python: nightly
include:
- python: "pypy"
- python: "pypy3"
- python: '3.6'
- python: '2.7'
- 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:
- "travis_retry pip install pycparser!=2.14" # TEMPORARY, WORKAROUND FOR https://github.com/eliben/pycparser/issues/147
- "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 [ "$DOCKER" == "" ]; then .travis/install.sh; 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
- if [ "$TRAVIS_PYTHON_VERSION" == "3.2" ]; then travis_retry pip install coverage==3.7.1; fi
- if [ "$TRAVIS_PYTHON_VERSION" != "3.2" ]; then travis_retry pip install coverage; fi
# 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
before_script:
# Qt needs a display for some of the tests, and it's only run on the system site packages install
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"
script:
- if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage erase; fi
- python setup.py clean
- CFLAGS="-coverage" python setup.py build_ext --inplace
- if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage run --append --include=PIL/* selftest.py; fi
- if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage run --append --include=PIL/* -m nose -vx Tests/test_*.py; fi
- pushd /tmp/check-manifest && check-manifest --ignore ".coveragerc,.editorconfig,*.yml,*.yaml,tox.ini" && popd
# Docs
- if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then make install && make doccheck; fi
- |
if [ "$DOCKER" == "" ]; then
.travis/script.sh
else
docker run -v $TRAVIS_BUILD_DIR:/Pillow pythonpillow/$DOCKER
fi
after_success:
# gather the coverage data
- travis_retry sudo apt-get -qq install lcov
- lcov --capture --directory . -b . --output-file coverage.info
# filter to remove system headers
- lcov --remove coverage.info '/usr/*' -o coverage.filtered.info
# convert to json
- travis_retry gem install coveralls-lcov
- coveralls-lcov -v -n coverage.filtered.info > coverage.c.json
- coverage report
- travis_retry pip install coveralls-merge
- coveralls-merge coverage.c.json
- travis_retry pip install pep8 pyflakes
- pep8 --statistics --count PIL/*.py
- pep8 --statistics --count Tests/*.py
- pyflakes *.py | tee >(wc -l)
- pyflakes PIL/*.py | tee >(wc -l)
- pyflakes Tests/*.py | tee >(wc -l)
# Coverage and quality reports on just the latest diff.
# (Installation is very slow on Py3, so just do it for Py2.)
- if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then depends/diffcover-install.sh; fi
- if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then depends/diffcover-run.sh; fi
# after_all
- |
if [ "$TRAVIS_REPO_SLUG" = "python-pillow/Pillow" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ]; then
curl -Lo travis_after_all.py https://raw.github.com/dmakhno/travis_after_all/master/travis_after_all.py
python travis_after_all.py
export $(cat .to_export_back)
if [ "$BUILD_LEADER" = "YES" ]; then
if [ "$BUILD_AGGREGATE_STATUS" = "others_succeeded" ]; then
echo "All jobs succeded! Triggering macOS build..."
# Trigger a macOS build at the pillow-wheels repo
./build_children.sh
else
echo "Some jobs failed"
fi
fi
fi
- .travis/after_success.sh
after_failure:
- |
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
fi
matrix:
fast_finish: true
allow_failures:
- python: nightly
env:
global:
# 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
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:

View File

@ -58,6 +58,13 @@ install:
python setup.py install
python selftest.py --installed
debug:
# make a debug version if we don't have a -dbg python. Leaves in symbols
# for our stuff, kills optimization, and redirects to dev null so we
# see any build failures.
make clean > /dev/null
CFLAGS='-g -O0' python setup.py build_ext install > /dev/null
install-req:
pip install -r requirements.txt

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

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).
* FTU files are not compressed.
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:
{header}
{format_directory}
@ -42,7 +42,7 @@ Note: All data is stored in little-Endian (Intel) byte order.
import struct
from io import BytesIO
from PIL import Image, ImageFile
from . import Image, ImageFile
MAGIC = b"FTEX"

View File

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

View File

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

View File

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

View File

@ -14,7 +14,7 @@
#
from math import pi, log, sin, sqrt
from PIL._binary import o8
from ._binary import o8
# --------------------------------------------------------------------
# Stuff to translate curve segments to palette values (derived from

View File

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

View File

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

View File

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

View File

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

View File

@ -25,7 +25,8 @@
import struct
from io import BytesIO
from PIL import Image, ImageFile, BmpImagePlugin, PngImagePlugin, _binary
from . import Image, ImageFile, BmpImagePlugin, PngImagePlugin
from ._binary import i8, i16le as i16, i32le as i32
from math import log, ceil
__version__ = "0.1"
@ -33,10 +34,6 @@ __version__ = "0.1"
#
# --------------------------------------------------------------------
i8 = _binary.i8
i16 = _binary.i16le
i32 = _binary.i32le
_MAGIC = b"\0\0\1\0"
@ -44,16 +41,19 @@ def _save(im, fp, filename):
fp.write(_MAGIC) # (2+2)
sizes = im.encoderinfo.get("sizes",
[(16, 16), (24, 24), (32, 32), (48, 48),
(64, 64), (128, 128), (255, 255)])
(64, 64), (128, 128), (256, 256)])
width, height = im.size
filter(lambda x: False if (x[0] > width or x[1] > height or
x[0] > 255 or x[1] > 255) else True, sizes)
sizes = filter(lambda x: False if (x[0] > width or x[1] > height or
x[0] > 256 or x[1] > 256) else True,
sizes)
sizes = list(sizes)
fp.write(struct.pack("<H", len(sizes))) # idCount(2)
offset = fp.tell() + len(sizes)*16
for size in sizes:
width, height = size
fp.write(struct.pack("B", width)) # bWidth(1)
fp.write(struct.pack("B", height)) # bHeight(1)
# 0 means 256
fp.write(struct.pack("B", width if width < 256 else 0)) # bWidth(1)
fp.write(struct.pack("B", height if height < 256 else 0)) # bHeight(1)
fp.write(b"\0") # bColorCount(1)
fp.write(b"\0") # bReserved(1)
fp.write(b"\0\0") # wPlanes(2)
@ -139,7 +139,7 @@ class IcoFile(object):
"""
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):
"""
@ -215,13 +215,13 @@ class IcoFile(object):
total_bytes = int((w * im.size[1]) / 8)
self.buf.seek(and_mask_offset)
maskData = self.buf.read(total_bytes)
mask_data = self.buf.read(total_bytes)
# convert raw data to image
mask = Image.frombuffer(
'1', # 1 bpp
im.size, # (w, h)
maskData, # source chars
mask_data, # source chars
'raw', # raw decoder
('1;I', int(w/8), -1) # 1bpp inverted, padded, reversed
)
@ -278,6 +278,7 @@ class IcoImageFile(ImageFile.ImageFile):
#
# --------------------------------------------------------------------
Image.register_open(IcoImageFile.format, IcoImageFile, _accept)
Image.register_save(IcoImageFile.format, _save)
Image.register_extension(IcoImageFile.format, ".ico")

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -16,7 +16,7 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image, ImageColor, ImageDraw, ImageFont, ImagePath
from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath
class Pen(object):

View File

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

View File

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

View File

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

View File

@ -15,7 +15,7 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image
from . import Image
import sys
if sys.platform not in ["win32", "darwin"]:
@ -41,7 +41,7 @@ def grab(bbox=None):
size, data = grabber()
im = Image.frombytes(
"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
)
if bbox:
@ -75,7 +75,7 @@ def grabclipboard():
debug = 0 # temporary interface
data = Image.core.grabclipboard(debug)
if isinstance(data, bytes):
from PIL import BmpImagePlugin
from . import BmpImagePlugin
import io
return BmpImagePlugin.DibImageFile(io.BytesIO(data))
return data

View File

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

View File

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

View File

@ -5,8 +5,9 @@
#
# Copyright (c) 2014 Dov Grobgeld <dov.grobgeld@gmail.com>
from PIL import Image
from PIL import _imagingmorph
from __future__ import print_function
from . import Image, _imagingmorph
import re
LUT_SIZE = 1 << 9
@ -78,7 +79,7 @@ class LutBuilder(object):
def build_default_lut(self):
symbols = [0, 1]
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):
return self.lut
@ -88,7 +89,7 @@ class LutBuilder(object):
string permuted according to the permutation list.
"""
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):
"""pattern_permute takes a basic pattern and its result and clones
@ -152,14 +153,14 @@ class LutBuilder(object):
# # Debugging
# for p,r in patterns:
# print p,r
# print '--'
# print(p,r)
# print('--')
# compile the patterns into regular expressions for speed
for i in range(len(patterns)):
p = patterns[i][0].replace('.', 'X').replace('X', '[01]')
for i, pattern in enumerate(patterns):
p = pattern[0].replace('.', 'X').replace('X', '[01]')
p = re.compile(p)
patterns[i] = (p, patterns[i][1])
patterns[i] = (p, pattern[1])
# Step through table and find patterns that match.
# 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.
#
from PIL import Image
from PIL._util import isStringType
from . import Image
from ._util import isStringType
import operator
import functools
@ -39,7 +39,7 @@ def _border(border):
def _color(color, mode):
if isStringType(color):
from PIL import ImageColor
from . import ImageColor
color = ImageColor.getcolor(color, mode)
return color
@ -206,7 +206,8 @@ def deform(image, deformer, resample=Image.BILINEAR):
:param image: The image to deform.
:param deformer: A deformer object. Any object that implements a
**getmesh** method can be used.
:param resample: What resampling filter to use.
:param resample: An optional resampling filter. Same values possible as
in the PIL.Image.transform function.
:return: An image.
"""
return image.transform(

View File

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

View File

@ -14,7 +14,7 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image
from . import Image
# the Python class below is overridden by the C implementation.

View File

@ -16,8 +16,8 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image
from PIL._util import isPath
from . import Image
from ._util import isPath
from io import BytesIO
qt_is_installed = True
@ -157,7 +157,6 @@ def _toqclass_helper(im):
else:
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)
return {
'data': __data, 'im': im, 'format': format, 'colortable': colortable
@ -175,8 +174,13 @@ if qt_is_installed:
string or a PyQt string object).
"""
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,
im_data['data'], im_data['im'].size[0],
self.__data, im_data['im'].size[0],
im_data['im'].size[1], im_data['format'])
if im_data['colortable']:
self.setColorTable(im_data['colortable'])

View File

@ -32,7 +32,14 @@ except ImportError:
tkinter = 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
@ -182,9 +189,15 @@ class PhotoImage(object):
except tkinter.TclError:
# activate Tkinter hook
try:
from PIL import _imagingtk
from . import _imagingtk
try:
_imagingtk.tkinit(tk.interpaddr(), 1)
if hasattr(tk, 'interp'):
# Pypy is using a ffi cdata element
# (Pdb) self.tk.interp
# <cdata 'Tcl_Interp *' 0x3061b50>
_imagingtk.tkinit(int(ffi.cast("uintptr_t", tk.interp)), 1)
else:
_imagingtk.tkinit(tk.interpaddr(), 1)
except AttributeError:
_imagingtk.tkinit(id(tk), 0)
tk.call("PyImagingPhoto", self.__photo, block.id)
@ -264,6 +277,8 @@ class BitmapImage(object):
def getimage(photo):
""" This function is unimplemented """
"""Copies the contents of a PhotoImage to a PIL image memory."""
photo.tk.call("PyImagingPhotoGet", photo)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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"
@ -25,8 +26,6 @@ __version__ = "0.1"
#
# read MSP files
i16 = _binary.i16le
def _accept(prefix):
return prefix[:4] in [b"DanM", b"LinS"]
@ -66,8 +65,6 @@ class MspImageFile(ImageFile.ImageFile):
#
# write MSP files (uncompressed only)
o16 = _binary.o16le
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.
#
from PIL import EpsImagePlugin
from . import EpsImagePlugin
import sys
##

View File

@ -13,7 +13,7 @@
# 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).
##
from PIL import Image, ImageFile, _binary
from . import Image, ImageFile
from ._binary import o8, o16be as o16b
__version__ = "1.0"
@ -80,13 +81,12 @@ _Palm8BitColormapValues = (
# so build a prototype image to be used for palette resampling
def build_prototype_image():
image = Image.new("L", (1, len(_Palm8BitColormapValues),))
image = Image.new("L", (1, len(_Palm8BitColormapValues)))
image.putdata(list(range(len(_Palm8BitColormapValues))))
palettedata = ()
for i in range(len(_Palm8BitColormapValues)):
palettedata = palettedata + _Palm8BitColormapValues[i]
for i in range(256 - len(_Palm8BitColormapValues)):
palettedata = palettedata + (0, 0, 0)
for colormapValue in _Palm8BitColormapValues:
palettedata += colormapValue
palettedata += (0, 0, 0)*(256 - len(_Palm8BitColormapValues))
image.putpalette(palettedata)
return image
@ -109,9 +109,6 @@ _COMPRESSION_TYPES = {
"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"
i8 = _binary.i8
##
# Image plugin for PhotoCD images. This plugin only reads the 768x512
@ -42,8 +41,9 @@ class PcdImageFile(ImageFile.ImageFile):
raise SyntaxError("not a PCD file")
orientation = i8(s[1538]) & 3
self.tile_post_rotate = None
if orientation == 1:
self.tile_post_rotate = 90 # hack
self.tile_post_rotate = 90
elif orientation == 3:
self.tile_post_rotate = -90
@ -51,6 +51,13 @@ class PcdImageFile(ImageFile.ImageFile):
self.size = 768, 512 # FIXME: not correct for rotated images!
self.tile = [("pcd", (0, 0)+self.size, 96*2048, None)]
def load_end(self):
if self.tile_post_rotate:
# Handle rotated PCDs
self.im = self.im.rotate(self.tile_post_rotate)
self.size = self.im.size
#
# registry

View File

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

View File

@ -28,14 +28,11 @@
from __future__ import print_function
import logging
from PIL import Image, ImageFile, ImagePalette, _binary
from . import Image, ImageFile, ImagePalette
from ._binary import i8, i16le as i16, o8, o16le as o16
logger = logging.getLogger(__name__)
i8 = _binary.i8
i16 = _binary.i16le
o8 = _binary.o8
__version__ = "0.6"
@ -123,8 +120,6 @@ SAVE = {
"RGB": (5, 8, 3, "RGB;L"),
}
o16 = _binary.o16le
def _save(im, fp, filename, check=0):

View File

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

View File

@ -19,16 +19,14 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image, ImageFile, _binary
from . import Image, ImageFile
from ._binary import i16le as i16
__version__ = "0.1"
#
# helpers
i16 = _binary.i16le
def _accept(prefix):
return prefix[:4] == b"\200\350\000\000"

View File

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

View File

@ -17,7 +17,7 @@
import string
from PIL import Image, ImageFile
from . import Image, ImageFile
__version__ = "0.2"
@ -123,11 +123,6 @@ class PpmImageFile(ImageFile.ImageFile):
self.fp.tell(),
(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"
from PIL import Image, ImageFile, ImagePalette, _binary
from . import Image, ImageFile, ImagePalette
from ._binary import i8, i16be as i16, i32be as i32
MODES = {
# (photoshop mode, bits) -> (pil mode, required channels)
@ -33,13 +34,6 @@ MODES = {
(9, 8): ("LAB", 3)
}
#
# helpers
i8 = _binary.i8
i16 = _binary.i16be
i32 = _binary.i32be
# --------------------------------------------------------------------.
# read PSD images

View File

@ -7,9 +7,12 @@
# See "The SGI Image File Format (Draft version 0.97)", Paul Haeberli.
# <ftp://ftp.sgi.com/graphics/SGIIMAGESPEC>
#
#
# History:
# 2016-16-10 mb Add save method without compression
# 1995-09-10 fl Created
#
# Copyright (c) 2016 by Mickael Bonfill.
# Copyright (c) 2008 by Karsten Hiddemann.
# Copyright (c) 1997 by Secret Labs AB.
# Copyright (c) 1995 by Fredrik Lundh.
@ -18,12 +21,12 @@
#
from PIL import Image, ImageFile, _binary
from . import Image, ImageFile
from ._binary import i8, o8, i16be as i16
import struct
import os
__version__ = "0.2"
i8 = _binary.i8
i16 = _binary.i16be
__version__ = "0.3"
def _accept(prefix):
@ -76,12 +79,79 @@ class SgiImageFile(ImageFile.ImageFile):
elif compression == 1:
raise ValueError("SGI RLE encoding not supported")
def _save(im, fp, filename):
if im.mode != "RGB" and im.mode != "RGBA" and im.mode != "L":
raise ValueError("Unsupported SGI image mode")
# Flip the image, since the origin of SGI file is the bottom-left corner
im = im.transpose(Image.FLIP_TOP_BOTTOM)
# Define the file as SGI File Format
magicNumber = 474
# Run-Length Encoding Compression - Unsupported at this time
rle = 0
# Byte-per-pixel precision, 1 = 8bits per pixel
bpc = 1
# Number of dimensions (x,y,z)
dim = 3
# X Dimension = width / Y Dimension = height
x, y = im.size
if im.mode == "L" and y == 1:
dim = 1
elif im.mode == "L":
dim = 2
# Z Dimension: Number of channels
z = len(im.mode)
if dim == 1 or dim == 2:
z = 1
# Minimum Byte value
pinmin = 0
# Maximum Byte value (255 = 8bits per pixel)
pinmax = 255
# Image name (79 characters max, truncated below in write)
imgName = os.path.splitext(os.path.basename(filename))[0]
if str is not bytes:
imgName = imgName.encode('ascii', 'ignore')
# Standard representation of pixel in the file
colormap = 0
fp.write(struct.pack('>h', magicNumber))
fp.write(o8(rle))
fp.write(o8(bpc))
fp.write(struct.pack('>H', dim))
fp.write(struct.pack('>H', x))
fp.write(struct.pack('>H', y))
fp.write(struct.pack('>H', z))
fp.write(struct.pack('>l', pinmin))
fp.write(struct.pack('>l', pinmax))
fp.write(struct.pack('4s', b'')) # dummy
fp.write(struct.pack('79s', imgName)) # truncates to 79 chars
fp.write(struct.pack('s', b'')) # force null byte after imgname
fp.write(struct.pack('>l', colormap))
fp.write(struct.pack('404s', b'')) # dummy
#assert we've got the right number of bands.
if len(im.getbands()) != z:
raise ValueError("incorrect number of bands in SGI write: %s vs %s" %
(z, len(im.getbands())))
for channel in im.split():
fp.write(channel.tobytes())
fp.close()
#
# registry
Image.register_open(SgiImageFile.format, SgiImageFile, _accept)
Image.register_save(SgiImageFile.format, _save)
Image.register_mime(SgiImageFile.format, "image/sgi")
Image.register_mime(SgiImageFile.format, "image/rgb")
Image.register_extension(SgiImageFile.format, ".bw")
Image.register_extension(SgiImageFile.format, ".rgb")
Image.register_extension(SgiImageFile.format, ".rgba")
Image.register_extension(SgiImageFile.format, ".sgi")
# End of file

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
# 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
def isSpiderHeader(t):
@ -75,7 +75,7 @@ def isSpiderHeader(t):
labrec = int(h[13]) # no. records in file header
labbyt = int(h[22]) # total no. of bytes in header
lenbyt = int(h[23]) # record length in bytes
# print "labrec = %d, labbyt = %d, lenbyt = %d" % (labrec,labbyt,lenbyt)
# print("labrec = %d, labbyt = %d, lenbyt = %d" % (labrec,labbyt,lenbyt))
if labbyt != (labrec * lenbyt):
return 0
# looks like a valid header
@ -83,9 +83,8 @@ def isSpiderHeader(t):
def isSpiderImage(filename):
fp = open(filename, 'rb')
f = fp.read(92) # read 23 * 4 bytes
fp.close()
with open(filename, 'rb') as fp:
f = fp.read(92) # read 23 * 4 bytes
t = struct.unpack('>23f', f) # try big-endian first
hdrlen = isSpiderHeader(t)
if hdrlen == 0:

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"
i32 = _binary.i32be
def _accept(prefix):
return len(prefix) >= 4 and i32(prefix) == 0x59a66a95
@ -38,6 +37,21 @@ class SunImageFile(ImageFile.ImageFile):
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
s = self.fp.read(32)
if i32(s) != 0x59a66a95:
@ -48,30 +62,70 @@ class SunImageFile(ImageFile.ImageFile):
self.size = i32(s[4:8]), i32(s[8:12])
depth = i32(s[12:16])
data_length = i32(s[16:20]) # unreliable, ignore.
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:
self.mode, rawmode = "1", "1;I"
elif depth == 4:
self.mode, rawmode = "L", "L;4"
elif depth == 8:
self.mode = rawmode = "L"
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:
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:
length = i32(s[28:32])
offset = offset + length
self.palette = ImagePalette.raw("RGB;L", self.fp.read(length))
if palette_type != 1:
raise SyntaxError("Unsupported Palette Type")
offset = offset + palette_length
self.palette = ImagePalette.raw("RGB;L", self.fp.read(palette_length))
if self.mode == "L":
self.mode = 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))]
elif compression == 2:
elif file_type == 2:
self.tile = [("sun_rle", (0, 0)+self.size, offset, rawmode)]
else:
raise SyntaxError('Unsupported Sun Raster file type')
#
# registry

View File

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

View File

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

View File

@ -23,7 +23,8 @@
from __future__ import print_function
from PIL import Image, _binary
from . import Image
from ._binary import i32le as i32
try:
import builtins
@ -31,8 +32,6 @@ except ImportError:
import __builtin__
builtins = __builtin__
i32 = _binary.i32le
def open(filename):
"""
@ -47,33 +46,35 @@ def open(filename):
# FIXME: modify to return a WalImageFile instance instead of
# plain Image object ?
def imopen(fp):
# read header fields
header = fp.read(32+24+32+12)
size = i32(header, 32), i32(header, 36)
offset = i32(header, 40)
# load pixel data
fp.seek(offset)
Image._decompression_bomb_check(size)
im = Image.frombytes("P", size, fp.read(size[0] * size[1]))
im.putpalette(quake2palette)
im.format = "WAL"
im.format_description = "Quake2 Texture"
# strings are null-terminated
im.info["name"] = header[:32].split(b"\0", 1)[0]
next_name = header[56:56+32].split(b"\0", 1)[0]
if next_name:
im.info["next_name"] = next_name
return im
if hasattr(filename, "read"):
fp = filename
return imopen(filename)
else:
fp = builtins.open(filename, "rb")
# read header fields
header = fp.read(32+24+32+12)
size = i32(header, 32), i32(header, 36)
offset = i32(header, 40)
# load pixel data
fp.seek(offset)
im = Image.frombytes("P", size, fp.read(size[0] * size[1]))
im.putpalette(quake2palette)
im.format = "WAL"
im.format_description = "Quake2 Texture"
# strings are null-terminated
im.info["name"] = header[:32].split(b"\0", 1)[0]
next_name = header[56:56+32].split(b"\0", 1)[0]
if next_name:
im.info["next_name"] = next_name
return im
with builtins.open(filename, "rb") as fp:
return imopen(fp)
quake2palette = (
# default palette taken from piffo 0.93 by Hans Häggström

View File

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

View File

@ -14,8 +14,17 @@
#
# See the README file for information on usage and redistribution.
#
# WMF/EMF reference documentation:
# https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-WMF/[MS-WMF].pdf
# http://wvware.sourceforge.net/caolan/index.html
# http://wvware.sourceforge.net/caolan/ora-wmf.html
from __future__ import print_function
from . import Image, ImageFile
from ._binary import i16le as word, si16le as short, i32le as dword, si32le as _long
from PIL import Image, ImageFile, _binary
__version__ = "0.2"
@ -53,20 +62,6 @@ if hasattr(Image.core, "drawwmf"):
register_handler(WmfHandler())
# --------------------------------------------------------------------
word = _binary.i16le
def short(c, o=0):
v = word(c, o)
if v >= 32768:
v -= 65536
return v
dword = _binary.i32le
#
# --------------------------------------------------------------------
# Read WMF file
@ -111,7 +106,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
self.info["dpi"] = 72
# print self.mode, self.size, self.info
# print(self.mode, self.size, self.info)
# sanity check (standard metafile header)
if s[22:26] != b"\x01\x00\t\x00":
@ -121,13 +116,13 @@ class WmfStubImageFile(ImageFile.StubImageFile):
# enhanced metafile
# get bounding box
x0 = dword(s, 8)
y0 = dword(s, 12)
x1 = dword(s, 16)
y1 = dword(s, 20)
x0 = _long(s, 8)
y0 = _long(s, 12)
x1 = _long(s, 16)
y1 = _long(s, 20)
# get frame (in 0.01 millimeter units)
frame = dword(s, 24), dword(s, 28), dword(s, 32), dword(s, 36)
frame = _long(s, 24), _long(s, 28), _long(s, 32), _long(s, 36)
# normalize size to 72 dots per inch
size = x1 - x0, y1 - y0

View File

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

View File

@ -20,13 +20,13 @@
#
import re
from PIL import Image, ImageFile
from . import Image, ImageFile
__version__ = "0.6"
# XBM header
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"(?P<hotspot>"
b"#define[ \t]+[^_]*_x_hot[ \t]+(?P<xhot>[0-9]+)[\r\n]+"

View File

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

View File

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

View File

@ -28,26 +28,43 @@ else:
# Input, le = little endian, be = big endian
# TODO: replace with more readable struct.unpack equivalent
def i16le(c, o=0):
"""
Converts a 2-bytes (16 bits) string to an integer.
Converts a 2-bytes (16 bits) string to an unsigned integer.
c: string containing bytes to convert
o: offset of bytes to convert in string
"""
return unpack("<H", c[o:o+2])[0]
def si16le(c, o=0):
"""
Converts a 2-bytes (16 bits) string to a signed integer.
c: string containing bytes to convert
o: offset of bytes to convert in string
"""
return unpack("<h", c[o:o+2])[0]
def i32le(c, o=0):
"""
Converts a 4-bytes (32 bits) string to an integer.
Converts a 4-bytes (32 bits) string to an unsigned integer.
c: string containing bytes to convert
o: offset of bytes to convert in string
"""
return unpack("<I", c[o:o+4])[0]
def si32le(c, o=0):
"""
Converts a 4-bytes (32 bits) string to a signed integer.
c: string containing bytes to convert
o: offset of bytes to convert in string
"""
return unpack("<i", c[o:o+4])[0]
def i16be(c, o=0):
return unpack(">H", c[o:o+2])[0]

View File

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

View File

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

View File

@ -7,13 +7,8 @@ class TestJ2kEncodeOverflow(PillowTestCase):
im = Image.new('RGBA', (1024, 131584))
target = self.tempfile('temp.jpc')
try:
with self.assertRaises(IOError):
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__':
unittest.main()

View File

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

View File

@ -5,11 +5,22 @@ from __future__ import print_function
import sys
import tempfile
import os
import unittest
if sys.version_info[:2] <= (2, 6):
import unittest2 as unittest
else:
import unittest
from PIL import Image, ImageMath
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):
@ -49,7 +60,7 @@ class PillowTestCase(unittest.TestCase):
len(a), len(b),
msg or "got length %s, expected %s" % (len(a), len(b)))
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))
except:
self.assertEqual(a, b, msg)
@ -84,14 +95,13 @@ class PillowTestCase(unittest.TestCase):
a.size, b.size,
msg or "got size %r, expected %r" % (a.size, b.size))
a, b = convert_to_comparable(a, b)
diff = 0
try:
ord(b'0')
for abyte, bbyte in zip(a.tobytes(), b.tobytes()):
diff += abs(ord(abyte)-ord(bbyte))
except:
for abyte, bbyte in zip(a.tobytes(), b.tobytes()):
diff += abs(abyte-bbyte)
for ach, bch in zip(a.split(), b.split()):
chdiff = ImageMath.eval("abs(a - b)", a=ach, b=bch).convert('L')
diff += sum(i * num for i, num in enumerate(chdiff.histogram()))
ave_diff = float(diff)/(a.size[0]*a.size[1])
self.assertGreaterEqual(
epsilon, ave_diff,
@ -139,9 +149,13 @@ class PillowTestCase(unittest.TestCase):
if skip:
self.skipTest(msg or "Known Bad Test")
def shortDescription(self):
# Prevents `nose -v` printing docstrings
return None
def tempfile(self, template):
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)
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