Merge from master, pre-unittest conversion

This commit is contained in:
wiredfool 2014-06-03 14:29:16 -07:00
commit da1523d877
44 changed files with 914 additions and 422 deletions

11
.coveragerc Normal file
View File

@ -0,0 +1,11 @@
# .coveragerc to control coverage.py
[report]
# Regexes for lines to exclude from consideration
exclude_lines =
# Have to re-enable the standard pragma:
pragma: no cover
# Don't complain if non-runnable code isn't run:
if 0:
if __name__ == .__main__.:

View File

@ -39,7 +39,7 @@ after_success:
- coverage report
- coveralls
- pip install pep8 pyflakes
- pep8 PIL/*.py
- pyflakes PIL/*.py
- pep8 Tests/*.py
- pyflakes Tests/*.py
- pep8 --statistics --count PIL/*.py
- pep8 --statistics --count Tests/*.py
- pyflakes PIL/*.py | tee >(wc -l)
- pyflakes Tests/*.py | tee >(wc -l)

View File

@ -3,6 +3,12 @@ Changelog (Pillow)
2.5.0 (unreleased)
------------------
- Added more ImageDraw tests
[hugovk]
- Added tests for Spider files
[hugovk]
- Use libtiff to write any compressed tiff files
[wiredfool]
@ -431,7 +437,7 @@ Changelog (Pillow)
- Add Python 3 support. (Pillow >= 2.0.0 supports Python 2.6, 2.7, 3.2, 3.3. Pillow < 2.0.0 supports Python 2.4, 2.5, 2.6, 2.7.)
[fluggo]
- Add PyPy support (experimental, please see: https://github.com/python-imaging/Pillow/issues/67)
- Add PyPy support (experimental, please see: https://github.com/python-pillow/Pillow/issues/67)
- Add WebP support.
[lqs]

View File

@ -1529,7 +1529,7 @@ class Image:
clockwise around its centre.
:param angle: In degrees counter clockwise.
:param filter: An optional resampling filter. This can be
:param resample: An optional resampling filter. This can be
one of :py:attr:`PIL.Image.NEAREST` (use nearest neighbour),
:py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2
environment), or :py:attr:`PIL.Image.BICUBIC`
@ -1551,7 +1551,6 @@ class Image:
-math.sin(angle), math.cos(angle), 0.0
]
def transform(x, y, matrix=matrix):
(a, b, c, d, e, f) = matrix
return a*x + b*y + c, d*x + e*y + f

View File

@ -1,19 +1,19 @@
#
# The Python Imaging Library.
# $Id$
#
# optional color managment support, based on Kevin Cazabon's PyCMS
# library.
#
# History:
# 2009-03-08 fl Added to PIL.
#
# Copyright (C) 2002-2003 Kevin Cazabon
# Copyright (c) 2009 by Fredrik Lundh
#
# See the README file for information on usage and redistribution. See
# below for the original description.
#
"""
The Python Imaging Library.
$Id$
Optional color managment support, based on Kevin Cazabon's PyCMS
library.
History:
2009-03-08 fl Added to PIL.
Copyright (C) 2002-2003 Kevin Cazabon
Copyright (c) 2009 by Fredrik Lundh
See the README file for information on usage and redistribution. See
below for the original description.
"""
from __future__ import print_function
@ -66,7 +66,8 @@ pyCMS
Added try/except statements arount type() checks of
potential CObjects... Python won't let you use type()
on them, and raises a TypeError (stupid, if you ask me!)
on them, and raises a TypeError (stupid, if you ask
me!)
Added buildProofTransformFromOpenProfiles() function.
Additional fixes in DLL, see DLL code for details.
@ -89,8 +90,8 @@ try:
except ImportError as ex:
# Allow error import for doc purposes, but error out when accessing
# anything in core.
from _util import deferred_error
_imagingcms = deferred_error(ex)
from _util import import_err
_imagingcms = import_err(ex)
from PIL._util import isStringType
core = _imagingcms
@ -113,22 +114,24 @@ DIRECTION_PROOF = 2
FLAGS = {
"MATRIXINPUT": 1,
"MATRIXOUTPUT": 2,
"MATRIXONLY": (1|2),
"NOWHITEONWHITEFIXUP": 4, # Don't hot fix scum dot
"NOPRELINEARIZATION": 16, # Don't create prelinearization tables on precalculated transforms (internal use)
"GUESSDEVICECLASS": 32, # Guess device class (for transform2devicelink)
"NOTCACHE": 64, # Inhibit 1-pixel cache
"MATRIXONLY": (1 | 2),
"NOWHITEONWHITEFIXUP": 4, # Don't hot fix scum dot
# Don't create prelinearization tables on precalculated transforms
# (internal use):
"NOPRELINEARIZATION": 16,
"GUESSDEVICECLASS": 32, # Guess device class (for transform2devicelink)
"NOTCACHE": 64, # Inhibit 1-pixel cache
"NOTPRECALC": 256,
"NULLTRANSFORM": 512, # Don't transform anyway
"HIGHRESPRECALC": 1024, # Use more memory to give better accurancy
"LOWRESPRECALC": 2048, # Use less memory to minimize resouces
"NULLTRANSFORM": 512, # Don't transform anyway
"HIGHRESPRECALC": 1024, # Use more memory to give better accurancy
"LOWRESPRECALC": 2048, # Use less memory to minimize resouces
"WHITEBLACKCOMPENSATION": 8192,
"BLACKPOINTCOMPENSATION": 8192,
"GAMUTCHECK": 4096, # Out of Gamut alarm
"SOFTPROOFING": 16384, # Do softproofing
"PRESERVEBLACK": 32768, # Black preservation
"NODEFAULTRESOURCEDEF": 16777216, # CRD special
"GRIDPOINTS": lambda n: ((n) & 0xFF) << 16 # Gridpoints
"GAMUTCHECK": 4096, # Out of Gamut alarm
"SOFTPROOFING": 16384, # Do softproofing
"PRESERVEBLACK": 32768, # Black preservation
"NODEFAULTRESOURCEDEF": 16777216, # CRD special
"GRIDPOINTS": lambda n: ((n) & 0xFF) << 16 # Gridpoints
}
_MAX_FLAG = 0
@ -136,6 +139,7 @@ for flag in FLAGS.values():
if isinstance(flag, int):
_MAX_FLAG = _MAX_FLAG | flag
# --------------------------------------------------------------------.
# Experimental PIL-level API
# --------------------------------------------------------------------.
@ -153,40 +157,42 @@ class ImageCmsProfile:
elif hasattr(profile, "read"):
self._set(core.profile_frombytes(profile.read()))
else:
self._set(profile) # assume it's already a profile
self._set(profile) # assume it's already a profile
def _set(self, profile, filename=None):
self.profile = profile
self.filename = filename
if profile:
self.product_name = None #profile.product_name
self.product_info = None #profile.product_info
self.product_name = None # profile.product_name
self.product_info = None # profile.product_info
else:
self.product_name = None
self.product_info = None
class ImageCmsTransform(Image.ImagePointHandler):
"""Transform. This can be used with the procedural API, or with the
standard Image.point() method.
"""
def __init__(self, input, output, input_mode, output_mode,
intent=INTENT_PERCEPTUAL,
proof=None, proof_intent=INTENT_ABSOLUTE_COLORIMETRIC, flags=0):
intent=INTENT_PERCEPTUAL, proof=None,
proof_intent=INTENT_ABSOLUTE_COLORIMETRIC, flags=0):
if proof is None:
self.transform = core.buildTransform(
input.profile, output.profile,
input_mode, output_mode,
intent,
flags
)
)
else:
self.transform = core.buildProofTransform(
input.profile, output.profile, proof.profile,
input_mode, output_mode,
intent, proof_intent,
flags
)
)
# Note: inputMode and outputMode are for pyCMS compatibility only
self.input_mode = self.inputMode = input_mode
self.output_mode = self.outputMode = output_mode
@ -198,16 +204,17 @@ class ImageCmsTransform(Image.ImagePointHandler):
im.load()
if imOut is None:
imOut = Image.new(self.output_mode, im.size, None)
result = self.transform.apply(im.im.id, imOut.im.id)
self.transform.apply(im.im.id, imOut.im.id)
return imOut
def apply_in_place(self, im):
im.load()
if im.mode != self.output_mode:
raise ValueError("mode mismatch") # wrong output mode
result = self.transform.apply(im.im.id, im.im.id)
raise ValueError("mode mismatch") # wrong output mode
self.transform.apply(im.im.id, im.im.id)
return im
def get_display_profile(handle=None):
""" (experimental) Fetches the profile for the current display device.
:returns: None if the profile is not known.
@ -229,15 +236,21 @@ def get_display_profile(handle=None):
profile = get()
return ImageCmsProfile(profile)
# --------------------------------------------------------------------.
# pyCMS compatible layer
# --------------------------------------------------------------------.
class PyCMSError(Exception):
""" (pyCMS) Exception class. This is used for all errors in the pyCMS API. """
""" (pyCMS) Exception class.
This is used for all errors in the pyCMS API. """
pass
def profileToProfile(im, inputProfile, outputProfile, renderingIntent=INTENT_PERCEPTUAL, outputMode=None, inPlace=0, flags=0):
def profileToProfile(
im, inputProfile, outputProfile, renderingIntent=INTENT_PERCEPTUAL,
outputMode=None, inPlace=0, flags=0):
"""
(pyCMS) Applies an ICC transformation to a given image, mapping from
inputProfile to outputProfile.
@ -259,40 +272,45 @@ def profileToProfile(im, inputProfile, outputProfile, renderingIntent=INTENT_PER
profiles, the input profile must handle RGB data, and the output
profile must handle CMYK data.
:param im: An open PIL image object (i.e. Image.new(...) or Image.open(...), etc.)
:param inputProfile: String, as a valid filename path to the ICC input profile
you wish to use for this image, or a profile object
:param im: An open PIL image object (i.e. Image.new(...) or
Image.open(...), etc.)
:param inputProfile: String, as a valid filename path to the ICC input
profile you wish to use for this image, or a profile object
:param outputProfile: String, as a valid filename path to the ICC output
profile you wish to use for this image, or a profile object
:param renderingIntent: Integer (0-3) specifying the rendering intent you wish
to use for the transform
:param renderingIntent: Integer (0-3) specifying the rendering intent you
wish to use for the transform
INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
see the pyCMS documentation for details on rendering intents and what they do.
:param outputMode: A valid PIL mode for the output image (i.e. "RGB", "CMYK",
etc.). Note: if rendering the image "inPlace", outputMode MUST be the
same mode as the input, or omitted completely. If omitted, the outputMode
will be the same as the mode of the input image (im.mode)
:param inPlace: Boolean (1 = True, None or 0 = False). If True, the original
image is modified in-place, and None is returned. If False (default), a
new Image object is returned with the transform applied.
see the pyCMS documentation for details on rendering intents and what
they do.
:param outputMode: A valid PIL mode for the output image (i.e. "RGB",
"CMYK", etc.). Note: if rendering the image "inPlace", outputMode
MUST be the same mode as the input, or omitted completely. If
omitted, the outputMode will be the same as the mode of the input
image (im.mode)
:param inPlace: Boolean (1 = True, None or 0 = False). If True, the
original image is modified in-place, and None is returned. If False
(default), a new Image object is returned with the transform applied.
:param flags: Integer (0-...) specifying additional flags
:returns: Either None or a new PIL image object, depending on value of inPlace
:returns: Either None or a new PIL image object, depending on value of
inPlace
:exception PyCMSError:
"""
if outputMode is None:
outputMode = im.mode
if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <=3):
if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3):
raise PyCMSError("renderingIntent must be an integer between 0 and 3")
if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG)
raise PyCMSError(
"flags must be an integer between 0 and %s" + _MAX_FLAG)
try:
if not isinstance(inputProfile, ImageCmsProfile):
@ -300,8 +318,9 @@ def profileToProfile(im, inputProfile, outputProfile, renderingIntent=INTENT_PER
if not isinstance(outputProfile, ImageCmsProfile):
outputProfile = ImageCmsProfile(outputProfile)
transform = ImageCmsTransform(
inputProfile, outputProfile, im.mode, outputMode, renderingIntent, flags=flags
)
inputProfile, outputProfile, im.mode, outputMode,
renderingIntent, flags=flags
)
if inPlace:
transform.apply_in_place(im)
imOut = None
@ -323,8 +342,8 @@ def getOpenProfile(profileFilename):
If profileFilename is not a vaild filename for an ICC profile, a PyCMSError
will be raised.
:param profileFilename: String, as a valid filename path to the ICC profile you
wish to open, or a file-like object.
:param profileFilename: String, as a valid filename path to the ICC profile
you wish to open, or a file-like object.
:returns: A CmsProfile class object.
:exception PyCMSError:
"""
@ -334,7 +353,10 @@ def getOpenProfile(profileFilename):
except (IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent=INTENT_PERCEPTUAL, flags=0):
def buildTransform(
inputProfile, outputProfile, inMode, outMode,
renderingIntent=INTENT_PERCEPTUAL, flags=0):
"""
(pyCMS) Builds an ICC transform mapping from the inputProfile to the
outputProfile. Use applyTransform to apply the transform to a given
@ -367,14 +389,14 @@ def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent
manually overridden if you really want to, but I don't know of any
time that would be of use, or would even work).
:param inputProfile: String, as a valid filename path to the ICC input profile
you wish to use for this transform, or a profile object
:param inputProfile: String, as a valid filename path to the ICC input
profile you wish to use for this transform, or a profile object
:param outputProfile: String, as a valid filename path to the ICC output
profile you wish to use for this transform, or a profile object
:param inMode: String, as a valid PIL mode that the appropriate profile also
supports (i.e. "RGB", "RGBA", "CMYK", etc.)
:param outMode: String, as a valid PIL mode that the appropriate profile also
supports (i.e. "RGB", "RGBA", "CMYK", etc.)
:param inMode: String, as a valid PIL mode that the appropriate profile
also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
:param outMode: String, as a valid PIL mode that the appropriate profile
also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
:param renderingIntent: Integer (0-3) specifying the rendering intent you
wish to use for the transform
@ -383,28 +405,37 @@ def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
see the pyCMS documentation for details on rendering intents and what they do.
see the pyCMS documentation for details on rendering intents and what
they do.
:param flags: Integer (0-...) specifying additional flags
:returns: A CmsTransform class object.
:exception PyCMSError:
"""
if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <=3):
if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3):
raise PyCMSError("renderingIntent must be an integer between 0 and 3")
if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG)
raise PyCMSError(
"flags must be an integer between 0 and %s" + _MAX_FLAG)
try:
if not isinstance(inputProfile, ImageCmsProfile):
inputProfile = ImageCmsProfile(inputProfile)
if not isinstance(outputProfile, ImageCmsProfile):
outputProfile = ImageCmsProfile(outputProfile)
return ImageCmsTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags)
return ImageCmsTransform(
inputProfile, outputProfile, inMode, outMode,
renderingIntent, flags=flags)
except (IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMode, renderingIntent=INTENT_PERCEPTUAL, proofRenderingIntent=INTENT_ABSOLUTE_COLORIMETRIC, flags=FLAGS["SOFTPROOFING"]):
def buildProofTransform(
inputProfile, outputProfile, proofProfile, inMode, outMode,
renderingIntent=INTENT_PERCEPTUAL,
proofRenderingIntent=INTENT_ABSOLUTE_COLORIMETRIC,
flags=FLAGS["SOFTPROOFING"]):
"""
(pyCMS) Builds an ICC transform mapping from the inputProfile to the
outputProfile, but tries to simulate the result that would be
@ -443,17 +474,17 @@ def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMo
when the simulated device has a much wider gamut than the output
device, you may obtain marginal results.
:param inputProfile: String, as a valid filename path to the ICC input profile
you wish to use for this transform, or a profile object
:param inputProfile: String, as a valid filename path to the ICC input
profile you wish to use for this transform, or a profile object
:param outputProfile: String, as a valid filename path to the ICC output
(monitor, usually) profile you wish to use for this transform, or a
profile object
:param proofProfile: String, as a valid filename path to the ICC proof profile
you wish to use for this transform, or a profile object
:param inMode: String, as a valid PIL mode that the appropriate profile also
supports (i.e. "RGB", "RGBA", "CMYK", etc.)
:param outMode: String, as a valid PIL mode that the appropriate profile also
supports (i.e. "RGB", "RGBA", "CMYK", etc.)
:param proofProfile: String, as a valid filename path to the ICC proof
profile you wish to use for this transform, or a profile object
:param inMode: String, as a valid PIL mode that the appropriate profile
also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
:param outMode: String, as a valid PIL mode that the appropriate profile
also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
:param renderingIntent: Integer (0-3) specifying the rendering intent you
wish to use for the input->proof (simulated) transform
@ -462,7 +493,8 @@ def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMo
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
see the pyCMS documentation for details on rendering intents and what they do.
see the pyCMS documentation for details on rendering intents and what
they do.
:param proofRenderingIntent: Integer (0-3) specifying the rendering intent you
wish to use for proof->output transform
@ -471,17 +503,19 @@ def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMo
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
see the pyCMS documentation for details on rendering intents and what they do.
see the pyCMS documentation for details on rendering intents and what
they do.
:param flags: Integer (0-...) specifying additional flags
:returns: A CmsTransform class object.
:exception PyCMSError:
"""
if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <=3):
if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3):
raise PyCMSError("renderingIntent must be an integer between 0 and 3")
if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG)
raise PyCMSError(
"flags must be an integer between 0 and %s" + _MAX_FLAG)
try:
if not isinstance(inputProfile, ImageCmsProfile):
@ -490,13 +524,16 @@ def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMo
outputProfile = ImageCmsProfile(outputProfile)
if not isinstance(proofProfile, ImageCmsProfile):
proofProfile = ImageCmsProfile(proofProfile)
return ImageCmsTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent, proofProfile, proofRenderingIntent, flags)
return ImageCmsTransform(
inputProfile, outputProfile, inMode, outMode, renderingIntent,
proofProfile, proofRenderingIntent, flags)
except (IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
buildTransformFromOpenProfiles = buildTransform
buildProofTransformFromOpenProfiles = buildProofTransform
def applyTransform(im, transform, inPlace=0):
"""
(pyCMS) Applies a transform to a given image.
@ -514,8 +551,8 @@ def applyTransform(im, transform, inPlace=0):
is raised.
This function applies a pre-calculated transform (from
ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles()) to an
image. The transform can be used for multiple images, saving
ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles())
to an image. The transform can be used for multiple images, saving
considerable calcuation time if doing the same conversion multiple times.
If you want to modify im in-place instead of receiving a new image as
@ -528,10 +565,12 @@ def applyTransform(im, transform, inPlace=0):
:param im: A PIL Image object, and im.mode must be the same as the inMode
supported by the transform.
:param transform: A valid CmsTransform class object
:param inPlace: Bool (1 == True, 0 or None == False). If True, im is modified
in place and None is returned, if False, a new Image object with the
transform applied is returned (and im is not changed). The default is False.
:returns: Either None, or a new PIL Image object, depending on the value of inPlace
:param inPlace: Bool (1 == True, 0 or None == False). If True, im is
modified in place and None is returned, if False, a new Image object
with the transform applied is returned (and im is not changed). The
default is False.
:returns: Either None, or a new PIL Image object, depending on the value of
inPlace
:exception PyCMSError:
"""
@ -546,6 +585,7 @@ def applyTransform(im, transform, inPlace=0):
return imOut
def createProfile(colorSpace, colorTemp=-1):
"""
(pyCMS) Creates a profile.
@ -562,30 +602,36 @@ def createProfile(colorSpace, colorTemp=-1):
ImageCms.buildTransformFromOpenProfiles() to create a transform to apply
to images.
:param colorSpace: String, the color space of the profile you wish to create.
:param colorSpace: String, the color space of the profile you wish to
create.
Currently only "LAB", "XYZ", and "sRGB" are supported.
:param colorTemp: Positive integer for the white point for the profile, in
degrees Kelvin (i.e. 5000, 6500, 9600, etc.). The default is for D50
illuminant if omitted (5000k). colorTemp is ONLY applied to LAB profiles,
and is ignored for XYZ and sRGB.
illuminant if omitted (5000k). colorTemp is ONLY applied to LAB
profiles, and is ignored for XYZ and sRGB.
:returns: A CmsProfile class object
:exception PyCMSError:
"""
if colorSpace not in ["LAB", "XYZ", "sRGB"]:
raise PyCMSError("Color space not supported for on-the-fly profile creation (%s)" % colorSpace)
raise PyCMSError(
"Color space not supported for on-the-fly profile creation (%s)"
% colorSpace)
if colorSpace == "LAB":
try:
colorTemp = float(colorTemp)
except:
raise PyCMSError("Color temperature must be numeric, \"%s\" not valid" % colorTemp)
raise PyCMSError(
"Color temperature must be numeric, \"%s\" not valid"
% colorTemp)
try:
return core.createProfile(colorSpace, colorTemp)
except (TypeError, ValueError) as v:
raise PyCMSError(v)
def getProfileName(profile):
"""
@ -600,10 +646,10 @@ def getProfileName(profile):
profile was originally created. Sometimes this tag also contains
additional information supplied by the creator.
:param profile: EITHER a valid CmsProfile object, OR a string of the filename
of an ICC profile.
:returns: A string containing the internal name of the profile as stored in an
ICC tag.
:param profile: EITHER a valid CmsProfile object, OR a string of the
filename of an ICC profile.
:returns: A string containing the internal name of the profile as stored
in an ICC tag.
:exception PyCMSError:
"""
@ -619,7 +665,7 @@ def getProfileName(profile):
manufacturer = profile.profile.product_manufacturer
if not (model or manufacturer):
return profile.profile.product_description+"\n"
return profile.profile.product_description + "\n"
if not manufacturer or len(model) > 30:
return model + "\n"
return "%s - %s\n" % (model, manufacturer)
@ -627,6 +673,7 @@ def getProfileName(profile):
except (AttributeError, IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
def getProfileInfo(profile):
"""
(pyCMS) Gets the internal product information for the given profile.
@ -641,10 +688,10 @@ def getProfileInfo(profile):
info tag. This often contains details about the profile, and how it
was created, as supplied by the creator.
:param profile: EITHER a valid CmsProfile object, OR a string of the filename
of an ICC profile.
:returns: A string containing the internal profile information stored in an ICC
tag.
:param profile: EITHER a valid CmsProfile object, OR a string of the
filename of an ICC profile.
:returns: A string containing the internal profile information stored in
an ICC tag.
:exception PyCMSError:
"""
@ -652,7 +699,8 @@ def getProfileInfo(profile):
if not isinstance(profile, ImageCmsProfile):
profile = ImageCmsProfile(profile)
# add an extra newline to preserve pyCMS compatibility
# Python, not C. the white point bits weren't working well, so skipping.
# Python, not C. the white point bits weren't working well,
# so skipping.
# // info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint
description = profile.profile.product_description
cpright = profile.profile.product_copyright
@ -660,7 +708,7 @@ def getProfileInfo(profile):
for elt in (description, cpright):
if elt:
arr.append(elt)
return "\r\n\r\n".join(arr)+"\r\n\r\n"
return "\r\n\r\n".join(arr) + "\r\n\r\n"
except (AttributeError, IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
@ -679,10 +727,10 @@ def getProfileCopyright(profile):
Use this function to obtain the information stored in the profile's
copyright tag.
:param profile: EITHER a valid CmsProfile object, OR a string of the filename
of an ICC profile.
:returns: A string containing the internal profile information stored in an ICC
tag.
:param profile: EITHER a valid CmsProfile object, OR a string of the
filename of an ICC profile.
:returns: A string containing the internal profile information stored in
an ICC tag.
:exception PyCMSError:
"""
try:
@ -693,6 +741,7 @@ def getProfileCopyright(profile):
except (AttributeError, IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
def getProfileManufacturer(profile):
"""
(pyCMS) Gets the manufacturer for the given profile.
@ -700,16 +749,16 @@ def getProfileManufacturer(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 manufacturer tag, a PyCMSError
is raised
If an error occurs while trying to obtain the manufacturer tag, a
PyCMSError is raised
Use this function to obtain the information stored in the profile's
manufacturer tag.
:param profile: EITHER a valid CmsProfile object, OR a string of the filename
of an ICC profile.
:returns: A string containing the internal profile information stored in an ICC
tag.
:param profile: EITHER a valid CmsProfile object, OR a string of the
filename of an ICC profile.
:returns: A string containing the internal profile information stored in
an ICC tag.
:exception PyCMSError:
"""
try:
@ -720,6 +769,7 @@ def getProfileManufacturer(profile):
except (AttributeError, IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
def getProfileModel(profile):
"""
(pyCMS) Gets the model for the given profile.
@ -733,10 +783,10 @@ def getProfileModel(profile):
Use this function to obtain the information stored in the profile's
model tag.
:param profile: EITHER a valid CmsProfile object, OR a string of the filename
of an ICC profile.
:returns: A string containing the internal profile information stored in an ICC
tag.
:param profile: EITHER a valid CmsProfile object, OR a string of the
filename of an ICC profile.
:returns: A string containing the internal profile information stored in
an ICC tag.
:exception PyCMSError:
"""
@ -748,6 +798,7 @@ def getProfileModel(profile):
except (AttributeError, IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
def getProfileDescription(profile):
"""
(pyCMS) Gets the description for the given profile.
@ -761,10 +812,10 @@ def getProfileDescription(profile):
Use this function to obtain the information stored in the profile's
description tag.
:param profile: EITHER a valid CmsProfile object, OR a string of the filename
of an ICC profile.
:returns: A string containing the internal profile information stored in an ICC
tag.
:param profile: EITHER a valid CmsProfile object, OR a string of the
filename of an ICC profile.
:returns: A string containing the internal profile information stored in an
ICC tag.
:exception PyCMSError:
"""
@ -793,16 +844,18 @@ def getDefaultIntent(profile):
If you wish to use a different intent than returned, use
ImageCms.isIntentSupported() to verify it will work first.
:param profile: EITHER a valid CmsProfile object, OR a string of the filename
of an ICC profile.
:returns: Integer 0-3 specifying the default rendering intent for this profile.
:param profile: EITHER a valid CmsProfile object, OR a string of the
filename of an ICC profile.
:returns: Integer 0-3 specifying the default rendering intent for this
profile.
INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
see the pyCMS documentation for details on rendering intents and what they do.
see the pyCMS documentation for details on rendering intents and what
they do.
:exception PyCMSError:
"""
@ -813,6 +866,7 @@ def getDefaultIntent(profile):
except (AttributeError, IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
def isIntentSupported(profile, intent, direction):
"""
(pyCMS) Checks if a given intent is supported.
@ -828,17 +882,18 @@ def isIntentSupported(profile, intent, direction):
potential PyCMSError that will occur if they don't support the modes
you select.
:param profile: EITHER a valid CmsProfile object, OR a string of the filename
of an ICC profile.
:param intent: Integer (0-3) specifying the rendering intent you wish to use
with this profile
:param profile: EITHER a valid CmsProfile object, OR a string of the
filename of an ICC profile.
:param intent: Integer (0-3) specifying the rendering intent you wish to
use with this profile
INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
see the pyCMS documentation for details on rendering intents and what they do.
see the pyCMS documentation for details on rendering intents and what
they do.
:param direction: Integer specifing if the profile is to be used for input,
output, or proof
@ -862,6 +917,7 @@ def isIntentSupported(profile, intent, direction):
except (AttributeError, IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
def versions():
"""
(pyCMS) Fetches versions.
@ -869,8 +925,9 @@ def versions():
import sys
return (
VERSION, core.littlecms_version, sys.version.split()[0], Image.VERSION
)
VERSION, core.littlecms_version,
sys.version.split()[0], Image.VERSION
)
# --------------------------------------------------------------------
@ -880,14 +937,16 @@ if __name__ == "__main__":
from PIL import ImageCms
print(__doc__)
for f in dir(pyCMS):
print("="*80)
print("%s" %f)
for f in dir(ImageCms):
doc = None
try:
exec ("doc = ImageCms.%s.__doc__" %(f))
exec("doc = %s.__doc__" % (f))
if "pyCMS" in doc:
# so we don't get the __doc__ string for imported modules
print("=" * 80)
print("%s" % f)
print(doc)
except AttributeError:
except (AttributeError, TypeError):
pass
# End of file

View File

@ -15,11 +15,12 @@
__version__ = "0.1"
from PIL import Image, ImageFile, _binary
from PIL import Image, ImageFile
import struct
import os
import io
def _parse_codestream(fp):
"""Parse the JPEG 2000 codestream to extract the size and component
count from the SIZ marker segment, returning a PIL (size, mode) tuple."""
@ -28,7 +29,7 @@ def _parse_codestream(fp):
lsiz = struct.unpack('>H', hdr)[0]
siz = hdr + fp.read(lsiz - 2)
lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, xtsiz, ytsiz, \
xtosiz, ytosiz, csiz \
xtosiz, ytosiz, csiz \
= struct.unpack('>HHIIIIIIIIH', siz[:38])
ssiz = [None]*csiz
xrsiz = [None]*csiz
@ -51,6 +52,7 @@ def _parse_codestream(fp):
return (size, mode)
def _parse_jp2_header(fp):
"""Parse the JP2 header box to extract size, component count and
color space information, returning a PIL (size, mode) tuple."""
@ -90,7 +92,7 @@ def _parse_jp2_header(fp):
if tbox == b'ihdr':
height, width, nc, bpc, c, unkc, ipr \
= struct.unpack('>IIHBBBB', content)
= struct.unpack('>IIHBBBB', content)
size = (width, height)
if unkc:
if nc == 1:
@ -112,13 +114,13 @@ def _parse_jp2_header(fp):
elif nc == 4:
mode = 'RGBA'
break
elif cs == 17: # grayscale
elif cs == 17: # grayscale
if nc == 1:
mode = 'L'
elif nc == 2:
mode = 'LA'
break
elif cs == 18: # sYCC
elif cs == 18: # sYCC
if nc == 3:
mode = 'RGB'
elif nc == 4:
@ -127,6 +129,7 @@ def _parse_jp2_header(fp):
return (size, mode)
##
# Image plugin for JPEG2000 images.
@ -190,10 +193,12 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
ImageFile.ImageFile.load(self)
def _accept(prefix):
return (prefix[:4] == b'\xff\x4f\xff\x51'
or prefix[:12] == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a')
# ------------------------------------------------------------
# Save support
@ -238,7 +243,7 @@ def _save(im, fp, filename):
progression,
cinema_mode,
fd
)
)
ImageFile._save(im, fp, [('jpeg2k', (0, 0)+im.size, 0, kind)])

View File

@ -7,7 +7,7 @@ This is an improved version of the OleFileIO module from [PIL](http://www.python
As far as I know, this module is now the most complete and robust Python implementation to read MS OLE2 files, portable on several operating systems. (please tell me if you know other similar Python modules)
OleFileIO_PL can be used as an independent module or with PIL. The goal is to have it integrated into [Pillow](http://python-imaging.github.io/), the friendly fork of PIL.
OleFileIO_PL can be used as an independent module or with PIL. The goal is to have it integrated into [Pillow](http://python-pillow.github.io/), the friendly fork of PIL.
OleFileIO\_PL is mostly meant for developers. If you are looking for tools to analyze OLE files or to extract data, then please also check [python-oletools](http://www.decalage.info/python/oletools), which are built upon OleFileIO_PL.

View File

@ -36,17 +36,23 @@
from __future__ import print_function
from PIL import Image, ImageFile
import os, struct, sys
import os
import struct
import sys
def isInt(f):
try:
i = int(f)
if f-i == 0: return 1
else: return 0
if f-i == 0:
return 1
else:
return 0
except:
return 0
iforms = [1,3,-11,-12,-21,-22]
iforms = [1, 3, -11, -12, -21, -22]
# There is no magic number to identify Spider files, so just check a
# series of header locations to see if they have reasonable values.
@ -56,30 +62,32 @@ iforms = [1,3,-11,-12,-21,-22]
def isSpiderHeader(t):
h = (99,) + t # add 1 value so can use spider header index start=1
# header values 1,2,5,12,13,22,23 should be integers
for i in [1,2,5,12,13,22,23]:
if not isInt(h[i]): return 0
for i in [1, 2, 5, 12, 13, 22, 23]:
if not isInt(h[i]):
return 0
# check iform
iform = int(h[5])
if not iform in iforms: return 0
if iform not in iforms:
return 0
# check other header values
labrec = int(h[13]) # no. records in file header
labbyt = int(h[22]) # total no. of bytes in header
lenbyt = int(h[23]) # record length in bytes
#print "labrec = %d, labbyt = %d, lenbyt = %d" % (labrec,labbyt,lenbyt)
if labbyt != (labrec * lenbyt): return 0
# print "labrec = %d, labbyt = %d, lenbyt = %d" % (labrec,labbyt,lenbyt)
if labbyt != (labrec * lenbyt):
return 0
# looks like a valid header
return labbyt
def isSpiderImage(filename):
fp = open(filename,'rb')
fp = open(filename, 'rb')
f = fp.read(92) # read 23 * 4 bytes
fp.close()
bigendian = 1
t = struct.unpack('>23f',f) # try big-endian first
t = struct.unpack('>23f', f) # try big-endian first
hdrlen = isSpiderHeader(t)
if hdrlen == 0:
bigendian = 0
t = struct.unpack('<23f',f) # little-endian
t = struct.unpack('<23f', f) # little-endian
hdrlen = isSpiderHeader(t)
return hdrlen
@ -96,11 +104,11 @@ class SpiderImageFile(ImageFile.ImageFile):
try:
self.bigendian = 1
t = struct.unpack('>27f',f) # try big-endian first
t = struct.unpack('>27f', f) # try big-endian first
hdrlen = isSpiderHeader(t)
if hdrlen == 0:
self.bigendian = 0
t = struct.unpack('<27f',f) # little-endian
t = struct.unpack('<27f', f) # little-endian
hdrlen = isSpiderHeader(t)
if hdrlen == 0:
raise SyntaxError("not a valid Spider file")
@ -112,7 +120,7 @@ class SpiderImageFile(ImageFile.ImageFile):
if iform != 1:
raise SyntaxError("not a Spider 2D image")
self.size = int(h[12]), int(h[2]) # size in pixels (width, height)
self.size = int(h[12]), int(h[2]) # size in pixels (width, height)
self.istack = int(h[24])
self.imgnumber = int(h[27])
@ -141,9 +149,10 @@ class SpiderImageFile(ImageFile.ImageFile):
self.rawmode = "F;32F"
self.mode = "F"
self.tile = [("raw", (0, 0) + self.size, offset,
(self.rawmode, 0, 1))]
self.__fp = self.fp # FIXME: hack
self.tile = [
("raw", (0, 0) + self.size, offset,
(self.rawmode, 0, 1))]
self.__fp = self.fp # FIXME: hack
# 1st image index is zero (although SPIDER imgnumber starts at 1)
def tell(self):
@ -176,6 +185,7 @@ class SpiderImageFile(ImageFile.ImageFile):
from PIL import ImageTk
return ImageTk.PhotoImage(self.convert2byte(), palette=256)
# --------------------------------------------------------------------
# Image series
@ -200,17 +210,19 @@ def loadImageSeries(filelist=None):
imglist.append(im)
return imglist
# --------------------------------------------------------------------
# For saving images in Spider format
def makeSpiderHeader(im):
nsam,nrow = im.size
nsam, nrow = im.size
lenbyt = nsam * 4 # There are labrec records in the header
labrec = 1024 / lenbyt
if 1024%lenbyt != 0: labrec += 1
if 1024 % lenbyt != 0:
labrec += 1
labbyt = labrec * lenbyt
hdr = []
nvalues = labbyt / 4
nvalues = int(labbyt / 4)
for i in range(nvalues):
hdr.append(0.0)
@ -218,13 +230,13 @@ def makeSpiderHeader(im):
return []
# NB these are Fortran indices
hdr[1] = 1.0 # nslice (=1 for an image)
hdr[2] = float(nrow) # number of rows per slice
hdr[5] = 1.0 # iform for 2D image
hdr[12] = float(nsam) # number of pixels per line
hdr[13] = float(labrec) # number of records in file header
hdr[22] = float(labbyt) # total number of bytes in header
hdr[23] = float(lenbyt) # record length in bytes
hdr[1] = 1.0 # nslice (=1 for an image)
hdr[2] = float(nrow) # number of rows per slice
hdr[5] = 1.0 # iform for 2D image
hdr[12] = float(nsam) # number of pixels per line
hdr[13] = float(labrec) # number of records in file header
hdr[22] = float(labbyt) # total number of bytes in header
hdr[23] = float(lenbyt) # record length in bytes
# adjust for Fortran indexing
hdr = hdr[1:]
@ -232,9 +244,10 @@ def makeSpiderHeader(im):
# pack binary data into a string
hdrstr = []
for v in hdr:
hdrstr.append(struct.pack('f',v))
hdrstr.append(struct.pack('f', v))
return hdrstr
def _save(im, fp, filename):
if im.mode[0] != "F":
im = im.convert('F')
@ -250,11 +263,12 @@ def _save(im, fp, filename):
raise IOError("Unable to open %s for writing" % filename)
fp.writelines(hdr)
rawmode = "F;32NF" #32-bit native floating point
ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode,0,1))])
rawmode = "F;32NF" # 32-bit native floating point
ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, 1))])
fp.close()
def _save_spider(im, fp, filename):
# get the filename extension and register it with Image
fn, ext = os.path.splitext(filename)
@ -292,5 +306,7 @@ if __name__ == "__main__":
if outfile != "":
# perform some image operation
im = im.transpose(Image.FLIP_LEFT_RIGHT)
print("saving a flipped version of %s as %s " % (os.path.basename(filename), outfile))
print(
"saving a flipped version of %s as %s " %
(os.path.basename(filename), outfile))
im.save(outfile, "SPIDER")

View File

@ -859,7 +859,7 @@ class TiffImageFile(ImageFile.ImageFile):
# libtiff handles the fillmode for us, so 1;IR should
# actually be 1;I. Including the R double reverses the
# bits, so stripes of the image are reversed. See
# https://github.com/python-imaging/Pillow/issues/279
# https://github.com/python-pillow/Pillow/issues/279
if fillorder == 2:
key = (
self.tag.prefix, photo, format, 1,

View File

@ -5,8 +5,8 @@ Pillow
Pillow is the "friendly" PIL fork by Alex Clark and Contributors. PIL is the Python Imaging Library by Fredrik Lundh and Contributors.
.. image:: https://travis-ci.org/python-imaging/Pillow.svg?branch=master
:target: https://travis-ci.org/python-imaging/Pillow
.. image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master
:target: https://travis-ci.org/python-pillow/Pillow
:alt: Travis CI build status
.. image:: https://pypip.in/v/Pillow/badge.png
@ -17,7 +17,7 @@ Pillow is the "friendly" PIL fork by Alex Clark and Contributors. PIL is the Pyt
:target: https://pypi.python.org/pypi/Pillow/
:alt: Number of PyPI downloads
.. image:: https://coveralls.io/repos/python-imaging/Pillow/badge.png?branch=master
:target: https://coveralls.io/r/python-imaging/Pillow?branch=master
.. image:: https://coveralls.io/repos/python-pillow/Pillow/badge.png?branch=master
:target: https://coveralls.io/r/python-pillow/Pillow?branch=master
The documentation is hosted at http://pillow.readthedocs.org/. It contains installation instructions, tutorials, reference, compatibility details, and more.

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 B

BIN
Tests/images/lena.spider Normal file

Binary file not shown.

View File

@ -37,7 +37,7 @@ def test_roundtrip():
assert_image_similar(reread.convert('RGB'), im, 50)
def test_roundtrip2():
#see https://github.com/python-imaging/Pillow/issues/403
#see https://github.com/python-pillow/Pillow/issues/403
out = tempfile('temp.gif')
im = Image.open('Images/lena.gif')
im2 = im.copy()
@ -48,7 +48,7 @@ def test_roundtrip2():
def test_palette_handling():
# see https://github.com/python-imaging/Pillow/issues/513
# see https://github.com/python-pillow/Pillow/issues/513
im = Image.open('Images/lena.gif')
im = im.convert('RGB')
@ -64,7 +64,7 @@ def test_palette_handling():
assert_image_similar(im, reloaded.convert('RGB'), 10)
def test_palette_434():
# see https://github.com/python-imaging/Pillow/issues/434
# see https://github.com/python-pillow/Pillow/issues/434
def roundtrip(im, *args, **kwargs):
out = tempfile('temp.gif')

View File

@ -126,7 +126,7 @@ def test_optimize():
def test_optimize_large_buffer():
# https://github.com/python-imaging/Pillow/issues/148
# https://github.com/python-pillow/Pillow/issues/148
f = tempfile('temp.jpg')
# this requires ~ 1.5x Image.MAXBLOCK
im = Image.new("RGB", (4096, 4096), 0xff3333)
@ -159,7 +159,7 @@ def test_progressive_large_buffer_highest_quality():
def test_large_exif():
# https://github.com/python-imaging/Pillow/issues/148
# https://github.com/python-pillow/Pillow/issues/148
f = tempfile('temp.jpg')
im = lena()
im.save(f, 'JPEG', quality=90, exif=b"1"*65532)
@ -231,7 +231,7 @@ def test_quality_keep():
def test_junk_jpeg_header():
# https://github.com/python-imaging/Pillow/issues/630
# https://github.com/python-pillow/Pillow/issues/630
filename = "Tests/images/junk_jpeg_header.jpg"
assert_no_exception(lambda: Image.open(filename))

View File

@ -71,7 +71,7 @@ def test_g4_eq_png():
assert_image_equal(g4, png)
# see https://github.com/python-imaging/Pillow/issues/279
# see https://github.com/python-pillow/Pillow/issues/279
def test_g4_fillorder_eq_png():
""" Checking that we're actually getting the data that we expect"""
png = Image.open('Tests/images/g4-fillorder-test.png')

View File

@ -101,7 +101,7 @@ def test_bad_text():
assert_equal(im.info, {'spam': 'egg\x00'})
def test_bad_ztxt():
# Test reading malformed zTXt chunks (python-imaging/Pillow#318)
# Test reading malformed zTXt chunks (python-pillow/Pillow#318)
im = load(HEAD + chunk(b'zTXt') + TAIL)
assert_equal(im.info, {})

36
Tests/test_file_spider.py Normal file
View File

@ -0,0 +1,36 @@
from tester import *
from PIL import Image
from PIL import SpiderImagePlugin
test_file = "Tests/images/lena.spider"
def test_sanity():
im = Image.open(test_file)
im.load()
assert_equal(im.mode, "F")
assert_equal(im.size, (128, 128))
assert_equal(im.format, "SPIDER")
def test_save():
# Arrange
temp = tempfile('temp.spider')
im = lena()
# Act
im.save(temp, "SPIDER")
# Assert
im2 = Image.open(temp)
assert_equal(im2.mode, "F")
assert_equal(im2.size, (128, 128))
assert_equal(im2.format, "SPIDER")
def test_isSpiderImage():
assert_true(SpiderImagePlugin.isSpiderImage(test_file))
# End of file

View File

@ -6,7 +6,7 @@ tag_ids = dict(zip(TiffTags.TAGS.values(), TiffTags.TAGS.keys()))
def test_rt_metadata():
""" Test writing arbitray metadata into the tiff image directory
Use case is ImageJ private tags, one numeric, one arbitrary
data. https://github.com/python-imaging/Pillow/issues/291
data. https://github.com/python-pillow/Pillow/issues/291
"""
img = lena()

View File

@ -28,7 +28,7 @@ def test_default():
assert_image(im, "RGB", im.size)
# ref https://github.com/python-imaging/Pillow/issues/274
# ref https://github.com/python-pillow/Pillow/issues/274
def _test_float_conversion(im):
orig = im.getpixel((5, 5))
@ -76,7 +76,7 @@ def test_trns_p():
assert_no_exception(lambda: rgb.save(f))
# ref https://github.com/python-imaging/Pillow/issues/664
# ref https://github.com/python-pillow/Pillow/issues/664
def test_trns_p_rgba():
# Arrange

View File

@ -36,7 +36,7 @@ def test_basic():
check(mode)
def test_signedness():
# see https://github.com/python-imaging/Pillow/issues/452
# see https://github.com/python-pillow/Pillow/issues/452
# pixelaccess is using signed int* instead of uint*
for mode in ("I;16", "I;16B"):
check(mode, 2**15-1)

View File

@ -4,7 +4,7 @@ from PIL import Image
if hasattr(sys, 'pypy_version_info'):
# This takes _forever_ on pypy. Open Bug,
# see https://github.com/python-imaging/Pillow/issues/484
# see https://github.com/python-pillow/Pillow/issues/484
skip()
def test_sanity():
@ -26,7 +26,7 @@ def test_sanity():
def test_16bit_lut():
""" Tests for 16 bit -> 8 bit lut for converting I->L images
see https://github.com/python-imaging/Pillow/issues/440
see https://github.com/python-pillow/Pillow/issues/440
"""
im = lena("I")

View File

@ -94,7 +94,7 @@ def test_alpha_premult_transform():
def test_blank_fill():
# attempting to hit
# https://github.com/python-imaging/Pillow/issues/254 reported
# https://github.com/python-pillow/Pillow/issues/254 reported
#
# issue is that transforms with transparent overflow area
# contained junk from previous images, especially on systems with

View File

@ -9,12 +9,13 @@ except ImportError:
SRGB = "Tests/icc/sRGB.icm"
def test_sanity():
# basic smoke test.
# this mostly follows the cms_test outline.
v = ImageCms.versions() # should return four strings
v = ImageCms.versions() # should return four strings
assert_equal(v[0], '1.0.0 pil')
assert_equal(list(map(type, v)), [str, str, str, str])
@ -24,10 +25,19 @@ def test_sanity():
i = ImageCms.profileToProfile(lena(), SRGB, SRGB)
assert_image(i, "RGB", (128, 128))
i = lena()
ImageCms.profileToProfile(i, SRGB, SRGB, inPlace=True)
assert_image(i, "RGB", (128, 128))
t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB")
i = ImageCms.applyTransform(lena(), t)
assert_image(i, "RGB", (128, 128))
i = lena()
t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB")
ImageCms.applyTransform(lena(), t, inPlace=True)
assert_image(i, "RGB", (128, 128))
p = ImageCms.createProfile("sRGB")
o = ImageCms.getOpenProfile(SRGB)
t = ImageCms.buildTransformFromOpenProfiles(p, o, "RGB", "RGB")
@ -41,24 +51,47 @@ def test_sanity():
assert_image(i, "RGB", (128, 128))
# test PointTransform convenience API
im = lena().point(t)
lena().point(t)
def test_name():
# get profile information for file
assert_equal(ImageCms.getProfileName(SRGB).strip(),
'IEC 61966-2.1 Default RGB colour space - sRGB')
def x_test_info():
def test_info():
assert_equal(ImageCms.getProfileInfo(SRGB).splitlines(),
['sRGB IEC61966-2.1', '',
'Copyright (c) 1998 Hewlett-Packard Company', '',
'WhitePoint : D65 (daylight)', '',
'Tests/icc/sRGB.icm'])
'Copyright (c) 1998 Hewlett-Packard Company', ''])
def test_copyright():
assert_equal(ImageCms.getProfileCopyright(SRGB).strip(),
'Copyright (c) 1998 Hewlett-Packard Company')
def test_manufacturer():
assert_equal(ImageCms.getProfileManufacturer(SRGB).strip(),
'IEC http://www.iec.ch')
def test_model():
assert_equal(ImageCms.getProfileModel(SRGB).strip(),
'IEC 61966-2.1 Default RGB colour space - sRGB')
def test_description():
assert_equal(ImageCms.getProfileDescription(SRGB).strip(),
'sRGB IEC61966-2.1')
def test_intent():
assert_equal(ImageCms.getDefaultIntent(SRGB), 0)
assert_equal(ImageCms.isIntentSupported(
SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC,
ImageCms.DIRECTION_INPUT), 1)
SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC,
ImageCms.DIRECTION_INPUT), 1)
def test_profile_object():
# same, using profile object
@ -69,8 +102,9 @@ def test_profile_object():
# ['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', ''])
assert_equal(ImageCms.getDefaultIntent(p), 0)
assert_equal(ImageCms.isIntentSupported(
p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC,
ImageCms.DIRECTION_INPUT), 1)
p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC,
ImageCms.DIRECTION_INPUT), 1)
def test_extensions():
# extensions
@ -79,12 +113,21 @@ def test_extensions():
assert_equal(ImageCms.getProfileName(p).strip(),
'IEC 61966-2.1 Default RGB colour space - sRGB')
def test_exceptions():
# the procedural pyCMS API uses PyCMSError for all sorts of errors
assert_exception(ImageCms.PyCMSError, lambda: ImageCms.profileToProfile(lena(), "foo", "bar"))
assert_exception(ImageCms.PyCMSError, lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB"))
assert_exception(ImageCms.PyCMSError, lambda: ImageCms.getProfileName(None))
assert_exception(ImageCms.PyCMSError, lambda: ImageCms.isIntentSupported(SRGB, None, None))
assert_exception(
ImageCms.PyCMSError,
lambda: ImageCms.profileToProfile(lena(), "foo", "bar"))
assert_exception(
ImageCms.PyCMSError,
lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB"))
assert_exception(
ImageCms.PyCMSError,
lambda: ImageCms.getProfileName(None))
assert_exception(
ImageCms.PyCMSError,
lambda: ImageCms.isIntentSupported(SRGB, None, None))
def test_display_profile():
@ -93,37 +136,37 @@ def test_display_profile():
def test_lab_color_profile():
pLab = ImageCms.createProfile("LAB", 5000)
pLab = ImageCms.createProfile("LAB", 6500)
ImageCms.createProfile("LAB", 5000)
ImageCms.createProfile("LAB", 6500)
def test_simple_lab():
i = Image.new('RGB', (10,10), (128,128,128))
i = Image.new('RGB', (10, 10), (128, 128, 128))
pLab = ImageCms.createProfile("LAB")
t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB")
i_lab = ImageCms.applyTransform(i, t)
assert_equal(i_lab.mode, 'LAB')
k = i_lab.getpixel((0,0))
assert_equal(k, (137,128,128)) # not a linear luminance map. so L != 128
k = i_lab.getpixel((0, 0))
assert_equal(k, (137, 128, 128)) # not a linear luminance map. so L != 128
L = i_lab.getdata(0)
L = i_lab.getdata(0)
a = i_lab.getdata(1)
b = i_lab.getdata(2)
assert_equal(list(L), [137]*100)
assert_equal(list(a), [128]*100)
assert_equal(list(b), [128]*100)
assert_equal(list(L), [137] * 100)
assert_equal(list(a), [128] * 100)
assert_equal(list(b), [128] * 100)
def test_lab_color():
pLab = ImageCms.createProfile("LAB")
t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB")
# need to add a type mapping for some PIL type to TYPE_Lab_8 in findLCMSType,
# and have that mapping work back to a PIL mode. (likely RGB)
# Need to add a type mapping for some PIL type to TYPE_Lab_8 in
# findLCMSType, and have that mapping work back to a PIL mode (likely RGB).
i = ImageCms.applyTransform(lena(), t)
assert_image(i, "LAB", (128, 128))
@ -133,6 +176,7 @@ def test_lab_color():
assert_image_similar(i, target, 30)
def test_lab_srgb():
pLab = ImageCms.createProfile("LAB")
t = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB")
@ -145,6 +189,7 @@ def test_lab_srgb():
assert_image_similar(lena(), img_srgb, 30)
def test_lab_roundtrip():
# check to see if we're at least internally consistent.
pLab = ImageCms.createProfile("LAB")
@ -156,5 +201,3 @@ def test_lab_roundtrip():
out = ImageCms.applyTransform(i, t2)
assert_image_similar(lena(), out, 2)

View File

@ -1,8 +1,27 @@
from tester import *
from PIL import Image
from PIL import ImageColor
from PIL import ImageDraw
# Image size
w, h = 100, 100
# Bounding box points
x0 = int(w / 4)
x1 = int(x0 * 3)
y0 = int(h / 4)
y1 = int(x0 * 3)
# Two kinds of bounding box
bbox1 = [(x0, y0), (x1, y1)]
bbox2 = [x0, y0, x1, y1]
# Two kinds of coordinate sequences
points1 = [(10, 10), (20, 40), (30, 30)]
points2 = [10, 10, 20, 40, 30, 30]
def test_sanity():
im = lena("RGB").copy()
@ -17,6 +36,7 @@ def test_sanity():
success()
def test_deprecated():
im = lena().copy()
@ -26,3 +46,220 @@ def test_deprecated():
assert_warning(DeprecationWarning, lambda: draw.setink(0))
assert_warning(DeprecationWarning, lambda: draw.setfill(0))
def helper_arc(bbox):
# Arrange
im = Image.new("RGB", (w, h))
draw = ImageDraw.Draw(im)
# Act
# FIXME Fill param should be named outline.
draw.arc(bbox, 0, 180)
del draw
# Assert
assert_image_equal(im, Image.open("Tests/images/imagedraw_arc.png"))
def test_arc1():
helper_arc(bbox1)
def test_arc2():
helper_arc(bbox2)
def test_bitmap():
# Arrange
small = Image.open("Tests/images/pil123rgba.png").resize((50, 50))
im = Image.new("RGB", (w, h))
draw = ImageDraw.Draw(im)
# Act
draw.bitmap((10, 10), small)
del draw
# Assert
assert_image_equal(im, Image.open("Tests/images/imagedraw_bitmap.png"))
def helper_chord(bbox):
# Arrange
im = Image.new("RGB", (w, h))
draw = ImageDraw.Draw(im)
# Act
draw.chord(bbox, 0, 180, fill="red", outline="yellow")
del draw
# Assert
assert_image_equal(im, Image.open("Tests/images/imagedraw_chord.png"))
def test_chord1():
helper_chord(bbox1)
def test_chord2():
helper_chord(bbox2)
def helper_ellipse(bbox):
# Arrange
im = Image.new("RGB", (w, h))
draw = ImageDraw.Draw(im)
# Act
draw.ellipse(bbox, fill="green", outline="blue")
del draw
# Assert
assert_image_equal(im, Image.open("Tests/images/imagedraw_ellipse.png"))
def test_ellipse1():
helper_ellipse(bbox1)
def test_ellipse2():
helper_ellipse(bbox2)
def helper_line(points):
# Arrange
im = Image.new("RGB", (w, h))
draw = ImageDraw.Draw(im)
# Act
draw.line(points1, fill="yellow", width=2)
del draw
# Assert
assert_image_equal(im, Image.open("Tests/images/imagedraw_line.png"))
def test_line1():
helper_line(points1)
def test_line2():
helper_line(points2)
def helper_pieslice(bbox):
# Arrange
im = Image.new("RGB", (w, h))
draw = ImageDraw.Draw(im)
# Act
draw.pieslice(bbox, -90, 45, fill="white", outline="blue")
del draw
# Assert
assert_image_equal(im, Image.open("Tests/images/imagedraw_pieslice.png"))
def test_pieslice1():
helper_pieslice(bbox1)
def test_pieslice2():
helper_pieslice(bbox2)
def helper_point(points):
# Arrange
im = Image.new("RGB", (w, h))
draw = ImageDraw.Draw(im)
# Act
draw.point(points1, fill="yellow")
del draw
# Assert
assert_image_equal(im, Image.open("Tests/images/imagedraw_point.png"))
def test_point1():
helper_point(points1)
def test_point2():
helper_point(points2)
def helper_polygon(points):
# Arrange
im = Image.new("RGB", (w, h))
draw = ImageDraw.Draw(im)
# Act
draw.polygon(points1, fill="red", outline="blue")
del draw
# Assert
assert_image_equal(im, Image.open("Tests/images/imagedraw_polygon.png"))
def test_polygon1():
helper_polygon(points1)
def test_polygon2():
helper_polygon(points2)
def helper_rectangle(bbox):
# Arrange
im = Image.new("RGB", (w, h))
draw = ImageDraw.Draw(im)
# Act
draw.rectangle(bbox, fill="black", outline="green")
del draw
# Assert
assert_image_equal(im, Image.open("Tests/images/imagedraw_rectangle.png"))
def test_rectangle1():
helper_rectangle(bbox1)
def test_rectangle2():
helper_rectangle(bbox2)
def test_floodfill():
# Arrange
im = Image.new("RGB", (w, h))
draw = ImageDraw.Draw(im)
draw.rectangle(bbox2, outline="yellow", fill="green")
centre_point = (int(w/2), int(h/2))
# Act
ImageDraw.floodfill(im, centre_point, ImageColor.getrgb("red"))
del draw
# Assert
assert_image_equal(im, Image.open("Tests/images/imagedraw_floodfill.png"))
def test_floodfill_border():
# Arrange
im = Image.new("RGB", (w, h))
draw = ImageDraw.Draw(im)
draw.rectangle(bbox2, outline="yellow", fill="green")
centre_point = (int(w/2), int(h/2))
# Act
ImageDraw.floodfill(
im, centre_point, ImageColor.getrgb("red"),
border=ImageColor.getrgb("black"))
del draw
# Assert
assert_image_equal(im, Image.open("Tests/images/imagedraw_floodfill2.png"))
# End of file

View File

@ -56,7 +56,7 @@ def test_parser():
assert_image(im1, im2.mode, im2.size)
# XXX Why assert exception and why does it fail?
# https://github.com/python-imaging/Pillow/issues/78
# https://github.com/python-pillow/Pillow/issues/78
#assert_exception(IOError, lambda: roundtrip("PDF"))
def test_ico():

View File

@ -3,7 +3,7 @@ from PIL import Image
import locale
# ref https://github.com/python-imaging/Pillow/issues/272
# ref https://github.com/python-pillow/Pillow/issues/272
## on windows, in polish locale:
## import locale

View File

@ -108,7 +108,7 @@ def test_to_array():
def test_point_lut():
# see https://github.com/python-imaging/Pillow/issues/439
# see https://github.com/python-pillow/Pillow/issues/439
data = list(range(256))*3
lut = numpy.array(data, dtype='uint8')

View File

@ -2399,17 +2399,35 @@ _draw_ink(ImagingDrawObject* self, PyObject* args)
static PyObject*
_draw_arc(ImagingDrawObject* self, PyObject* args)
{
int x0, y0, x1, y1;
double* xy;
int n;
PyObject* data;
int ink;
int start, end;
int op = 0;
if (!PyArg_ParseTuple(args, "(iiii)iii|i",
&x0, &y0, &x1, &y1,
&start, &end, &ink))
if (!PyArg_ParseTuple(args, "Oiii|i", &data, &start, &end, &ink))
return NULL;
if (ImagingDrawArc(self->image->image, x0, y0, x1, y1, start, end,
&ink, op) < 0)
n = PyPath_Flatten(data, &xy);
if (n < 0)
return NULL;
if (n != 2) {
PyErr_SetString(PyExc_TypeError,
"coordinate list must contain exactly 2 coordinates"
);
return NULL;
}
n = ImagingDrawArc(self->image->image,
(int) xy[0], (int) xy[1],
(int) xy[2], (int) xy[3],
start, end, &ink, op
);
free(xy);
if (n < 0)
return NULL;
Py_INCREF(Py_None);
@ -2455,15 +2473,35 @@ _draw_bitmap(ImagingDrawObject* self, PyObject* args)
static PyObject*
_draw_chord(ImagingDrawObject* self, PyObject* args)
{
int x0, y0, x1, y1;
double* xy;
int n;
PyObject* data;
int ink, fill;
int start, end;
if (!PyArg_ParseTuple(args, "(iiii)iiii",
&x0, &y0, &x1, &y1, &start, &end, &ink, &fill))
if (!PyArg_ParseTuple(args, "Oiiii",
&data, &start, &end, &ink, &fill))
return NULL;
if (ImagingDrawChord(self->image->image, x0, y0, x1, y1,
start, end, &ink, fill, self->blend) < 0)
n = PyPath_Flatten(data, &xy);
if (n < 0)
return NULL;
if (n != 2) {
PyErr_SetString(PyExc_TypeError,
"coordinate list must contain exactly 2 coordinates"
);
return NULL;
}
n = ImagingDrawChord(self->image->image,
(int) xy[0], (int) xy[1],
(int) xy[2], (int) xy[3],
start, end, &ink, fill, self->blend
);
free(xy);
if (n < 0)
return NULL;
Py_INCREF(Py_None);
@ -2656,15 +2694,34 @@ _draw_outline(ImagingDrawObject* self, PyObject* args)
static PyObject*
_draw_pieslice(ImagingDrawObject* self, PyObject* args)
{
int x0, y0, x1, y1;
double* xy;
int n;
PyObject* data;
int ink, fill;
int start, end;
if (!PyArg_ParseTuple(args, "(iiii)iiii",
&x0, &y0, &x1, &y1, &start, &end, &ink, &fill))
if (!PyArg_ParseTuple(args, "Oiiii", &data, &start, &end, &ink, &fill))
return NULL;
if (ImagingDrawPieslice(self->image->image, x0, y0, x1, y1,
start, end, &ink, fill, self->blend) < 0)
n = PyPath_Flatten(data, &xy);
if (n < 0)
return NULL;
if (n != 2) {
PyErr_SetString(PyExc_TypeError,
"coordinate list must contain exactly 2 coordinates"
);
return NULL;
}
n = ImagingDrawPieslice(self->image->image,
(int) xy[0], (int) xy[1],
(int) xy[2], (int) xy[3],
start, end, &ink, fill, self->blend
);
free(xy);
if (n < 0)
return NULL;
Py_INCREF(Py_None);

View File

@ -12,7 +12,7 @@
<p>
If you've discovered a bug, you can
<a href="https://github.com/python-imaging/Pillow/issues/new">open an issue
<a href="https://github.com/python-pillow/Pillow/issues/new">open an issue
on Github</a>.
</p>

View File

@ -11,8 +11,8 @@ The fork authors' goal is to foster active development of PIL through:
- Regular releases to the `Python Package Index`_
- Solicitation for community contributions and involvement on `Image-SIG`_
.. _Travis CI: https://travis-ci.org/python-imaging/Pillow
.. _GitHub: https://github.com/python-imaging/Pillow
.. _Travis CI: https://travis-ci.org/python-pillow/Pillow
.. _GitHub: https://github.com/python-pillow/Pillow
.. _Python Package Index: https://pypi.python.org/pypi/Pillow
.. _Image-SIG: http://mail.python.org/mailman/listinfo/image-sig
@ -60,6 +60,6 @@ announcement. So if you still want to support PIL, please
.. _report issues here first: https://bitbucket.org/effbot/pil-2009-raclette/issues
.. _open the corresponding Pillow tickets here: https://github.com/python-imaging/Pillow/issues
.. _open the corresponding Pillow tickets here: https://github.com/python-pillow/Pillow/issues
Please provide a link to the PIL ticket so we can track the issue(s) upstream.

View File

@ -4,8 +4,8 @@ Pillow
Pillow is the 'friendly' PIL fork by Alex Clark and Contributors. PIL is the
Python Imaging Library by Fredrik Lundh and Contributors.
.. image:: https://travis-ci.org/python-imaging/Pillow.svg?branch=master
:target: https://travis-ci.org/python-imaging/Pillow
.. image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master
:target: https://travis-ci.org/python-pillow/Pillow
:alt: Travis CI build status
.. image:: https://pypip.in/v/Pillow/badge.png
@ -16,15 +16,15 @@ Python Imaging Library by Fredrik Lundh and Contributors.
:target: https://pypi.python.org/pypi/Pillow/
:alt: Number of PyPI downloads
.. image:: https://coveralls.io/repos/python-imaging/Pillow/badge.png?branch=master
:target: https://coveralls.io/r/python-imaging/Pillow?branch=master
.. image:: https://coveralls.io/repos/python-pillow/Pillow/badge.png?branch=master
:target: https://coveralls.io/r/python-pillow/Pillow?branch=master
:alt: Test coverage
To start using Pillow, please read the :doc:`installation
instructions <installation>`.
You can get the source and contribute at
https://github.com/python-imaging/Pillow. You can download archives
https://github.com/python-pillow/Pillow. You can download archives
and old versions from `PyPI <https://pypi.python.org/pypi/Pillow>`_.
.. toctree::
@ -42,7 +42,7 @@ Support Pillow!
PIL needs you! Please help us maintain the Python Imaging Library here:
- `GitHub <https://github.com/python-imaging/Pillow>`_
- `GitHub <https://github.com/python-pillow/Pillow>`_
- `Freenode <irc://irc.freenode.net#pil>`_
- `Image-SIG <http://mail.python.org/mailman/listinfo/image-sig>`_

View File

@ -185,6 +185,25 @@ to a specific version:
$ pip install --use-wheel Pillow==2.3.0
FreeBSD installation
---------------------
.. Note:: Only FreeBSD 10 tested
Make sure you have Python's development libraries installed.::
$ sudo pkg install python2
Or for Python 3::
$ sudo pkg install python3
Prerequisites are installed on **FreeBSD 10** with::
$ sudo pkg install jpeg tiff webp lcms2 freetype2
Platform support
----------------
@ -224,6 +243,8 @@ current versions of Linux, OS X, and Windows.
+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+
| Gentoo Linux |Yes | 2.7,3.2 | 2.1.0 |x86-64 |
+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+
| FreeBSD 10 |Yes | 2.7,3.4 | 2.4,2.3.1 |x86-64 |
+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+
| Windows 7 Pro |Yes | 2.7,3.2,3.3 | 2.2.1 |x86-64 |
+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+
| Windows Server 2008 R2 Enterprise|Yes | 3.3 | |x86-64 |
@ -232,4 +253,3 @@ current versions of Linux, OS X, and Windows.
+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+
| Windows 8.1 Pro |Yes | 2.6,2.7,3.2,3.3,3.4 | 2.3.0, 2.4.0 |x86,x86-64 |
+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+

View File

@ -91,9 +91,12 @@ Methods
Draws an arc (a portion of a circle outline) between the start and end
angles, inside the given bounding box.
:param xy: Four points to define the bounding box. Sequence of either
:param xy: Four points to define the bounding box. Sequence of
``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``.
:param outline: Color to use for the outline.
:param start: Starting angle, in degrees. Angles are measured from
3 o'clock, increasing clockwise.
:param end: Ending angle, in degrees.
:param fill: Color to use for the arc.
.. py:method:: PIL.ImageDraw.Draw.bitmap(xy, bitmap, fill=None)
@ -111,7 +114,7 @@ Methods
Same as :py:meth:`~PIL.ImageDraw.Draw.arc`, but connects the end points
with a straight line.
:param xy: Four points to define the bounding box. Sequence of either
:param xy: Four points to define the bounding box. Sequence of
``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``.
:param outline: Color to use for the outline.
:param fill: Color to use for the fill.
@ -144,7 +147,7 @@ Methods
Same as arc, but also draws straight lines between the end points and the
center of the bounding box.
:param xy: Four points to define the bounding box. Sequence of either
:param xy: Four points to define the bounding box. Sequence of
``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``.
:param outline: Color to use for the outline.
:param fill: Color to use for the fill.

View File

@ -691,7 +691,7 @@ setup(
_read('CHANGES.rst')).decode('utf-8'),
author='Alex Clark (fork author)',
author_email='aclark@aclark.net',
url='http://python-imaging.github.io/',
url='http://python-pillow.github.io/',
classifiers=[
"Development Status :: 6 - Mature",
"Topic :: Multimedia :: Graphics",