mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-26 17:24:31 +03:00
Merge branch 'master' of https://github.com/python-pillow/Pillow
This commit is contained in:
commit
6605bf22e5
|
@ -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
|
||||
|
|
14
CHANGES.rst
14
CHANGES.rst
|
@ -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
|
||||
|
|
21
Makefile
21
Makefile
|
@ -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&
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
197
PIL/OleFileIO.py
197
PIL/OleFileIO.py
|
@ -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:
|
||||
|
|
|
@ -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 ***")
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
BIN
Tests/images/deerstalker.cur
Normal file
BIN
Tests/images/deerstalker.cur
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
BIN
Tests/images/lena.bw
Normal file
BIN
Tests/images/lena.bw
Normal file
Binary file not shown.
BIN
Tests/images/lena.dcx
Normal file
BIN
Tests/images/lena.dcx
Normal file
Binary file not shown.
BIN
Tests/images/lena.ras
Normal file
BIN
Tests/images/lena.ras
Normal file
Binary file not shown.
BIN
Tests/images/lena.rgb
Normal file
BIN
Tests/images/lena.rgb
Normal file
Binary file not shown.
BIN
Tests/images/transparent.sgi
Normal file
BIN
Tests/images/transparent.sgi
Normal file
Binary file not shown.
27
Tests/test_file_cur.py
Normal file
27
Tests/test_file_cur.py
Normal 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
45
Tests/test_file_dcx.py
Normal 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
39
Tests/test_file_sgi.py
Normal 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
|
|
@ -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
23
Tests/test_file_sun.py
Normal 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
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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"))
|
||||
|
||||
|
|
|
@ -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 = (
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -14,7 +14,7 @@ MAXBLOCK = ImageFile.MAXBLOCK
|
|||
SAFEBLOCK = ImageFile.SAFEBLOCK
|
||||
|
||||
|
||||
class TestImagePutData(PillowTestCase):
|
||||
class TestImageFile(PillowTestCase):
|
||||
|
||||
def test_parser(self):
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
|
@ -3,7 +3,7 @@ from helper import unittest, PillowTestCase
|
|||
from PIL import Image
|
||||
|
||||
|
||||
class TestSanity(PillowTestCase):
|
||||
class TestLibImage(PillowTestCase):
|
||||
|
||||
def test_setmode(self):
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ except ImportError:
|
|||
pass
|
||||
|
||||
|
||||
class TestPyroma(unittest.TestCase):
|
||||
class TestPyroma(PillowTestCase):
|
||||
|
||||
def setUp(self):
|
||||
try:
|
||||
|
|
|
@ -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
|
||||
|
|
25
docs/PIL.rst
25
docs/PIL.rst
|
@ -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
|
||||
-------------------------
|
||||
|
||||
|
|
|
@ -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
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
|
13
docs/reference/ImageCms.rst
Normal file
13
docs/reference/ImageCms.rst
Normal 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:
|
|
@ -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
|
||||
-------
|
||||
|
||||
|
|
13
docs/reference/ImageMorph.rst
Normal file
13
docs/reference/ImageMorph.rst
Normal 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:
|
|
@ -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.
|
||||
|
|
364
docs/reference/OleFileIO.rst
Normal file
364
docs/reference/OleFileIO.rst
Normal 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:
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user