Merge remote-tracking branch 'upstream/master' into unittest0

Conflicts:
	.travis.yml
This commit is contained in:
hugovk 2014-06-05 12:29:35 +03:00
commit 45fd3b8bf5
9 changed files with 341 additions and 231 deletions

View File

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

View File

@ -3,6 +3,10 @@ Changelog (Pillow)
2.5.0 (unreleased) 2.5.0 (unreleased)
------------------ ------------------
- ImageCms fixes
[hugovk]
- Added more ImageDraw tests - Added more ImageDraw tests
[hugovk] [hugovk]

View File

@ -1529,7 +1529,7 @@ class Image:
clockwise around its centre. clockwise around its centre.
:param angle: In degrees counter clockwise. :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), one of :py:attr:`PIL.Image.NEAREST` (use nearest neighbour),
:py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2 :py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2
environment), or :py:attr:`PIL.Image.BICUBIC` environment), or :py:attr:`PIL.Image.BICUBIC`
@ -1550,7 +1550,6 @@ class Image:
math.cos(angle), math.sin(angle), 0.0, math.cos(angle), math.sin(angle), 0.0,
-math.sin(angle), math.cos(angle), 0.0 -math.sin(angle), math.cos(angle), 0.0
] ]
def transform(x, y, matrix=matrix): def transform(x, y, matrix=matrix):
(a, b, c, d, e, f) = matrix (a, b, c, d, e, f) = matrix

View File

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

View File

@ -15,20 +15,21 @@
__version__ = "0.1" __version__ = "0.1"
from PIL import Image, ImageFile, _binary from PIL import Image, ImageFile
import struct import struct
import os import os
import io import io
def _parse_codestream(fp): def _parse_codestream(fp):
"""Parse the JPEG 2000 codestream to extract the size and component """Parse the JPEG 2000 codestream to extract the size and component
count from the SIZ marker segment, returning a PIL (size, mode) tuple.""" count from the SIZ marker segment, returning a PIL (size, mode) tuple."""
hdr = fp.read(2) hdr = fp.read(2)
lsiz = struct.unpack('>H', hdr)[0] lsiz = struct.unpack('>H', hdr)[0]
siz = hdr + fp.read(lsiz - 2) siz = hdr + fp.read(lsiz - 2)
lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, xtsiz, ytsiz, \ lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, xtsiz, ytsiz, \
xtosiz, ytosiz, csiz \ xtosiz, ytosiz, csiz \
= struct.unpack('>HHIIIIIIIIH', siz[:38]) = struct.unpack('>HHIIIIIIIIH', siz[:38])
ssiz = [None]*csiz ssiz = [None]*csiz
xrsiz = [None]*csiz xrsiz = [None]*csiz
@ -48,13 +49,14 @@ def _parse_codestream(fp):
mode == 'RGBA' mode == 'RGBA'
else: else:
mode = None mode = None
return (size, mode) return (size, mode)
def _parse_jp2_header(fp): def _parse_jp2_header(fp):
"""Parse the JP2 header box to extract size, component count and """Parse the JP2 header box to extract size, component count and
color space information, returning a PIL (size, mode) tuple.""" color space information, returning a PIL (size, mode) tuple."""
# Find the JP2 header box # Find the JP2 header box
header = None header = None
while True: while True:
@ -76,7 +78,7 @@ def _parse_jp2_header(fp):
size = None size = None
mode = None mode = None
hio = io.BytesIO(header) hio = io.BytesIO(header)
while True: while True:
lbox, tbox = struct.unpack('>I4s', hio.read(8)) lbox, tbox = struct.unpack('>I4s', hio.read(8))
@ -90,7 +92,7 @@ def _parse_jp2_header(fp):
if tbox == b'ihdr': if tbox == b'ihdr':
height, width, nc, bpc, c, unkc, ipr \ height, width, nc, bpc, c, unkc, ipr \
= struct.unpack('>IIHBBBB', content) = struct.unpack('>IIHBBBB', content)
size = (width, height) size = (width, height)
if unkc: if unkc:
if nc == 1: if nc == 1:
@ -112,13 +114,13 @@ def _parse_jp2_header(fp):
elif nc == 4: elif nc == 4:
mode = 'RGBA' mode = 'RGBA'
break break
elif cs == 17: # grayscale elif cs == 17: # grayscale
if nc == 1: if nc == 1:
mode = 'L' mode = 'L'
elif nc == 2: elif nc == 2:
mode = 'LA' mode = 'LA'
break break
elif cs == 18: # sYCC elif cs == 18: # sYCC
if nc == 3: if nc == 3:
mode = 'RGB' mode = 'RGB'
elif nc == 4: elif nc == 4:
@ -127,6 +129,7 @@ def _parse_jp2_header(fp):
return (size, mode) return (size, mode)
## ##
# Image plugin for JPEG2000 images. # Image plugin for JPEG2000 images.
@ -141,16 +144,16 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
self.size, self.mode = _parse_codestream(self.fp) self.size, self.mode = _parse_codestream(self.fp)
else: else:
sig = sig + self.fp.read(8) sig = sig + self.fp.read(8)
if sig == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a': if sig == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a':
self.codec = "jp2" self.codec = "jp2"
self.size, self.mode = _parse_jp2_header(self.fp) self.size, self.mode = _parse_jp2_header(self.fp)
else: else:
raise SyntaxError('not a JPEG 2000 file') raise SyntaxError('not a JPEG 2000 file')
if self.size is None or self.mode is None: if self.size is None or self.mode is None:
raise SyntaxError('unable to determine size/mode') raise SyntaxError('unable to determine size/mode')
self.reduce = 0 self.reduce = 0
self.layers = 0 self.layers = 0
@ -177,13 +180,15 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
t = self.tile[0] t = self.tile[0]
t3 = (t[3][0], self.reduce, self.layers, t[3][3]) t3 = (t[3][0], self.reduce, self.layers, t[3][3])
self.tile = [(t[0], (0, 0) + self.size, t[2], t3)] self.tile = [(t[0], (0, 0) + self.size, t[2], t3)]
ImageFile.ImageFile.load(self) ImageFile.ImageFile.load(self)
def _accept(prefix): def _accept(prefix):
return (prefix[:4] == b'\xff\x4f\xff\x51' return (prefix[:4] == b'\xff\x4f\xff\x51'
or prefix[:12] == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a') or prefix[:12] == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a')
# ------------------------------------------------------------ # ------------------------------------------------------------
# Save support # Save support
@ -214,7 +219,7 @@ def _save(im, fp, filename):
fd = fp.fileno() fd = fp.fileno()
except: except:
fd = -1 fd = -1
im.encoderconfig = ( im.encoderconfig = (
offset, offset,
tile_offset, tile_offset,
@ -228,10 +233,10 @@ def _save(im, fp, filename):
progression, progression,
cinema_mode, cinema_mode,
fd fd
) )
ImageFile._save(im, fp, [('jpeg2k', (0, 0)+im.size, 0, kind)]) ImageFile._save(im, fp, [('jpeg2k', (0, 0)+im.size, 0, kind)])
# ------------------------------------------------------------ # ------------------------------------------------------------
# Registry stuff # Registry stuff

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) 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. 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.
@ -348,4 +348,4 @@ By obtaining, using, and/or copying this software and/or its associated document
Permission to use, copy, modify, and distribute this software and its associated documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appears in all copies, and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Secret Labs AB or the author not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. Permission to use, copy, modify, and distribute this software and its associated documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appears in all copies, and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Secret Labs AB or the author not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission.
SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -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/ :target: https://pypi.python.org/pypi/Pillow/
:alt: Number of PyPI downloads :alt: Number of PyPI downloads
.. image:: https://coveralls.io/repos/python-imaging/Pillow/badge.png?branch=master .. image:: https://coveralls.io/repos/python-pillow/Pillow/badge.png?branch=master
:target: https://coveralls.io/r/python-imaging/Pillow?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. The documentation is hosted at http://pillow.readthedocs.org/. It contains installation instructions, tutorials, reference, compatibility details, and more.

View File

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

View File

@ -16,8 +16,8 @@ Python Imaging Library by Fredrik Lundh and Contributors.
:target: https://pypi.python.org/pypi/Pillow/ :target: https://pypi.python.org/pypi/Pillow/
:alt: Number of PyPI downloads :alt: Number of PyPI downloads
.. image:: https://coveralls.io/repos/python-imaging/Pillow/badge.png?branch=master .. image:: https://coveralls.io/repos/python-pillow/Pillow/badge.png?branch=master
:target: https://coveralls.io/r/python-imaging/Pillow?branch=master :target: https://coveralls.io/r/python-pillow/Pillow?branch=master
:alt: Test coverage :alt: Test coverage
To start using Pillow, please read the :doc:`installation To start using Pillow, please read the :doc:`installation