Merge branch 'master' into tiff_metadata_encoding_fix

Resolved conflict in PIL/TiffImagePlugin.py
This commit is contained in:
Bogdan Kubala 2015-07-25 15:55:28 +02:00
commit e06c93c737
99 changed files with 1195 additions and 486 deletions

View File

@ -1,2 +1,3 @@
strictness: medium strictness: medium
test-warnings: yes test-warnings: yes
max-line-length: 80

View File

@ -20,6 +20,7 @@ install:
- "travis_retry sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-qt4 ghostscript libffi-dev libjpeg-turbo-progs cmake imagemagick" - "travis_retry 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 cffi"
- "travis_retry pip install coverage nose" - "travis_retry pip install coverage nose"
- "travis_retry pip install check-manifest"
# Pyroma tests sometimes hang on PyPy; skip for PyPy # Pyroma tests sometimes hang on PyPy; skip for PyPy
- if [ $TRAVIS_PYTHON_VERSION != "pypy" ]; then travis_retry pip install pyroma; fi - if [ $TRAVIS_PYTHON_VERSION != "pypy" ]; then travis_retry pip install pyroma; fi
@ -38,6 +39,7 @@ script:
- coverage run --append --include=PIL/* selftest.py - coverage run --append --include=PIL/* selftest.py
- coverage run --append --include=PIL/* -m nose -vx Tests/test_*.py - coverage run --append --include=PIL/* -m nose -vx Tests/test_*.py
- check-manifest --ignore "depends/*"
after_success: after_success:
# gather the coverage data # gather the coverage data

View File

@ -1,8 +1,39 @@
Changelog (Pillow) Changelog (Pillow)
================== ==================
2.9.0 (Unreleased) 3.0.0 (Unreleased)
------------------ ------------------
- Treat MPO with unknown header as base JPEG file #1350
[hugovk, radarhere]
- Added various tests #1330, #1344
[radarhere]
- More ImageFont tests #1327
[hugovk]
- Use logging instead of print #1207
[anntzer]
2.9.0 (2015-07-01)
------------------
- Added test for GimpPaletteFile #1324
[radarhere]
- Fixed ValueError in Python 2.6 #1315 #1316
[cgohlke, radarhere]
- Fixed tox test script path #1308
[radarhere]
- Added width and height properties #1304
[radarhere]
- Update tiff and tk tcl 8.5 versions #1303
[radarhere, wiredfool]
- Add functions to convert: Image <-> QImage; Image <-> QPixmap #1217 - Add functions to convert: Image <-> QImage; Image <-> QPixmap #1217
[radarhere, rominf] [radarhere, rominf]

View File

@ -1,14 +1,16 @@
include *.c include *.c
include *.h include *.h
include *.in
include *.md include *.md
include *.py include *.py
include *.sh
include *.rst include *.rst
include *.sh
include *.txt include *.txt
include *.yaml include *.yaml
include *.yml
include .coveragerc include .coveragerc
include .gitattributes include .gitattributes
include .travis.yml
include LICENSE include LICENSE
include Makefile include Makefile
include tox.ini include tox.ini
@ -16,7 +18,6 @@ recursive-include PIL *.md
recursive-include Scripts *.py recursive-include Scripts *.py
recursive-include Scripts *.rst recursive-include Scripts *.rst
recursive-include Scripts *.sh recursive-include Scripts *.sh
recursive-include Scripts README.rst
recursive-include Tests *.bdf recursive-include Tests *.bdf
recursive-include Tests *.bin recursive-include Tests *.bin
recursive-include Tests *.bmp recursive-include Tests *.bmp
@ -28,16 +29,19 @@ recursive-include Tests *.eps
recursive-include Tests *.fli recursive-include Tests *.fli
recursive-include Tests *.ggr recursive-include Tests *.ggr
recursive-include Tests *.gif recursive-include Tests *.gif
recursive-include Tests *.gpl
recursive-include Tests *.gnuplot recursive-include Tests *.gnuplot
recursive-include Tests *.html recursive-include Tests *.html
recursive-include Tests *.icc recursive-include Tests *.icc
recursive-include Tests *.icns recursive-include Tests *.icns
recursive-include Tests *.ico recursive-include Tests *.ico
recursive-include Tests *.im
recursive-include Tests *.j2k recursive-include Tests *.j2k
recursive-include Tests *.jp2 recursive-include Tests *.jp2
recursive-include Tests *.jpg recursive-include Tests *.jpg
recursive-include Tests *.lut recursive-include Tests *.lut
recursive-include Tests *.mpo recursive-include Tests *.mpo
recursive-include Tests *.msp
recursive-include Tests *.pbm recursive-include Tests *.pbm
recursive-include Tests *.pcf recursive-include Tests *.pcf
recursive-include Tests *.pcx recursive-include Tests *.pcx
@ -59,8 +63,8 @@ recursive-include Tests *.tiff
recursive-include Tests *.ttf recursive-include Tests *.ttf
recursive-include Tests *.txt recursive-include Tests *.txt
recursive-include Tests *.webp recursive-include Tests *.webp
recursive-include Tests *.xbm
recursive-include Tests *.xpm recursive-include Tests *.xpm
recursive-include Tests *.msp
recursive-include Tk *.c recursive-include Tk *.c
recursive-include Tk *.rst recursive-include Tk *.rst
recursive-include depends *.rst recursive-include depends *.rst
@ -71,9 +75,13 @@ recursive-include docs *.html
recursive-include docs *.py recursive-include docs *.py
recursive-include docs *.rst recursive-include docs *.rst
recursive-include docs *.txt recursive-include docs *.txt
recursive-include docs BUILDME
recursive-include docs COPYING
recursive-include docs Guardfile
recursive-include docs Makefile recursive-include docs Makefile
recursive-include docs Guardfile
recursive-include docs COPYING
recursive-include docs BUILDME
recursive-include libImaging *.c recursive-include libImaging *.c
recursive-include libImaging *.h recursive-include libImaging *.h
recursive-include winbuild *.gitignore
recursive-include winbuild *.md
recursive-include winbuild *.opt
recursive-include winbuild *.py

View File

@ -1,5 +1,6 @@
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html # https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
.PHONY: clean coverage doc docserve help inplace install install-req release-test sdist test upload upload-test .PHONY: clean coverage doc docserve help inplace install install-req release-test sdist test upload upload-test
.DEFAULT_GOAL := release-test
clean: clean:
python setup.py clean python setup.py clean

View File

@ -103,7 +103,9 @@ class BmpImageFile(ImageFile.ImageFile):
file_info['pixels_per_meter'] = (i32(header_data[20:24]), i32(header_data[24:28])) file_info['pixels_per_meter'] = (i32(header_data[20:24]), i32(header_data[24:28]))
file_info['colors'] = i32(header_data[28:32]) file_info['colors'] = i32(header_data[28:32])
file_info['palette_padding'] = 4 file_info['palette_padding'] = 4
self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), file_info['pixels_per_meter'])) self.info["dpi"] = tuple(
map(lambda x: int(math.ceil(x / 39.3701)),
file_info['pixels_per_meter']))
if file_info['compression'] == self.BITFIELDS: if file_info['compression'] == self.BITFIELDS:
if len(header_data) >= 52: if len(header_data) >= 52:
for idx, mask in enumerate(['r_mask', 'g_mask', 'b_mask', 'a_mask']): for idx, mask in enumerate(['r_mask', 'g_mask', 'b_mask', 'a_mask']):

View File

@ -66,6 +66,10 @@ class DcxImageFile(PcxImageFile):
def n_frames(self): def n_frames(self):
return len(self._offset) return len(self._offset)
@property
def is_animated(self):
return len(self._offset) > 1
def seek(self, frame): def seek(self, frame):
if frame >= len(self._offset): if frame >= len(self._offset):
raise EOFError("attempt to seek outside DCX directory") raise EOFError("attempt to seek outside DCX directory")

View File

@ -90,6 +90,7 @@ class FliImageFile(ImageFile.ImageFile):
self.__fp = self.fp self.__fp = self.fp
self.__rewind = self.fp.tell() self.__rewind = self.fp.tell()
self._n_frames = None self._n_frames = None
self._is_animated = None
self.seek(0) self.seek(0)
def _palette(self, palette, shift): def _palette(self, palette, shift):
@ -122,13 +123,33 @@ class FliImageFile(ImageFile.ImageFile):
self.seek(current) self.seek(current)
return self._n_frames return self._n_frames
@property
def is_animated(self):
if self._is_animated is None:
current = self.tell()
try:
self.seek(1)
self._is_animated = True
except EOFError:
self._is_animated = False
self.seek(current)
return self._is_animated
def seek(self, frame): def seek(self, frame):
if frame == self.__frame: if frame == self.__frame:
return return
if frame < self.__frame: if frame < self.__frame:
self._seek(0) self._seek(0)
last_frame = self.__frame
for f in range(self.__frame + 1, frame + 1): for f in range(self.__frame + 1, frame + 1):
self._seek(f) try:
self._seek(f)
except EOFError:
self.seek(last_frame)
raise EOFError("no more images in FLI file")
def _seek(self, frame): def _seek(self, frame):
if frame == 0: if frame == 0:

View File

@ -24,7 +24,7 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import Image, ImageFile, ImagePalette, _binary from PIL import Image, ImageFile, ImagePalette, ImageChops, ImageSequence, _binary
__version__ = "0.9" __version__ = "0.9"
@ -88,6 +88,7 @@ class GifImageFile(ImageFile.ImageFile):
self.__fp = self.fp # FIXME: hack self.__fp = self.fp # FIXME: hack
self.__rewind = self.fp.tell() self.__rewind = self.fp.tell()
self._n_frames = None self._n_frames = None
self._is_animated = None
self._seek(0) # get ready to read first frame self._seek(0) # get ready to read first frame
@property @property
@ -102,13 +103,33 @@ class GifImageFile(ImageFile.ImageFile):
self.seek(current) self.seek(current)
return self._n_frames return self._n_frames
@property
def is_animated(self):
if self._is_animated is None:
current = self.tell()
try:
self.seek(1)
self._is_animated = True
except EOFError:
self._is_animated = False
self.seek(current)
return self._is_animated
def seek(self, frame): def seek(self, frame):
if frame == self.__frame: if frame == self.__frame:
return return
if frame < self.__frame: if frame < self.__frame:
self._seek(0) self._seek(0)
last_frame = self.__frame
for f in range(self.__frame + 1, frame + 1): for f in range(self.__frame + 1, frame + 1):
self._seek(f) try:
self._seek(f)
except EOFError:
self.seek(last_frame)
raise EOFError("no more images in GIF file")
def _seek(self, frame): def _seek(self, frame):
@ -241,7 +262,7 @@ class GifImageFile(ImageFile.ImageFile):
if not self.tile: if not self.tile:
# self.__fp = None # self.__fp = None
raise EOFError("no more images in GIF file") raise EOFError
self.mode = "L" self.mode = "L"
if self.palette: if self.palette:
@ -279,7 +300,22 @@ RAWMODE = {
} }
def _save(im, fp, filename): def _convert_mode(im):
# convert on the fly (EXPERIMENTAL -- I'm not sure PIL
# should automatically convert images on save...)
if Image.getmodebase(im.mode) == "RGB":
palette_size = 256
if im.palette:
palette_size = len(im.palette.getdata()[1]) // 3
return im.convert("P", palette=1, colors=palette_size)
return im.convert("L")
def _save_all(im, fp, filename):
_save(im, fp, filename, save_all=True)
def _save(im, fp, filename, save_all=False):
if _imaging_gif: if _imaging_gif:
# call external driver # call external driver
@ -292,15 +328,7 @@ def _save(im, fp, filename):
if im.mode in RAWMODE: if im.mode in RAWMODE:
im_out = im.copy() im_out = im.copy()
else: else:
# convert on the fly (EXPERIMENTAL -- I'm not sure PIL im_out = _convert_mode(im)
# should automatically convert images on save...)
if Image.getmodebase(im.mode) == "RGB":
palette_size = 256
if im.palette:
palette_size = len(im.palette.getdata()[1]) // 3
im_out = im.convert("P", palette=1, colors=palette_size)
else:
im_out = im.convert("L")
# header # header
try: try:
@ -309,23 +337,49 @@ def _save(im, fp, filename):
palette = None palette = None
im.encoderinfo["optimize"] = im.encoderinfo.get("optimize", True) im.encoderinfo["optimize"] = im.encoderinfo.get("optimize", True)
header, used_palette_colors = getheader(im_out, palette, im.encoderinfo) if save_all:
for s in header: previous = None
fp.write(s)
flags = 0 for im_frame in ImageSequence.Iterator(im):
im_frame = _convert_mode(im_frame)
if get_interlace(im): # To specify duration, add the time in milliseconds to getdata(),
flags = flags | 64 # e.g. getdata(im_frame, duration=1000)
if not previous:
# global header
for s in getheader(im_frame, palette, im.encoderinfo)[0] + getdata(im_frame):
fp.write(s)
else:
# delta frame
delta = ImageChops.subtract_modulo(im_frame, previous)
bbox = delta.getbbox()
# local image header if bbox:
get_local_header(fp, im, (0, 0), flags) # compress difference
for s in getdata(im_frame.crop(bbox), offset=bbox[:2]):
fp.write(s)
else:
# FIXME: what should we do in this case?
pass
previous = im_frame.copy()
else:
header = getheader(im_out, palette, im.encoderinfo)[0]
for s in header:
fp.write(s)
im_out.encoderconfig = (8, get_interlace(im)) flags = 0
ImageFile._save(im_out, fp, [("gif", (0, 0)+im.size, 0,
RAWMODE[im_out.mode])])
fp.write(b"\0") # end of image data if get_interlace(im):
flags = flags | 64
# local image header
_get_local_header(fp, im, (0, 0), flags)
im_out.encoderconfig = (8, get_interlace(im))
ImageFile._save(im_out, fp, [("gif", (0, 0)+im.size, 0,
RAWMODE[im_out.mode])])
fp.write(b"\0") # end of image data
fp.write(b";") # end of file fp.write(b";") # end of file
@ -348,7 +402,7 @@ def get_interlace(im):
return interlace return interlace
def get_local_header(fp, im, offset, flags): def _get_local_header(fp, im, offset, flags):
transparent_color_exists = False transparent_color_exists = False
try: try:
transparency = im.encoderinfo["transparency"] transparency = im.encoderinfo["transparency"]
@ -571,7 +625,7 @@ def getdata(im, offset=(0, 0), **params):
im.encoderinfo = params im.encoderinfo = params
# local image header # local image header
get_local_header(fp, im, offset, 0) _get_local_header(fp, im, offset, 0)
ImageFile._save(im, fp, [("gif", (0, 0)+im.size, 0, RAWMODE[im.mode])]) ImageFile._save(im, fp, [("gif", (0, 0)+im.size, 0, RAWMODE[im.mode])])
@ -588,6 +642,7 @@ def getdata(im, offset=(0, 0), **params):
Image.register_open(GifImageFile.format, GifImageFile, _accept) Image.register_open(GifImageFile.format, GifImageFile, _accept)
Image.register_save(GifImageFile.format, _save) Image.register_save(GifImageFile.format, _save)
Image.register_save_all(GifImageFile.format, _save_all)
Image.register_extension(GifImageFile.format, ".gif") Image.register_extension(GifImageFile.format, ".gif")
Image.register_mime(GifImageFile.format, "image/gif") Image.register_mime(GifImageFile.format, "image/gif")

View File

@ -264,6 +264,10 @@ class ImImageFile(ImageFile.ImageFile):
def n_frames(self): def n_frames(self):
return self.info[FRAMES] return self.info[FRAMES]
@property
def is_animated(self):
return self.info[FRAMES] > 1
def seek(self, frame): def seek(self, frame):
if frame < 0 or frame >= self.info[FRAMES]: if frame < 0 or frame >= self.info[FRAMES]:

View File

@ -28,8 +28,11 @@ from __future__ import print_function
from PIL import VERSION, PILLOW_VERSION, _plugins from PIL import VERSION, PILLOW_VERSION, _plugins
import logging
import warnings import warnings
logger = logging.getLogger(__name__)
class DecompressionBombWarning(RuntimeWarning): class DecompressionBombWarning(RuntimeWarning):
pass pass
@ -138,11 +141,6 @@ def isImageType(t):
""" """
return hasattr(t, "im") return hasattr(t, "im")
#
# Debug level
DEBUG = 0
# #
# Constants (also defined in _imagingmodule.c!) # Constants (also defined in _imagingmodule.c!)
@ -204,6 +202,7 @@ ID = []
OPEN = {} OPEN = {}
MIME = {} MIME = {}
SAVE = {} SAVE = {}
SAVE_ALL = {}
EXTENSION = {} EXTENSION = {}
# -------------------------------------------------------------------- # --------------------------------------------------------------------
@ -386,13 +385,10 @@ def init():
for plugin in _plugins: for plugin in _plugins:
try: try:
if DEBUG: logger.debug("Importing %s", plugin)
print("Importing %s" % plugin)
__import__("PIL.%s" % plugin, globals(), locals(), []) __import__("PIL.%s" % plugin, globals(), locals(), [])
except ImportError: except ImportError as e:
if DEBUG: logger.debug("Image: failed to import %s: %s", plugin, e)
print("Image: failed to import", end=' ')
print(plugin, ":", sys.exc_info()[1])
if OPEN or SAVE: if OPEN or SAVE:
_initialized = 2 _initialized = 2
@ -504,6 +500,14 @@ class Image(object):
self.readonly = 0 self.readonly = 0
self.pyaccess = None self.pyaccess = None
@property
def width(self):
return self.size[0]
@property
def height(self):
return self.size[1]
def _new(self, im): def _new(self, im):
new = Image() new = Image()
new.im = im new.im = im
@ -546,8 +550,7 @@ class Image(object):
try: try:
self.fp.close() self.fp.close()
except Exception as msg: except Exception as msg:
if DEBUG: logger.debug("Error closing: %s" % msg)
print("Error closing: %s" % msg)
# Instead of simply setting to None, we're setting up a # Instead of simply setting to None, we're setting up a
# deferred error that will better explain that the core image # deferred error that will better explain that the core image
@ -1661,6 +1664,10 @@ class Image(object):
# may mutate self! # may mutate self!
self.load() self.load()
save_all = False
if 'save_all' in params:
save_all = params['save_all']
del params['save_all']
self.encoderinfo = params self.encoderinfo = params
self.encoderconfig = () self.encoderconfig = ()
@ -1669,20 +1676,16 @@ class Image(object):
ext = os.path.splitext(filename)[1].lower() ext = os.path.splitext(filename)[1].lower()
if not format: if not format:
try: if ext not in EXTENSION:
format = EXTENSION[ext]
except KeyError:
init() init()
try: format = EXTENSION[ext]
format = EXTENSION[ext]
except KeyError:
raise KeyError(ext) # unknown extension
try: if format.upper() not in SAVE:
save_handler = SAVE[format.upper()]
except KeyError:
init() init()
save_handler = SAVE[format.upper()] # unknown format if save_all:
save_handler = SAVE_ALL[format.upper()]
else:
save_handler = SAVE[format.upper()]
if isPath(fp): if isPath(fp):
fp = builtins.open(fp, "wb") fp = builtins.open(fp, "wb")
@ -2262,7 +2265,7 @@ def open(fp, mode="r"):
:py:meth:`~PIL.Image.Image.load` method). See :py:meth:`~PIL.Image.Image.load` method). See
:py:func:`~PIL.Image.new`. :py:func:`~PIL.Image.new`.
:param file: A filename (string) or a file object. The file object :param fp: A filename (string) or a file object. The file object
must implement :py:meth:`~file.read`, :py:meth:`~file.seek`, and must implement :py:meth:`~file.read`, :py:meth:`~file.seek`, and
:py:meth:`~file.tell` methods, and be opened in binary mode. :py:meth:`~file.tell` methods, and be opened in binary mode.
:param mode: The mode. If given, this argument must be "r". :param mode: The mode. If given, this argument must be "r".
@ -2298,9 +2301,7 @@ def open(fp, mode="r"):
_decompression_bomb_check(im.size) _decompression_bomb_check(im.size)
return im return im
except (SyntaxError, IndexError, TypeError, struct.error): except (SyntaxError, IndexError, TypeError, struct.error):
# import traceback logger.debug("", exc_info=True)
# traceback.print_exc()
pass
if init(): if init():
@ -2313,9 +2314,7 @@ def open(fp, mode="r"):
_decompression_bomb_check(im.size) _decompression_bomb_check(im.size)
return im return im
except (SyntaxError, IndexError, TypeError, struct.error): except (SyntaxError, IndexError, TypeError, struct.error):
# import traceback logger.debug("", exc_info=True)
# traceback.print_exc()
pass
raise IOError("cannot identify image file %r" raise IOError("cannot identify image file %r"
% (filename if filename else fp)) % (filename if filename else fp))
@ -2461,6 +2460,18 @@ def register_save(id, driver):
SAVE[id.upper()] = driver SAVE[id.upper()] = driver
def register_save_all(id, driver):
"""
Registers an image function to save all the frames
of a multiframe format. This function should not be
used in application code.
:param id: An image format identifier.
:param driver: A function to save images in this format.
"""
SAVE_ALL[id.upper()] = driver
def register_extension(id, extension): def register_extension(id, extension):
""" """
Registers an image extension. This function should not be Registers an image extension. This function should not be

View File

@ -30,10 +30,13 @@
from PIL import Image from PIL import Image
from PIL._util import isPath from PIL._util import isPath
import io import io
import logging
import os import os
import sys import sys
import traceback import traceback
logger = logging.getLogger(__name__)
MAXBLOCK = 65536 MAXBLOCK = 65536
SAFEBLOCK = 1024*1024 SAFEBLOCK = 1024*1024
@ -95,21 +98,11 @@ class ImageFile(Image.Image):
try: try:
self._open() self._open()
except IndexError as v: # end of data except (IndexError, # end of data
if Image.DEBUG > 1: TypeError, # end of data (ord)
traceback.print_exc() KeyError, # unsupported mode
raise SyntaxError(v) EOFError) as v: # got header but not the first frame
except TypeError as v: # end of data (ord) logger.exception("%s")
if Image.DEBUG > 1:
traceback.print_exc()
raise SyntaxError(v)
except KeyError as v: # unsupported mode
if Image.DEBUG > 1:
traceback.print_exc()
raise SyntaxError(v)
except EOFError as v: # got header but not the first frame
if Image.DEBUG > 1:
traceback.print_exc()
raise SyntaxError(v) raise SyntaxError(v)
if not self.mode or self.size[0] <= 0: if not self.mode or self.size[0] <= 0:

View File

@ -37,6 +37,7 @@ __version__ = "0.6"
import array import array
import struct import struct
import io import io
import warnings
from struct import unpack from struct import unpack
from PIL import Image, ImageFile, TiffImagePlugin, _binary from PIL import Image, ImageFile, TiffImagePlugin, _binary
from PIL.JpegPresets import presets from PIL.JpegPresets import presets
@ -713,8 +714,8 @@ def _save_cjpeg(im, fp, filename):
# Factory for making JPEG and MPO instances # Factory for making JPEG and MPO instances
def jpeg_factory(fp=None, filename=None): def jpeg_factory(fp=None, filename=None):
im = JpegImageFile(fp, filename) im = JpegImageFile(fp, filename)
mpheader = im._getmp()
try: try:
mpheader = im._getmp()
if mpheader[45057] > 1: if mpheader[45057] > 1:
# It's actually an MPO # It's actually an MPO
from .MpoImagePlugin import MpoImageFile from .MpoImagePlugin import MpoImageFile
@ -722,6 +723,10 @@ def jpeg_factory(fp=None, filename=None):
except (TypeError, IndexError): except (TypeError, IndexError):
# It is really a JPEG # It is really a JPEG
pass pass
except SyntaxError:
warnings.warn("Image appears to be a malformed MPO file, it will be "
"interpreted as a base JPEG file")
pass
return im return im

View File

@ -75,6 +75,10 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
def n_frames(self): def n_frames(self):
return len(self.images) return len(self.images)
@property
def is_animated(self):
return len(self.images) > 1
def seek(self, frame): def seek(self, frame):
try: try:

View File

@ -66,6 +66,10 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
def n_frames(self): def n_frames(self):
return self.__framecount return self.__framecount
@property
def is_animated(self):
return self.__framecount > 1
def seek(self, frame): def seek(self, frame):
if frame < 0 or frame >= self.__framecount: if frame < 0 or frame >= self.__framecount:
raise EOFError("no more images in MPO file") raise EOFError("no more images in MPO file")

View File

@ -27,8 +27,11 @@
from __future__ import print_function from __future__ import print_function
import logging
from PIL import Image, ImageFile, ImagePalette, _binary from PIL import Image, ImageFile, ImagePalette, _binary
logger = logging.getLogger(__name__)
i8 = _binary.i8 i8 = _binary.i8
i16 = _binary.i16le i16 = _binary.i16le
o8 = _binary.o8 o8 = _binary.o8
@ -59,17 +62,15 @@ class PcxImageFile(ImageFile.ImageFile):
bbox = i16(s, 4), i16(s, 6), i16(s, 8)+1, i16(s, 10)+1 bbox = i16(s, 4), i16(s, 6), i16(s, 8)+1, i16(s, 10)+1
if bbox[2] <= bbox[0] or bbox[3] <= bbox[1]: if bbox[2] <= bbox[0] or bbox[3] <= bbox[1]:
raise SyntaxError("bad PCX image size") raise SyntaxError("bad PCX image size")
if Image.DEBUG: logger.debug("BBox: %s %s %s %s", *bbox)
print("BBox: %s %s %s %s" % bbox)
# format # format
version = i8(s[1]) version = i8(s[1])
bits = i8(s[3]) bits = i8(s[3])
planes = i8(s[65]) planes = i8(s[65])
stride = i16(s, 66) stride = i16(s, 66)
if Image.DEBUG: logger.debug("PCX version %s, bits %s, planes %s, stride %s",
print("PCX version %s, bits %s, planes %s, stride %s" % version, bits, planes, stride)
(version, bits, planes, stride))
self.info["dpi"] = i16(s, 12), i16(s, 14) self.info["dpi"] = i16(s, 12), i16(s, 14)
@ -107,8 +108,7 @@ class PcxImageFile(ImageFile.ImageFile):
self.size = bbox[2]-bbox[0], bbox[3]-bbox[1] self.size = bbox[2]-bbox[0], bbox[3]-bbox[1]
bbox = (0, 0) + self.size bbox = (0, 0) + self.size
if Image.DEBUG: logger.debug("size: %sx%s", *self.size)
print("size: %sx%s" % self.size)
self.tile = [("pcx", bbox, self.fp.tell(), (rawmode, planes * stride))] self.tile = [("pcx", bbox, self.fp.tell(), (rawmode, planes * stride))]
@ -144,9 +144,8 @@ def _save(im, fp, filename, check=0):
# Ideally it should be passed in in the state, but the bytes value # Ideally it should be passed in in the state, but the bytes value
# gets overwritten. # gets overwritten.
if Image.DEBUG: logger.debug("PcxImagePlugin._save: xwidth: %d, bits: %d, stride: %d",
print("PcxImagePlugin._save: xwidth: %d, bits: %d, stride: %d" % ( im.size[0], bits, stride)
im.size[0], bits, stride))
# under windows, we could determine the current screen size with # under windows, we could determine the current screen size with
# "Image.core.display_mode()[1]", but I think that's overkill... # "Image.core.display_mode()[1]", but I think that's overkill...

View File

@ -35,10 +35,13 @@ from __future__ import print_function
__version__ = "0.9" __version__ = "0.9"
import logging
import re import re
import zlib
from PIL import Image, ImageFile, ImagePalette, _binary from PIL import Image, ImageFile, ImagePalette, _binary
import zlib
logger = logging.getLogger(__name__)
i8 = _binary.i8 i8 = _binary.i8
i16 = _binary.i16be i16 = _binary.i16be
@ -129,8 +132,7 @@ class ChunkStream(object):
def call(self, cid, pos, length): def call(self, cid, pos, length):
"Call the appropriate chunk handler" "Call the appropriate chunk handler"
if Image.DEBUG: logger.debug("STREAM %s %s %s", cid, pos, length)
print("STREAM", cid, pos, length)
return getattr(self, "chunk_" + cid.decode('ascii'))(pos, length) return getattr(self, "chunk_" + cid.decode('ascii'))(pos, length)
def crc(self, cid, data): def crc(self, cid, data):
@ -293,9 +295,8 @@ class PngStream(ChunkStream):
# Compression method 1 byte (0) # Compression method 1 byte (0)
# Compressed profile n bytes (zlib with deflate compression) # Compressed profile n bytes (zlib with deflate compression)
i = s.find(b"\0") i = s.find(b"\0")
if Image.DEBUG: logger.debug("iCCP profile name %s", s[:i])
print("iCCP profile name", s[:i]) logger.debug("Compression method %s", i8(s[i]))
print("Compression method", i8(s[i]))
comp_method = i8(s[i]) comp_method = i8(s[i])
if comp_method != 0: if comp_method != 0:
raise SyntaxError("Unknown compression method %s in iCCP chunk" % raise SyntaxError("Unknown compression method %s in iCCP chunk" %
@ -507,8 +508,7 @@ class PngImageFile(ImageFile.ImageFile):
except EOFError: except EOFError:
break break
except AttributeError: except AttributeError:
if Image.DEBUG: logger.debug("%s %s %s (unknown)", cid, pos, length)
print(cid, pos, length, "(unknown)")
s = ImageFile._safe_read(self.fp, length) s = ImageFile._safe_read(self.fp, length)
self.png.crc(cid, s) self.png.crc(cid, s)

View File

@ -136,6 +136,10 @@ class PsdImageFile(ImageFile.ImageFile):
def n_frames(self): def n_frames(self):
return len(self.layers) return len(self.layers)
@property
def is_animated(self):
return len(self.layers) > 1
def seek(self, layer): def seek(self, layer):
# seek to given layer (1..max) # seek to given layer (1..max)
if layer == self.frame: if layer == self.frame:

View File

@ -22,10 +22,14 @@
from __future__ import print_function from __future__ import print_function
from cffi import FFI import logging
import sys import sys
DEBUG = 0 from cffi import FFI
logger = logging.getLogger(__name__)
defs = """ defs = """
struct Pixel_RGBA { struct Pixel_RGBA {
@ -50,8 +54,7 @@ class PyAccess(object):
self.xsize = vals['xsize'] self.xsize = vals['xsize']
self.ysize = vals['ysize'] self.ysize = vals['ysize']
if DEBUG: logger.debug("%s", vals)
print(vals)
self._post_init() self._post_init()
def _post_init(self): def _post_init(self):
@ -305,11 +308,9 @@ else:
def new(img, readonly=False): def new(img, readonly=False):
access_type = mode_map.get(img.mode, None) access_type = mode_map.get(img.mode, None)
if not access_type: if not access_type:
if DEBUG: logger.debug("PyAccess Not Implemented: %s", img.mode)
print("PyAccess Not Implemented: %s" % img.mode)
return None return None
if DEBUG: logger.debug("New PyAccess: %s", img.mode)
print("New PyAccess: %s" % img.mode)
return access_type(img, readonly) return access_type(img, readonly)
# End of file # End of file

View File

@ -158,6 +158,10 @@ class SpiderImageFile(ImageFile.ImageFile):
def n_frames(self): def n_frames(self):
return self._nimages return self._nimages
@property
def is_animated(self):
return self._nimages > 1
# 1st image index is zero (although SPIDER imgnumber starts at 1) # 1st image index is zero (although SPIDER imgnumber starts at 1)
def tell(self): def tell(self):
if self.imgnumber < 1: if self.imgnumber < 1:

View File

@ -42,6 +42,7 @@
from __future__ import print_function from __future__ import print_function
__version__ = "1.3.5" __version__ = "1.3.5"
DEBUG = False # Needs to be merged with the new logging approach.
from PIL import Image, ImageFile from PIL import Image, ImageFile
from PIL import ImagePalette from PIL import ImagePalette
@ -434,7 +435,7 @@ class ImageFileDirectory(collections.MutableMapping):
tag, typ = i16(ifd), i16(ifd, 2) tag, typ = i16(ifd), i16(ifd, 2)
if Image.DEBUG: if DEBUG:
from PIL import TiffTags from PIL import TiffTags
tagname = TiffTags.TAGS.get(tag, "unknown") tagname = TiffTags.TAGS.get(tag, "unknown")
typname = TiffTags.TYPES.get(typ, "unknown") typname = TiffTags.TYPES.get(typ, "unknown")
@ -444,7 +445,7 @@ class ImageFileDirectory(collections.MutableMapping):
try: try:
dispatch = self.load_dispatch[typ] dispatch = self.load_dispatch[typ]
except KeyError: except KeyError:
if Image.DEBUG: if DEBUG:
print("- unsupported type", typ) print("- unsupported type", typ)
continue # ignore unsupported type continue # ignore unsupported type
@ -455,10 +456,10 @@ class ImageFileDirectory(collections.MutableMapping):
# Get and expand tag value # Get and expand tag value
if size > 4: if size > 4:
here = fp.tell() here = fp.tell()
if Image.DEBUG: if DEBUG:
print("Tag Location: %s" % here) print("Tag Location: %s" % here)
fp.seek(i32(ifd, 8)) fp.seek(i32(ifd, 8))
if Image.DEBUG: if DEBUG:
print("Data Location: %s" % fp.tell()) print("Data Location: %s" % fp.tell())
data = ImageFile._safe_read(fp, size) data = ImageFile._safe_read(fp, size)
fp.seek(here) fp.seek(here)
@ -474,7 +475,7 @@ class ImageFileDirectory(collections.MutableMapping):
self.tagdata[tag] = data self.tagdata[tag] = data
self.tagtype[tag] = typ self.tagtype[tag] = typ
if Image.DEBUG: if DEBUG:
if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK,
ICCPROFILE, XMP): ICCPROFILE, XMP):
print("- value: <table: %d bytes>" % size) print("- value: <table: %d bytes>" % size)
@ -517,8 +518,8 @@ class ImageFileDirectory(collections.MutableMapping):
if tag in self.tagtype: if tag in self.tagtype:
typ = self.tagtype[tag] typ = self.tagtype[tag]
if Image.DEBUG: if DEBUG:
print("Tag %s, Type: %s, Value: %s" % (tag, typ, value)) print ("Tag %s, Type: %s, Value: %s" % (tag, typ, value))
if typ == 1: if typ == 1:
# byte data # byte data
@ -573,7 +574,7 @@ class ImageFileDirectory(collections.MutableMapping):
else: else:
data = b"".join(map(o32, value)) data = b"".join(map(o32, value))
if Image.DEBUG: if DEBUG:
from PIL import TiffTags from PIL import TiffTags
tagname = TiffTags.TAGS.get(tag, "unknown") tagname = TiffTags.TAGS.get(tag, "unknown")
typname = TiffTags.TYPES.get(typ, "unknown") typname = TiffTags.TYPES.get(typ, "unknown")
@ -610,7 +611,7 @@ class ImageFileDirectory(collections.MutableMapping):
# pass 2: write directory to file # pass 2: write directory to file
for tag, typ, count, value, data in directory: for tag, typ, count, value, data in directory:
if Image.DEBUG > 1: if DEBUG > 1:
print(tag, typ, count, repr(value), repr(data)) print(tag, typ, count, repr(value), repr(data))
fp.write(o16(tag) + o16(typ) + o32(count) + value) fp.write(o16(tag) + o16(typ) + o32(count) + value)
@ -652,11 +653,12 @@ class TiffImageFile(ImageFile.ImageFile):
self.__fp = self.fp self.__fp = self.fp
self._frame_pos = [] self._frame_pos = []
self._n_frames = None self._n_frames = None
self._is_animated = None
if Image.DEBUG: if DEBUG:
print("*** TiffImageFile._open ***") print ("*** TiffImageFile._open ***")
print("- __first:", self.__first) print ("- __first:", self.__first)
print("- ifh: ", ifh) print ("- ifh: ", ifh)
# and load the first frame # and load the first frame
self._seek(0) self._seek(0)
@ -673,6 +675,20 @@ class TiffImageFile(ImageFile.ImageFile):
self.seek(current) self.seek(current)
return self._n_frames return self._n_frames
@property
def is_animated(self):
if self._is_animated is None:
current = self.tell()
try:
self.seek(1)
self._is_animated = True
except EOFError:
self._is_animated = False
self.seek(current)
return self._is_animated
def seek(self, frame): def seek(self, frame):
"Select a given frame as current image" "Select a given frame as current image"
self._seek(max(frame, 0)) # Questionable backwards compatibility. self._seek(max(frame, 0)) # Questionable backwards compatibility.
@ -687,7 +703,7 @@ class TiffImageFile(ImageFile.ImageFile):
while len(self._frame_pos) <= frame: while len(self._frame_pos) <= frame:
if not self.__next: if not self.__next:
raise EOFError("no more images in TIFF file") raise EOFError("no more images in TIFF file")
if Image.DEBUG: if DEBUG:
print("Seeking to frame %s, on frame %s, __next %s, location: %s" % print("Seeking to frame %s, on frame %s, __next %s, location: %s" %
(frame, self.__frame, self.__next, self.fp.tell())) (frame, self.__frame, self.__next, self.fp.tell()))
# reset python3 buffered io handle in case fp # reset python3 buffered io handle in case fp
@ -695,7 +711,7 @@ class TiffImageFile(ImageFile.ImageFile):
self.fp.tell() self.fp.tell()
self.fp.seek(self.__next) self.fp.seek(self.__next)
self._frame_pos.append(self.__next) self._frame_pos.append(self.__next)
if Image.DEBUG: if DEBUG:
print("Loading tags, location: %s" % self.fp.tell()) print("Loading tags, location: %s" % self.fp.tell())
self.tag.load(self.fp) self.tag.load(self.fp)
self.__next = self.tag.next self.__next = self.tag.next
@ -773,20 +789,20 @@ class TiffImageFile(ImageFile.ImageFile):
# Rearranging for supporting byteio items, since they have a fileno # Rearranging for supporting byteio items, since they have a fileno
# that returns an IOError if there's no underlying fp. Easier to # that returns an IOError if there's no underlying fp. Easier to
# deal with here by reordering. # deal with here by reordering.
if Image.DEBUG: if DEBUG:
print("have getvalue. just sending in a string from getvalue") print ("have getvalue. just sending in a string from getvalue")
n, err = decoder.decode(self.fp.getvalue()) n, err = decoder.decode(self.fp.getvalue())
elif hasattr(self.fp, "fileno"): elif hasattr(self.fp, "fileno"):
# we've got a actual file on disk, pass in the fp. # we've got a actual file on disk, pass in the fp.
if Image.DEBUG: if DEBUG:
print("have fileno, calling fileno version of the decoder.") print ("have fileno, calling fileno version of the decoder.")
self.fp.seek(0) self.fp.seek(0)
# 4 bytes, otherwise the trace might error out # 4 bytes, otherwise the trace might error out
n, err = decoder.decode(b"fpfp") n, err = decoder.decode(b"fpfp")
else: else:
# we have something else. # we have something else.
if Image.DEBUG: if DEBUG:
print("don't have fileno or getvalue. just reading") print ("don't have fileno or getvalue. just reading")
# UNDONE -- so much for that buffer size thing. # UNDONE -- so much for that buffer size thing.
n, err = decoder.decode(self.fp.read()) n, err = decoder.decode(self.fp.read())
@ -823,7 +839,7 @@ class TiffImageFile(ImageFile.ImageFile):
fillorder = getscalar(FILLORDER, 1) fillorder = getscalar(FILLORDER, 1)
if Image.DEBUG: if DEBUG:
print("*** Summary ***") print("*** Summary ***")
print("- compression:", self._compression) print("- compression:", self._compression)
print("- photometric_interpretation:", photo) print("- photometric_interpretation:", photo)
@ -835,7 +851,7 @@ class TiffImageFile(ImageFile.ImageFile):
ysize = getscalar(IMAGELENGTH) ysize = getscalar(IMAGELENGTH)
self.size = xsize, ysize self.size = xsize, ysize
if Image.DEBUG: if DEBUG:
print("- size:", self.size) print("- size:", self.size)
format = getscalar(SAMPLEFORMAT, 1) format = getscalar(SAMPLEFORMAT, 1)
@ -846,16 +862,16 @@ class TiffImageFile(ImageFile.ImageFile):
self.tag.get(BITSPERSAMPLE, (1,)), self.tag.get(BITSPERSAMPLE, (1,)),
self.tag.get(EXTRASAMPLES, ()) self.tag.get(EXTRASAMPLES, ())
) )
if Image.DEBUG: if DEBUG:
print("format key:", key) print("format key:", key)
try: try:
self.mode, rawmode = OPEN_INFO[key] self.mode, rawmode = OPEN_INFO[key]
except KeyError: except KeyError:
if Image.DEBUG: if DEBUG:
print("- unsupported format") print("- unsupported format")
raise SyntaxError("unknown pixel mode") raise SyntaxError("unknown pixel mode")
if Image.DEBUG: if DEBUG:
print("- raw mode:", rawmode) print("- raw mode:", rawmode)
print("- pil mode:", self.mode) print("- pil mode:", self.mode)
@ -895,7 +911,7 @@ class TiffImageFile(ImageFile.ImageFile):
"tiff_sgilog", "tiff_sgilog",
"tiff_sgilog24", "tiff_sgilog24",
"tiff_raw_16"]: "tiff_raw_16"]:
# if Image.DEBUG: # if DEBUG:
# print "Activating g4 compression for whole file" # print "Activating g4 compression for whole file"
# Decoder expects entire file as one tile. # Decoder expects entire file as one tile.
@ -936,7 +952,7 @@ class TiffImageFile(ImageFile.ImageFile):
self.tag.get(BITSPERSAMPLE, (1,)), self.tag.get(BITSPERSAMPLE, (1,)),
self.tag.get(EXTRASAMPLES, ()) self.tag.get(EXTRASAMPLES, ())
) )
if Image.DEBUG: if DEBUG:
print("format key:", key) print("format key:", key)
# this should always work, since all the # this should always work, since all the
# fillorder==2 modes have a corresponding # fillorder==2 modes have a corresponding
@ -965,8 +981,8 @@ class TiffImageFile(ImageFile.ImageFile):
(self._compression, (self._compression,
(0, min(y, ysize), w, min(y+h, ysize)), (0, min(y, ysize), w, min(y+h, ysize)),
offsets[i], a)) offsets[i], a))
if Image.DEBUG: if DEBUG:
print("tiles: ", self.tile) print ("tiles: ", self.tile)
y = y + h y = y + h
if y >= self.size[1]: if y >= self.size[1]:
x = y = 0 x = y = 0
@ -994,7 +1010,7 @@ class TiffImageFile(ImageFile.ImageFile):
l += 1 l += 1
a = None a = None
else: else:
if Image.DEBUG: if DEBUG:
print("- unsupported data organization") print("- unsupported data organization")
raise SyntaxError("unknown data organization") raise SyntaxError("unknown data organization")
@ -1075,7 +1091,7 @@ def _save(im, fp, filename):
# write any arbitrary tags passed in as an ImageFileDirectory # write any arbitrary tags passed in as an ImageFileDirectory
info = im.encoderinfo.get("tiffinfo", {}) info = im.encoderinfo.get("tiffinfo", {})
if Image.DEBUG: if DEBUG:
print("Tiffinfo Keys: %s" % info.keys) print("Tiffinfo Keys: %s" % info.keys)
keys = list(info.keys()) keys = list(info.keys())
for key in keys: for key in keys:
@ -1150,9 +1166,9 @@ def _save(im, fp, filename):
ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression, 1) ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression, 1)
if libtiff: if libtiff:
if Image.DEBUG: if DEBUG:
print("Saving using libtiff encoder") print ("Saving using libtiff encoder")
print(ifd.items()) print (ifd.items())
_fp = 0 _fp = 0
if hasattr(fp, "fileno"): if hasattr(fp, "fileno"):
try: try:
@ -1208,8 +1224,8 @@ def _save(im, fp, filename):
# int or similar # int or similar
atts[k] = v atts[k] = v
if Image.DEBUG: if DEBUG:
print(atts) print (atts)
# libtiff always expects the bytes in native order. # libtiff always expects the bytes in native order.
# we're storing image byte order. So, if the rawmode # we're storing image byte order. So, if the rawmode

View File

@ -12,7 +12,7 @@
# ;-) # ;-)
VERSION = '1.1.7' # PIL version VERSION = '1.1.7' # PIL version
PILLOW_VERSION = '2.9.0.dev0' # Pillow PILLOW_VERSION = '3.0.0.dev0' # Pillow
_plugins = ['BmpImagePlugin', _plugins = ['BmpImagePlugin',
'BufrStubImagePlugin', 'BufrStubImagePlugin',

View File

@ -26,7 +26,7 @@ Pillow is the "friendly PIL fork" by `Alex Clark and Contributors <https://githu
:target: https://pypi.python.org/pypi/Pillow/ :target: https://pypi.python.org/pypi/Pillow/
:alt: Number of PyPI downloads :alt: Number of PyPI downloads
.. image:: https://coveralls.io/repos/python-pillow/Pillow/badge.svg?branch=master .. image:: https://coveralls.io/repos/python-pillow/Pillow/badge.svg?branch=master&service=github
:target: https://coveralls.io/r/python-pillow/Pillow?branch=master :target: https://coveralls.io/r/python-pillow/Pillow?branch=master
:alt: Code coverage :alt: Code coverage

View File

@ -7,9 +7,9 @@ Released quarterly on the first day of January, April, July, October.
* [ ] Open a release ticket e.g. https://github.com/python-pillow/Pillow/issues/1174 * [ ] Open a release ticket e.g. https://github.com/python-pillow/Pillow/issues/1174
* [ ] Develop and prepare release in ``master`` branch. * [ ] Develop and prepare release in ``master`` branch.
* [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in ``master`` branch. * [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in ``master`` branch.
* [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in: * [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in:
``` ```
PIL/__init__.py setup.py _imaging.c PIL/__init__.py setup.py _imaging.c appveyor.yml
``` ```
* [ ] Update `CHANGES.rst`. * [ ] Update `CHANGES.rst`.
* [ ] Run pre-release check via `make pre`. * [ ] Run pre-release check via `make pre`.
@ -32,14 +32,14 @@ Released quarterly on the first day of January, April, July, October.
Released as needed for security, installation or critical bug fixes. Released as needed for security, installation or critical bug fixes.
* [ ] Make necessary changes in ``master`` branch. * [ ] Make necessary changes in ``master`` branch.
* [ ] Update `CHANGES.rst`. * [ ] Update `CHANGES.rst`.
* [ ] Cherry pick individual commits from ``master`` branch to release branch e.g. ``2.9.x``. * [ ] Cherry pick individual commits from ``master`` branch to release branch e.g. ``2.9.x``.
* [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in release branch e.g. ``2.9.x``. * [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in release branch e.g. ``2.9.x``.
* [ ] Checkout release branch e.g.: * [ ] Checkout release branch e.g.:
``` ```
git checkout -t remotes/origin/2.9.x git checkout -t remotes/origin/2.9.x
``` ```
* [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in: * [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in:
``` ```
PIL/__init__.py setup.py _imaging.c PIL/__init__.py setup.py _imaging.c
``` ```

View File

@ -14,104 +14,9 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
#
# For special purposes, you can import this module and call
# the makedelta or compress functions yourself. For example,
# if you have an application that generates a sequence of
# images, you can convert it to a GIF animation using some-
# thing like the following code:
#
# import Image
# import gifmaker
#
# sequence = []
#
# # generate sequence
# for i in range(100):
# im = <generate image i>
# sequence.append(im)
#
# # write GIF animation
# fp = open("out.gif", "wb")
# gifmaker.makedelta(fp, sequence)
# fp.close()
#
# Alternatively, use an iterator to generate the sequence, and
# write data directly to a socket. Or something...
#
from __future__ import print_function from __future__ import print_function
from PIL import Image, ImageChops, ImageSequence from PIL import Image
from PIL.GifImagePlugin import getheader, getdata
# --------------------------------------------------------------------
# straightforward delta encoding
def makedelta(fp, sequence):
"""Convert list of image frames to a GIF animation file"""
frames = 0
previous = None
for im in sequence:
# To specify duration, add the time in milliseconds to getdata(),
# e.g. getdata(im, duration=1000)
if not previous:
# global header
for s in getheader(im)[0] + getdata(im):
fp.write(s)
else:
# delta frame
delta = ImageChops.subtract_modulo(im, previous)
bbox = delta.getbbox()
if bbox:
# compress difference
for s in getdata(im.crop(bbox), offset=bbox[:2]):
fp.write(s)
else:
# FIXME: what should we do in this case?
pass
previous = im.copy()
frames += 1
fp.write(b";")
return frames
# --------------------------------------------------------------------
# main hack
def compress(infile, outfile):
# open input image, and force loading of first frame
im = Image.open(infile)
im.load()
# open output file
fp = open(outfile, "wb")
seq = ImageSequence.Iterator(im)
makedelta(fp, seq)
fp.close()
if __name__ == "__main__": if __name__ == "__main__":
@ -122,4 +27,5 @@ if __name__ == "__main__":
print("Usage: gifmaker infile outfile") print("Usage: gifmaker infile outfile")
sys.exit(1) sys.exit(1)
compress(sys.argv[1], sys.argv[2]) im = Image.open(sys.argv[1])
im.save(sys.argv[2], save_all=True)

View File

@ -21,6 +21,7 @@ from __future__ import print_function
import getopt import getopt
import glob import glob
import logging
import sys import sys
from PIL import Image from PIL import Image
@ -42,6 +43,7 @@ except getopt.error as v:
sys.exit(1) sys.exit(1)
verbose = quiet = verify = 0 verbose = quiet = verify = 0
logging_level = "WARNING"
for o, a in opt: for o, a in opt:
if o == "-f": if o == "-f":
@ -58,7 +60,9 @@ for o, a in opt:
elif o == "-v": elif o == "-v":
verify = 1 verify = 1
elif o == "-D": elif o == "-D":
Image.DEBUG += 1 logging_level = "DEBUG"
logging.basicConfig(level=logging_level)
def globfix(files): def globfix(files):

View File

@ -15,9 +15,6 @@ from PIL import Image, ImageTk
import sys import sys
Image.DEBUG = 0
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# an image animation player # an image animation player

View File

@ -3,7 +3,7 @@
from __future__ import division from __future__ import division
from helper import unittest, PillowTestCase from helper import unittest, PillowTestCase
import sys import sys
from PIL import Image, ImageFilter from PIL import Image
min_iterations = 100 min_iterations = 100
max_iterations = 10000 max_iterations = 10000
@ -31,7 +31,8 @@ class TestImagingLeaks(PillowTestCase):
def test_leak_putdata(self): def test_leak_putdata(self):
im = Image.new('RGB', (25, 25)) im = Image.new('RGB', (25, 25))
self._test_leak(min_iterations, max_iterations, im.putdata, im.getdata()) self._test_leak(min_iterations, max_iterations,
im.putdata, im.getdata())
def test_leak_getlist(self): def test_leak_getlist(self):
im = Image.new('P', (25, 25)) im = Image.new('P', (25, 25))

View File

@ -42,7 +42,8 @@ class TestPngDos(PillowTestCase):
total_len = 0 total_len = 0
for txt in im2.text.values(): for txt in im2.text.values():
total_len += len(txt) total_len += len(txt)
self.assertLess(total_len, 64*1024*1024, "Total text chunks greater than 64M") self.assertLess(total_len, 64*1024*1024,
"Total text chunks greater than 64M")
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -0,0 +1,12 @@
GIMP Palette
Name: badpaletteentry
Columns: 4
#
0 0 0 Index 3
65 38
103 62 49 Index 6
79 73 72 Index 7
114 101 97 Index 8
208 127 100 Index 9
151 144 142 Index 10
221 207 199 Index 11

View File

@ -0,0 +1,12 @@
GIMP Palette
Name: badpalettefile
Columns: 4
#
0 0 0 Index 3
01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
103 62 49 Index 6
79 73 72 Index 7
114 101 97 Index 8
208 127 100 Index 9
151 144 142 Index 10
221 207 199 Index 11

View File

@ -0,0 +1,12 @@
GIMP Palette
Name: custompalette
Columns: 4
#
0 0 0 Index 3
65 38 30 Index 4
103 62 49 Index 6
79 73 72 Index 7
114 101 97 Index 8
208 127 100 Index 9
151 144 142 Index 10
221 207 199 Index 11

BIN
Tests/images/hopper.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

4
Tests/images/test.gpl Normal file
View File

@ -0,0 +1,4 @@
GIMP Palette
Name: Test
Columns: 0
#

View File

@ -61,6 +61,10 @@ class TestCffi(PillowTestCase):
for y in range(0, h, 10): for y in range(0, h, 10):
self.assertEqual(access[(x, y)], caccess[(x, y)]) self.assertEqual(access[(x, y)], caccess[(x, y)])
# Access an out-of-range pixel
self.assertRaises(ValueError,
lambda: access[(access.xsize+1, access.ysize+1)])
def test_get_vs_c(self): def test_get_vs_c(self):
rgb = hopper('RGB') rgb = hopper('RGB')
rgb.load() rgb.load()
@ -70,7 +74,7 @@ class TestCffi(PillowTestCase):
self._test_get_access(hopper('LA')) self._test_get_access(hopper('LA'))
self._test_get_access(hopper('1')) self._test_get_access(hopper('1'))
self._test_get_access(hopper('P')) self._test_get_access(hopper('P'))
# self._test_get_access(hopper('PA')) # PA -- how do I make a PA image? # self._test_get_access(hopper('PA')) # PA -- how do I make a PA image?
self._test_get_access(hopper('F')) self._test_get_access(hopper('F'))
im = Image.new('I;16', (10, 10), 40000) im = Image.new('I;16', (10, 10), 40000)
@ -103,6 +107,14 @@ class TestCffi(PillowTestCase):
access[(x, y)] = color access[(x, y)] = color
self.assertEqual(color, caccess[(x, y)]) self.assertEqual(color, caccess[(x, y)])
# Attempt to set the value on a read-only image
access = PyAccess.new(im, True)
try:
access[(0, 0)] = color
except ValueError:
return
self.fail("Putpixel did not fail on a read-only image")
def test_set_vs_c(self): def test_set_vs_c(self):
rgb = hopper('RGB') rgb = hopper('RGB')
rgb.load() rgb.load()

View File

@ -1,6 +1,6 @@
from helper import unittest, PillowTestCase, hopper from helper import unittest, PillowTestCase, hopper
from PIL import Image from PIL import Image, BmpImagePlugin
import io import io
@ -25,6 +25,11 @@ class TestFileBmp(PillowTestCase):
self.roundtrip(hopper("P")) self.roundtrip(hopper("P"))
self.roundtrip(hopper("RGB")) self.roundtrip(hopper("RGB"))
def test_invalid_file(self):
with open("Tests/images/flower.jpg", "rb") as fp:
self.assertRaises(SyntaxError,
lambda: BmpImagePlugin.BmpImageFile(fp))
def test_save_to_bytes(self): def test_save_to_bytes(self):
output = io.BytesIO() output = io.BytesIO()
im = hopper() im = hopper()
@ -49,6 +54,22 @@ class TestFileBmp(PillowTestCase):
self.assertEqual(reloaded.info["dpi"], dpi) self.assertEqual(reloaded.info["dpi"], dpi)
def test_save_bmp_with_dpi(self):
# Test for #1301
# Arrange
outfile = self.tempfile("temp.jpg")
im = Image.open("Tests/images/hopper.bmp")
# Act
im.save(outfile, 'JPEG', dpi=im.info['dpi'])
# Assert
reloaded = Image.open(outfile)
reloaded.load()
self.assertEqual(im.info['dpi'], reloaded.info['dpi'])
self.assertEqual(im.size, reloaded.size)
self.assertEqual(reloaded.format, "JPEG")
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -0,0 +1,19 @@
from helper import unittest, PillowTestCase
from PIL import BufrStubImagePlugin
class TestFileBufrStub(PillowTestCase):
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda:
BufrStubImagePlugin.BufrStubImageFile(invalid_file))
if __name__ == '__main__':
unittest.main()
# End of file

View File

@ -20,6 +20,12 @@ class TestFileCur(PillowTestCase):
self.assertEqual(im.getpixel((11, 1)), (253, 254, 254, 1)) self.assertEqual(im.getpixel((11, 1)), (253, 254, 254, 1))
self.assertEqual(im.getpixel((16, 16)), (84, 87, 86, 255)) self.assertEqual(im.getpixel((16, 16)), (84, 87, 86, 255))
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda: CurImagePlugin.CurImageFile(invalid_file))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -20,6 +20,11 @@ class TestFileDcx(PillowTestCase):
orig = hopper() orig = hopper()
self.assert_image_equal(im, orig) self.assert_image_equal(im, orig)
def test_invalid_file(self):
with open("Tests/images/flower.jpg", "rb") as fp:
self.assertRaises(SyntaxError,
lambda: DcxImagePlugin.DcxImageFile(fp))
def test_tell(self): def test_tell(self):
# Arrange # Arrange
im = Image.open(TEST_FILE) im = Image.open(TEST_FILE)
@ -33,6 +38,19 @@ class TestFileDcx(PillowTestCase):
def test_n_frames(self): def test_n_frames(self):
im = Image.open(TEST_FILE) im = Image.open(TEST_FILE)
self.assertEqual(im.n_frames, 1) self.assertEqual(im.n_frames, 1)
self.assertFalse(im.is_animated)
def test_eoferror(self):
im = Image.open(TEST_FILE)
n_frames = im.n_frames
while True:
n_frames -= 1
try:
im.seek(n_frames)
break
except EOFError:
self.assertTrue(im.tell() < n_frames)
def test_seek_too_far(self): def test_seek_too_far(self):
# Arrange # Arrange

View File

@ -51,6 +51,12 @@ class TestFileEps(PillowTestCase):
self.assertEqual(image2_scale2.size, (720, 504)) self.assertEqual(image2_scale2.size, (720, 504))
self.assertEqual(image2_scale2.format, "EPS") self.assertEqual(image2_scale2.format, "EPS")
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda: EpsImagePlugin.EpsImageFile(invalid_file))
def test_file_object(self): def test_file_object(self):
# issue 479 # issue 479
image1 = Image.open(file1) image1 = Image.open(file1)
@ -149,7 +155,9 @@ class TestFileEps(PillowTestCase):
Image.open(file3) Image.open(file3)
def _test_readline(self, t, ending): def _test_readline(self, t, ending):
ending = "Failure with line ending: %s" % ("".join("%s" % ord(s) for s in ending)) ending = "Failure with line ending: %s" % ("".join(
"%s" % ord(s)
for s in ending))
self.assertEqual(t.readline().strip('\r\n'), 'something', ending) self.assertEqual(t.readline().strip('\r\n'), 'something', ending)
self.assertEqual(t.readline().strip('\r\n'), 'else', ending) self.assertEqual(t.readline().strip('\r\n'), 'else', ending)
self.assertEqual(t.readline().strip('\r\n'), 'baz', ending) self.assertEqual(t.readline().strip('\r\n'), 'baz', ending)

View File

@ -0,0 +1,19 @@
from helper import unittest, PillowTestCase
from PIL import FitsStubImagePlugin
class TestFileFitsStub(PillowTestCase):
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda:
FitsStubImagePlugin.FITSStubImageFile(invalid_file))
if __name__ == '__main__':
unittest.main()
# End of file

View File

@ -1,6 +1,6 @@
from helper import unittest, PillowTestCase from helper import unittest, PillowTestCase
from PIL import Image from PIL import Image, FliImagePlugin
# sample ppm stream # sample ppm stream
# created as an export of a palette image from Gimp2.6 # created as an export of a palette image from Gimp2.6
@ -18,9 +18,28 @@ class TestFileFli(PillowTestCase):
self.assertEqual(im.size, (128, 128)) self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "FLI") self.assertEqual(im.format, "FLI")
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda: FliImagePlugin.FliImageFile(invalid_file))
def test_n_frames(self): def test_n_frames(self):
im = Image.open(test_file) im = Image.open(test_file)
self.assertEqual(im.n_frames, 2) self.assertEqual(im.n_frames, 1)
self.assertFalse(im.is_animated)
def test_eoferror(self):
im = Image.open(test_file)
n_frames = im.n_frames
while True:
n_frames -= 1
try:
im.seek(n_frames)
break
except EOFError:
self.assertTrue(im.tell() < n_frames)
if __name__ == '__main__': if __name__ == '__main__':

18
Tests/test_file_fpx.py Normal file
View File

@ -0,0 +1,18 @@
from helper import unittest, PillowTestCase
from PIL import FpxImagePlugin
class TestFileFpx(PillowTestCase):
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda: FpxImagePlugin.FpxImageFile(invalid_file))
if __name__ == '__main__':
unittest.main()
# End of file

18
Tests/test_file_gbr.py Normal file
View File

@ -0,0 +1,18 @@
from helper import unittest, PillowTestCase
from PIL import GbrImagePlugin
class TestFileGbr(PillowTestCase):
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda: GbrImagePlugin.GbrImageFile(invalid_file))
if __name__ == '__main__':
unittest.main()
# End of file

View File

@ -25,6 +25,12 @@ class TestFileGif(PillowTestCase):
self.assertEqual(im.size, (128, 128)) self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "GIF") self.assertEqual(im.format, "GIF")
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda: GifImagePlugin.GifImageFile(invalid_file))
def test_optimize(self): def test_optimize(self):
from io import BytesIO from io import BytesIO
@ -71,6 +77,24 @@ class TestFileGif(PillowTestCase):
self.assert_image_similar(reread.convert('RGB'), hopper(), 50) self.assert_image_similar(reread.convert('RGB'), hopper(), 50)
def test_roundtrip_save_all(self):
# Single frame image
out = self.tempfile('temp.gif')
im = hopper()
im.save(out, save_all=True)
reread = Image.open(out)
self.assert_image_similar(reread.convert('RGB'), im, 50)
# Multiframe image
im = Image.open("Tests/images/dispose_bgnd.gif")
out = self.tempfile('temp.gif')
im.save(out, save_all=True)
reread = Image.open(out)
self.assertEqual(reread.n_frames, 5)
def test_palette_handling(self): def test_palette_handling(self):
# see https://github.com/python-pillow/Pillow/issues/513 # see https://github.com/python-pillow/Pillow/issues/513
@ -135,8 +159,25 @@ class TestFileGif(PillowTestCase):
self.assertEqual(framecount, 5) self.assertEqual(framecount, 5)
def test_n_frames(self): def test_n_frames(self):
im = Image.open(TEST_GIF)
self.assertEqual(im.n_frames, 1)
self.assertFalse(im.is_animated)
im = Image.open("Tests/images/iss634.gif") im = Image.open("Tests/images/iss634.gif")
self.assertEqual(im.n_frames, 43) self.assertEqual(im.n_frames, 42)
self.assertTrue(im.is_animated)
def test_eoferror(self):
im = Image.open(TEST_GIF)
n_frames = im.n_frames
while True:
n_frames -= 1
try:
im.seek(n_frames)
break
except EOFError:
self.assertTrue(im.tell() < n_frames)
def test_dispose_none(self): def test_dispose_none(self):
img = Image.open("Tests/images/dispose_none.gif") img = Image.open("Tests/images/dispose_none.gif")

View File

@ -0,0 +1,36 @@
from helper import unittest, PillowTestCase
from PIL.GimpPaletteFile import GimpPaletteFile
class TestImage(PillowTestCase):
def test_sanity(self):
with open('Tests/images/test.gpl', 'rb') as fp:
GimpPaletteFile(fp)
with open('Tests/images/hopper.jpg', 'rb') as fp:
self.assertRaises(SyntaxError, lambda: GimpPaletteFile(fp))
with open('Tests/images/bad_palette_file.gpl', 'rb') as fp:
self.assertRaises(SyntaxError, lambda: GimpPaletteFile(fp))
with open('Tests/images/bad_palette_entry.gpl', 'rb') as fp:
self.assertRaises(ValueError, lambda: GimpPaletteFile(fp))
def test_get_palette(self):
# Arrange
with open('Tests/images/custom_gimp_palette.gpl', 'rb') as fp:
palette_file = GimpPaletteFile(fp)
# Act
palette, mode = palette_file.getpalette()
# Assert
self.assertEqual(mode, "RGB")
if __name__ == '__main__':
unittest.main()
# End of file

View File

@ -0,0 +1,19 @@
from helper import unittest, PillowTestCase
from PIL import GribStubImagePlugin
class TestFileGribStub(PillowTestCase):
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda:
GribStubImagePlugin.GribStubImageFile(invalid_file))
if __name__ == '__main__':
unittest.main()
# End of file

View File

@ -0,0 +1,19 @@
from helper import unittest, PillowTestCase
from PIL import Hdf5StubImagePlugin
class TestFileHdf5Stub(PillowTestCase):
def test_invalid_file(self):
test_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda:
Hdf5StubImagePlugin.HDF5StubImageFile(test_file))
if __name__ == '__main__':
unittest.main()
# End of file

View File

@ -1,7 +1,7 @@
from helper import unittest, PillowTestCase, hopper from helper import unittest, PillowTestCase, hopper
import io import io
from PIL import Image from PIL import Image, IcoImagePlugin
# sample ppm stream # sample ppm stream
TEST_ICO_FILE = "Tests/images/hopper.ico" TEST_ICO_FILE = "Tests/images/hopper.ico"
@ -17,6 +17,11 @@ class TestFileIco(PillowTestCase):
self.assertEqual(im.size, (16, 16)) self.assertEqual(im.size, (16, 16))
self.assertEqual(im.format, "ICO") self.assertEqual(im.format, "ICO")
def test_invalid_file(self):
with open("Tests/images/flower.jpg", "rb") as fp:
self.assertRaises(SyntaxError,
lambda: IcoImagePlugin.IcoImageFile(fp))
def test_save_to_bytes(self): def test_save_to_bytes(self):
output = io.BytesIO() output = io.BytesIO()
im = hopper() im = hopper()
@ -30,7 +35,8 @@ class TestFileIco(PillowTestCase):
self.assertEqual(im.mode, reloaded.mode) self.assertEqual(im.mode, reloaded.mode)
self.assertEqual((64, 64), reloaded.size) self.assertEqual((64, 64), reloaded.size)
self.assertEqual(reloaded.format, "ICO") self.assertEqual(reloaded.format, "ICO")
self.assert_image_equal(reloaded, hopper().resize((64, 64), Image.LANCZOS)) self.assert_image_equal(reloaded,
hopper().resize((64, 64), Image.LANCZOS))
# the other one # the other one
output.seek(0) output.seek(0)
@ -40,7 +46,8 @@ class TestFileIco(PillowTestCase):
self.assertEqual(im.mode, reloaded.mode) self.assertEqual(im.mode, reloaded.mode)
self.assertEqual((32, 32), reloaded.size) self.assertEqual((32, 32), reloaded.size)
self.assertEqual(reloaded.format, "ICO") self.assertEqual(reloaded.format, "ICO")
self.assert_image_equal(reloaded, hopper().resize((32, 32), Image.LANCZOS)) self.assert_image_equal(reloaded,
hopper().resize((32, 32), Image.LANCZOS))
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -1,6 +1,6 @@
from helper import unittest, PillowTestCase, hopper from helper import unittest, PillowTestCase, hopper
from PIL import Image from PIL import Image, ImImagePlugin
# sample im # sample im
TEST_IM = "Tests/images/hopper.im" TEST_IM = "Tests/images/hopper.im"
@ -18,6 +18,19 @@ class TestFileIm(PillowTestCase):
def test_n_frames(self): def test_n_frames(self):
im = Image.open(TEST_IM) im = Image.open(TEST_IM)
self.assertEqual(im.n_frames, 1) self.assertEqual(im.n_frames, 1)
self.assertFalse(im.is_animated)
def test_eoferror(self):
im = Image.open(TEST_IM)
n_frames = im.n_frames
while True:
n_frames -= 1
try:
im.seek(n_frames)
break
except EOFError:
self.assertTrue(im.tell() < n_frames)
def test_roundtrip(self): def test_roundtrip(self):
out = self.tempfile('temp.im') out = self.tempfile('temp.im')
@ -27,6 +40,13 @@ class TestFileIm(PillowTestCase):
self.assert_image_equal(reread, im) self.assert_image_equal(reread, im)
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda: ImImagePlugin.ImImageFile(invalid_file))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -291,22 +291,24 @@ class TestFileJpeg(PillowTestCase):
# dict of qtable lists # dict of qtable lists
self.assert_image_similar(im, self.assert_image_similar(im,
self.roundtrip(im, self.roundtrip(im, qtables={
qtables={0: standard_l_qtable, 0: standard_l_qtable,
1: standard_chrominance_qtable}), 1: standard_chrominance_qtable
30) }), 30)
# not a sequence # not a sequence
self.assertRaises(Exception, lambda: self.roundtrip(im, qtables='a')) self.assertRaises(Exception, lambda: self.roundtrip(im, qtables='a'))
# sequence wrong length # sequence wrong length
self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[])) self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[]))
# sequence wrong length # sequence wrong length
self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[1, 2, 3, 4, 5])) self.assertRaises(Exception,
lambda: self.roundtrip(im, qtables=[1, 2, 3, 4, 5]))
# qtable entry not a sequence # qtable entry not a sequence
self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[1])) self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[1]))
# qtable entry has wrong number of items # qtable entry has wrong number of items
self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[[1, 2, 3, 4]])) self.assertRaises(Exception,
lambda: self.roundtrip(im, qtables=[[1, 2, 3, 4]]))
@unittest.skipUnless(djpeg_available(), "djpeg not available") @unittest.skipUnless(djpeg_available(), "djpeg not available")
def test_load_djpeg(self): def test_load_djpeg(self):
@ -337,7 +339,8 @@ class TestFileJpeg(PillowTestCase):
""" Generates a very hard to compress file """ Generates a very hard to compress file
:param size: tuple :param size: tuple
""" """
return Image.frombytes('RGB', size, os.urandom(size[0]*size[1] * 3)) return Image.frombytes('RGB',
size, os.urandom(size[0]*size[1] * 3))
im = gen_random_image((512, 512)) im = gen_random_image((512, 512))
f = self.tempfile("temp.jpeg") f = self.tempfile("temp.jpeg")
@ -350,6 +353,17 @@ class TestFileJpeg(PillowTestCase):
reloaded.save(f, quality='keep', progressive=True) reloaded.save(f, quality='keep', progressive=True)
reloaded.save(f, quality='keep', optimize=True) reloaded.save(f, quality='keep', optimize=True)
def test_bad_mpo_header(self):
""" Treat unknown MPO as JPEG """
# Arrange
# Act
# Shouldn't raise error
im = Image.open("Tests/images/sugarshack_bad_mpo_header.jpg")
# Assert
self.assertEqual(im.format, "JPEG")
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -1,6 +1,6 @@
from helper import unittest, PillowTestCase from helper import unittest, PillowTestCase
from PIL import Image from PIL import Image, Jpeg2KImagePlugin
from io import BytesIO from io import BytesIO
codecs = dir(Image.core) codecs = dir(Image.core)
@ -39,6 +39,13 @@ class TestFileJpeg2k(PillowTestCase):
self.assertEqual(im.size, (640, 480)) self.assertEqual(im.size, (640, 480))
self.assertEqual(im.format, 'JPEG2000') self.assertEqual(im.format, 'JPEG2000')
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda:
Jpeg2KImagePlugin.Jpeg2KImageFile(invalid_file))
def test_bytesio(self): def test_bytesio(self):
with open('Tests/images/test-card-lossless.jp2', 'rb') as f: with open('Tests/images/test-card-lossless.jp2', 'rb') as f:
data = BytesIO(f.read()) data = BytesIO(f.read())

View File

@ -1,11 +1,14 @@
from __future__ import print_function from __future__ import print_function
from helper import unittest, PillowTestCase, hopper, py3 from helper import unittest, PillowTestCase, hopper, py3
import os
import io import io
import logging
import os
from PIL import Image, TiffImagePlugin from PIL import Image, TiffImagePlugin
logger = logging.getLogger(__name__)
class LibTiffTestCase(PillowTestCase): class LibTiffTestCase(PillowTestCase):
@ -231,7 +234,6 @@ class TestFileLibTiff(LibTiffTestCase):
""" Are we generating the same interpretation """ Are we generating the same interpretation
of the image as Imagemagick is? """ of the image as Imagemagick is? """
TiffImagePlugin.READ_LIBTIFF = True TiffImagePlugin.READ_LIBTIFF = True
# Image.DEBUG = True
im = Image.open('Tests/images/12bit.cropped.tif') im = Image.open('Tests/images/12bit.cropped.tif')
im.load() im.load()
TiffImagePlugin.READ_LIBTIFF = False TiffImagePlugin.READ_LIBTIFF = False
@ -243,14 +245,8 @@ class TestFileLibTiff(LibTiffTestCase):
im2 = Image.open('Tests/images/12in16bit.tif') im2 = Image.open('Tests/images/12in16bit.tif')
if Image.DEBUG: logger.debug("%s", [img.getpixel((0, idx))
print(im.getpixel((0, 0))) for img in [im, im2] for idx in range(3)])
print(im.getpixel((0, 1)))
print(im.getpixel((0, 2)))
print(im2.getpixel((0, 0)))
print(im2.getpixel((0, 1)))
print(im2.getpixel((0, 2)))
self.assert_image_equal(im, im2) self.assert_image_equal(im, im2)

19
Tests/test_file_mcidas.py Normal file
View File

@ -0,0 +1,19 @@
from helper import unittest, PillowTestCase
from PIL import McIdasImagePlugin
class TestFileMcIdas(PillowTestCase):
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda:
McIdasImagePlugin.McIdasImageFile(invalid_file))
if __name__ == '__main__':
unittest.main()
# End of file

18
Tests/test_file_mic.py Normal file
View File

@ -0,0 +1,18 @@
from helper import unittest, PillowTestCase
from PIL import MicImagePlugin
class TestFileMic(PillowTestCase):
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda: MicImagePlugin.MicImageFile(invalid_file))
if __name__ == '__main__':
unittest.main()
# End of file

View File

@ -98,6 +98,19 @@ class TestFileMpo(PillowTestCase):
def test_n_frames(self): def test_n_frames(self):
im = Image.open("Tests/images/sugarshack.mpo") im = Image.open("Tests/images/sugarshack.mpo")
self.assertEqual(im.n_frames, 2) self.assertEqual(im.n_frames, 2)
self.assertTrue(im.is_animated)
def test_eoferror(self):
im = Image.open("Tests/images/sugarshack.mpo")
n_frames = im.n_frames
while True:
n_frames -= 1
try:
im.seek(n_frames)
break
except EOFError:
self.assertTrue(im.tell() < n_frames)
def test_image_grab(self): def test_image_grab(self):
for test_file in test_files: for test_file in test_files:

View File

@ -1,6 +1,6 @@
from helper import unittest, PillowTestCase, hopper from helper import unittest, PillowTestCase, hopper
from PIL import Image from PIL import Image, MspImagePlugin
TEST_FILE = "Tests/images/hopper.msp" TEST_FILE = "Tests/images/hopper.msp"
@ -18,6 +18,12 @@ class TestFileMsp(PillowTestCase):
self.assertEqual(im.size, (128, 128)) self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "MSP") self.assertEqual(im.format, "MSP")
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda: MspImagePlugin.MspImageFile(invalid_file))
def test_open(self): def test_open(self):
# Arrange # Arrange
# Act # Act

View File

@ -1,6 +1,6 @@
from helper import unittest, PillowTestCase, hopper from helper import unittest, PillowTestCase, hopper
from PIL import Image from PIL import Image, PcxImagePlugin
class TestFilePcx(PillowTestCase): class TestFilePcx(PillowTestCase):
@ -19,6 +19,12 @@ class TestFilePcx(PillowTestCase):
for mode in ('1', 'L', 'P', 'RGB'): for mode in ('1', 'L', 'P', 'RGB'):
self._roundtrip(hopper(mode)) self._roundtrip(hopper(mode))
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda: PcxImagePlugin.PcxImageFile(invalid_file))
def test_odd(self): def test_odd(self):
# see issue #523, odd sized images should have a stride that's even. # see issue #523, odd sized images should have a stride that's even.
# not that imagemagick or gimp write pcx that way. # not that imagemagick or gimp write pcx that way.

View File

@ -52,6 +52,12 @@ class TestFilePdf(PillowTestCase):
# Act / Assert # Act / Assert
self.helper_save_as_pdf(mode) self.helper_save_as_pdf(mode)
def test_unsupported_mode(self):
im = hopper("LA")
outfile = self.tempfile("temp_LA.pdf")
self.assertRaises(ValueError, lambda: im.save(outfile))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -81,6 +81,12 @@ class TestFilePng(PillowTestCase):
hopper("I").save(test_file) hopper("I").save(test_file)
im = Image.open(test_file) im = Image.open(test_file)
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda: PngImagePlugin.PngImageFile(invalid_file))
def test_broken(self): def test_broken(self):
# Check reading of totally broken files. In this case, the test # Check reading of totally broken files. In this case, the test
# file was checked into Subversion as a text file. # file was checked into Subversion as a text file.

View File

@ -1,6 +1,6 @@
from helper import unittest, PillowTestCase from helper import unittest, PillowTestCase
from PIL import Image from PIL import Image, PsdImagePlugin
# sample ppm stream # sample ppm stream
test_file = "Tests/images/hopper.psd" test_file = "Tests/images/hopper.psd"
@ -16,12 +16,33 @@ class TestImagePsd(PillowTestCase):
self.assertEqual(im.size, (128, 128)) self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "PSD") self.assertEqual(im.format, "PSD")
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda: PsdImagePlugin.PsdImageFile(invalid_file))
def test_n_frames(self): def test_n_frames(self):
im = Image.open("Tests/images/hopper_merged.psd") im = Image.open("Tests/images/hopper_merged.psd")
self.assertEqual(im.n_frames, 1) self.assertEqual(im.n_frames, 1)
self.assertFalse(im.is_animated)
im = Image.open(test_file) im = Image.open(test_file)
self.assertEqual(im.n_frames, 2) self.assertEqual(im.n_frames, 2)
self.assertTrue(im.is_animated)
def test_eoferror(self):
im = Image.open(test_file)
n_frames = im.n_frames
while True:
n_frames -= 1
try:
# PSD seek index starts at 1 rather than 0
im.seek(n_frames+1)
break
except EOFError:
self.assertTrue(im.tell() < n_frames)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -1,6 +1,6 @@
from helper import unittest, PillowTestCase from helper import unittest, PillowTestCase
from PIL import Image from PIL import Image, SgiImagePlugin
class TestFileSgi(PillowTestCase): class TestFileSgi(PillowTestCase):
@ -32,6 +32,13 @@ class TestFileSgi(PillowTestCase):
# Act / Assert # Act / Assert
self.assertRaises(ValueError, lambda: Image.open(test_file)) self.assertRaises(ValueError, lambda: Image.open(test_file))
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(ValueError,
lambda:
SgiImagePlugin.SgiImageFile(invalid_file))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -45,6 +45,7 @@ class TestImageSpider(PillowTestCase):
def test_n_frames(self): def test_n_frames(self):
im = Image.open(TEST_FILE) im = Image.open(TEST_FILE)
self.assertEqual(im.n_frames, 1) self.assertEqual(im.n_frames, 1)
self.assertFalse(im.is_animated)
def test_loadImageSeries(self): def test_loadImageSeries(self):
# Arrange # Arrange

View File

@ -1,6 +1,6 @@
from helper import unittest, PillowTestCase from helper import unittest, PillowTestCase
from PIL import Image from PIL import Image, SunImagePlugin
class TestFileSun(PillowTestCase): class TestFileSun(PillowTestCase):
@ -16,6 +16,10 @@ class TestFileSun(PillowTestCase):
# Assert # Assert
self.assertEqual(im.size, (128, 128)) self.assertEqual(im.size, (128, 128))
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda: SunImagePlugin.SunImageFile(invalid_file))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -15,6 +15,25 @@ class TestFileTga(PillowTestCase):
# Assert # Assert
self.assertEqual(im.size, (100, 100)) self.assertEqual(im.size, (100, 100))
def test_save(self):
test_file = "Tests/images/tga_id_field.tga"
im = Image.open(test_file)
test_file = self.tempfile("temp.tga")
# Save
im.save(test_file)
test_im = Image.open(test_file)
self.assertEqual(test_im.size, (100, 100))
# RGBA save
im.convert("RGBA").save(test_file)
test_im = Image.open(test_file)
self.assertEqual(test_im.size, (100, 100))
# Unsupported mode save
self.assertRaises(IOError, lambda: im.convert("LA").save(test_file))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -1,9 +1,12 @@
from __future__ import print_function from __future__ import print_function
import logging
import struct
from helper import unittest, PillowTestCase, hopper, py3 from helper import unittest, PillowTestCase, hopper, py3
from PIL import Image, TiffImagePlugin from PIL import Image, TiffImagePlugin
import struct logger = logging.getLogger(__name__)
class TestFileTiff(PillowTestCase): class TestFileTiff(PillowTestCase):
@ -79,11 +82,25 @@ class TestFileTiff(PillowTestCase):
im._setup() im._setup()
self.assertEqual(im.info['dpi'], (72., 72.)) self.assertEqual(im.info['dpi'], (72., 72.))
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda: TiffImagePlugin.TiffImageFile(invalid_file))
def test_bad_exif(self): def test_bad_exif(self):
try: try:
Image.open('Tests/images/hopper_bad_exif.jpg')._getexif() Image.open('Tests/images/hopper_bad_exif.jpg')._getexif()
except struct.error: except struct.error:
self.fail("Bad EXIF data should not pass incorrect values to _binary unpack") self.fail(
"Bad EXIF data passed incorrect values to _binary unpack")
def test_save_unsupported_mode(self):
im = hopper("HSV")
outfile = self.tempfile("temp.tif")
self.assertRaises(IOError, lambda: im.save(outfile))
def test_little_endian(self): def test_little_endian(self):
im = Image.open('Tests/images/16bit.cropped.tif') im = Image.open('Tests/images/16bit.cropped.tif')
@ -118,7 +135,6 @@ class TestFileTiff(PillowTestCase):
""" Are we generating the same interpretation """ Are we generating the same interpretation
of the image as Imagemagick is? """ of the image as Imagemagick is? """
# Image.DEBUG = True
im = Image.open('Tests/images/12bit.cropped.tif') im = Image.open('Tests/images/12bit.cropped.tif')
# to make the target -- # to make the target --
@ -129,14 +145,8 @@ class TestFileTiff(PillowTestCase):
im2 = Image.open('Tests/images/12in16bit.tif') im2 = Image.open('Tests/images/12in16bit.tif')
if Image.DEBUG: logger.debug("%s", [img.getpixel((0, idx))
print(im.getpixel((0, 0))) for img in [im, im2] for idx in range(3)])
print(im.getpixel((0, 1)))
print(im.getpixel((0, 2)))
print(im2.getpixel((0, 0)))
print(im2.getpixel((0, 1)))
print(im2.getpixel((0, 2)))
self.assert_image_equal(im, im2) self.assert_image_equal(im, im2)
@ -153,9 +163,23 @@ class TestFileTiff(PillowTestCase):
def test_n_frames(self): def test_n_frames(self):
im = Image.open('Tests/images/multipage-lastframe.tif') im = Image.open('Tests/images/multipage-lastframe.tif')
self.assertEqual(im.n_frames, 1) self.assertEqual(im.n_frames, 1)
self.assertFalse(im.is_animated)
im = Image.open('Tests/images/multipage.tiff') im = Image.open('Tests/images/multipage.tiff')
self.assertEqual(im.n_frames, 3) self.assertEqual(im.n_frames, 3)
self.assertTrue(im.is_animated)
def test_eoferror(self):
im = Image.open('Tests/images/multipage-lastframe.tif')
n_frames = im.n_frames
while True:
n_frames -= 1
try:
im.seek(n_frames)
break
except EOFError:
self.assertTrue(im.tell() < n_frames)
def test_multipage(self): def test_multipage(self):
# issue #862 # issue #862

View File

@ -72,6 +72,11 @@ class TestFileWebp(PillowTestCase):
target = hopper("RGB") target = hopper("RGB")
self.assert_image_similar(image, target, 12) self.assert_image_similar(image, target, 12)
def test_write_unsupported_mode(self):
temp_file = self.tempfile("temp.webp")
self.assertRaises(IOError, lambda: hopper("L").save(temp_file))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -83,7 +83,8 @@ class TestFileWebpAlpha(PillowTestCase):
image.load() image.load()
image.getdata() image.getdata()
# early versions of webp are known to produce higher deviations: deal with it # early versions of webp are known to produce higher deviations:
# deal with it
if _webp.WebPDecoderVersion(self) <= 0x201: if _webp.WebPDecoderVersion(self) <= 0x201:
self.assert_image_similar(image, pil_image, 3.0) self.assert_image_similar(image, pil_image, 3.0)
else: else:

View File

@ -1,6 +1,6 @@
from helper import unittest, PillowTestCase, hopper from helper import unittest, PillowTestCase, hopper
from PIL import Image from PIL import Image, XpmImagePlugin
# sample ppm stream # sample ppm stream
TEST_FILE = "Tests/images/hopper.xpm" TEST_FILE = "Tests/images/hopper.xpm"
@ -18,6 +18,12 @@ class TestFileXpm(PillowTestCase):
# large error due to quantization->44 colors. # large error due to quantization->44 colors.
self.assert_image_similar(im.convert('RGB'), hopper('RGB'), 60) self.assert_image_similar(im.convert('RGB'), hopper('RGB'), 60)
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda: XpmImagePlugin.XpmImageFile(invalid_file))
def test_load_read(self): def test_load_read(self):
# Arrange # Arrange
im = Image.open(TEST_FILE) im = Image.open(TEST_FILE)

View File

@ -15,6 +15,10 @@ class TestFontBdf(PillowTestCase):
self.assertIsInstance(font, FontFile.FontFile) self.assertIsInstance(font, FontFile.FontFile)
self.assertEqual(len([_f for _f in font.glyph if _f]), 190) self.assertEqual(len([_f for _f in font.glyph if _f]), 190)
def test_invalid_file(self):
with open("Tests/images/flower.jpg", "rb") as fp:
self.assertRaises(SyntaxError, lambda: BdfFontFile.BdfFontFile(fp))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -30,6 +30,10 @@ class TestFontPcf(PillowTestCase):
def test_sanity(self): def test_sanity(self):
self.save_font() self.save_font()
def test_invalid_file(self):
with open("Tests/images/flower.jpg", "rb") as fp:
self.assertRaises(SyntaxError, lambda: PcfFontFile.PcfFontFile(fp))
def xtest_draw(self): def xtest_draw(self):
tempname = self.save_font() tempname = self.save_font()

View File

@ -30,6 +30,15 @@ class TestImage(PillowTestCase):
# self.assertRaises( # self.assertRaises(
# MemoryError, lambda: Image.new("L", (1000000, 1000000))) # MemoryError, lambda: Image.new("L", (1000000, 1000000)))
def test_width_height(self):
im = Image.new("RGB", (1, 2))
self.assertEqual(im.width, 1)
self.assertEqual(im.height, 2)
im.size = (3, 4)
self.assertEqual(im.width, 3)
self.assertEqual(im.height, 4)
def test_invalid_image(self): def test_invalid_image(self):
if str is bytes: if str is bytes:
import StringIO import StringIO

View File

@ -88,6 +88,12 @@ class TestImageFilter(PillowTestCase):
self.assertEqual(rankfilter("I"), (0, 4, 8)) self.assertEqual(rankfilter("I"), (0, 4, 8))
self.assertEqual(rankfilter("F"), (0.0, 4.0, 8.0)) self.assertEqual(rankfilter("F"), (0.0, 4.0, 8.0))
def test_rankfilter_properties(self):
rankfilter = ImageFilter.RankFilter(1, 2)
self.assertEqual(rankfilter.size, 1)
self.assertEqual(rankfilter.rank, 2)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -21,12 +21,14 @@ class TestImageMode(PillowTestCase):
m = ImageMode.getmode("1") m = ImageMode.getmode("1")
self.assertEqual(m.mode, "1") self.assertEqual(m.mode, "1")
self.assertEqual(str(m), "1")
self.assertEqual(m.bands, ("1",)) self.assertEqual(m.bands, ("1",))
self.assertEqual(m.basemode, "L") self.assertEqual(m.basemode, "L")
self.assertEqual(m.basetype, "L") self.assertEqual(m.basetype, "L")
m = ImageMode.getmode("RGB") m = ImageMode.getmode("RGB")
self.assertEqual(m.mode, "RGB") self.assertEqual(m.mode, "RGB")
self.assertEqual(str(m), "RGB")
self.assertEqual(m.bands, ("R", "G", "B")) self.assertEqual(m.bands, ("R", "G", "B"))
self.assertEqual(m.basemode, "RGB") self.assertEqual(m.basemode, "RGB")
self.assertEqual(m.basetype, "L") self.assertEqual(m.basetype, "L")

View File

@ -19,7 +19,7 @@ class TestToQImage(PillowQtTestCase, PillowTestCase):
self.assertFalse(data.isNull()) self.assertFalse(data.isNull())
# Test saving the file # Test saving the file
tempfile = self.tempfile('temp_{}.png'.format(mode)) tempfile = self.tempfile('temp_{0}.png'.format(mode))
data.save(tempfile) data.save(tempfile)

View File

@ -19,7 +19,7 @@ class TestToQPixmap(PillowQPixmapTestCase, PillowTestCase):
self.assertFalse(data.isNull()) self.assertFalse(data.isNull())
# Test saving the file # Test saving the file
tempfile = self.tempfile('temp_{}.png'.format(mode)) tempfile = self.tempfile('temp_{0}.png'.format(mode))
data.save(tempfile) data.save(tempfile)

View File

@ -18,6 +18,9 @@ class TestImageColor(PillowTestCase):
(255, 0, 0, 0), ImageColor.getrgb("rgba(255, 0, 0, 0)")) (255, 0, 0, 0), ImageColor.getrgb("rgba(255, 0, 0, 0)"))
self.assertEqual((255, 0, 0), ImageColor.getrgb("red")) self.assertEqual((255, 0, 0), ImageColor.getrgb("red"))
self.assertRaises(ValueError,
lambda: ImageColor.getrgb("invalid color"))
# look for rounding errors (based on code by Tim Hatch) # look for rounding errors (based on code by Tim Hatch)
def test_rounding_errors(self): def test_rounding_errors(self):
@ -43,8 +46,8 @@ class TestImageColor(PillowTestCase):
self.assertEqual(0, ImageColor.getcolor("black", "L")) self.assertEqual(0, ImageColor.getcolor("black", "L"))
self.assertEqual(255, ImageColor.getcolor("white", "L")) self.assertEqual(255, ImageColor.getcolor("white", "L"))
self.assertEqual( self.assertEqual(162,
162, ImageColor.getcolor("rgba(0, 255, 115, 33)", "L")) ImageColor.getcolor("rgba(0, 255, 115, 33)", "L"))
Image.new("L", (1, 1), "white") Image.new("L", (1, 1), "white")
self.assertEqual(0, ImageColor.getcolor("black", "1")) self.assertEqual(0, ImageColor.getcolor("black", "1"))

View File

@ -52,6 +52,12 @@ class TestImageDraw(PillowTestCase):
self.assert_warning(DeprecationWarning, lambda: draw.setink(0)) self.assert_warning(DeprecationWarning, lambda: draw.setink(0))
self.assert_warning(DeprecationWarning, lambda: draw.setfill(0)) self.assert_warning(DeprecationWarning, lambda: draw.setfill(0))
def test_mode_mismatch(self):
im = hopper("RGB").copy()
self.assertRaises(ValueError,
lambda: ImageDraw.ImageDraw(im, mode="L"))
def helper_arc(self, bbox): def helper_arc(self, bbox):
# Arrange # Arrange
im = Image.new("RGB", (W, H)) im = Image.new("RGB", (W, H))

View File

@ -93,6 +93,9 @@ class TestImageFile(PillowTestCase):
self.assert_image_equal(im1, im2) self.assert_image_equal(im1, im2)
def test_raise_ioerror(self):
self.assertRaises(IOError, lambda: ImageFile.raise_ioerror(1))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -182,7 +182,10 @@ try:
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
# Act/Assert # Act/Assert
self.assertRaises(AssertionError, lambda: draw.multiline_text((0, 0), TEST_TEXT, font=ttf, align="unknown")) self.assertRaises(AssertionError,
lambda: draw.multiline_text((0, 0), TEST_TEXT,
font=ttf,
align="unknown"))
def test_multiline_size(self): def test_multiline_size(self):
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
@ -199,7 +202,8 @@ try:
draw = ImageDraw.Draw(im) draw = ImageDraw.Draw(im)
self.assertEqual(draw.textsize("longest line", font=ttf)[0], self.assertEqual(draw.textsize("longest line", font=ttf)[0],
draw.multiline_textsize("longest line\nline", font=ttf)[0]) draw.multiline_textsize("longest line\nline",
font=ttf)[0])
def test_multiline_spacing(self): def test_multiline_spacing(self):
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
@ -256,6 +260,34 @@ try:
# Check boxes a and b are same size # Check boxes a and b are same size
self.assertEqual(box_size_a, box_size_b) self.assertEqual(box_size_a, box_size_b)
def test_rotated_transposed_font_get_mask(self):
# Arrange
text = "mask this"
font = ImageFont.truetype(FONT_PATH, FONT_SIZE)
orientation = Image.ROTATE_90
transposed_font = ImageFont.TransposedFont(
font, orientation=orientation)
# Act
mask = transposed_font.getmask(text)
# Assert
self.assertEqual(mask.size, (13, 108))
def test_unrotated_transposed_font_get_mask(self):
# Arrange
text = "mask this"
font = ImageFont.truetype(FONT_PATH, FONT_SIZE)
orientation = None
transposed_font = ImageFont.TransposedFont(
font, orientation=orientation)
# Act
mask = transposed_font.getmask(text)
# Assert
self.assertEqual(mask.size, (108, 13))
def test_free_type_font_get_name(self): def test_free_type_font_get_name(self):
# Arrange # Arrange
font = ImageFont.truetype(FONT_PATH, FONT_SIZE) font = ImageFont.truetype(FONT_PATH, FONT_SIZE)
@ -278,6 +310,28 @@ try:
self.assertIsInstance(descent, int) self.assertIsInstance(descent, int)
self.assertEqual((ascent, descent), (16, 4)) # too exact check? self.assertEqual((ascent, descent), (16, 4)) # too exact check?
def test_free_type_font_get_offset(self):
# Arrange
font = ImageFont.truetype(FONT_PATH, FONT_SIZE)
text = "offset this"
# Act
offset = font.getoffset(text)
# Assert
self.assertEqual(offset, (0, 3))
def test_free_type_font_get_mask(self):
# Arrange
font = ImageFont.truetype(FONT_PATH, FONT_SIZE)
text = "mask this"
# Act
mask = font.getmask(text)
# Assert
self.assertEqual(mask.size, (108, 13))
def test_load_path_not_found(self): def test_load_path_not_found(self):
# Arrange # Arrange
filename = "somefilenamethatdoesntexist.ttf" filename = "somefilenamethatdoesntexist.ttf"

View File

@ -5,16 +5,19 @@ from PIL import Image, ImageFont, ImageDraw
class TestImageFontBitmap(PillowTestCase): class TestImageFontBitmap(PillowTestCase):
def test_similar(self): def test_similar(self):
text = 'EmbeddedBitmap' text = 'EmbeddedBitmap'
font_outline = ImageFont.truetype(font='Tests/fonts/DejaVuSans.ttf', size=24) font_outline = ImageFont.truetype(
font_bitmap = ImageFont.truetype(font='Tests/fonts/DejaVuSans-bitmap.ttf', size=24) font='Tests/fonts/DejaVuSans.ttf', size=24)
font_bitmap = ImageFont.truetype(
font='Tests/fonts/DejaVuSans-bitmap.ttf', size=24)
size_outline, size_bitmap = font_outline.getsize(text), font_bitmap.getsize(text) size_outline, size_bitmap = font_outline.getsize(text), font_bitmap.getsize(text)
size_final = max(size_outline[0], size_bitmap[0]), max(size_outline[1], size_bitmap[1]) size_final = max(size_outline[0], size_bitmap[0]), max(size_outline[1], size_bitmap[1])
im_bitmap = Image.new('RGB', size_final, (255, 255, 255)) im_bitmap = Image.new('RGB', size_final, (255, 255, 255))
im_outline = im_bitmap.copy() im_outline = im_bitmap.copy()
draw_bitmap, draw_outline = ImageDraw.Draw(im_bitmap), ImageDraw.Draw(im_outline) draw_bitmap, draw_outline = ImageDraw.Draw(im_bitmap), ImageDraw.Draw(im_outline)
# Metrics are different on the bitmap and ttf fonts, more so on some platforms # Metrics are different on the bitmap and ttf fonts,
# and versions of freetype than others. Mac has a 1px difference, linux doesn't. # more so on some platforms and versions of freetype than others.
# Mac has a 1px difference, linux doesn't.
draw_bitmap.text((0, size_final[1] - size_bitmap[1]), draw_bitmap.text((0, size_final[1] - size_bitmap[1]),
text, fill=(0, 0, 0), font=font_bitmap) text, fill=(0, 0, 0), font=font_bitmap)
draw_outline.text((0, size_final[1] - size_outline[1]), draw_outline.text((0, size_final[1] - size_outline[1]),

View File

@ -75,7 +75,9 @@ class TestImageOpsUsm(PillowTestCase):
(4, 3, 2), (4, 2, 2)]: (4, 3, 2), (4, 2, 2)]:
self.assertGreaterEqual(i.im.getpixel((x, y))[c], 250) self.assertGreaterEqual(i.im.getpixel((x, y))[c], 250)
# Fuzzy match. # Fuzzy match.
gp = lambda x, y: i.im.getpixel((x, y))
def gp(x, y):
return i.im.getpixel((x, y))
self.assertTrue(236 <= gp(7, 4)[0] <= 239) self.assertTrue(236 <= gp(7, 4)[0] <= 239)
self.assertTrue(236 <= gp(7, 5)[2] <= 239) self.assertTrue(236 <= gp(7, 5)[2] <= 239)
self.assertTrue(236 <= gp(7, 6)[2] <= 239) self.assertTrue(236 <= gp(7, 6)[2] <= 239)

View File

@ -22,15 +22,14 @@ class TestImageSequence(PillowTestCase):
self.assertEqual(index, 1) self.assertEqual(index, 1)
self.assertRaises(AttributeError, lambda: ImageSequence.Iterator(0))
def _test_multipage_tiff(self, dbg=False): def _test_multipage_tiff(self, dbg=False):
# debug had side effect of calling fp.tell.
Image.DEBUG = dbg
im = Image.open('Tests/images/multipage.tiff') im = Image.open('Tests/images/multipage.tiff')
for index, frame in enumerate(ImageSequence.Iterator(im)): for index, frame in enumerate(ImageSequence.Iterator(im)):
frame.load() frame.load()
self.assertEqual(index, im.tell()) self.assertEqual(index, im.tell())
frame.convert('RGB') frame.convert('RGB')
Image.DEBUG = False
def test_tiff(self): def test_tiff(self):
# self._test_multipage_tiff(True) # self._test_multipage_tiff(True)

View File

@ -115,7 +115,8 @@ class TestImageWinDib(PillowTestCase):
# Act/Assert # Act/Assert
self.assert_warning(DeprecationWarning, dib.tostring) self.assert_warning(DeprecationWarning, dib.tostring)
self.assert_warning(DeprecationWarning, lambda: dib.fromstring(test_buffer)) self.assert_warning(DeprecationWarning,
lambda: dib.fromstring(test_buffer))
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -7,25 +7,20 @@ import PIL.OleFileIO as OleFileIO
class TestOleFileIo(PillowTestCase): class TestOleFileIo(PillowTestCase):
def test_isOleFile_false(self): def test_isOleFile(self):
# Arrange
non_ole_file = "Tests/images/flower.jpg"
# Act
is_ole = OleFileIO.isOleFile(non_ole_file)
# Assert
self.assertFalse(is_ole)
def test_isOleFile_true(self):
# Arrange
ole_file = "Tests/images/test-ole-file.doc" ole_file = "Tests/images/test-ole-file.doc"
# Act self.assertTrue(OleFileIO.isOleFile(ole_file))
is_ole = OleFileIO.isOleFile(ole_file) with open(ole_file, 'rb') as fp:
self.assertTrue(OleFileIO.isOleFile(fp))
self.assertTrue(OleFileIO.isOleFile(fp.read()))
# Assert non_ole_file = "Tests/images/flower.jpg"
self.assertTrue(is_ole)
self.assertFalse(OleFileIO.isOleFile(non_ole_file))
with open(non_ole_file, 'rb') as fp:
self.assertFalse(OleFileIO.isOleFile(fp))
self.assertFalse(OleFileIO.isOleFile(fp.read()))
def test_exists_worddocument(self): def test_exists_worddocument(self):
# Arrange # Arrange

View File

@ -30,10 +30,10 @@ class Test_scipy_resize(PillowTestCase):
def test_imresize4(self): def test_imresize4(self):
im = np.array([[1, 2], im = np.array([[1, 2],
[3, 4]]) [3, 4]])
res = np.array([[1. , 1.25, 1.75, 2. ], res = np.array([[1., 1.25, 1.75, 2.],
[1.5, 1.75, 2.25, 2.5], [1.5, 1.75, 2.25, 2.5],
[2.5, 2.75, 3.25, 3.5], [2.5, 2.75, 3.25, 3.5],
[3. , 3.25, 3.75, 4. ]], dtype=np.float32) [3., 3.25, 3.75, 4.]], dtype=np.float32)
# Check that resizing by target size, float and int are the same # Check that resizing by target size, float and int are the same
im2 = misc.imresize(im, (4, 4), mode='F') # output size im2 = misc.imresize(im, (4, 4), mode='F') # output size
im3 = misc.imresize(im, 2., mode='F') # fraction im3 = misc.imresize(im, 2., mode='F') # fraction

View File

@ -71,7 +71,7 @@
* See the README file for information on usage and redistribution. * See the README file for information on usage and redistribution.
*/ */
#define PILLOW_VERSION "2.9.0.dev0" #define PILLOW_VERSION "3.0.0.dev0"
#include "Python.h" #include "Python.h"

View File

@ -1,5 +1,4 @@
version: 2.9.pre.{build} version: 3.0.pre.{build}
shallow_clone: true
clone_folder: c:\pillow clone_folder: c:\pillow
init: init:
- ECHO %PYTHON% - ECHO %PYTHON%
@ -25,3 +24,5 @@ test_script:
- cd c:\pillow - cd c:\pillow
- '%PYTHON%\Scripts\pip.exe install nose' - '%PYTHON%\Scripts\pip.exe install nose'
- '%PYTHON%\python.exe test-installed.py' - '%PYTHON%\python.exe test-installed.py'
matrix:
fast_finish: true

View File

@ -77,8 +77,8 @@ Reading sequences
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~
The GIF loader supports the :py:meth:`~file.seek` and :py:meth:`~file.tell` The GIF loader supports the :py:meth:`~file.seek` and :py:meth:`~file.tell`
methods. You can seek to the next frame (``im.seek(im.tell() + 1``), or rewind methods. You can seek to the next frame (``im.seek(im.tell() + 1)``), or rewind
the file by seeking to the first frame. Random access is not supported. the file by seeking to the first frame. Random access is not supported. ``im.seek()`` raises an ``EOFError`` if you try to seek after the last frame.
Reading local images Reading local images
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~

View File

@ -181,6 +181,18 @@ Instances of the :py:class:`Image` class have the following attributes:
:type: ``(width, height)`` :type: ``(width, height)``
.. py:attribute:: width
Image width, in pixels.
:type: :py:class:`int`
.. py:attribute:: height
Image height, in pixels.
:type: :py:class:`int`
.. py:attribute:: palette .. py:attribute:: palette
Colour palette table, if any. If mode is “P”, this should be an instance of Colour palette table, if any. If mode is “P”, this should be an instance of

View File

@ -234,16 +234,17 @@ Methods
:param xy: Top left corner of the text. :param xy: Top left corner of the text.
:param text: Text to be drawn. If it contains any newline characters, :param text: Text to be drawn. If it contains any newline characters,
the text is passed on to mulitiline_text() the text is passed on to mulitiline_text()
:param font: An :py:class:`~PIL.ImageFont.ImageFont` instance.
:param fill: Color to use for the text. :param fill: Color to use for the text.
:param font: An :py:class:`~PIL.ImageFont.ImageFont` instance.
.. py:method:: PIL.ImageDraw.Draw.multiline_text(xy, text, fill=None, font=None, anchor=None, spacing=0, align="left") .. py:method:: PIL.ImageDraw.Draw.multiline_text(xy, text, fill=None, font=None, anchor=None, spacing=0, align="left")
Draws the string at the given position. Draws the string at the given position.
:param xy: Top left corner of the text. :param xy: Top left corner of the text.
:param text: Text to be drawn. If it contains any newline characters, :param text: Text to be drawn.
the text is split and passed on to mulitiline_text() :param fill: Color to use for the text.
:param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance.
:param spacing: The number of pixels between lines. :param spacing: The number of pixels between lines.
:param align: "left", "center" or "right". :param align: "left", "center" or "right".
@ -260,8 +261,7 @@ Methods
Return the size of the given string, in pixels. Return the size of the given string, in pixels.
:param text: Text to be measured. If it contains any newline characters, :param text: Text to be measured.
the text is split and passed on to mulitiline_textsize()
:param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance.
:param spacing: The number of pixels between lines. :param spacing: The number of pixels between lines.

View File

@ -90,7 +90,7 @@ except (ImportError, OSError):
NAME = 'Pillow' NAME = 'Pillow'
PILLOW_VERSION = '2.9.0.dev0' PILLOW_VERSION = '3.0.0.dev0'
TCL_ROOT = None TCL_ROOT = None
JPEG_ROOT = None JPEG_ROOT = None
JPEG2K_ROOT = None JPEG2K_ROOT = None
@ -464,11 +464,11 @@ class pil_build_ext(build_ext):
if _find_library_file(self, "lcms2"): if _find_library_file(self, "lcms2"):
feature.lcms = "lcms2" feature.lcms = "lcms2"
elif _find_library_file(self, "lcms2_static"): elif _find_library_file(self, "lcms2_static"):
#alternate Windows name. # alternate Windows name.
feature.lcms = "lcms2_static" feature.lcms = "lcms2_static"
if _tkinter and _find_include_file(self, "tk.h"): if _tkinter and _find_include_file(self, "tk.h"):
# the library names may vary somewhat (e.g. tcl84 or tcl8.4) # the library names may vary somewhat (e.g. tcl85 or tcl8.5)
version = TCL_VERSION[0] + TCL_VERSION[2] version = TCL_VERSION[0] + TCL_VERSION[2]
if feature.want('tcl'): if feature.want('tcl'):
if _find_library_file(self, "tcl" + version): if _find_library_file(self, "tcl" + version):
@ -571,32 +571,33 @@ class pil_build_ext(build_ext):
exts.append(Extension( exts.append(Extension(
"PIL._webp", ["_webp.c"], libraries=libs, define_macros=defs)) "PIL._webp", ["_webp.c"], libraries=libs, define_macros=defs))
if sys.platform == "darwin": if feature.tcl and feature.tk:
# locate Tcl/Tk frameworks if sys.platform == "darwin":
frameworks = [] # locate Tcl/Tk frameworks
framework_roots = [ frameworks = []
"/Library/Frameworks", framework_roots = [
"/System/Library/Frameworks"] "/Library/Frameworks",
for root in framework_roots: "/System/Library/Frameworks"]
if ( for root in framework_roots:
os.path.exists(os.path.join(root, "Tcl.framework")) and root_tcl = os.path.join(root, "Tcl.framework")
os.path.exists(os.path.join(root, "Tk.framework"))): root_tk = os.path.join(root, "Tk.framework")
print("--- using frameworks at %s" % root) if (os.path.exists(root_tcl) and os.path.exists(root_tk)):
frameworks = ["-framework", "Tcl", "-framework", "Tk"] print("--- using frameworks at %s" % root)
dir = os.path.join(root, "Tcl.framework", "Headers") frameworks = ["-framework", "Tcl", "-framework", "Tk"]
_add_directory(self.compiler.include_dirs, dir, 0) dir = os.path.join(root_tcl, "Headers")
dir = os.path.join(root, "Tk.framework", "Headers") _add_directory(self.compiler.include_dirs, dir, 0)
_add_directory(self.compiler.include_dirs, dir, 1) dir = os.path.join(root_tk, "Headers")
break _add_directory(self.compiler.include_dirs, dir, 1)
if frameworks: break
if frameworks:
exts.append(Extension(
"PIL._imagingtk", ["_imagingtk.c", "Tk/tkImaging.c"],
extra_compile_args=frameworks,
extra_link_args=frameworks))
else:
exts.append(Extension( exts.append(Extension(
"PIL._imagingtk", ["_imagingtk.c", "Tk/tkImaging.c"], "PIL._imagingtk", ["_imagingtk.c", "Tk/tkImaging.c"],
extra_compile_args=frameworks, extra_link_args=frameworks)) libraries=[feature.tcl, feature.tk]))
feature.tcl = feature.tk = 1 # mark as present
elif feature.tcl and feature.tk:
exts.append(Extension(
"PIL._imagingtk", ["_imagingtk.c", "Tk/tkImaging.c"],
libraries=[feature.tcl, feature.tk]))
if os.path.isfile("_imagingmath.c"): if os.path.isfile("_imagingmath.c"):
exts.append(Extension("PIL._imagingmath", ["_imagingmath.c"])) exts.append(Extension("PIL._imagingmath", ["_imagingmath.c"]))

View File

@ -11,4 +11,4 @@ commands =
{envpython} setup.py clean {envpython} setup.py clean
{envpython} setup.py build_ext --inplace {envpython} setup.py build_ext --inplace
{envpython} selftest.py {envpython} selftest.py
{envpython} Tests/run.py --installed {envpython} test-installed.py --installed

View File

@ -13,8 +13,8 @@ def setup_vms():
ret = [] ret = []
for py in pythons.keys(): for py in pythons.keys():
for arch in ('', X64_EXT): for arch in ('', X64_EXT):
ret.append("virtualenv -p c:/Python%s%s/python.exe --clear %s%s%s" % ret.append("virtualenv -p c:/Python%s%s/python.exe --clear %s%s%s"
(py, arch, VIRT_BASE, py, arch)) % (py, arch, VIRT_BASE, py, arch))
ret.append("%s%s%s\Scripts\pip.exe install nose" % ret.append("%s%s%s\Scripts\pip.exe install nose" %
(VIRT_BASE, py, arch)) (VIRT_BASE, py, arch))
if py == '26': if py == '26':
@ -109,13 +109,15 @@ def main(op):
scripts.append((py_version, scripts.append((py_version,
"\n".join([header(op), "\n".join([header(op),
build_one(py_version, build_one(py_version,
compilers[(compiler_version, 32)]), compilers[(compiler_version,
32)]),
footer()]))) footer()])))
scripts.append(("%s%s" % (py_version, X64_EXT), scripts.append(("%s%s" % (py_version, X64_EXT),
"\n".join([header(op), "\n".join([header(op),
build_one("%sx64" % py_version, build_one("%sx64" % py_version,
compilers[(compiler_version, 64)]), compilers[(compiler_version,
64)]),
footer()]))) footer()])))
results = map(run_script, scripts) results = map(run_script, scripts)

View File

@ -4,7 +4,7 @@ from untar import untar
import os import os
import hashlib import hashlib
from config import * from config import bin_libs, compilers, compiler_fromEnv, libs
def _relpath(*args): def _relpath(*args):
@ -64,10 +64,12 @@ def fetch_libs():
if name == 'openjpeg': if name == 'openjpeg':
filename = check_hash(fetch(lib['url']), lib['hash']) filename = check_hash(fetch(lib['url']), lib['hash'])
for compiler in compilers.values(): for compiler in compilers.values():
if not os.path.exists(os.path.join(build_dir, lib['dir']+compiler['inc_dir'])): if not os.path.exists(os.path.join(
build_dir, lib['dir']+compiler['inc_dir'])):
extract(filename, build_dir) extract(filename, build_dir)
os.rename(os.path.join(build_dir, lib['dir']), os.rename(os.path.join(build_dir, lib['dir']),
os.path.join(build_dir, lib['dir']+compiler['inc_dir'])) os.path.join(
build_dir, lib['dir']+compiler['inc_dir']))
else: else:
extract(check_hash(fetch(lib['url']), lib['hash']), build_dir) extract(check_hash(fetch(lib['url']), lib['hash']), build_dir)
@ -91,18 +93,19 @@ endlocal
""" % compiler """ % compiler
def cp_tk(): def cp_tk(ver_85, ver_86):
versions = {'ver_85':ver_85, 'ver_86':ver_86}
return r""" return r"""
mkdir %INCLIB%\tcl85\include\X11 mkdir %%INCLIB%%\tcl85\include\X11
copy /Y /B %BUILD%\tcl8.5.13\generic\*.h %INCLIB%\tcl85\include\ copy /Y /B %%BUILD%%\tcl%(ver_85)s\generic\*.h %%INCLIB%%\tcl85\include\
copy /Y /B %BUILD%\tk8.5.13\generic\*.h %INCLIB%\tcl85\include\ copy /Y /B %%BUILD%%\tk%(ver_85)s\generic\*.h %%INCLIB%%\tcl85\include\
copy /Y /B %BUILD%\tk8.5.13\xlib\X11\* %INCLIB%\tcl85\include\X11\ copy /Y /B %%BUILD%%\tk%(ver_85)s\xlib\X11\* %%INCLIB%%\tcl85\include\X11\
mkdir %INCLIB%\tcl86\include\X11 mkdir %%INCLIB%%\tcl86\include\X11
copy /Y /B %BUILD%\tcl8.6.4\generic\*.h %INCLIB%\tcl86\include\ copy /Y /B %%BUILD%%\tcl%(ver_86)s\generic\*.h %%INCLIB%%\tcl86\include\
copy /Y /B %BUILD%\tk8.6.4\generic\*.h %INCLIB%\tcl86\include\ copy /Y /B %%BUILD%%\tk%(ver_86)s\generic\*.h %%INCLIB%%\tcl86\include\
copy /Y /B %BUILD%\tk8.6.4\xlib\X11\* %INCLIB%\tcl86\include\X11\ copy /Y /B %%BUILD%%\tk%(ver_86)s\xlib\X11\* %%INCLIB%%\tcl86\include\X11\
""" """ % versions
def header(): def header():
@ -305,7 +308,7 @@ def add_compiler(compiler):
mkdirs() mkdirs()
fetch_libs() fetch_libs()
# extract_binlib() # extract_binlib()
script = [header(), cp_tk()] script = [header(), cp_tk(libs['tk-8.5']['version'],libs['tk-8.6']['version'] )]
if 'PYTHON' in os.environ: if 'PYTHON' in os.environ:

View File

@ -11,108 +11,108 @@ pythons = {#'26': 7,
VIRT_BASE = "c:/vp/" VIRT_BASE = "c:/vp/"
X64_EXT = os.environ.get('X64_EXT', "x64") X64_EXT = os.environ.get('X64_EXT', "x64")
libs = {'zlib': { libs = {
'url': 'http://zlib.net/zlib128.zip', 'zlib': {
'hash': 'md5:126f8676442ffbd97884eb4d6f32afb4', 'url': 'http://zlib.net/zlib128.zip',
'dir': 'zlib-1.2.8', 'hash': 'md5:126f8676442ffbd97884eb4d6f32afb4',
'dir': 'zlib-1.2.8',
}, },
'jpeg': { 'jpeg': {
'url': 'http://www.ijg.org/files/jpegsr9a.zip', 'url': 'http://www.ijg.org/files/jpegsr9a.zip',
'hash': 'md5:a34f3c82760270ee1e1885b15b90a72e', # not found - generated by wiredfool 'hash': 'md5:a34f3c82760270ee1e1885b15b90a72e', # not found - generated by wiredfool
'dir': 'jpeg-9a', 'dir': 'jpeg-9a',
}, },
'tiff': { 'tiff': {
'url': 'ftp://ftp.remotesensing.org/pub/libtiff/tiff-4.0.3.zip', 'url': 'ftp://ftp.remotesensing.org/pub/libtiff/tiff-4.0.4.zip',
'hash': 'md5:dd70349cedb3981371686e1c9b89a7f9', # not found - generated by wiredfool 'hash': 'md5:8f538a34156188f9a8dcddb679c65d1e',
'dir': 'tiff-4.0.3', 'dir': 'tiff-4.0.4',
},
'freetype': {
'url': 'http://download.savannah.gnu.org/releases/freetype/freetype-2.6.tar.gz',
'hash': 'md5:1d733ea6c1b7b3df38169fbdbec47d2b',
'dir': 'freetype-2.6',
}, },
'lcms': { 'freetype': {
'url': SF_MIRROR+'/project/lcms/lcms/2.7/lcms2-2.7.zip', 'url': 'http://download.savannah.gnu.org/releases/freetype/freetype-2.6.tar.gz',
'hash': 'sha1:7ff1a5b721ca719760ba6eb4ec6f38d5e65381cf', 'hash': 'md5:1d733ea6c1b7b3df38169fbdbec47d2b',
'dir': 'lcms2-2.7', 'dir': 'freetype-2.6',
},
'tcl-8.5': {
'url': SF_MIRROR+'/project/tcl/Tcl/8.5.13/tcl8513-src.zip',
'hash': 'sha1:3e01585c91293c532a3cd594ec59deca92153a5e',
'dir': '',
},
'tk-8.5': {
'url': SF_MIRROR+'/project/tcl/Tcl/8.5.13/tk8513-src.zip',
'hash': 'sha1:23a1d7ddd416e11e06dfdb9f86111d4bab9420b4',
'dir': '',
}, },
'tcl-8.6': { 'lcms': {
'url': SF_MIRROR+'/project/tcl/Tcl/8.6.4/tcl864-src.zip', 'url': SF_MIRROR+'/project/lcms/lcms/2.7/lcms2-2.7.zip',
'hash': 'md5:35748d2fc61e08a2fdb23b85c6f8c4a0', 'hash': 'sha1:7ff1a5b721ca719760ba6eb4ec6f38d5e65381cf',
'dir': '', 'dir': 'lcms2-2.7',
},
'tk-8.6': {
'url': SF_MIRROR+'/project/tcl/Tcl/8.6.4/tk864-src.zip',
'hash': 'md5:111d45061a69e7f5250b6ec8ca7c4f35',
'dir': '',
}, },
'webp': { 'tcl-8.5': {
'url': 'http://downloads.webmproject.org/releases/webp/libwebp-0.4.3.tar.gz', 'url': SF_MIRROR+'/project/tcl/Tcl/8.5.18/tcl8518-src.zip',
'hash': 'sha1:1c307a61c4d0018620b4ba9a58e8f48a8d6640ef', 'hash': 'sha1:4c2aed9043088c630a4c795265e2738ef1b4db3b',
'dir': 'libwebp-0.4.3', 'dir': '',
}, },
'openjpeg': { 'tk-8.5': {
'url': SF_MIRROR+'/project/openjpeg/openjpeg/2.1.0/openjpeg-2.1.0.tar.gz', 'url': SF_MIRROR+'/project/tcl/Tcl/8.5.18/tk8518-src.zip',
'hash': 'md5:f6419fcc233df84f9a81eb36633c6db6', 'hash': 'sha1:273f55148777413774aa722ecad25cabda1e31ae',
'dir': 'openjpeg-2.1.0', 'dir': '',
'version':'8.5.18',
}, },
'tcl-8.6': {
} 'url': SF_MIRROR+'/project/tcl/Tcl/8.6.4/tcl864-src.zip',
'hash': 'md5:35748d2fc61e08a2fdb23b85c6f8c4a0',
'dir': '',
},
'tk-8.6': {
'url': SF_MIRROR+'/project/tcl/Tcl/8.6.4/tk864-src.zip',
'hash': 'md5:111d45061a69e7f5250b6ec8ca7c4f35',
'dir': '',
'version':'8.6.4',
},
'webp': {
'url': 'http://downloads.webmproject.org/releases/webp/libwebp-0.4.3.tar.gz',
'hash': 'sha1:1c307a61c4d0018620b4ba9a58e8f48a8d6640ef',
'dir': 'libwebp-0.4.3',
},
'openjpeg': {
'url': SF_MIRROR+'/project/openjpeg/openjpeg/2.1.0/openjpeg-2.1.0.tar.gz',
'hash': 'md5:f6419fcc233df84f9a81eb36633c6db6',
'dir': 'openjpeg-2.1.0',
},
}
bin_libs = { bin_libs = {
'openjpeg': { 'openjpeg': {
'filename': 'openjpeg-2.0.0-win32-x86.zip', 'filename': 'openjpeg-2.0.0-win32-x86.zip',
'hash': 'sha1:xxx', 'hash': 'sha1:xxx',
'version': '2.0' 'version': '2.0'
},
}
compilers = {(7, 64): {
'env_version': 'v7.0',
'vc_version': '2008',
'env_flags': '/x64 /xp',
'inc_dir': 'msvcr90-x64',
'platform': 'x64',
'webp_platform': 'x64',
}, },
(7, 32): { }
'env_version': 'v7.0',
'vc_version': '2008',
'env_flags': '/x86 /xp',
'inc_dir': 'msvcr90-x32',
'platform': 'Win32',
'webp_platform': 'x86',
},
(7.1, 64): { compilers = {
'env_version': 'v7.1', (7, 64): {
'vc_version': '2010', 'env_version': 'v7.0',
'env_flags': '/x64 /vista', 'vc_version': '2008',
'inc_dir': 'msvcr10-x64', 'env_flags': '/x64 /xp',
'platform': 'x64', 'inc_dir': 'msvcr90-x64',
'webp_platform': 'x64', 'platform': 'x64',
}, 'webp_platform': 'x64',
(7.1, 32): {
'env_version': 'v7.1',
'vc_version': '2010',
'env_flags': '/x86 /vista',
'inc_dir': 'msvcr10-x32',
'platform': 'Win32',
'webp_platform': 'x86',
}, },
(7, 32): {
} 'env_version': 'v7.0',
'vc_version': '2008',
'env_flags': '/x86 /xp',
'inc_dir': 'msvcr90-x32',
'platform': 'Win32',
'webp_platform': 'x86',
},
(7.1, 64): {
'env_version': 'v7.1',
'vc_version': '2010',
'env_flags': '/x64 /vista',
'inc_dir': 'msvcr10-x64',
'platform': 'x64',
'webp_platform': 'x64',
},
(7.1, 32): {
'env_version': 'v7.1',
'vc_version': '2010',
'env_flags': '/x86 /vista',
'inc_dir': 'msvcr10-x32',
'platform': 'Win32',
'webp_platform': 'x86',
},
}
def pyversion_fromEnv(): def pyversion_fromEnv():

View File

@ -5,8 +5,8 @@ if __name__ == '__main__':
for version in ['2.6.5', '2.7.6', '3.2.5', '3.3.5', '3.4.3']: for version in ['2.6.5', '2.7.6', '3.2.5', '3.3.5', '3.4.3']:
for platform in ['', '.amd64']: for platform in ['', '.amd64']:
for extension in ['', '.asc']: for extension in ['', '.asc']:
fetch('https://www.python.org/ftp/python/%s/python-%s%s.msi%s' % ( fetch('https://www.python.org/ftp/python/%s/python-%s%s.msi%s'
version, version, platform, extension)) % (version, version, platform, extension))
# find pip, if it's not in the path! # find pip, if it's not in the path!
os.system('pip install virtualenv') os.system('pip install virtualenv')

View File

@ -5,14 +5,15 @@ import os
import glob import glob
import sys import sys
from config import * from config import pythons, VIRT_BASE, X64_EXT
def test_one(params): def test_one(params):
python, architecture = params python, architecture = params
try: try:
print("Running: %s, %s" % params) print("Running: %s, %s" % params)
command = [r'%s\%s%s\Scripts\python.exe' % (VIRT_BASE, python, architecture), command = [r'%s\%s%s\Scripts\python.exe' %
(VIRT_BASE, python, architecture),
'test-installed.py', 'test-installed.py',
'--processes=-0', '--processes=-0',
'--process-timeout=30', '--process-timeout=30',