This commit is contained in:
Eric W. Brown 2014-07-23 09:22:15 -04:00
commit 6605bf22e5
49 changed files with 1130 additions and 312 deletions

View File

@ -9,6 +9,7 @@ python:
- "pypy"
- 2.6
- 2.7
- "2.7_with_system_site_packages" # For PyQt4
- 3.2
- 3.3
- 3.4
@ -33,11 +34,11 @@ script:
# Don't cover PyPy: it fails intermittently and is x5.8 slower (#640)
- if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then time python selftest.py; fi
- if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then time nosetests Tests/test_*.py; fi
- if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then time nosetests -vx Tests/test_*.py; fi
# Cover the others
- if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time coverage run --append --include=PIL/* selftest.py; fi
- if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time coverage run --append --include=PIL/* -m nose Tests/test_*.py; fi
- if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time coverage run --append --include=PIL/* -m nose -vx Tests/test_*.py; fi
after_success:
- coverage report

View File

@ -4,10 +4,22 @@ Changelog (Pillow)
2.6.0 (unreleased)
------------------
- Removed unusable ImagePalette.new()
[hugovk]
- Fix Scrambled XPM #808
[wiredfool]
- Doc cleanup
[wiredfool]
- Fix `ImageStat` docs
[akx]
- Added docs for ExifTags
[Wintermute3]
- More tests for ImageFont, ImageMath, and _util
- More tests for CurImagePlugin, DcxImagePlugin, ImageFont, ImageMath, ImagePalette, SpiderImagePlugin, SgiImagePlugin, XpmImagePlugin and _util
[hugovk]
- Fix return value of FreeTypeFont.textsize() does not include font offsets

View File

@ -1,4 +1,16 @@
.PHONY: pre clean install test inplace coverage test-dep help docs livedocs
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " clean remove build products"
@echo " install make and install"
@echo " test run tests on installed pillow"
@echo " inplace make inplace extension"
@echo " coverage run coverage test (in progress)"
@echo " docs make html docs"
@echo " docserver run an http server on the docs directory"
@echo " test-dep install coveraget and test dependencies"
pre:
virtualenv .
@ -18,12 +30,11 @@ clean:
rm -r build || true
find . -name __pycache__ | xargs rm -r || true
install:
python setup.py install
python selftest.py --installed
test: install
test:
python test-installed.py
inplace: clean
@ -42,3 +53,9 @@ coverage:
test-dep:
pip install coveralls nose nose-cov pep8 pyflakes
docs:
$(MAKE) -C docs html
docserver:
cd docs/_build/html && python -mSimpleHTTPServer 2> /dev/null&

View File

@ -33,6 +33,7 @@ i32 = _binary.i32le
def _accept(prefix):
return prefix[:4] == b"\0\0\2\0"
##
# Image plugin for Windows Cursor files.
@ -48,7 +49,7 @@ class CurImageFile(BmpImagePlugin.BmpImageFile):
# check magic
s = self.fp.read(6)
if not _accept(s):
raise SyntaxError("not an CUR file")
raise SyntaxError("not a CUR file")
# pick the largest cursor in the file
m = b""
@ -58,14 +59,14 @@ class CurImageFile(BmpImagePlugin.BmpImageFile):
m = s
elif i8(s[0]) > i8(m[0]) and i8(s[1]) > i8(m[1]):
m = s
#print "width", i8(s[0])
#print "height", i8(s[1])
#print "colors", i8(s[2])
#print "reserved", i8(s[3])
#print "hotspot x", i16(s[4:])
#print "hotspot y", i16(s[6:])
#print "bytes", i32(s[8:])
#print "offset", i32(s[12:])
# print "width", i8(s[0])
# print "height", i8(s[1])
# print "colors", i8(s[2])
# print "reserved", i8(s[3])
# print "hotspot x", i16(s[4:])
# print "hotspot y", i16(s[6:])
# print "bytes", i32(s[8:])
# print "offset", i32(s[12:])
# load as bitmap
self._bitmap(i32(m[12:]) + offset)
@ -73,7 +74,7 @@ class CurImageFile(BmpImagePlugin.BmpImageFile):
# patch up the bitmap height
self.size = self.size[0], self.size[1]//2
d, e, o, a = self.tile[0]
self.tile[0] = d, (0,0)+self.size, o, a
self.tile[0] = d, (0, 0)+self.size, o, a
return

View File

@ -27,13 +27,15 @@ from PIL import Image, _binary
from PIL.PcxImagePlugin import PcxImageFile
MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then?
MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then?
i32 = _binary.i32le
def _accept(prefix):
return i32(prefix) == MAGIC
##
# Image plugin for the Intel DCX format.

View File

@ -1,19 +1,19 @@
"""
The Python Imaging Library.
$Id$
## The Python Imaging Library.
## $Id$
Optional color managment support, based on Kevin Cazabon's PyCMS
library.
## Optional color managment support, based on Kevin Cazabon's PyCMS
## library.
History:
2009-03-08 fl Added to PIL.
## History:
Copyright (C) 2002-2003 Kevin Cazabon
Copyright (c) 2009 by Fredrik Lundh
## 2009-03-08 fl Added to PIL.
See the README file for information on usage and redistribution. See
below for the original description.
"""
## Copyright (C) 2002-2003 Kevin Cazabon
## Copyright (c) 2009 by Fredrik Lundh
## Copyright (c) 2013 by Eric Soroos
## See the README file for information on usage and redistribution. See
## below for the original description.
from __future__ import print_function
@ -637,7 +637,7 @@ def getProfileName(profile):
(pyCMS) Gets the internal product name for the given profile.
If profile isn't a valid CmsProfile object or filename to a profile,
If profile isn't a valid CmsProfile object or filename to a profile,
a PyCMSError is raised If an error occurs while trying to obtain the
name tag, a PyCMSError is raised.
@ -876,7 +876,7 @@ def isIntentSupported(profile, intent, direction):
input/output/proof profile as you desire.
Some profiles are created specifically for one "direction", can cannot
be used for others. Some profiles can only be used for certain
be used for others. Some profiles can only be used for certain
rendering intents... so it's best to either verify this before trying
to create a transform with them (using this function), or catch the
potential PyCMSError that will occur if they don't support the modes

View File

@ -133,11 +133,27 @@ class ImageFile(Image.Image):
return pixel
self.map = None
use_mmap = self.filename and len(self.tile) == 1
# As of pypy 2.1.0, memory mapping was failing here.
use_mmap = use_mmap and not hasattr(sys, 'pypy_version_info')
readonly = 0
if self.filename and len(self.tile) == 1 and not hasattr(sys, 'pypy_version_info'):
# As of pypy 2.1.0, memory mapping was failing here.
# look for read/seek overrides
try:
read = self.load_read
# don't use mmap if there are custom read/seek functions
use_mmap = False
except AttributeError:
read = self.fp.read
try:
seek = self.load_seek
use_mmap = False
except AttributeError:
seek = self.fp.seek
if use_mmap:
# try memory mapping
d, e, o, a = self.tile[0]
if d == "raw" and a[0] == self.mode and a[0] in Image._MAPMODES:
@ -165,19 +181,7 @@ class ImageFile(Image.Image):
self.load_prepare()
# look for read/seek overrides
try:
read = self.load_read
except AttributeError:
read = self.fp.read
try:
seek = self.load_seek
except AttributeError:
seek = self.fp.seek
if not self.map:
# sort tiles in file order
self.tile.sort(key=_tilesort)

View File

@ -17,19 +17,20 @@
#
import array
from PIL import Image, ImageColor
import warnings
from PIL import ImageColor
class ImagePalette:
"Color palette for palette mapped images"
def __init__(self, mode = "RGB", palette = None, size = 0):
def __init__(self, mode="RGB", palette=None, size=0):
self.mode = mode
self.rawmode = None # if set, palette contains raw data
self.rawmode = None # if set, palette contains raw data
self.palette = palette or list(range(256))*len(self.mode)
self.colors = {}
self.dirty = None
if ((size == 0 and len(self.mode)*256 != len(self.palette)) or
if ((size == 0 and len(self.mode)*256 != len(self.palette)) or
(size != 0 and size != len(self.palette))):
raise ValueError("wrong palette size")
@ -55,7 +56,7 @@ class ImagePalette:
return self.palette
arr = array.array("B", self.palette)
if hasattr(arr, 'tobytes'):
#py3k has a tobytes, tostring is deprecated.
# py3k has a tobytes, tostring is deprecated.
return arr.tobytes()
return arr.tostring()
@ -109,6 +110,7 @@ class ImagePalette:
fp.write("\n")
fp.close()
# --------------------------------------------------------------------
# Internal
@ -119,32 +121,53 @@ def raw(rawmode, data):
palette.dirty = 1
return palette
# --------------------------------------------------------------------
# Factories
def _make_linear_lut(black, white):
warnings.warn(
'_make_linear_lut() is deprecated. '
'Please call make_linear_lut() instead.',
DeprecationWarning,
stacklevel=2
)
return make_linear_lut(black, white)
def _make_gamma_lut(exp):
warnings.warn(
'_make_gamma_lut() is deprecated. '
'Please call make_gamma_lut() instead.',
DeprecationWarning,
stacklevel=2
)
return make_gamma_lut(exp)
def make_linear_lut(black, white):
lut = []
if black == 0:
for i in range(256):
lut.append(white*i//255)
else:
raise NotImplementedError # FIXME
raise NotImplementedError # FIXME
return lut
def _make_gamma_lut(exp, mode="RGB"):
def make_gamma_lut(exp):
lut = []
for i in range(256):
lut.append(int(((i / 255.0) ** exp) * 255.0 + 0.5))
return lut
def new(mode, data):
return Image.core.new_palette(mode, data)
def negative(mode="RGB"):
palette = list(range(256))
palette.reverse()
return ImagePalette(mode, palette * len(mode))
def random(mode="RGB"):
from random import randint
palette = []
@ -152,16 +175,19 @@ def random(mode="RGB"):
palette.append(randint(0, 255))
return ImagePalette(mode, palette)
def sepia(white="#fff0c0"):
r, g, b = ImageColor.getrgb(white)
r = _make_linear_lut(0, r)
g = _make_linear_lut(0, g)
b = _make_linear_lut(0, b)
r = make_linear_lut(0, r)
g = make_linear_lut(0, g)
b = make_linear_lut(0, b)
return ImagePalette("RGB", r + g + b)
def wedge(mode="RGB"):
return ImagePalette(mode, list(range(256)) * len(mode))
def load(filename):
# FIXME: supports GIMP gradients only
@ -177,8 +203,8 @@ def load(filename):
p = GimpPaletteFile.GimpPaletteFile(fp)
lut = p.getpalette()
except (SyntaxError, ValueError):
#import traceback
#traceback.print_exc()
# import traceback
# traceback.print_exc()
pass
if not lut:
@ -188,8 +214,8 @@ def load(filename):
p = GimpGradientFile.GimpGradientFile(fp)
lut = p.getpalette()
except (SyntaxError, ValueError):
#import traceback
#traceback.print_exc()
# import traceback
# traceback.print_exc()
pass
if not lut:
@ -206,4 +232,4 @@ def load(filename):
if not lut:
raise IOError("cannot load palette")
return lut # data, rawmode
return lut # data, rawmode

View File

@ -1,28 +1,29 @@
#!/usr/local/bin/python
# -*- coding: latin-1 -*-
"""
OleFileIO_PL:
Module to read Microsoft OLE2 files (also called Structured Storage or
Microsoft Compound Document File Format), such as Microsoft Office
documents, Image Composer and FlashPix files, Outlook messages, ...
This version is compatible with Python 2.6+ and 3.x
## OleFileIO_PL:
## Module to read Microsoft OLE2 files (also called Structured Storage or
## Microsoft Compound Document File Format), such as Microsoft Office
## documents, Image Composer and FlashPix files, Outlook messages, ...
## This version is compatible with Python 2.6+ and 3.x
version 0.30 2014-02-04 Philippe Lagadec - http://www.decalage.info
## version 0.30 2014-02-04 Philippe Lagadec - http://www.decalage.info
Project website: http://www.decalage.info/python/olefileio
## Project website: http://www.decalage.info/python/olefileio
Improved version of the OleFileIO module from PIL library v1.1.6
See: http://www.pythonware.com/products/pil/index.htm
## Improved version of the OleFileIO module from PIL library v1.1.6
## See: http://www.pythonware.com/products/pil/index.htm
The Python Imaging Library (PIL) is
Copyright (c) 1997-2005 by Secret Labs AB
Copyright (c) 1995-2005 by Fredrik Lundh
OleFileIO_PL changes are Copyright (c) 2005-2014 by Philippe Lagadec
## The Python Imaging Library (PIL) is
See source code and LICENSE.txt for information on usage and redistribution.
## Copyright (c) 1997-2005 by Secret Labs AB
## Copyright (c) 1995-2005 by Fredrik Lundh
## OleFileIO_PL changes are Copyright (c) 2005-2014 by Philippe Lagadec
## See source code and LICENSE.txt for information on usage and redistribution.
## WARNING: THIS IS (STILL) WORK IN PROGRESS.
WARNING: THIS IS (STILL) WORK IN PROGRESS.
"""
# Starting with OleFileIO_PL v0.30, only Python 2.6+ and 3.x is supported
# This import enables print() as a function rather than a keyword
@ -370,8 +371,9 @@ for key in list(vars().keys()):
def isOleFile (filename):
"""
Test if file is an OLE container (according to its header).
filename: file name or path (str, unicode)
return: True if OLE, False otherwise.
:param filename: file name or path (str, unicode)
:returns: True if OLE, False otherwise.
"""
f = open(filename, 'rb')
header = f.read(len(MAGIC))
@ -397,8 +399,8 @@ def i16(c, o = 0):
"""
Converts a 2-bytes (16 bits) string to an integer.
c: string containing bytes to convert
o: offset of bytes to convert in string
:param c: string containing bytes to convert
:param o: offset of bytes to convert in string
"""
return i8(c[o]) | (i8(c[o+1])<<8)
@ -407,8 +409,8 @@ def i32(c, o = 0):
"""
Converts a 4-bytes (32 bits) string to an integer.
c: string containing bytes to convert
o: offset of bytes to convert in string
:param c: string containing bytes to convert
:param o: offset of bytes to convert in string
"""
## return int(ord(c[o])+(ord(c[o+1])<<8)+(ord(c[o+2])<<16)+(ord(c[o+3])<<24))
## # [PL]: added int() because "<<" gives long int since Python 2.4
@ -419,7 +421,8 @@ def i32(c, o = 0):
def _clsid(clsid):
"""
Converts a CLSID to a human-readable string.
clsid: string of length 16.
:param clsid: string of length 16.
"""
assert len(clsid) == 16
# if clsid is only made of null bytes, return an empty string:
@ -439,8 +442,8 @@ def _unicode(s, errors='replace'):
"""
Map unicode string to Latin 1. (Python with Unicode support)
s: UTF-16LE unicode string to convert to Latin-1
errors: 'replace', 'ignore' or 'strict'.
:param s: UTF-16LE unicode string to convert to Latin-1
:param errors: 'replace', 'ignore' or 'strict'.
"""
#TODO: test if it OleFileIO works with Unicode strings, instead of
# converting to Latin-1.
@ -650,14 +653,14 @@ class _OleStream(io.BytesIO):
"""
Constructor for _OleStream class.
fp : file object, the OLE container or the MiniFAT stream
sect : sector index of first sector in the stream
size : total size of the stream
offset : offset in bytes for the first FAT or MiniFAT sector
sectorsize: size of one sector
fat : array/list of sector indexes (FAT or MiniFAT)
filesize : size of OLE file (for debugging)
return : a BytesIO instance containing the OLE stream
:param fp : file object, the OLE container or the MiniFAT stream
:param sect : sector index of first sector in the stream
:param size : total size of the stream
:param offset : offset in bytes for the first FAT or MiniFAT sector
:param sectorsize: size of one sector
:param fat : array/list of sector indexes (FAT or MiniFAT)
:param filesize : size of OLE file (for debugging)
:returns : a BytesIO instance containing the OLE stream
"""
debug('_OleStream.__init__:')
debug(' sect=%d (%X), size=%d, offset=%d, sectorsize=%d, len(fat)=%d, fp=%s'
@ -793,9 +796,9 @@ class _OleDirectoryEntry:
Constructor for an _OleDirectoryEntry object.
Parses a 128-bytes entry from the OLE Directory stream.
entry : string (must be 128 bytes long)
sid : index of this directory entry in the OLE file directory
olefile: OleFileIO containing this directory entry
:param entry : string (must be 128 bytes long)
:param sid : index of this directory entry in the OLE file directory
:param olefile: OleFileIO containing this directory entry
"""
self.sid = sid
# ref to olefile is stored for future use
@ -989,7 +992,7 @@ class _OleDirectoryEntry:
"""
Return modification time of a directory entry.
return: None if modification time is null, a python datetime object
:returns: None if modification time is null, a python datetime object
otherwise (UTC timezone)
new in version 0.26
@ -1003,7 +1006,7 @@ class _OleDirectoryEntry:
"""
Return creation time of a directory entry.
return: None if modification time is null, a python datetime object
:returns: None if modification time is null, a python datetime object
otherwise (UTC timezone)
new in version 0.26
@ -1020,7 +1023,8 @@ class OleFileIO:
OLE container object
This class encapsulates the interface to an OLE 2 structured
storage file. Use the {@link listdir} and {@link openstream} methods to
storage file. Use the :py:meth:`~PIL.OleFileIO.OleFileIO.listdir` and
:py:meth:`~PIL.OleFileIO.OleFileIO.openstream` methods to
access the contents of this file.
Object names are given as a list of strings, one for each subentry
@ -1048,8 +1052,8 @@ class OleFileIO:
"""
Constructor for OleFileIO class.
filename: file to open.
raise_defects: minimal level for defects to be raised as exceptions.
:param filename: file to open.
:param raise_defects: minimal level for defects to be raised as exceptions.
(use DEFECT_FATAL for a typical application, DEFECT_INCORRECT for a
security-oriented application, see source code for details)
"""
@ -1068,13 +1072,13 @@ class OleFileIO:
It may raise an IOError exception according to the minimal level chosen
for the OleFileIO object.
defect_level: defect level, possible values are:
:param defect_level: defect level, possible values are:
DEFECT_UNSURE : a case which looks weird, but not sure it's a defect
DEFECT_POTENTIAL : a potential defect
DEFECT_INCORRECT : an error according to specifications, but parsing can go on
DEFECT_FATAL : an error which cannot be ignored, parsing is impossible
message: string describing the defect, used with raised exception.
exception_type: exception class to be raised, IOError by default
:param message: string describing the defect, used with raised exception.
:param exception_type: exception class to be raised, IOError by default
"""
# added by [PL]
if defect_level >= self._raise_defects_level:
@ -1089,7 +1093,7 @@ class OleFileIO:
Open an OLE2 file.
Reads the header, FAT and directory.
filename: string-like or file-like object
:param filename: string-like or file-like object
"""
#[PL] check if filename is a string-like or file-like object:
# (it is better to check for a read() method)
@ -1276,8 +1280,8 @@ class OleFileIO:
Checks if a stream has not been already referenced elsewhere.
This method should only be called once for each known stream, and only
if stream size is not null.
first_sect: index of first sector of the stream in FAT
minifat: if True, stream is located in the MiniFAT, else in the FAT
:param first_sect: index of first sector of the stream in FAT
:param minifat: if True, stream is located in the MiniFAT, else in the FAT
"""
if minifat:
debug('_check_duplicate_stream: sect=%d in MiniFAT' % first_sect)
@ -1371,8 +1375,9 @@ class OleFileIO:
def loadfat_sect(self, sect):
"""
Adds the indexes of the given sector to the FAT
sect: string containing the first FAT sector, or array of long integers
return: index of last FAT sector.
:param sect: string containing the first FAT sector, or array of long integers
:returns: index of last FAT sector.
"""
# a FAT sector is an array of ulong integers.
if isinstance(sect, array.array):
@ -1505,8 +1510,9 @@ class OleFileIO:
def getsect(self, sect):
"""
Read given sector from file on disk.
sect: sector index
returns a string containing the sector data.
:param sect: sector index
:returns: a string containing the sector data.
"""
# [PL] this original code was wrong when sectors are 4KB instead of
# 512 bytes:
@ -1530,7 +1536,8 @@ class OleFileIO:
def loaddirectory(self, sect):
"""
Load the directory.
sect: sector index of directory stream.
:param sect: sector index of directory stream.
"""
# The directory is stored in a standard
# substream, independent of its size.
@ -1567,9 +1574,10 @@ class OleFileIO:
Load a directory entry from the directory.
This method should only be called once for each storage/stream when
loading the directory.
sid: index of storage/stream in the directory.
return: a _OleDirectoryEntry object
raise: IOError if the entry has always been referenced.
:param sid: index of storage/stream in the directory.
:returns: a _OleDirectoryEntry object
:exception IOError: if the entry has always been referenced.
"""
# check if SID is OK:
if sid<0 or sid>=len(self.direntries):
@ -1598,9 +1606,9 @@ class OleFileIO:
Open a stream, either in FAT or MiniFAT according to its size.
(openstream helper)
start: index of first sector
size: size of stream (or nothing if size is unknown)
force_FAT: if False (default), stream will be opened in FAT or MiniFAT
:param start: index of first sector
:param size: size of stream (or nothing if size is unknown)
:param force_FAT: if False (default), stream will be opened in FAT or MiniFAT
according to size. If True, it will always be opened in FAT.
"""
debug('OleFileIO.open(): sect=%d, size=%d, force_FAT=%s' %
@ -1630,11 +1638,11 @@ class OleFileIO:
def _list(self, files, prefix, node, streams=True, storages=False):
"""
(listdir helper)
files: list of files to fill in
prefix: current location in storage tree (list of names)
node: current node (_OleDirectoryEntry object)
streams: bool, include streams if True (True by default) - new in v0.26
storages: bool, include storages if True (False by default) - new in v0.26
:param files: list of files to fill in
:param prefix: current location in storage tree (list of names)
:param node: current node (_OleDirectoryEntry object)
:param streams: bool, include streams if True (True by default) - new in v0.26
:param storages: bool, include storages if True (False by default) - new in v0.26
(note: the root storage is never included)
"""
prefix = prefix + [node.name]
@ -1657,9 +1665,9 @@ class OleFileIO:
"""
Return a list of streams stored in this file
streams: bool, include streams if True (True by default) - new in v0.26
storages: bool, include storages if True (False by default) - new in v0.26
(note: the root storage is never included)
:param streams: bool, include streams if True (True by default) - new in v0.26
:param storages: bool, include storages if True (False by default) - new in v0.26
(note: the root storage is never included)
"""
files = []
self._list(files, [], self.root, streams, storages)
@ -1671,12 +1679,13 @@ class OleFileIO:
Returns directory entry of given filename. (openstream helper)
Note: this method is case-insensitive.
filename: path of stream in storage tree (except root entry), either:
:param filename: path of stream in storage tree (except root entry), either:
- a string using Unix path syntax, for example:
'storage_1/storage_1.2/stream'
- a list of storage filenames, path to the desired stream/storage.
Example: ['storage_1', 'storage_1.2', 'stream']
return: sid of requested filename
:returns: sid of requested filename
raise IOError if file not found
"""
@ -1700,13 +1709,15 @@ class OleFileIO:
"""
Open a stream as a read-only file object (BytesIO).
filename: path of stream in storage tree (except root entry), either:
:param filename: path of stream in storage tree (except root entry), either:
- a string using Unix path syntax, for example:
'storage_1/storage_1.2/stream'
- a list of storage filenames, path to the desired stream/storage.
Example: ['storage_1', 'storage_1.2', 'stream']
return: file object (read-only)
raise IOError if filename not found, or if this is not a stream.
:returns: file object (read-only)
:exception IOError: if filename not found, or if this is not a stream.
"""
sid = self._find(filename)
entry = self.direntries[sid]
@ -1720,8 +1731,9 @@ class OleFileIO:
Test if given filename exists as a stream or a storage in the OLE
container, and return its type.
filename: path of stream in storage tree. (see openstream for syntax)
return: False if object does not exist, its entry type (>0) otherwise:
:param filename: path of stream in storage tree. (see openstream for syntax)
:returns: False if object does not exist, its entry type (>0) otherwise:
- STGTY_STREAM: a stream
- STGTY_STORAGE: a storage
- STGTY_ROOT: the root entry
@ -1738,10 +1750,10 @@ class OleFileIO:
"""
Return modification time of a stream/storage.
filename: path of stream/storage in storage tree. (see openstream for
syntax)
return: None if modification time is null, a python datetime object
otherwise (UTC timezone)
:param filename: path of stream/storage in storage tree. (see openstream for
syntax)
:returns: None if modification time is null, a python datetime object
otherwise (UTC timezone)
new in version 0.26
"""
@ -1754,10 +1766,10 @@ class OleFileIO:
"""
Return creation time of a stream/storage.
filename: path of stream/storage in storage tree. (see openstream for
syntax)
return: None if creation time is null, a python datetime object
otherwise (UTC timezone)
:param filename: path of stream/storage in storage tree. (see openstream for
syntax)
:returns: None if creation time is null, a python datetime object
otherwise (UTC timezone)
new in version 0.26
"""
@ -1771,8 +1783,8 @@ class OleFileIO:
Test if given filename exists as a stream or a storage in the OLE
container.
filename: path of stream in storage tree. (see openstream for syntax)
return: True if object exist, else False.
:param filename: path of stream in storage tree. (see openstream for syntax)
:returns: True if object exist, else False.
"""
try:
sid = self._find(filename)
@ -1785,9 +1797,10 @@ class OleFileIO:
"""
Return size of a stream in the OLE container, in bytes.
filename: path of stream in storage tree (see openstream for syntax)
return: size in bytes (long integer)
raise: IOError if file not found, TypeError if this is not a stream.
:param filename: path of stream in storage tree (see openstream for syntax)
:returns: size in bytes (long integer)
:exception IOError: if file not found
:exception TypeError: if this is not a stream
"""
sid = self._find(filename)
entry = self.direntries[sid]
@ -1809,11 +1822,11 @@ class OleFileIO:
"""
Return properties described in substream.
filename: path of stream in storage tree (see openstream for syntax)
convert_time: bool, if True timestamps will be converted to Python datetime
no_conversion: None or list of int, timestamps not to be converted
(for example total editing time is not a real timestamp)
return: a dictionary of values indexed by id (integer)
:param filename: path of stream in storage tree (see openstream for syntax)
:param convert_time: bool, if True timestamps will be converted to Python datetime
:param no_conversion: None or list of int, timestamps not to be converted
(for example total editing time is not a real timestamp)
:returns: a dictionary of values indexed by id (integer)
"""
# make sure no_conversion is a list, just to simplify code below:
if no_conversion == None:

View File

@ -73,9 +73,8 @@ class PSDraw:
def setink(self, ink):
"""
.. warning::
.. warning:: This has been in the PIL API for ages but was never implemented.
This has been in the PIL API for ages but was never implemented.
"""
print("*** NOT YET IMPLEMENTED ***")

View File

@ -31,6 +31,7 @@ i32 = _binary.i32be
def _accept(prefix):
return i16(prefix) == 474
##
# Image plugin for SGI images.
@ -44,7 +45,7 @@ class SgiImageFile(ImageFile.ImageFile):
# HEAD
s = self.fp.read(512)
if i16(s) != 474:
raise SyntaxError("not an SGI image file")
raise ValueError("Not an SGI image file")
# relevant header entries
compression = i8(s[2])
@ -60,22 +61,22 @@ class SgiImageFile(ImageFile.ImageFile):
elif layout == (1, 3, 4):
self.mode = "RGBA"
else:
raise SyntaxError("unsupported SGI image mode")
raise ValueError("Unsupported SGI image mode")
# size
self.size = i16(s[6:]), i16(s[8:])
# decoder info
if compression == 0:
offset = 512
pagesize = self.size[0]*self.size[1]*layout[0]
self.tile = []
for layer in self.mode:
self.tile.append(("raw", (0,0)+self.size, offset, (layer,0,-1)))
self.tile.append(
("raw", (0, 0)+self.size, offset, (layer, 0, -1)))
offset = offset + pagesize
elif compression == 1:
self.tile = [("sgi_rle", (0,0)+self.size, 512, (self.mode, 0, -1))]
raise ValueError("SGI RLE encoding not supported")
#
# registry
@ -85,5 +86,6 @@ Image.register_open("SGI", SgiImageFile, _accept)
Image.register_extension("SGI", ".bw")
Image.register_extension("SGI", ".rgb")
Image.register_extension("SGI", ".rgba")
Image.register_extension("SGI", ".sgi")
Image.register_extension("SGI", ".sgi") # really?
# End of file

View File

@ -29,6 +29,7 @@ i32 = _binary.i32be
def _accept(prefix):
return i32(prefix) == 0x59a66a95
##
# Image plugin for Sun raster files.
@ -70,9 +71,9 @@ class SunImageFile(ImageFile.ImageFile):
stride = (((self.size[0] * depth + 7) // 8) + 3) & (~3)
if compression == 1:
self.tile = [("raw", (0,0)+self.size, offset, (rawmode, stride))]
self.tile = [("raw", (0, 0)+self.size, offset, (rawmode, stride))]
elif compression == 2:
self.tile = [("sun_rle", (0,0)+self.size, offset, rawmode)]
self.tile = [("sun_rle", (0, 0)+self.size, offset, rawmode)]
#
# registry

View File

@ -29,6 +29,7 @@ xpm_head = re.compile(b"\"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)")
def _accept(prefix):
return prefix[:9] == b"/* XPM */"
##
# Image plugin for X11 pixel maps.
@ -86,9 +87,9 @@ class XpmImageFile(ImageFile.ImageFile):
elif rgb[0:1] == b"#":
# FIXME: handle colour names (see ImagePalette.py)
rgb = int(rgb[1:], 16)
palette[c] = o8((rgb >> 16) & 255) +\
o8((rgb >> 8) & 255) +\
o8(rgb & 255)
palette[c] = (o8((rgb >> 16) & 255) +
o8((rgb >> 8) & 255) +
o8(rgb & 255))
else:
# unknown colour
raise ValueError("cannot read this XPM file")

View File

@ -19,6 +19,10 @@ class PillowTestCase(unittest.TestCase):
# holds last result object passed to run method:
self.currentResult = None
# Nicer output for --verbose
def __str__(self):
return self.__class__.__name__ + "." + self._testMethodName
def run(self, result=None):
self.currentResult = result # remember result for use later
unittest.TestCase.run(self, result) # call superclass run method

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
Tests/images/lena.bw Normal file

Binary file not shown.

BIN
Tests/images/lena.dcx Normal file

Binary file not shown.

BIN
Tests/images/lena.ras Normal file

Binary file not shown.

BIN
Tests/images/lena.rgb Normal file

Binary file not shown.

Binary file not shown.

27
Tests/test_file_cur.py Normal file
View File

@ -0,0 +1,27 @@
from helper import unittest, PillowTestCase
from PIL import Image, CurImagePlugin
class TestFileCur(PillowTestCase):
def test_sanity(self):
# Arrange
test_file = "Tests/images/deerstalker.cur"
# Act
im = Image.open(test_file)
# Assert
self.assertEqual(im.size, (32, 32))
self.assertIsInstance(im, CurImagePlugin.CurImageFile)
# Check some pixel colors to ensure image is loaded properly
self.assertEqual(im.getpixel((10, 1)), (0, 0, 0))
self.assertEqual(im.getpixel((11, 1)), (253, 254, 254))
self.assertEqual(im.getpixel((16, 16)), (84, 87, 86))
if __name__ == '__main__':
unittest.main()
# End of file

45
Tests/test_file_dcx.py Normal file
View File

@ -0,0 +1,45 @@
from helper import unittest, PillowTestCase, lena
from PIL import Image, DcxImagePlugin
# Created with ImageMagick: convert lena.ppm lena.dcx
TEST_FILE = "Tests/images/lena.dcx"
class TestFileDcx(PillowTestCase):
def test_sanity(self):
# Arrange
# Act
im = Image.open(TEST_FILE)
# Assert
self.assertEqual(im.size, (128, 128))
self.assertIsInstance(im, DcxImagePlugin.DcxImageFile)
orig = lena()
self.assert_image_equal(im, orig)
def test_tell(self):
# Arrange
im = Image.open(TEST_FILE)
# Act
frame = im.tell()
# Assert
self.assertEqual(frame, 0)
def test_seek_too_far(self):
# Arrange
im = Image.open(TEST_FILE)
frame = 999 # too big on purpose
# Act / Assert
self.assertRaises(EOFError, lambda: im.seek(frame))
if __name__ == '__main__':
unittest.main()
# End of file

39
Tests/test_file_sgi.py Normal file
View File

@ -0,0 +1,39 @@
from helper import unittest, PillowTestCase
from PIL import Image
class TestFileSgi(PillowTestCase):
def test_rgb(self):
# Arrange
# Created with ImageMagick then renamed:
# convert lena.ppm lena.sgi
test_file = "Tests/images/lena.rgb"
# Act / Assert
self.assertRaises(ValueError, lambda: Image.open(test_file))
def test_l(self):
# Arrange
# Created with ImageMagick then renamed:
# convert lena.ppm -monochrome lena.sgi
test_file = "Tests/images/lena.bw"
# Act / Assert
self.assertRaises(ValueError, lambda: Image.open(test_file))
def test_rgba(self):
# Arrange
# Created with ImageMagick:
# convert transparent.png transparent.sgi
test_file = "Tests/images/transparent.sgi"
# Act / Assert
self.assertRaises(ValueError, lambda: Image.open(test_file))
if __name__ == '__main__':
unittest.main()
# End of file

View File

@ -3,13 +3,13 @@ from helper import unittest, PillowTestCase, lena
from PIL import Image
from PIL import SpiderImagePlugin
test_file = "Tests/images/lena.spider"
TEST_FILE = "Tests/images/lena.spider"
class TestImageSpider(PillowTestCase):
def test_sanity(self):
im = Image.open(test_file)
im = Image.open(TEST_FILE)
im.load()
self.assertEqual(im.mode, "F")
self.assertEqual(im.size, (128, 128))
@ -30,7 +30,50 @@ class TestImageSpider(PillowTestCase):
self.assertEqual(im2.format, "SPIDER")
def test_isSpiderImage(self):
self.assertTrue(SpiderImagePlugin.isSpiderImage(test_file))
self.assertTrue(SpiderImagePlugin.isSpiderImage(TEST_FILE))
def test_tell(self):
# Arrange
im = Image.open(TEST_FILE)
# Act
index = im.tell()
# Assert
self.assertEqual(index, 0)
def test_loadImageSeries(self):
# Arrange
not_spider_file = "Tests/images/lena.ppm"
file_list = [TEST_FILE, not_spider_file, "path/not_found.ext"]
# Act
img_list = SpiderImagePlugin.loadImageSeries(file_list)
# Assert
self.assertEqual(len(img_list), 1)
self.assertIsInstance(img_list[0], Image.Image)
self.assertEqual(img_list[0].size, (128, 128))
def test_loadImageSeries_no_input(self):
# Arrange
file_list = None
# Act
img_list = SpiderImagePlugin.loadImageSeries(file_list)
# Assert
self.assertEqual(img_list, None)
def test_isInt_not_a_number(self):
# Arrange
not_a_number = "a"
# Act
ret = SpiderImagePlugin.isInt(not_a_number)
# Assert
self.assertEqual(ret, 0)
if __name__ == '__main__':

23
Tests/test_file_sun.py Normal file
View File

@ -0,0 +1,23 @@
from helper import unittest, PillowTestCase
from PIL import Image
class TestFileSun(PillowTestCase):
def test_sanity(self):
# Arrange
# Created with ImageMagick: convert lena.ppm lena.ras
test_file = "Tests/images/lena.ras"
# Act
im = Image.open(test_file)
# Assert
self.assertEqual(im.size, (128, 128))
if __name__ == '__main__':
unittest.main()
# End of file

View File

@ -1,21 +1,34 @@
from helper import unittest, PillowTestCase
from helper import unittest, PillowTestCase, lena
from PIL import Image
# sample ppm stream
file = "Tests/images/lena.xpm"
data = open(file, "rb").read()
TEST_FILE = "Tests/images/lena.xpm"
class TestFileXpm(PillowTestCase):
def test_sanity(self):
im = Image.open(file)
im = Image.open(TEST_FILE)
im.load()
self.assertEqual(im.mode, "P")
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "XPM")
#large error due to quantization->44 colors.
self.assert_image_similar(im.convert('RGB'), lena('RGB'), 60)
def test_load_read(self):
# Arrange
im = Image.open(TEST_FILE)
dummy_bytes = 1
# Act
data = im.load_read(dummy_bytes)
# Assert
self.assertEqual(len(data), 16384)
if __name__ == '__main__':
unittest.main()

View File

@ -1,4 +1,4 @@
from helper import unittest, PillowTestCase
from helper import unittest, PillowTestCase, lena
from PIL import Image
@ -55,6 +55,91 @@ class TestImage(PillowTestCase):
self.assertFalse(item == None)
self.assertFalse(item == num)
def test_expand_x(self):
# Arrange
im = lena()
orig_size = im.size
xmargin = 5
# Act
im = im._expand(xmargin)
# Assert
self.assertEqual(im.size[0], orig_size[0] + 2*xmargin)
self.assertEqual(im.size[1], orig_size[1] + 2*xmargin)
def test_expand_xy(self):
# Arrange
im = lena()
orig_size = im.size
xmargin = 5
ymargin = 3
# Act
im = im._expand(xmargin, ymargin)
# Assert
self.assertEqual(im.size[0], orig_size[0] + 2*xmargin)
self.assertEqual(im.size[1], orig_size[1] + 2*ymargin)
def test_getbands(self):
# Arrange
im = lena()
# Act
bands = im.getbands()
# Assert
self.assertEqual(bands, ('R', 'G', 'B'))
def test_getbbox(self):
# Arrange
im = lena()
# Act
bbox = im.getbbox()
# Assert
self.assertEqual(bbox, (0, 0, 128, 128))
def test_ne(self):
# Arrange
im1 = Image.new('RGB', (25, 25), 'black')
im2 = Image.new('RGB', (25, 25), 'white')
# Act / Assert
self.assertTrue(im1 != im2)
def test_alpha_composite(self):
# http://stackoverflow.com/questions/3374878
# Arrange
from PIL import ImageDraw
expected_colors = sorted([
(1122, (128, 127, 0, 255)),
(1089, (0, 255, 0, 255)),
(3300, (255, 0, 0, 255)),
(1156, (170, 85, 0, 192)),
(1122, (0, 255, 0, 128)),
(1122, (255, 0, 0, 128)),
(1089, (0, 255, 0, 0))])
dst = Image.new('RGBA', size=(100, 100), color=(0, 255, 0, 255))
draw = ImageDraw.Draw(dst)
draw.rectangle((0, 33, 100, 66), fill=(0, 255, 0, 128))
draw.rectangle((0, 67, 100, 100), fill=(0, 255, 0, 0))
src = Image.new('RGBA', size=(100, 100), color=(255, 0, 0, 255))
draw = ImageDraw.Draw(src)
draw.rectangle((33, 0, 66, 100), fill=(255, 0, 0, 128))
draw.rectangle((67, 0, 100, 100), fill=(255, 0, 0, 0))
# Act
img = Image.alpha_composite(dst, src)
# Assert
img_colors = sorted(img.getcolors())
self.assertEqual(img_colors, expected_colors)
if __name__ == '__main__':
unittest.main()

View File

@ -5,7 +5,7 @@ from PIL import Image
im = lena().resize((128, 100))
class TestImageCrop(PillowTestCase):
class TestImageArray(PillowTestCase):
def test_toarray(self):
def test(mode):

View File

@ -29,6 +29,10 @@ class TestImageFilter(PillowTestCase):
filter(ImageFilter.MinFilter)
filter(ImageFilter.ModeFilter)
filter(ImageFilter.Kernel((3, 3), list(range(9))))
filter(ImageFilter.GaussianBlur)
filter(ImageFilter.GaussianBlur(5))
filter(ImageFilter.UnsharpMask)
filter(ImageFilter.UnsharpMask(10))
self.assertRaises(TypeError, lambda: filter("hello"))

View File

@ -10,6 +10,27 @@ class TestImageMode(PillowTestCase):
im = lena()
im.mode
from PIL import ImageMode
ImageMode.getmode("1")
ImageMode.getmode("L")
ImageMode.getmode("P")
ImageMode.getmode("RGB")
ImageMode.getmode("I")
ImageMode.getmode("F")
m = ImageMode.getmode("1")
self.assertEqual(m.mode, "1")
self.assertEqual(m.bands, ("1",))
self.assertEqual(m.basemode, "L")
self.assertEqual(m.basetype, "L")
m = ImageMode.getmode("RGB")
self.assertEqual(m.mode, "RGB")
self.assertEqual(m.bands, ("R", "G", "B"))
self.assertEqual(m.basemode, "RGB")
self.assertEqual(m.basetype, "L")
def test_properties(self):
def check(mode, *result):
signature = (

View File

@ -1,7 +1,7 @@
from helper import unittest, lena
from helper import unittest, PillowTestCase, lena
class TestImageToBytes(unittest.TestCase):
class TestImageToBytes(PillowTestCase):
def test_sanity(self):
data = lena().tobytes()

View File

@ -5,6 +5,22 @@ from PIL import Image
class TestImageTransform(PillowTestCase):
def test_sanity(self):
from PIL import ImageTransform
im = Image.new("L", (100, 100))
seq = tuple(range(10))
transform = ImageTransform.AffineTransform(seq[:6])
im.transform((100, 100), transform)
transform = ImageTransform.ExtentTransform(seq[:4])
im.transform((100, 100), transform)
transform = ImageTransform.QuadTransform(seq[:8])
im.transform((100, 100), transform)
transform = ImageTransform.MeshTransform([(seq[:4], seq[:8])])
im.transform((100, 100), transform)
def test_extent(self):
im = lena('RGB')
(w, h) = im.size

View File

@ -14,7 +14,7 @@ MAXBLOCK = ImageFile.MAXBLOCK
SAFEBLOCK = ImageFile.SAFEBLOCK
class TestImagePutData(PillowTestCase):
class TestImageFile(PillowTestCase):
def test_parser(self):

View File

@ -1,37 +0,0 @@
from helper import unittest, PillowTestCase
from PIL import ImageFilter
class TestImageFilter(PillowTestCase):
def test_sanity(self):
# see test_image_filter for more tests
# Check these run. Exceptions cause failures.
ImageFilter.MaxFilter
ImageFilter.MedianFilter
ImageFilter.MinFilter
ImageFilter.ModeFilter
ImageFilter.Kernel((3, 3), list(range(9)))
ImageFilter.GaussianBlur
ImageFilter.GaussianBlur(5)
ImageFilter.UnsharpMask
ImageFilter.UnsharpMask(10)
ImageFilter.BLUR
ImageFilter.CONTOUR
ImageFilter.DETAIL
ImageFilter.EDGE_ENHANCE
ImageFilter.EDGE_ENHANCE_MORE
ImageFilter.EMBOSS
ImageFilter.FIND_EDGES
ImageFilter.SMOOTH
ImageFilter.SMOOTH_MORE
ImageFilter.SHARPEN
if __name__ == '__main__':
unittest.main()
# End of file

View File

@ -3,7 +3,7 @@ from helper import unittest, PillowTestCase
try:
from PIL import ImageGrab
class TestImageCopy(PillowTestCase):
class TestImageGrab(PillowTestCase):
def test_grab(self):
im = ImageGrab.grab()
@ -14,7 +14,7 @@ try:
self.assert_image(im, im.mode, im.size)
except ImportError:
class TestImageCopy(PillowTestCase):
class TestImageGrab(PillowTestCase):
def test_skip(self):
self.skipTest("ImportError")

View File

@ -1,32 +0,0 @@
from helper import unittest, PillowTestCase
from PIL import ImageMode
class TestImageMode(PillowTestCase):
def test_sanity(self):
ImageMode.getmode("1")
ImageMode.getmode("L")
ImageMode.getmode("P")
ImageMode.getmode("RGB")
ImageMode.getmode("I")
ImageMode.getmode("F")
m = ImageMode.getmode("1")
self.assertEqual(m.mode, "1")
self.assertEqual(m.bands, ("1",))
self.assertEqual(m.basemode, "L")
self.assertEqual(m.basetype, "L")
m = ImageMode.getmode("RGB")
self.assertEqual(m.mode, "RGB")
self.assertEqual(m.bands, ("R", "G", "B"))
self.assertEqual(m.basemode, "RGB")
self.assertEqual(m.basetype, "L")
if __name__ == '__main__':
unittest.main()
# End of file

View File

@ -44,6 +44,109 @@ class TestImagePalette(PillowTestCase):
self.assertIsInstance(p, ImagePalette)
self.assertEqual(p.palette, palette.tobytes())
def test_make_linear_lut(self):
# Arrange
from PIL.ImagePalette import make_linear_lut
black = 0
white = 255
# Act
lut = make_linear_lut(black, white)
# Assert
self.assertIsInstance(lut, list)
self.assertEqual(len(lut), 256)
# Check values
for i in range(0, len(lut)):
self.assertEqual(lut[i], i)
def test_make_linear_lut_not_yet_implemented(self):
# Update after FIXME
# Arrange
from PIL.ImagePalette import make_linear_lut
black = 1
white = 255
# Act
self.assertRaises(
NotImplementedError,
lambda: make_linear_lut(black, white))
def test_make_gamma_lut(self):
# Arrange
from PIL.ImagePalette import make_gamma_lut
exp = 5
# Act
lut = make_gamma_lut(exp)
# Assert
self.assertIsInstance(lut, list)
self.assertEqual(len(lut), 256)
# Check a few values
self.assertEqual(lut[0], 0)
self.assertEqual(lut[63], 0)
self.assertEqual(lut[127], 8)
self.assertEqual(lut[191], 60)
self.assertEqual(lut[255], 255)
def test_private_make_linear_lut_warning(self):
# Arrange
from PIL.ImagePalette import _make_linear_lut
black = 0
white = 255
# Act / Assert
self.assert_warning(
DeprecationWarning,
lambda: _make_linear_lut(black, white))
def test_private_make_gamma_lut_warning(self):
# Arrange
from PIL.ImagePalette import _make_gamma_lut
exp = 5
# Act / Assert
self.assert_warning(
DeprecationWarning,
lambda: _make_gamma_lut(exp))
def test_rawmode_valueerrors(self):
# Arrange
from PIL.ImagePalette import raw
palette = raw("RGB", list(range(256))*3)
# Act / Assert
self.assertRaises(ValueError, lambda: palette.tobytes())
self.assertRaises(ValueError, lambda: palette.getcolor((1, 2, 3)))
f = self.tempfile("temp.lut")
self.assertRaises(ValueError, lambda: palette.save(f))
def test_getdata(self):
# Arrange
data_in = list(range(256))*3
palette = ImagePalette("RGB", data_in)
# Act
mode, data_out = palette.getdata()
# Assert
self.assertEqual(mode, "RGB;L")
def test_rawmode_getdata(self):
# Arrange
from PIL.ImagePalette import raw
data_in = list(range(256))*3
palette = raw("RGB", data_in)
# Act
rawmode, data_out = palette.getdata()
# Assert
self.assertEqual(rawmode, "RGB")
self.assertEqual(data_in, data_out)
if __name__ == '__main__':
unittest.main()

View File

@ -1,27 +0,0 @@
from helper import unittest, PillowTestCase
from PIL import Image
from PIL import ImageTransform
class TestImageTransform(PillowTestCase):
def test_sanity(self):
im = Image.new("L", (100, 100))
seq = tuple(range(10))
transform = ImageTransform.AffineTransform(seq[:6])
im.transform((100, 100), transform)
transform = ImageTransform.ExtentTransform(seq[:4])
im.transform((100, 100), transform)
transform = ImageTransform.QuadTransform(seq[:8])
im.transform((100, 100), transform)
transform = ImageTransform.MeshTransform([(seq[:4], seq[:8])])
im.transform((100, 100), transform)
if __name__ == '__main__':
unittest.main()
# End of file

View File

@ -3,7 +3,7 @@ from helper import unittest, PillowTestCase
from PIL import Image
class TestSanity(PillowTestCase):
class TestLibImage(PillowTestCase):
def test_setmode(self):

View File

@ -7,7 +7,7 @@ except ImportError:
pass
class TestPyroma(unittest.TestCase):
class TestPyroma(PillowTestCase):
def setUp(self):
try:

View File

@ -6,6 +6,8 @@
* http://www.cazabon.com
* Adapted/reworked for PIL by Fredrik Lundh
* Copyright (c) 2009 Fredrik Lundh
* Updated to LCMS2
* Copyright (c) 2013 Eric Soroos
*
* pyCMS home page: http://www.cazabon.com/pyCMS
* littleCMS home page: http://www.littlecms.com

View File

@ -52,15 +52,8 @@ can be found here.
:undoc-members:
:show-inheritance:
:mod:`ImageCms` Module
----------------------
.. automodule:: PIL.ImageCms
:members:
:undoc-members:
:show-inheritance:
.. intentionally skipped documenting this because it's not documented anywhere
:mod:`ImageDraw2` Module
------------------------
@ -70,6 +63,7 @@ can be found here.
:show-inheritance:
.. intentionally skipped documenting this because it's deprecated
:mod:`ImageFileIO` Module
-------------------------
@ -78,13 +72,6 @@ can be found here.
:undoc-members:
:show-inheritance:
:mod:`ImageMorph` Module
------------------------
.. automodule:: PIL.ImageMorph
:members:
:undoc-members:
:show-inheritance:
:mod:`ImageShow` Module
-----------------------
@ -110,14 +97,6 @@ can be found here.
:undoc-members:
:show-inheritance:
:mod:`OleFileIO` Module
-----------------------
.. automodule:: PIL.OleFileIO
:members:
:undoc-members:
:show-inheritance:
:mod:`PaletteFile` Module
-------------------------

View File

@ -49,7 +49,10 @@ Functions
.. autofunction:: open
.. warning:: > To protect against potential DOS attacks caused by "`decompression bombs<https://en.wikipedia.org/wiki/Zip_bomb>`_" (i.e. malicious files which decompress into a huge amount of data and are designed to crash or cause disruption by using up a lot of memory), Pillow will issue a `DecompressionBombWarning` if the image is over a certain limit. If desired, the warning can be turned into an error with `warnings.simplefilter('error', Image.DecompressionBombWarning)` or suppressed entirely with `warnings.simplefilter('ignore', Image.DecompressionBombWarning)`. See also `the logging documentation<https://docs.python.org/2/library/logging.html?highlight=logging#integration-with-the-warnings-module>`_ to have warnings output to the logging facility instead of stderr.
.. warning:: To protect against potential DOS attacks caused by "`decompression bombs`_" (i.e. malicious files which decompress into a huge amount of data and are designed to crash or cause disruption by using up a lot of memory), Pillow will issue a `DecompressionBombWarning` if the image is over a certain limit. If desired, the warning can be turned into an error with `warnings.simplefilter('error', Image.DecompressionBombWarning)` or suppressed entirely with `warnings.simplefilter('ignore', Image.DecompressionBombWarning)`. See also `the logging documentation`_ to have warnings output to the logging facility instead of stderr.
.. _decompression bombs: https://en.wikipedia.org/wiki/Zip_bomb
.. _the logging documentation: https://docs.python.org/2/library/logging.html?highlight=logging#integration-with-the-warnings-module
Image processing
^^^^^^^^^^^^^^^^

View File

@ -0,0 +1,13 @@
.. py:module:: PIL.ImageCms
.. py:currentmodule:: PIL.ImageCms
:py:mod:`ImageCms` Module
=========================
The :py:mod:`ImageCms` module provides color profile management
support using the LittleCMS2 color management engine, based on Kevin
Cazabon's PyCMS library.
.. automodule:: PIL.ImageCms
:members:
:noindex:

View File

@ -74,6 +74,34 @@ To load a OpenType/TrueType font, use the truetype function in the
:py:mod:`~PIL.ImageFont` module. Note that this function depends on third-party
libraries, and may not available in all PIL builds.
Example: Draw Partial Opacity Text
----------------------------------
.. code-block:: python
from PIL import Image, ImageDraw, ImageFont
# get an image
base = Image.open('Pillow/Tests/images/lena.png').convert('RGBA')
# make a blank image for the text, initialized to transparent text color
txt = Image.new('RGBA', base.size, (255,255,255,0))
# get a font
fnt = ImageFont.truetype('Pillow/Tests/fonts/FreeMono.ttf', 40)
# get a drawing context
d = ImageDraw.Draw(txt)
# draw text, half opacity
d.text((10,10), "Hello", font=fnt, fill=(255,255,255,128))
# draw text, full opacity
d.text((10,60), "World", font=fnt, fill=(255,255,255,255))
out = Image.alpha_composite(base, txt)
out.show()
Functions
---------
@ -83,6 +111,13 @@ Functions
Note that the image will be modified in place.
:param im: The image to draw in.
:param mode: Optional mode to use for color values. For RGB
images, this argument can be RGB or RGBA (to blend the
drawing into the image). For all other modes, this argument
must be the same as the image mode. If omitted, the mode
defaults to the mode of the image.
Methods
-------

View File

@ -0,0 +1,13 @@
.. py:module:: PIL.ImageMorph
.. py:currentmodule:: PIL.ImageMorph
:py:mod:`ImageMorph` Module
===========================
The :py:mod:`ImageMorph` module provides morphology operations on images.
.. automodule:: PIL.ImageMorph
:members:
:undoc-members:
:show-inheritance:
:noindex:

View File

@ -22,32 +22,32 @@ for a region of an image.
.. py:attribute:: count
Total number of pixels.
Total number of pixels for each band in the image.
.. py:attribute:: sum
Sum of all pixels.
Sum of all pixels for each band in the image.
.. py:attribute:: sum2
Squared sum of all pixels.
Squared sum of all pixels for each band in the image.
.. py:attribute:: pixel
.. py:attribute:: mean
Average pixel level.
Average (arithmetic mean) pixel level for each band in the image.
.. py:attribute:: median
Median pixel level.
Median pixel level for each band in the image.
.. py:attribute:: rms
RMS (root-mean-square).
RMS (root-mean-square) for each band in the image.
.. py:attribute:: var
Variance.
Variance for each band in the image.
.. py:attribute:: stddev
Standard deviation.
Standard deviation for each band in the image.

View File

@ -0,0 +1,364 @@
.. py:module:: PIL.OleFileIO
.. py:currentmodule:: PIL.OleFileIO
:py:mod:`OleFileIO` Module
===========================
The :py:mod:`OleFileIO` module reads Microsoft OLE2 files (also called
Structured Storage or Microsoft Compound Document File Format), such
as Microsoft Office documents, Image Composer and FlashPix files, and
Outlook messages.
This module is the `OleFileIO\_PL`_ project by Philippe Lagadec, v0.30,
merged back into Pillow.
.. _OleFileIO\_PL: http://www.decalage.info/python/olefileio
How to use this module
----------------------
For more information, see also the file **PIL/OleFileIO.py**, sample
code at the end of the module itself, and docstrings within the code.
About the structure of OLE files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
An OLE file can be seen as a mini file system or a Zip archive: It
contains **streams** of data that look like files embedded within the
OLE file. Each stream has a name. For example, the main stream of a MS
Word document containing its text is named "WordDocument".
An OLE file can also contain **storages**. A storage is a folder that
contains streams or other storages. For example, a MS Word document with
VBA macros has a storage called "Macros".
Special streams can contain **properties**. A property is a specific
value that can be used to store information such as the metadata of a
document (title, author, creation date, etc). Property stream names
usually start with the character '05'.
For example, a typical MS Word document may look like this:
::
\x05DocumentSummaryInformation (stream)
\x05SummaryInformation (stream)
WordDocument (stream)
Macros (storage)
PROJECT (stream)
PROJECTwm (stream)
VBA (storage)
Module1 (stream)
ThisDocument (stream)
_VBA_PROJECT (stream)
dir (stream)
ObjectPool (storage)
Test if a file is an OLE container
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Use isOleFile to check if the first bytes of the file contain the Magic
for OLE files, before opening it. isOleFile returns True if it is an OLE
file, False otherwise.
.. code-block:: python
assert OleFileIO.isOleFile('myfile.doc')
Open an OLE file from disk
~~~~~~~~~~~~~~~~~~~~~~~~~~
Create an OleFileIO object with the file path as parameter:
.. code-block:: python
ole = OleFileIO.OleFileIO('myfile.doc')
Open an OLE file from a file-like object
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This is useful if the file is not on disk, e.g. already stored in a
string or as a file-like object.
.. code-block:: python
ole = OleFileIO.OleFileIO(f)
For example the code below reads a file into a string, then uses BytesIO
to turn it into a file-like object.
.. code-block:: python
data = open('myfile.doc', 'rb').read()
f = io.BytesIO(data) # or StringIO.StringIO for Python 2.x
ole = OleFileIO.OleFileIO(f)
How to handle malformed OLE files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
By default, the parser is configured to be as robust and permissive as
possible, allowing to parse most malformed OLE files. Only fatal errors
will raise an exception. It is possible to tell the parser to be more
strict in order to raise exceptions for files that do not fully conform
to the OLE specifications, using the raise\_defect option:
.. code-block:: python
ole = OleFileIO.OleFileIO('myfile.doc', raise_defects=DEFECT_INCORRECT)
When the parsing is done, the list of non-fatal issues detected is
available as a list in the parsing\_issues attribute of the OleFileIO
object:
.. code-block:: python
print('Non-fatal issues raised during parsing:')
if ole.parsing_issues:
for exctype, msg in ole.parsing_issues:
print('- %s: %s' % (exctype.__name__, msg))
else:
print('None')
Syntax for stream and storage path
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Two different syntaxes are allowed for methods that need or return the
path of streams and storages:
1) Either a **list of strings** including all the storages from the root
up to the stream/storage name. For example a stream called
"WordDocument" at the root will have ['WordDocument'] as full path. A
stream called "ThisDocument" located in the storage "Macros/VBA" will
be ['Macros', 'VBA', 'ThisDocument']. This is the original syntax
from PIL. While hard to read and not very convenient, this syntax
works in all cases.
2) Or a **single string with slashes** to separate storage and stream
names (similar to the Unix path syntax). The previous examples would
be 'WordDocument' and 'Macros/VBA/ThisDocument'. This syntax is
easier, but may fail if a stream or storage name contains a slash.
Both are case-insensitive.
Switching between the two is easy:
.. code-block:: python
slash_path = '/'.join(list_path)
list_path = slash_path.split('/')
Get the list of streams
~~~~~~~~~~~~~~~~~~~~~~~
listdir() returns a list of all the streams contained in the OLE file,
including those stored in storages. Each stream is listed itself as a
list, as described above.
.. code-block:: python
print(ole.listdir())
Sample result:
.. code-block:: python
[['\x01CompObj'], ['\x05DocumentSummaryInformation'], ['\x05SummaryInformation']
, ['1Table'], ['Macros', 'PROJECT'], ['Macros', 'PROJECTwm'], ['Macros', 'VBA',
'Module1'], ['Macros', 'VBA', 'ThisDocument'], ['Macros', 'VBA', '_VBA_PROJECT']
, ['Macros', 'VBA', 'dir'], ['ObjectPool'], ['WordDocument']]
As an option it is possible to choose if storages should also be listed,
with or without streams:
.. code-block:: python
ole.listdir (streams=False, storages=True)
Test if known streams/storages exist:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
exists(path) checks if a given stream or storage exists in the OLE file.
.. code-block:: python
if ole.exists('worddocument'):
print("This is a Word document.")
if ole.exists('macros/vba'):
print("This document seems to contain VBA macros.")
Read data from a stream
~~~~~~~~~~~~~~~~~~~~~~~
openstream(path) opens a stream as a file-like object.
The following example extracts the "Pictures" stream from a PPT file:
.. code-block:: python
pics = ole.openstream('Pictures')
data = pics.read()
Get information about a stream/storage
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Several methods can provide the size, type and timestamps of a given
stream/storage:
get\_size(path) returns the size of a stream in bytes:
.. code-block:: python
s = ole.get_size('WordDocument')
get\_type(path) returns the type of a stream/storage, as one of the
following constants: STGTY\_STREAM for a stream, STGTY\_STORAGE for a
storage, STGTY\_ROOT for the root entry, and False for a non existing
path.
.. code-block:: python
t = ole.get_type('WordDocument')
get\_ctime(path) and get\_mtime(path) return the creation and
modification timestamps of a stream/storage, as a Python datetime object
with UTC timezone. Please note that these timestamps are only present if
the application that created the OLE file explicitly stored them, which
is rarely the case. When not present, these methods return None.
.. code-block:: python
c = ole.get_ctime('WordDocument')
m = ole.get_mtime('WordDocument')
The root storage is a special case: You can get its creation and
modification timestamps using the OleFileIO.root attribute:
.. code-block:: python
c = ole.root.getctime()
m = ole.root.getmtime()
Extract metadata
~~~~~~~~~~~~~~~~
get\_metadata() will check if standard property streams exist, parse all
the properties they contain, and return an OleMetadata object with the
found properties as attributes.
.. code-block:: python
meta = ole.get_metadata()
print('Author:', meta.author)
print('Title:', meta.title)
print('Creation date:', meta.create_time)
# print all metadata:
meta.dump()
Available attributes include:
::
codepage, title, subject, author, keywords, comments, template,
last_saved_by, revision_number, total_edit_time, last_printed, create_time,
last_saved_time, num_pages, num_words, num_chars, thumbnail,
creating_application, security, codepage_doc, category, presentation_target,
bytes, lines, paragraphs, slides, notes, hidden_slides, mm_clips,
scale_crop, heading_pairs, titles_of_parts, manager, company, links_dirty,
chars_with_spaces, unused, shared_doc, link_base, hlinks, hlinks_changed,
version, dig_sig, content_type, content_status, language, doc_version
See the source code of the OleMetadata class for more information.
Parse a property stream
~~~~~~~~~~~~~~~~~~~~~~~
get\_properties(path) can be used to parse any property stream that is
not handled by get\_metadata. It returns a dictionary indexed by
integers. Each integer is the index of the property, pointing to its
value. For example in the standard property stream
'05SummaryInformation', the document title is property #2, and the
subject is #3.
.. code-block:: python
p = ole.getproperties('specialprops')
By default as in the original PIL version, timestamp properties are
converted into a number of seconds since Jan 1,1601. With the option
convert\_time, you can obtain more convenient Python datetime objects
(UTC timezone). If some time properties should not be converted (such as
total editing time in '05SummaryInformation'), the list of indexes can
be passed as no\_conversion:
.. code-block:: python
p = ole.getproperties('specialprops', convert_time=True, no_conversion=[10])
Close the OLE file
~~~~~~~~~~~~~~~~~~
Unless your application is a simple script that terminates after
processing an OLE file, do not forget to close each OleFileIO object
after parsing to close the file on disk.
.. code-block:: python
ole.close()
Use OleFileIO as a script
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OleFileIO can also be used as a script from the command-line to
display the structure of an OLE file and its metadata, for example:
::
PIL/OleFileIO.py myfile.doc
You can use the option -c to check that all streams can be read fully,
and -d to generate very verbose debugging information.
How to contribute
-----------------
The code is available in `a Mercurial repository on
bitbucket <https://bitbucket.org/decalage/olefileio_pl>`_. You may use
it to submit enhancements or to report any issue.
If you would like to help us improve this module, or simply provide
feedback, please `contact me <http://decalage.info/contact>`_. You can
help in many ways:
- test this module on different platforms / Python versions
- find and report bugs
- improve documentation, code samples, docstrings
- write unittest test cases
- provide tricky malformed files
How to report bugs
------------------
To report a bug, for example a normal file which is not parsed
correctly, please use the `issue reporting
page <https://bitbucket.org/decalage/olefileio_pl/issues?status=new&status=open>`_,
or if you prefer to do it privately, use this `contact
form <http://decalage.info/contact>`_. Please provide all the
information about the context and how to reproduce the bug.
If possible please join the debugging output of OleFileIO. For this,
launch the following command :
::
PIL/OleFileIO.py -d -c file >debug.txt
Classes and Methods
-------------------
.. automodule:: PIL.OleFileIO
:members:
:undoc-members:
:show-inheritance:
:noindex:

View File

@ -8,6 +8,7 @@ Reference
Image
ImageChops
ImageColor
ImageCms
ImageDraw
ImageEnhance
ImageFile
@ -15,6 +16,7 @@ Reference
ImageFont
ImageGrab
ImageMath
ImageMorph
ImageOps
ImagePalette
ImagePath
@ -24,5 +26,6 @@ Reference
ImageTk
ImageWin
ExifTags
OleFileIO
PSDraw
../PIL