mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-10-31 16:07:30 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			975 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			975 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # The Python Imaging Library.
 | |
| # $Id$
 | |
| 
 | |
| # Optional color management 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
 | |
| # Copyright (c) 2013 by Eric Soroos
 | |
| 
 | |
| # See the README file for information on usage and redistribution.  See
 | |
| # below for the original description.
 | |
| 
 | |
| from __future__ import print_function
 | |
| import sys
 | |
| 
 | |
| from PIL import Image
 | |
| try:
 | |
|     from PIL import _imagingcms
 | |
| 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 PIL._util import isStringType
 | |
| 
 | |
| DESCRIPTION = """
 | |
| pyCMS
 | |
| 
 | |
|     a Python / PIL interface to the littleCMS ICC Color Management System
 | |
|     Copyright (C) 2002-2003 Kevin Cazabon
 | |
|     kevin@cazabon.com
 | |
|     http://www.cazabon.com
 | |
| 
 | |
|     pyCMS home page:  http://www.cazabon.com/pyCMS
 | |
|     littleCMS home page:  http://www.littlecms.com
 | |
|     (littleCMS is Copyright (C) 1998-2001 Marti Maria)
 | |
| 
 | |
|     Originally released under LGPL.  Graciously donated to PIL in
 | |
|     March 2009, for distribution under the standard PIL license
 | |
| 
 | |
|     The pyCMS.py module provides a "clean" interface between Python/PIL and
 | |
|     pyCMSdll, taking care of some of the more complex handling of the direct
 | |
|     pyCMSdll functions, as well as error-checking and making sure that all
 | |
|     relevant data is kept together.
 | |
| 
 | |
|     While it is possible to call pyCMSdll functions directly, it's not highly
 | |
|     recommended.
 | |
| 
 | |
|     Version History:
 | |
| 
 | |
|         1.0.0 pil       Oct 2013 Port to LCMS 2.
 | |
| 
 | |
|         0.1.0 pil mod   March 10, 2009
 | |
| 
 | |
|                         Renamed display profile to proof profile. The proof
 | |
|                         profile is the profile of the device that is being
 | |
|                         simulated, not the profile of the device which is
 | |
|                         actually used to display/print the final simulation
 | |
|                         (that'd be the output profile) - also see LCMSAPI.txt
 | |
|                         input colorspace -> using 'renderingIntent' -> proof
 | |
|                         colorspace -> using 'proofRenderingIntent' -> output
 | |
|                         colorspace
 | |
| 
 | |
|                         Added LCMS FLAGS support.
 | |
|                         Added FLAGS["SOFTPROOFING"] as default flag for
 | |
|                         buildProofTransform (otherwise the proof profile/intent
 | |
|                         would be ignored).
 | |
| 
 | |
|         0.1.0 pil       March 2009 - added to PIL, as PIL.ImageCms
 | |
| 
 | |
|         0.0.2 alpha     Jan 6, 2002
 | |
| 
 | |
|                         Added try/except statements around type() checks of
 | |
|                         potential CObjects... Python won't let you use type()
 | |
|                         on them, and raises a TypeError (stupid, if you ask
 | |
|                         me!)
 | |
| 
 | |
|                         Added buildProofTransformFromOpenProfiles() function.
 | |
|                         Additional fixes in DLL, see DLL code for details.
 | |
| 
 | |
|         0.0.1 alpha     first public release, Dec. 26, 2002
 | |
| 
 | |
|     Known to-do list with current version (of Python interface, not pyCMSdll):
 | |
| 
 | |
|         none
 | |
| 
 | |
| """
 | |
| 
 | |
| VERSION = "1.0.0 pil"
 | |
| 
 | |
| # --------------------------------------------------------------------.
 | |
| 
 | |
| core = _imagingcms
 | |
| 
 | |
| #
 | |
| # intent/direction values
 | |
| 
 | |
| INTENT_PERCEPTUAL = 0
 | |
| INTENT_RELATIVE_COLORIMETRIC = 1
 | |
| INTENT_SATURATION = 2
 | |
| INTENT_ABSOLUTE_COLORIMETRIC = 3
 | |
| 
 | |
| DIRECTION_INPUT = 0
 | |
| DIRECTION_OUTPUT = 1
 | |
| DIRECTION_PROOF = 2
 | |
| 
 | |
| #
 | |
| # flags
 | |
| 
 | |
| FLAGS = {
 | |
|     "MATRIXINPUT": 1,
 | |
|     "MATRIXOUTPUT": 2,
 | |
|     "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 accuracy
 | |
|     "LOWRESPRECALC": 2048,  # Use less memory to minimize resources
 | |
|     "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
 | |
| }
 | |
| 
 | |
| _MAX_FLAG = 0
 | |
| for flag in FLAGS.values():
 | |
|     if isinstance(flag, int):
 | |
|         _MAX_FLAG = _MAX_FLAG | flag
 | |
| 
 | |
| 
 | |
| # --------------------------------------------------------------------.
 | |
| # Experimental PIL-level API
 | |
| # --------------------------------------------------------------------.
 | |
| 
 | |
| ##
 | |
| # Profile.
 | |
| 
 | |
| class ImageCmsProfile(object):
 | |
| 
 | |
|     def __init__(self, profile):
 | |
|         """
 | |
|         :param profile: Either a string representing a filename,
 | |
|             a file like object containing a profile or a
 | |
|             low-level profile object
 | |
| 
 | |
|         """
 | |
| 
 | |
|         if isStringType(profile):
 | |
|             self._set(core.profile_open(profile), profile)
 | |
|         elif hasattr(profile, "read"):
 | |
|             self._set(core.profile_frombytes(profile.read()))
 | |
|         elif isinstance(profile, _imagingcms.CmsProfile):
 | |
|             self._set(profile)
 | |
|         else:
 | |
|             raise TypeError("Invalid type for 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
 | |
|         else:
 | |
|             self.product_name = None
 | |
|             self.product_info = None
 | |
| 
 | |
|     def tobytes(self):
 | |
|         """
 | |
|         Returns the profile in a format suitable for embedding in
 | |
|         saved images.
 | |
| 
 | |
|         :returns: a bytes object containing the ICC profile.
 | |
|         """
 | |
| 
 | |
|         return core.profile_tobytes(self.profile)
 | |
| 
 | |
| 
 | |
| class ImageCmsTransform(Image.ImagePointHandler):
 | |
| 
 | |
|     """
 | |
|     Transform.  This can be used with the procedural API, or with the standard
 | |
|     Image.point() method.
 | |
| 
 | |
|     Will return the output profile in the output.info['icc_profile'].
 | |
|     """
 | |
| 
 | |
|     def __init__(self, input, output, input_mode, output_mode,
 | |
|                  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
 | |
| 
 | |
|         self.output_profile = output
 | |
| 
 | |
|     def point(self, im):
 | |
|         return self.apply(im)
 | |
| 
 | |
|     def apply(self, im, imOut=None):
 | |
|         im.load()
 | |
|         if imOut is None:
 | |
|             imOut = Image.new(self.output_mode, im.size, None)
 | |
|         self.transform.apply(im.im.id, imOut.im.id)
 | |
|         imOut.info['icc_profile'] = self.output_profile.tobytes()
 | |
|         return imOut
 | |
| 
 | |
|     def apply_in_place(self, im):
 | |
|         im.load()
 | |
|         if im.mode != self.output_mode:
 | |
|             raise ValueError("mode mismatch")  # wrong output mode
 | |
|         self.transform.apply(im.im.id, im.im.id)
 | |
|         im.info['icc_profile'] = self.output_profile.tobytes()
 | |
|         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.
 | |
|     """
 | |
| 
 | |
|     if sys.platform == "win32":
 | |
|         from PIL import ImageWin
 | |
|         if isinstance(handle, ImageWin.HDC):
 | |
|             profile = core.get_display_profile_win32(handle, 1)
 | |
|         else:
 | |
|             profile = core.get_display_profile_win32(handle or 0)
 | |
|     else:
 | |
|         try:
 | |
|             get = _imagingcms.get_display_profile
 | |
|         except AttributeError:
 | |
|             return None
 | |
|         else:
 | |
|             profile = get()
 | |
|     return ImageCmsProfile(profile)
 | |
| 
 | |
| 
 | |
| # --------------------------------------------------------------------.
 | |
| # pyCMS compatible layer
 | |
| # --------------------------------------------------------------------.
 | |
| 
 | |
| class PyCMSError(Exception):
 | |
| 
 | |
|     """ (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):
 | |
|     """
 | |
|     (pyCMS) Applies an ICC transformation to a given image, mapping from
 | |
|     inputProfile to outputProfile.
 | |
| 
 | |
|     If the input or output profiles specified are not valid filenames, a
 | |
|     PyCMSError will be raised.  If inPlace == TRUE and outputMode != im.mode,
 | |
|     a PyCMSError will be raised.  If an error occurs during application of
 | |
|     the profiles, a PyCMSError will be raised.  If outputMode is not a mode
 | |
|     supported by the outputProfile (or by pyCMS), a PyCMSError will be
 | |
|     raised.
 | |
| 
 | |
|     This function applies an ICC transformation to im from inputProfile's
 | |
|     color space to outputProfile's color space using the specified rendering
 | |
|     intent to decide how to handle out-of-gamut colors.
 | |
| 
 | |
|     OutputMode can be used to specify that a color mode conversion is to
 | |
|     be done using these profiles, but the specified profiles must be able
 | |
|     to handle that mode.  I.e., if converting im from RGB to CMYK using
 | |
|     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 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
 | |
| 
 | |
|             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.
 | |
|     :param flags: Integer (0-...) specifying additional flags
 | |
|     :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):
 | |
|         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)
 | |
| 
 | |
|     try:
 | |
|         if not isinstance(inputProfile, ImageCmsProfile):
 | |
|             inputProfile = ImageCmsProfile(inputProfile)
 | |
|         if not isinstance(outputProfile, ImageCmsProfile):
 | |
|             outputProfile = ImageCmsProfile(outputProfile)
 | |
|         transform = ImageCmsTransform(
 | |
|             inputProfile, outputProfile, im.mode, outputMode,
 | |
|             renderingIntent, flags=flags
 | |
|         )
 | |
|         if inPlace:
 | |
|             transform.apply_in_place(im)
 | |
|             imOut = None
 | |
|         else:
 | |
|             imOut = transform.apply(im)
 | |
|     except (IOError, TypeError, ValueError) as v:
 | |
|         raise PyCMSError(v)
 | |
| 
 | |
|     return imOut
 | |
| 
 | |
| 
 | |
| def getOpenProfile(profileFilename):
 | |
|     """
 | |
|     (pyCMS) Opens an ICC profile file.
 | |
| 
 | |
|     The PyCMSProfile object can be passed back into pyCMS for use in creating
 | |
|     transforms and such (as in ImageCms.buildTransformFromOpenProfiles()).
 | |
| 
 | |
|     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.
 | |
|     :returns: A CmsProfile class object.
 | |
|     :exception PyCMSError:
 | |
|     """
 | |
| 
 | |
|     try:
 | |
|         return ImageCmsProfile(profileFilename)
 | |
|     except (IOError, TypeError, ValueError) as v:
 | |
|         raise PyCMSError(v)
 | |
| 
 | |
| 
 | |
| 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
 | |
|     image.
 | |
| 
 | |
|     If the input or output profiles specified are not valid filenames, a
 | |
|     PyCMSError will be raised.  If an error occurs during creation of the
 | |
|     transform, a PyCMSError will be raised.
 | |
| 
 | |
|     If inMode or outMode are not a mode supported by the outputProfile (or
 | |
|     by pyCMS), a PyCMSError will be raised.
 | |
| 
 | |
|     This function builds and returns an ICC transform from the inputProfile
 | |
|     to the outputProfile using the renderingIntent to determine what to do
 | |
|     with out-of-gamut colors.  It will ONLY work for converting images that
 | |
|     are in inMode to images that are in outMode color format (PIL mode,
 | |
|     i.e. "RGB", "RGBA", "CMYK", etc.).
 | |
| 
 | |
|     Building the transform is a fair part of the overhead in
 | |
|     ImageCms.profileToProfile(), so if you're planning on converting multiple
 | |
|     images using the same input/output settings, this can save you time.
 | |
|     Once you have a transform object, it can be used with
 | |
|     ImageCms.applyProfile() to convert images without the need to re-compute
 | |
|     the lookup table for the transform.
 | |
| 
 | |
|     The reason pyCMS returns a class object rather than a handle directly
 | |
|     to the transform is that it needs to keep track of the PIL input/output
 | |
|     modes that the transform is meant for.  These attributes are stored in
 | |
|     the "inMode" and "outMode" attributes of the object (which can be
 | |
|     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 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 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 flags: Integer (0-...) specifying additional flags
 | |
|     :returns: A CmsTransform class object.
 | |
|     :exception PyCMSError:
 | |
|     """
 | |
| 
 | |
|     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)
 | |
| 
 | |
|     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)
 | |
|     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"]):
 | |
|     """
 | |
|     (pyCMS) Builds an ICC transform mapping from the inputProfile to the
 | |
|     outputProfile, but tries to simulate the result that would be
 | |
|     obtained on the proofProfile device.
 | |
| 
 | |
|     If the input, output, or proof profiles specified are not valid
 | |
|     filenames, a PyCMSError will be raised.
 | |
| 
 | |
|     If an error occurs during creation of the transform, a PyCMSError will
 | |
|     be raised.
 | |
| 
 | |
|     If inMode or outMode are not a mode supported by the outputProfile
 | |
|     (or by pyCMS), a PyCMSError will be raised.
 | |
| 
 | |
|     This function builds and returns an ICC transform from the inputProfile
 | |
|     to the outputProfile, but tries to simulate the result that would be
 | |
|     obtained on the proofProfile device using renderingIntent and
 | |
|     proofRenderingIntent to determine what to do with out-of-gamut
 | |
|     colors.  This is known as "soft-proofing".  It will ONLY work for
 | |
|     converting images that are in inMode to images that are in outMode
 | |
|     color format (PIL mode, i.e. "RGB", "RGBA", "CMYK", etc.).
 | |
| 
 | |
|     Usage of the resulting transform object is exactly the same as with
 | |
|     ImageCms.buildTransform().
 | |
| 
 | |
|     Proof profiling is generally used when using an output device to get a
 | |
|     good idea of what the final printed/displayed image would look like on
 | |
|     the proofProfile device when it's quicker and easier to use the
 | |
|     output device for judging color.  Generally, this means that the
 | |
|     output device is a monitor, or a dye-sub printer (etc.), and the simulated
 | |
|     device is something more expensive, complicated, or time consuming
 | |
|     (making it difficult to make a real print for color judgement purposes).
 | |
| 
 | |
|     Soft-proofing basically functions by adjusting the colors on the
 | |
|     output device to match the colors of the device being simulated. However,
 | |
|     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 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 renderingIntent: Integer (0-3) specifying the rendering intent you
 | |
|         wish to use for the input->proof (simulated) 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 proofRenderingIntent: Integer (0-3) specifying the rendering intent you
 | |
|         wish to use for proof->output 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 flags: Integer (0-...) specifying additional flags
 | |
|     :returns: A CmsTransform class object.
 | |
|     :exception PyCMSError:
 | |
|     """
 | |
| 
 | |
|     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)
 | |
| 
 | |
|     try:
 | |
|         if not isinstance(inputProfile, ImageCmsProfile):
 | |
|             inputProfile = ImageCmsProfile(inputProfile)
 | |
|         if not isinstance(outputProfile, ImageCmsProfile):
 | |
|             outputProfile = ImageCmsProfile(outputProfile)
 | |
|         if not isinstance(proofProfile, ImageCmsProfile):
 | |
|             proofProfile = ImageCmsProfile(proofProfile)
 | |
|         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.
 | |
| 
 | |
|     If im.mode != transform.inMode, a PyCMSError is raised.
 | |
| 
 | |
|     If inPlace == TRUE and transform.inMode != transform.outMode, a
 | |
|     PyCMSError is raised.
 | |
| 
 | |
|     If im.mode, transfer.inMode, or transfer.outMode is not supported by
 | |
|     pyCMSdll or the profiles you used for the transform, a PyCMSError is
 | |
|     raised.
 | |
| 
 | |
|     If an error occurs while the transform is being applied, a PyCMSError
 | |
|     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
 | |
|     considerable calculation time if doing the same conversion multiple times.
 | |
| 
 | |
|     If you want to modify im in-place instead of receiving a new image as
 | |
|     the return value, set inPlace to TRUE.  This can only be done if
 | |
|     transform.inMode and transform.outMode are the same, because we can't
 | |
|     change the mode in-place (the buffer sizes for some modes are
 | |
|     different).  The  default behavior is to return a new Image object of
 | |
|     the same dimensions in mode transform.outMode.
 | |
| 
 | |
|     :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. The profile will be returned in the image's
 | |
|         info['icc_profile'].
 | |
|     :exception PyCMSError:
 | |
|     """
 | |
| 
 | |
|     try:
 | |
|         if inPlace:
 | |
|             transform.apply_in_place(im)
 | |
|             imOut = None
 | |
|         else:
 | |
|             imOut = transform.apply(im)
 | |
|     except (TypeError, ValueError) as v:
 | |
|         raise PyCMSError(v)
 | |
| 
 | |
|     return imOut
 | |
| 
 | |
| 
 | |
| def createProfile(colorSpace, colorTemp=-1):
 | |
|     """
 | |
|     (pyCMS) Creates a profile.
 | |
| 
 | |
|     If colorSpace not in ["LAB", "XYZ", "sRGB"], a PyCMSError is raised
 | |
| 
 | |
|     If using LAB and colorTemp != a positive integer, a PyCMSError is raised.
 | |
| 
 | |
|     If an error occurs while creating the profile, a PyCMSError is raised.
 | |
| 
 | |
|     Use this function to create common profiles on-the-fly instead of
 | |
|     having to supply a profile on disk and knowing the path to it.  It
 | |
|     returns a normal CmsProfile object that can be passed to
 | |
|     ImageCms.buildTransformFromOpenProfiles() to create a transform to apply
 | |
|     to images.
 | |
| 
 | |
|     :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.
 | |
|     :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)
 | |
| 
 | |
|     if colorSpace == "LAB":
 | |
|         try:
 | |
|             colorTemp = float(colorTemp)
 | |
|         except:
 | |
|             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):
 | |
|     """
 | |
| 
 | |
|     (pyCMS) Gets the internal product name for the given profile.
 | |
| 
 | |
|     If profile isn't a valid CmsProfile object or filename to a profile,
 | |
|     a PyCMSError is raised If an error occurs while trying to obtain the
 | |
|     name tag, a PyCMSError is raised.
 | |
| 
 | |
|     Use this function to obtain the INTERNAL name of the profile (stored
 | |
|     in an ICC tag in the profile itself), usually the one used when the
 | |
|     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.
 | |
|     :exception PyCMSError:
 | |
|     """
 | |
| 
 | |
|     try:
 | |
|         # add an extra newline to preserve pyCMS compatibility
 | |
|         if not isinstance(profile, ImageCmsProfile):
 | |
|             profile = ImageCmsProfile(profile)
 | |
|         # do it in python, not c.
 | |
|         #    // name was "%s - %s" (model, manufacturer) || Description ,
 | |
|         #    // but if the Model and Manufacturer were the same or the model
 | |
|         #    // was long, Just the model,  in 1.x
 | |
|         model = profile.profile.product_model
 | |
|         manufacturer = profile.profile.product_manufacturer
 | |
| 
 | |
|         if not (model or manufacturer):
 | |
|             return profile.profile.product_description + "\n"
 | |
|         if not manufacturer or len(model) > 30:
 | |
|             return model + "\n"
 | |
|         return "%s - %s\n" % (model, manufacturer)
 | |
| 
 | |
|     except (AttributeError, IOError, TypeError, ValueError) as v:
 | |
|         raise PyCMSError(v)
 | |
| 
 | |
| 
 | |
| def getProfileInfo(profile):
 | |
|     """
 | |
|     (pyCMS) Gets the internal product information for the given 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 info tag, a PyCMSError
 | |
|     is raised
 | |
| 
 | |
|     Use this function to obtain the information stored in the profile's
 | |
|     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.
 | |
|     :exception PyCMSError:
 | |
|     """
 | |
| 
 | |
|     try:
 | |
|         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.
 | |
|         #    // 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
 | |
|         arr = []
 | |
|         for elt in (description, cpright):
 | |
|             if elt:
 | |
|                 arr.append(elt)
 | |
|         return "\r\n\r\n".join(arr) + "\r\n\r\n"
 | |
| 
 | |
|     except (AttributeError, IOError, TypeError, ValueError) as v:
 | |
|         raise PyCMSError(v)
 | |
| 
 | |
| 
 | |
| def getProfileCopyright(profile):
 | |
|     """
 | |
|     (pyCMS) Gets the copyright for the given 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 copyright tag, a PyCMSError
 | |
|     is raised
 | |
| 
 | |
|     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.
 | |
|     :exception PyCMSError:
 | |
|     """
 | |
|     try:
 | |
|         # add an extra newline to preserve pyCMS compatibility
 | |
|         if not isinstance(profile, ImageCmsProfile):
 | |
|             profile = ImageCmsProfile(profile)
 | |
|         return profile.profile.product_copyright + "\n"
 | |
|     except (AttributeError, IOError, TypeError, ValueError) as v:
 | |
|         raise PyCMSError(v)
 | |
| 
 | |
| 
 | |
| def getProfileManufacturer(profile):
 | |
|     """
 | |
|     (pyCMS) Gets the manufacturer for the given 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
 | |
| 
 | |
|     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.
 | |
|     :exception PyCMSError:
 | |
|     """
 | |
|     try:
 | |
|         # add an extra newline to preserve pyCMS compatibility
 | |
|         if not isinstance(profile, ImageCmsProfile):
 | |
|             profile = ImageCmsProfile(profile)
 | |
|         return profile.profile.product_manufacturer + "\n"
 | |
|     except (AttributeError, IOError, TypeError, ValueError) as v:
 | |
|         raise PyCMSError(v)
 | |
| 
 | |
| 
 | |
| def getProfileModel(profile):
 | |
|     """
 | |
|     (pyCMS) Gets the model for the given 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 model tag, a PyCMSError
 | |
|     is raised
 | |
| 
 | |
|     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.
 | |
|     :exception PyCMSError:
 | |
|     """
 | |
| 
 | |
|     try:
 | |
|         # add an extra newline to preserve pyCMS compatibility
 | |
|         if not isinstance(profile, ImageCmsProfile):
 | |
|             profile = ImageCmsProfile(profile)
 | |
|         return profile.profile.product_model + "\n"
 | |
|     except (AttributeError, IOError, TypeError, ValueError) as v:
 | |
|         raise PyCMSError(v)
 | |
| 
 | |
| 
 | |
| def getProfileDescription(profile):
 | |
|     """
 | |
|     (pyCMS) Gets the description for the given 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 description tag, a PyCMSError
 | |
|     is raised
 | |
| 
 | |
|     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.
 | |
|     :exception PyCMSError:
 | |
|     """
 | |
| 
 | |
|     try:
 | |
|         # add an extra newline to preserve pyCMS compatibility
 | |
|         if not isinstance(profile, ImageCmsProfile):
 | |
|             profile = ImageCmsProfile(profile)
 | |
|         return profile.profile.product_description + "\n"
 | |
|     except (AttributeError, IOError, TypeError, ValueError) as v:
 | |
|         raise PyCMSError(v)
 | |
| 
 | |
| 
 | |
| def getDefaultIntent(profile):
 | |
|     """
 | |
|     (pyCMS) Gets the default intent name for the given 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 default intent, a
 | |
|     PyCMSError is raised.
 | |
| 
 | |
|     Use this function to determine the default (and usually best optimized)
 | |
|     rendering intent for this profile.  Most profiles support multiple
 | |
|     rendering intents, but are intended mostly for one type of conversion.
 | |
|     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.
 | |
| 
 | |
|             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.
 | |
|     :exception PyCMSError:
 | |
|     """
 | |
| 
 | |
|     try:
 | |
|         if not isinstance(profile, ImageCmsProfile):
 | |
|             profile = ImageCmsProfile(profile)
 | |
|         return profile.profile.rendering_intent
 | |
|     except (AttributeError, IOError, TypeError, ValueError) as v:
 | |
|         raise PyCMSError(v)
 | |
| 
 | |
| 
 | |
| def isIntentSupported(profile, intent, direction):
 | |
|     """
 | |
|     (pyCMS) Checks if a given intent is supported.
 | |
| 
 | |
|     Use this function to verify that you can use your desired
 | |
|     renderingIntent with profile, and that profile can be used for the
 | |
|     input/output/proof profile as you desire.
 | |
| 
 | |
|     Some profiles are created specifically for one "direction", can cannot
 | |
|     be used for others.  Some profiles can only be used for certain
 | |
|     rendering intents... so it's best to either verify this before trying
 | |
|     to create a transform with them (using this function), or catch the
 | |
|     potential PyCMSError that will occur if they don't support the modes
 | |
|     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
 | |
| 
 | |
|             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 direction: Integer specifying if the profile is to be used for input,
 | |
|         output, or proof
 | |
| 
 | |
|             INPUT  = 0 (or use ImageCms.DIRECTION_INPUT)
 | |
|             OUTPUT = 1 (or use ImageCms.DIRECTION_OUTPUT)
 | |
|             PROOF  = 2 (or use ImageCms.DIRECTION_PROOF)
 | |
| 
 | |
|     :returns: 1 if the intent/direction are supported, -1 if they are not.
 | |
|     :exception PyCMSError:
 | |
|     """
 | |
| 
 | |
|     try:
 | |
|         if not isinstance(profile, ImageCmsProfile):
 | |
|             profile = ImageCmsProfile(profile)
 | |
|         # FIXME: I get different results for the same data w. different
 | |
|         # compilers.  Bug in LittleCMS or in the binding?
 | |
|         if profile.profile.is_intent_supported(intent, direction):
 | |
|             return 1
 | |
|         else:
 | |
|             return -1
 | |
|     except (AttributeError, IOError, TypeError, ValueError) as v:
 | |
|         raise PyCMSError(v)
 | |
| 
 | |
| 
 | |
| def versions():
 | |
|     """
 | |
|     (pyCMS) Fetches versions.
 | |
|     """
 | |
| 
 | |
|     return (
 | |
|         VERSION, core.littlecms_version,
 | |
|         sys.version.split()[0], Image.VERSION
 | |
|     )
 | |
| 
 | |
| # --------------------------------------------------------------------
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     # create a cheap manual from the __doc__ strings for the functions above
 | |
| 
 | |
|     print(__doc__)
 | |
| 
 | |
|     for f in dir(sys.modules[__name__]):
 | |
|         doc = None
 | |
|         try:
 | |
|             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, TypeError):
 | |
|             pass
 |