Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/PIL/ImageCms.py: 35%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

306 statements  

1# The Python Imaging Library. 

2# $Id$ 

3 

4# Optional color management support, based on Kevin Cazabon's PyCMS 

5# library. 

6 

7# Originally released under LGPL. Graciously donated to PIL in 

8# March 2009, for distribution under the standard PIL license 

9 

10# History: 

11 

12# 2009-03-08 fl Added to PIL. 

13 

14# Copyright (C) 2002-2003 Kevin Cazabon 

15# Copyright (c) 2009 by Fredrik Lundh 

16# Copyright (c) 2013 by Eric Soroos 

17 

18# See the README file for information on usage and redistribution. See 

19# below for the original description. 

20from __future__ import annotations 

21 

22import operator 

23import sys 

24from enum import IntEnum, IntFlag 

25from functools import reduce 

26from typing import Any, Literal, SupportsFloat, SupportsInt, Union 

27 

28from . import Image, __version__ 

29from ._deprecate import deprecate 

30from ._typing import SupportsRead 

31 

32try: 

33 from . import _imagingcms as core 

34 

35 _CmsProfileCompatible = Union[ 

36 str, SupportsRead[bytes], core.CmsProfile, "ImageCmsProfile" 

37 ] 

38except ImportError as ex: 

39 # Allow error import for doc purposes, but error out when accessing 

40 # anything in core. 

41 from ._util import DeferredError 

42 

43 core = DeferredError.new(ex) 

44 

45_DESCRIPTION = """ 

46pyCMS 

47 

48 a Python / PIL interface to the littleCMS ICC Color Management System 

49 Copyright (C) 2002-2003 Kevin Cazabon 

50 kevin@cazabon.com 

51 https://www.cazabon.com 

52 

53 pyCMS home page: https://www.cazabon.com/pyCMS 

54 littleCMS home page: https://www.littlecms.com 

55 (littleCMS is Copyright (C) 1998-2001 Marti Maria) 

56 

57 Originally released under LGPL. Graciously donated to PIL in 

58 March 2009, for distribution under the standard PIL license 

59 

60 The pyCMS.py module provides a "clean" interface between Python/PIL and 

61 pyCMSdll, taking care of some of the more complex handling of the direct 

62 pyCMSdll functions, as well as error-checking and making sure that all 

63 relevant data is kept together. 

64 

65 While it is possible to call pyCMSdll functions directly, it's not highly 

66 recommended. 

67 

68 Version History: 

69 

70 1.0.0 pil Oct 2013 Port to LCMS 2. 

71 

72 0.1.0 pil mod March 10, 2009 

73 

74 Renamed display profile to proof profile. The proof 

75 profile is the profile of the device that is being 

76 simulated, not the profile of the device which is 

77 actually used to display/print the final simulation 

78 (that'd be the output profile) - also see LCMSAPI.txt 

79 input colorspace -> using 'renderingIntent' -> proof 

80 colorspace -> using 'proofRenderingIntent' -> output 

81 colorspace 

82 

83 Added LCMS FLAGS support. 

84 Added FLAGS["SOFTPROOFING"] as default flag for 

85 buildProofTransform (otherwise the proof profile/intent 

86 would be ignored). 

87 

88 0.1.0 pil March 2009 - added to PIL, as PIL.ImageCms 

89 

90 0.0.2 alpha Jan 6, 2002 

91 

92 Added try/except statements around type() checks of 

93 potential CObjects... Python won't let you use type() 

94 on them, and raises a TypeError (stupid, if you ask 

95 me!) 

96 

97 Added buildProofTransformFromOpenProfiles() function. 

98 Additional fixes in DLL, see DLL code for details. 

99 

100 0.0.1 alpha first public release, Dec. 26, 2002 

101 

102 Known to-do list with current version (of Python interface, not pyCMSdll): 

103 

104 none 

105 

106""" 

107 

108_VERSION = "1.0.0 pil" 

109 

110 

111def __getattr__(name: str) -> Any: 

112 if name == "DESCRIPTION": 

113 deprecate("PIL.ImageCms.DESCRIPTION", 12) 

114 return _DESCRIPTION 

115 elif name == "VERSION": 

116 deprecate("PIL.ImageCms.VERSION", 12) 

117 return _VERSION 

118 elif name == "FLAGS": 

119 deprecate("PIL.ImageCms.FLAGS", 12, "PIL.ImageCms.Flags") 

120 return _FLAGS 

121 msg = f"module '{__name__}' has no attribute '{name}'" 

122 raise AttributeError(msg) 

123 

124 

125# --------------------------------------------------------------------. 

126 

127 

128# 

129# intent/direction values 

130 

131 

132class Intent(IntEnum): 

133 PERCEPTUAL = 0 

134 RELATIVE_COLORIMETRIC = 1 

135 SATURATION = 2 

136 ABSOLUTE_COLORIMETRIC = 3 

137 

138 

139class Direction(IntEnum): 

140 INPUT = 0 

141 OUTPUT = 1 

142 PROOF = 2 

143 

144 

145# 

146# flags 

147 

148 

149class Flags(IntFlag): 

150 """Flags and documentation are taken from ``lcms2.h``.""" 

151 

152 NONE = 0 

153 NOCACHE = 0x0040 

154 """Inhibit 1-pixel cache""" 

155 NOOPTIMIZE = 0x0100 

156 """Inhibit optimizations""" 

157 NULLTRANSFORM = 0x0200 

158 """Don't transform anyway""" 

159 GAMUTCHECK = 0x1000 

160 """Out of Gamut alarm""" 

161 SOFTPROOFING = 0x4000 

162 """Do softproofing""" 

163 BLACKPOINTCOMPENSATION = 0x2000 

164 NOWHITEONWHITEFIXUP = 0x0004 

165 """Don't fix scum dot""" 

166 HIGHRESPRECALC = 0x0400 

167 """Use more memory to give better accuracy""" 

168 LOWRESPRECALC = 0x0800 

169 """Use less memory to minimize resources""" 

170 # this should be 8BITS_DEVICELINK, but that is not a valid name in Python: 

171 USE_8BITS_DEVICELINK = 0x0008 

172 """Create 8 bits devicelinks""" 

173 GUESSDEVICECLASS = 0x0020 

174 """Guess device class (for ``transform2devicelink``)""" 

175 KEEP_SEQUENCE = 0x0080 

176 """Keep profile sequence for devicelink creation""" 

177 FORCE_CLUT = 0x0002 

178 """Force CLUT optimization""" 

179 CLUT_POST_LINEARIZATION = 0x0001 

180 """create postlinearization tables if possible""" 

181 CLUT_PRE_LINEARIZATION = 0x0010 

182 """create prelinearization tables if possible""" 

183 NONEGATIVES = 0x8000 

184 """Prevent negative numbers in floating point transforms""" 

185 COPY_ALPHA = 0x04000000 

186 """Alpha channels are copied on ``cmsDoTransform()``""" 

187 NODEFAULTRESOURCEDEF = 0x01000000 

188 

189 _GRIDPOINTS_1 = 1 << 16 

190 _GRIDPOINTS_2 = 2 << 16 

191 _GRIDPOINTS_4 = 4 << 16 

192 _GRIDPOINTS_8 = 8 << 16 

193 _GRIDPOINTS_16 = 16 << 16 

194 _GRIDPOINTS_32 = 32 << 16 

195 _GRIDPOINTS_64 = 64 << 16 

196 _GRIDPOINTS_128 = 128 << 16 

197 

198 @staticmethod 

199 def GRIDPOINTS(n: int) -> Flags: 

200 """ 

201 Fine-tune control over number of gridpoints 

202 

203 :param n: :py:class:`int` in range ``0 <= n <= 255`` 

204 """ 

205 return Flags.NONE | ((n & 0xFF) << 16) 

206 

207 

208_MAX_FLAG = reduce(operator.or_, Flags) 

209 

210 

211_FLAGS = { 

212 "MATRIXINPUT": 1, 

213 "MATRIXOUTPUT": 2, 

214 "MATRIXONLY": (1 | 2), 

215 "NOWHITEONWHITEFIXUP": 4, # Don't hot fix scum dot 

216 # Don't create prelinearization tables on precalculated transforms 

217 # (internal use): 

218 "NOPRELINEARIZATION": 16, 

219 "GUESSDEVICECLASS": 32, # Guess device class (for transform2devicelink) 

220 "NOTCACHE": 64, # Inhibit 1-pixel cache 

221 "NOTPRECALC": 256, 

222 "NULLTRANSFORM": 512, # Don't transform anyway 

223 "HIGHRESPRECALC": 1024, # Use more memory to give better accuracy 

224 "LOWRESPRECALC": 2048, # Use less memory to minimize resources 

225 "WHITEBLACKCOMPENSATION": 8192, 

226 "BLACKPOINTCOMPENSATION": 8192, 

227 "GAMUTCHECK": 4096, # Out of Gamut alarm 

228 "SOFTPROOFING": 16384, # Do softproofing 

229 "PRESERVEBLACK": 32768, # Black preservation 

230 "NODEFAULTRESOURCEDEF": 16777216, # CRD special 

231 "GRIDPOINTS": lambda n: (n & 0xFF) << 16, # Gridpoints 

232} 

233 

234 

235# --------------------------------------------------------------------. 

236# Experimental PIL-level API 

237# --------------------------------------------------------------------. 

238 

239## 

240# Profile. 

241 

242 

243class ImageCmsProfile: 

244 def __init__(self, profile: str | SupportsRead[bytes] | core.CmsProfile) -> None: 

245 """ 

246 :param profile: Either a string representing a filename, 

247 a file like object containing a profile or a 

248 low-level profile object 

249 

250 """ 

251 

252 if isinstance(profile, str): 

253 if sys.platform == "win32": 

254 profile_bytes_path = profile.encode() 

255 try: 

256 profile_bytes_path.decode("ascii") 

257 except UnicodeDecodeError: 

258 with open(profile, "rb") as f: 

259 self._set(core.profile_frombytes(f.read())) 

260 return 

261 self._set(core.profile_open(profile), profile) 

262 elif hasattr(profile, "read"): 

263 self._set(core.profile_frombytes(profile.read())) 

264 elif isinstance(profile, core.CmsProfile): 

265 self._set(profile) 

266 else: 

267 msg = "Invalid type for Profile" # type: ignore[unreachable] 

268 raise TypeError(msg) 

269 

270 def _set(self, profile: core.CmsProfile, filename: str | None = None) -> None: 

271 self.profile = profile 

272 self.filename = filename 

273 self.product_name = None # profile.product_name 

274 self.product_info = None # profile.product_info 

275 

276 def tobytes(self) -> bytes: 

277 """ 

278 Returns the profile in a format suitable for embedding in 

279 saved images. 

280 

281 :returns: a bytes object containing the ICC profile. 

282 """ 

283 

284 return core.profile_tobytes(self.profile) 

285 

286 

287class ImageCmsTransform(Image.ImagePointHandler): 

288 """ 

289 Transform. This can be used with the procedural API, or with the standard 

290 :py:func:`~PIL.Image.Image.point` method. 

291 

292 Will return the output profile in the ``output.info['icc_profile']``. 

293 """ 

294 

295 def __init__( 

296 self, 

297 input: ImageCmsProfile, 

298 output: ImageCmsProfile, 

299 input_mode: str, 

300 output_mode: str, 

301 intent: Intent = Intent.PERCEPTUAL, 

302 proof: ImageCmsProfile | None = None, 

303 proof_intent: Intent = Intent.ABSOLUTE_COLORIMETRIC, 

304 flags: Flags = Flags.NONE, 

305 ): 

306 supported_modes = ( 

307 "RGB", 

308 "RGBA", 

309 "RGBX", 

310 "CMYK", 

311 "I;16", 

312 "I;16L", 

313 "I;16B", 

314 "YCbCr", 

315 "LAB", 

316 "L", 

317 "1", 

318 ) 

319 for mode in (input_mode, output_mode): 

320 if mode not in supported_modes: 

321 deprecate( 

322 mode, 

323 12, 

324 { 

325 "L;16": "I;16 or I;16L", 

326 "L:16B": "I;16B", 

327 "YCCA": "YCbCr", 

328 "YCC": "YCbCr", 

329 }.get(mode), 

330 ) 

331 if proof is None: 

332 self.transform = core.buildTransform( 

333 input.profile, output.profile, input_mode, output_mode, intent, flags 

334 ) 

335 else: 

336 self.transform = core.buildProofTransform( 

337 input.profile, 

338 output.profile, 

339 proof.profile, 

340 input_mode, 

341 output_mode, 

342 intent, 

343 proof_intent, 

344 flags, 

345 ) 

346 # Note: inputMode and outputMode are for pyCMS compatibility only 

347 self.input_mode = self.inputMode = input_mode 

348 self.output_mode = self.outputMode = output_mode 

349 

350 self.output_profile = output 

351 

352 def point(self, im: Image.Image) -> Image.Image: 

353 return self.apply(im) 

354 

355 def apply(self, im: Image.Image, imOut: Image.Image | None = None) -> Image.Image: 

356 if imOut is None: 

357 imOut = Image.new(self.output_mode, im.size, None) 

358 self.transform.apply(im.getim(), imOut.getim()) 

359 imOut.info["icc_profile"] = self.output_profile.tobytes() 

360 return imOut 

361 

362 def apply_in_place(self, im: Image.Image) -> Image.Image: 

363 if im.mode != self.output_mode: 

364 msg = "mode mismatch" 

365 raise ValueError(msg) # wrong output mode 

366 self.transform.apply(im.getim(), im.getim()) 

367 im.info["icc_profile"] = self.output_profile.tobytes() 

368 return im 

369 

370 

371def get_display_profile(handle: SupportsInt | None = None) -> ImageCmsProfile | None: 

372 """ 

373 (experimental) Fetches the profile for the current display device. 

374 

375 :returns: ``None`` if the profile is not known. 

376 """ 

377 

378 if sys.platform != "win32": 

379 return None 

380 

381 from . import ImageWin # type: ignore[unused-ignore, unreachable] 

382 

383 if isinstance(handle, ImageWin.HDC): 

384 profile = core.get_display_profile_win32(int(handle), 1) 

385 else: 

386 profile = core.get_display_profile_win32(int(handle or 0)) 

387 if profile is None: 

388 return None 

389 return ImageCmsProfile(profile) 

390 

391 

392# --------------------------------------------------------------------. 

393# pyCMS compatible layer 

394# --------------------------------------------------------------------. 

395 

396 

397class PyCMSError(Exception): 

398 """(pyCMS) Exception class. 

399 This is used for all errors in the pyCMS API.""" 

400 

401 pass 

402 

403 

404def profileToProfile( 

405 im: Image.Image, 

406 inputProfile: _CmsProfileCompatible, 

407 outputProfile: _CmsProfileCompatible, 

408 renderingIntent: Intent = Intent.PERCEPTUAL, 

409 outputMode: str | None = None, 

410 inPlace: bool = False, 

411 flags: Flags = Flags.NONE, 

412) -> Image.Image | None: 

413 """ 

414 (pyCMS) Applies an ICC transformation to a given image, mapping from 

415 ``inputProfile`` to ``outputProfile``. 

416 

417 If the input or output profiles specified are not valid filenames, a 

418 :exc:`PyCMSError` will be raised. If ``inPlace`` is ``True`` and 

419 ``outputMode != im.mode``, a :exc:`PyCMSError` will be raised. 

420 If an error occurs during application of the profiles, 

421 a :exc:`PyCMSError` will be raised. 

422 If ``outputMode`` is not a mode supported by the ``outputProfile`` (or by pyCMS), 

423 a :exc:`PyCMSError` will be raised. 

424 

425 This function applies an ICC transformation to im from ``inputProfile``'s 

426 color space to ``outputProfile``'s color space using the specified rendering 

427 intent to decide how to handle out-of-gamut colors. 

428 

429 ``outputMode`` can be used to specify that a color mode conversion is to 

430 be done using these profiles, but the specified profiles must be able 

431 to handle that mode. I.e., if converting im from RGB to CMYK using 

432 profiles, the input profile must handle RGB data, and the output 

433 profile must handle CMYK data. 

434 

435 :param im: An open :py:class:`~PIL.Image.Image` object (i.e. Image.new(...) 

436 or Image.open(...), etc.) 

437 :param inputProfile: String, as a valid filename path to the ICC input 

438 profile you wish to use for this image, or a profile object 

439 :param outputProfile: String, as a valid filename path to the ICC output 

440 profile you wish to use for this image, or a profile object 

441 :param renderingIntent: Integer (0-3) specifying the rendering intent you 

442 wish to use for the transform 

443 

444 ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) 

445 ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 

446 ImageCms.Intent.SATURATION = 2 

447 ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 

448 

449 see the pyCMS documentation for details on rendering intents and what 

450 they do. 

451 :param outputMode: A valid PIL mode for the output image (i.e. "RGB", 

452 "CMYK", etc.). Note: if rendering the image "inPlace", outputMode 

453 MUST be the same mode as the input, or omitted completely. If 

454 omitted, the outputMode will be the same as the mode of the input 

455 image (im.mode) 

456 :param inPlace: Boolean. If ``True``, the original image is modified in-place, 

457 and ``None`` is returned. If ``False`` (default), a new 

458 :py:class:`~PIL.Image.Image` object is returned with the transform applied. 

459 :param flags: Integer (0-...) specifying additional flags 

460 :returns: Either None or a new :py:class:`~PIL.Image.Image` object, depending on 

461 the value of ``inPlace`` 

462 :exception PyCMSError: 

463 """ 

464 

465 if outputMode is None: 

466 outputMode = im.mode 

467 

468 if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): 

469 msg = "renderingIntent must be an integer between 0 and 3" 

470 raise PyCMSError(msg) 

471 

472 if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): 

473 msg = f"flags must be an integer between 0 and {_MAX_FLAG}" 

474 raise PyCMSError(msg) 

475 

476 try: 

477 if not isinstance(inputProfile, ImageCmsProfile): 

478 inputProfile = ImageCmsProfile(inputProfile) 

479 if not isinstance(outputProfile, ImageCmsProfile): 

480 outputProfile = ImageCmsProfile(outputProfile) 

481 transform = ImageCmsTransform( 

482 inputProfile, 

483 outputProfile, 

484 im.mode, 

485 outputMode, 

486 renderingIntent, 

487 flags=flags, 

488 ) 

489 if inPlace: 

490 transform.apply_in_place(im) 

491 imOut = None 

492 else: 

493 imOut = transform.apply(im) 

494 except (OSError, TypeError, ValueError) as v: 

495 raise PyCMSError(v) from v 

496 

497 return imOut 

498 

499 

500def getOpenProfile( 

501 profileFilename: str | SupportsRead[bytes] | core.CmsProfile, 

502) -> ImageCmsProfile: 

503 """ 

504 (pyCMS) Opens an ICC profile file. 

505 

506 The PyCMSProfile object can be passed back into pyCMS for use in creating 

507 transforms and such (as in ImageCms.buildTransformFromOpenProfiles()). 

508 

509 If ``profileFilename`` is not a valid filename for an ICC profile, 

510 a :exc:`PyCMSError` will be raised. 

511 

512 :param profileFilename: String, as a valid filename path to the ICC profile 

513 you wish to open, or a file-like object. 

514 :returns: A CmsProfile class object. 

515 :exception PyCMSError: 

516 """ 

517 

518 try: 

519 return ImageCmsProfile(profileFilename) 

520 except (OSError, TypeError, ValueError) as v: 

521 raise PyCMSError(v) from v 

522 

523 

524def buildTransform( 

525 inputProfile: _CmsProfileCompatible, 

526 outputProfile: _CmsProfileCompatible, 

527 inMode: str, 

528 outMode: str, 

529 renderingIntent: Intent = Intent.PERCEPTUAL, 

530 flags: Flags = Flags.NONE, 

531) -> ImageCmsTransform: 

532 """ 

533 (pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the 

534 ``outputProfile``. Use applyTransform to apply the transform to a given 

535 image. 

536 

537 If the input or output profiles specified are not valid filenames, a 

538 :exc:`PyCMSError` will be raised. If an error occurs during creation 

539 of the transform, a :exc:`PyCMSError` will be raised. 

540 

541 If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile`` 

542 (or by pyCMS), a :exc:`PyCMSError` will be raised. 

543 

544 This function builds and returns an ICC transform from the ``inputProfile`` 

545 to the ``outputProfile`` using the ``renderingIntent`` to determine what to do 

546 with out-of-gamut colors. It will ONLY work for converting images that 

547 are in ``inMode`` to images that are in ``outMode`` color format (PIL mode, 

548 i.e. "RGB", "RGBA", "CMYK", etc.). 

549 

550 Building the transform is a fair part of the overhead in 

551 ImageCms.profileToProfile(), so if you're planning on converting multiple 

552 images using the same input/output settings, this can save you time. 

553 Once you have a transform object, it can be used with 

554 ImageCms.applyProfile() to convert images without the need to re-compute 

555 the lookup table for the transform. 

556 

557 The reason pyCMS returns a class object rather than a handle directly 

558 to the transform is that it needs to keep track of the PIL input/output 

559 modes that the transform is meant for. These attributes are stored in 

560 the ``inMode`` and ``outMode`` attributes of the object (which can be 

561 manually overridden if you really want to, but I don't know of any 

562 time that would be of use, or would even work). 

563 

564 :param inputProfile: String, as a valid filename path to the ICC input 

565 profile you wish to use for this transform, or a profile object 

566 :param outputProfile: String, as a valid filename path to the ICC output 

567 profile you wish to use for this transform, or a profile object 

568 :param inMode: String, as a valid PIL mode that the appropriate profile 

569 also supports (i.e. "RGB", "RGBA", "CMYK", etc.) 

570 :param outMode: String, as a valid PIL mode that the appropriate profile 

571 also supports (i.e. "RGB", "RGBA", "CMYK", etc.) 

572 :param renderingIntent: Integer (0-3) specifying the rendering intent you 

573 wish to use for the transform 

574 

575 ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) 

576 ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 

577 ImageCms.Intent.SATURATION = 2 

578 ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 

579 

580 see the pyCMS documentation for details on rendering intents and what 

581 they do. 

582 :param flags: Integer (0-...) specifying additional flags 

583 :returns: A CmsTransform class object. 

584 :exception PyCMSError: 

585 """ 

586 

587 if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): 

588 msg = "renderingIntent must be an integer between 0 and 3" 

589 raise PyCMSError(msg) 

590 

591 if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): 

592 msg = f"flags must be an integer between 0 and {_MAX_FLAG}" 

593 raise PyCMSError(msg) 

594 

595 try: 

596 if not isinstance(inputProfile, ImageCmsProfile): 

597 inputProfile = ImageCmsProfile(inputProfile) 

598 if not isinstance(outputProfile, ImageCmsProfile): 

599 outputProfile = ImageCmsProfile(outputProfile) 

600 return ImageCmsTransform( 

601 inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags 

602 ) 

603 except (OSError, TypeError, ValueError) as v: 

604 raise PyCMSError(v) from v 

605 

606 

607def buildProofTransform( 

608 inputProfile: _CmsProfileCompatible, 

609 outputProfile: _CmsProfileCompatible, 

610 proofProfile: _CmsProfileCompatible, 

611 inMode: str, 

612 outMode: str, 

613 renderingIntent: Intent = Intent.PERCEPTUAL, 

614 proofRenderingIntent: Intent = Intent.ABSOLUTE_COLORIMETRIC, 

615 flags: Flags = Flags.SOFTPROOFING, 

616) -> ImageCmsTransform: 

617 """ 

618 (pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the 

619 ``outputProfile``, but tries to simulate the result that would be 

620 obtained on the ``proofProfile`` device. 

621 

622 If the input, output, or proof profiles specified are not valid 

623 filenames, a :exc:`PyCMSError` will be raised. 

624 

625 If an error occurs during creation of the transform, 

626 a :exc:`PyCMSError` will be raised. 

627 

628 If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile`` 

629 (or by pyCMS), a :exc:`PyCMSError` will be raised. 

630 

631 This function builds and returns an ICC transform from the ``inputProfile`` 

632 to the ``outputProfile``, but tries to simulate the result that would be 

633 obtained on the ``proofProfile`` device using ``renderingIntent`` and 

634 ``proofRenderingIntent`` to determine what to do with out-of-gamut 

635 colors. This is known as "soft-proofing". It will ONLY work for 

636 converting images that are in ``inMode`` to images that are in outMode 

637 color format (PIL mode, i.e. "RGB", "RGBA", "CMYK", etc.). 

638 

639 Usage of the resulting transform object is exactly the same as with 

640 ImageCms.buildTransform(). 

641 

642 Proof profiling is generally used when using an output device to get a 

643 good idea of what the final printed/displayed image would look like on 

644 the ``proofProfile`` device when it's quicker and easier to use the 

645 output device for judging color. Generally, this means that the 

646 output device is a monitor, or a dye-sub printer (etc.), and the simulated 

647 device is something more expensive, complicated, or time consuming 

648 (making it difficult to make a real print for color judgement purposes). 

649 

650 Soft-proofing basically functions by adjusting the colors on the 

651 output device to match the colors of the device being simulated. However, 

652 when the simulated device has a much wider gamut than the output 

653 device, you may obtain marginal results. 

654 

655 :param inputProfile: String, as a valid filename path to the ICC input 

656 profile you wish to use for this transform, or a profile object 

657 :param outputProfile: String, as a valid filename path to the ICC output 

658 (monitor, usually) profile you wish to use for this transform, or a 

659 profile object 

660 :param proofProfile: String, as a valid filename path to the ICC proof 

661 profile you wish to use for this transform, or a profile object 

662 :param inMode: String, as a valid PIL mode that the appropriate profile 

663 also supports (i.e. "RGB", "RGBA", "CMYK", etc.) 

664 :param outMode: String, as a valid PIL mode that the appropriate profile 

665 also supports (i.e. "RGB", "RGBA", "CMYK", etc.) 

666 :param renderingIntent: Integer (0-3) specifying the rendering intent you 

667 wish to use for the input->proof (simulated) transform 

668 

669 ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) 

670 ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 

671 ImageCms.Intent.SATURATION = 2 

672 ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 

673 

674 see the pyCMS documentation for details on rendering intents and what 

675 they do. 

676 :param proofRenderingIntent: Integer (0-3) specifying the rendering intent 

677 you wish to use for proof->output transform 

678 

679 ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) 

680 ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 

681 ImageCms.Intent.SATURATION = 2 

682 ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 

683 

684 see the pyCMS documentation for details on rendering intents and what 

685 they do. 

686 :param flags: Integer (0-...) specifying additional flags 

687 :returns: A CmsTransform class object. 

688 :exception PyCMSError: 

689 """ 

690 

691 if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): 

692 msg = "renderingIntent must be an integer between 0 and 3" 

693 raise PyCMSError(msg) 

694 

695 if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): 

696 msg = f"flags must be an integer between 0 and {_MAX_FLAG}" 

697 raise PyCMSError(msg) 

698 

699 try: 

700 if not isinstance(inputProfile, ImageCmsProfile): 

701 inputProfile = ImageCmsProfile(inputProfile) 

702 if not isinstance(outputProfile, ImageCmsProfile): 

703 outputProfile = ImageCmsProfile(outputProfile) 

704 if not isinstance(proofProfile, ImageCmsProfile): 

705 proofProfile = ImageCmsProfile(proofProfile) 

706 return ImageCmsTransform( 

707 inputProfile, 

708 outputProfile, 

709 inMode, 

710 outMode, 

711 renderingIntent, 

712 proofProfile, 

713 proofRenderingIntent, 

714 flags, 

715 ) 

716 except (OSError, TypeError, ValueError) as v: 

717 raise PyCMSError(v) from v 

718 

719 

720buildTransformFromOpenProfiles = buildTransform 

721buildProofTransformFromOpenProfiles = buildProofTransform 

722 

723 

724def applyTransform( 

725 im: Image.Image, transform: ImageCmsTransform, inPlace: bool = False 

726) -> Image.Image | None: 

727 """ 

728 (pyCMS) Applies a transform to a given image. 

729 

730 If ``im.mode != transform.input_mode``, a :exc:`PyCMSError` is raised. 

731 

732 If ``inPlace`` is ``True`` and ``transform.input_mode != transform.output_mode``, a 

733 :exc:`PyCMSError` is raised. 

734 

735 If ``im.mode``, ``transform.input_mode`` or ``transform.output_mode`` is not 

736 supported by pyCMSdll or the profiles you used for the transform, a 

737 :exc:`PyCMSError` is raised. 

738 

739 If an error occurs while the transform is being applied, 

740 a :exc:`PyCMSError` is raised. 

741 

742 This function applies a pre-calculated transform (from 

743 ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles()) 

744 to an image. The transform can be used for multiple images, saving 

745 considerable calculation time if doing the same conversion multiple times. 

746 

747 If you want to modify im in-place instead of receiving a new image as 

748 the return value, set ``inPlace`` to ``True``. This can only be done if 

749 ``transform.input_mode`` and ``transform.output_mode`` are the same, because we 

750 can't change the mode in-place (the buffer sizes for some modes are 

751 different). The default behavior is to return a new :py:class:`~PIL.Image.Image` 

752 object of the same dimensions in mode ``transform.output_mode``. 

753 

754 :param im: An :py:class:`~PIL.Image.Image` object, and ``im.mode`` must be the same 

755 as the ``input_mode`` supported by the transform. 

756 :param transform: A valid CmsTransform class object 

757 :param inPlace: Bool. If ``True``, ``im`` is modified in place and ``None`` is 

758 returned, if ``False``, a new :py:class:`~PIL.Image.Image` object with the 

759 transform applied is returned (and ``im`` is not changed). The default is 

760 ``False``. 

761 :returns: Either ``None``, or a new :py:class:`~PIL.Image.Image` object, 

762 depending on the value of ``inPlace``. The profile will be returned in 

763 the image's ``info['icc_profile']``. 

764 :exception PyCMSError: 

765 """ 

766 

767 try: 

768 if inPlace: 

769 transform.apply_in_place(im) 

770 imOut = None 

771 else: 

772 imOut = transform.apply(im) 

773 except (TypeError, ValueError) as v: 

774 raise PyCMSError(v) from v 

775 

776 return imOut 

777 

778 

779def createProfile( 

780 colorSpace: Literal["LAB", "XYZ", "sRGB"], colorTemp: SupportsFloat = 0 

781) -> core.CmsProfile: 

782 """ 

783 (pyCMS) Creates a profile. 

784 

785 If colorSpace not in ``["LAB", "XYZ", "sRGB"]``, 

786 a :exc:`PyCMSError` is raised. 

787 

788 If using LAB and ``colorTemp`` is not a positive integer, 

789 a :exc:`PyCMSError` is raised. 

790 

791 If an error occurs while creating the profile, 

792 a :exc:`PyCMSError` is raised. 

793 

794 Use this function to create common profiles on-the-fly instead of 

795 having to supply a profile on disk and knowing the path to it. It 

796 returns a normal CmsProfile object that can be passed to 

797 ImageCms.buildTransformFromOpenProfiles() to create a transform to apply 

798 to images. 

799 

800 :param colorSpace: String, the color space of the profile you wish to 

801 create. 

802 Currently only "LAB", "XYZ", and "sRGB" are supported. 

803 :param colorTemp: Positive number for the white point for the profile, in 

804 degrees Kelvin (i.e. 5000, 6500, 9600, etc.). The default is for D50 

805 illuminant if omitted (5000k). colorTemp is ONLY applied to LAB 

806 profiles, and is ignored for XYZ and sRGB. 

807 :returns: A CmsProfile class object 

808 :exception PyCMSError: 

809 """ 

810 

811 if colorSpace not in ["LAB", "XYZ", "sRGB"]: 

812 msg = ( 

813 f"Color space not supported for on-the-fly profile creation ({colorSpace})" 

814 ) 

815 raise PyCMSError(msg) 

816 

817 if colorSpace == "LAB": 

818 try: 

819 colorTemp = float(colorTemp) 

820 except (TypeError, ValueError) as e: 

821 msg = f'Color temperature must be numeric, "{colorTemp}" not valid' 

822 raise PyCMSError(msg) from e 

823 

824 try: 

825 return core.createProfile(colorSpace, colorTemp) 

826 except (TypeError, ValueError) as v: 

827 raise PyCMSError(v) from v 

828 

829 

830def getProfileName(profile: _CmsProfileCompatible) -> str: 

831 """ 

832 

833 (pyCMS) Gets the internal product name for the given profile. 

834 

835 If ``profile`` isn't a valid CmsProfile object or filename to a profile, 

836 a :exc:`PyCMSError` is raised If an error occurs while trying 

837 to obtain the name tag, a :exc:`PyCMSError` is raised. 

838 

839 Use this function to obtain the INTERNAL name of the profile (stored 

840 in an ICC tag in the profile itself), usually the one used when the 

841 profile was originally created. Sometimes this tag also contains 

842 additional information supplied by the creator. 

843 

844 :param profile: EITHER a valid CmsProfile object, OR a string of the 

845 filename of an ICC profile. 

846 :returns: A string containing the internal name of the profile as stored 

847 in an ICC tag. 

848 :exception PyCMSError: 

849 """ 

850 

851 try: 

852 # add an extra newline to preserve pyCMS compatibility 

853 if not isinstance(profile, ImageCmsProfile): 

854 profile = ImageCmsProfile(profile) 

855 # do it in python, not c. 

856 # // name was "%s - %s" (model, manufacturer) || Description , 

857 # // but if the Model and Manufacturer were the same or the model 

858 # // was long, Just the model, in 1.x 

859 model = profile.profile.model 

860 manufacturer = profile.profile.manufacturer 

861 

862 if not (model or manufacturer): 

863 return (profile.profile.profile_description or "") + "\n" 

864 if not manufacturer or (model and len(model) > 30): 

865 return f"{model}\n" 

866 return f"{model} - {manufacturer}\n" 

867 

868 except (AttributeError, OSError, TypeError, ValueError) as v: 

869 raise PyCMSError(v) from v 

870 

871 

872def getProfileInfo(profile: _CmsProfileCompatible) -> str: 

873 """ 

874 (pyCMS) Gets the internal product information for the given profile. 

875 

876 If ``profile`` isn't a valid CmsProfile object or filename to a profile, 

877 a :exc:`PyCMSError` is raised. 

878 

879 If an error occurs while trying to obtain the info tag, 

880 a :exc:`PyCMSError` is raised. 

881 

882 Use this function to obtain the information stored in the profile's 

883 info tag. This often contains details about the profile, and how it 

884 was created, as supplied by the creator. 

885 

886 :param profile: EITHER a valid CmsProfile object, OR a string of the 

887 filename of an ICC profile. 

888 :returns: A string containing the internal profile information stored in 

889 an ICC tag. 

890 :exception PyCMSError: 

891 """ 

892 

893 try: 

894 if not isinstance(profile, ImageCmsProfile): 

895 profile = ImageCmsProfile(profile) 

896 # add an extra newline to preserve pyCMS compatibility 

897 # Python, not C. the white point bits weren't working well, 

898 # so skipping. 

899 # info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint 

900 description = profile.profile.profile_description 

901 cpright = profile.profile.copyright 

902 elements = [element for element in (description, cpright) if element] 

903 return "\r\n\r\n".join(elements) + "\r\n\r\n" 

904 

905 except (AttributeError, OSError, TypeError, ValueError) as v: 

906 raise PyCMSError(v) from v 

907 

908 

909def getProfileCopyright(profile: _CmsProfileCompatible) -> str: 

910 """ 

911 (pyCMS) Gets the copyright for the given profile. 

912 

913 If ``profile`` isn't a valid CmsProfile object or filename to a profile, a 

914 :exc:`PyCMSError` is raised. 

915 

916 If an error occurs while trying to obtain the copyright tag, 

917 a :exc:`PyCMSError` is raised. 

918 

919 Use this function to obtain the information stored in the profile's 

920 copyright tag. 

921 

922 :param profile: EITHER a valid CmsProfile object, OR a string of the 

923 filename of an ICC profile. 

924 :returns: A string containing the internal profile information stored in 

925 an ICC tag. 

926 :exception PyCMSError: 

927 """ 

928 try: 

929 # add an extra newline to preserve pyCMS compatibility 

930 if not isinstance(profile, ImageCmsProfile): 

931 profile = ImageCmsProfile(profile) 

932 return (profile.profile.copyright or "") + "\n" 

933 except (AttributeError, OSError, TypeError, ValueError) as v: 

934 raise PyCMSError(v) from v 

935 

936 

937def getProfileManufacturer(profile: _CmsProfileCompatible) -> str: 

938 """ 

939 (pyCMS) Gets the manufacturer for the given profile. 

940 

941 If ``profile`` isn't a valid CmsProfile object or filename to a profile, a 

942 :exc:`PyCMSError` is raised. 

943 

944 If an error occurs while trying to obtain the manufacturer tag, a 

945 :exc:`PyCMSError` is raised. 

946 

947 Use this function to obtain the information stored in the profile's 

948 manufacturer tag. 

949 

950 :param profile: EITHER a valid CmsProfile object, OR a string of the 

951 filename of an ICC profile. 

952 :returns: A string containing the internal profile information stored in 

953 an ICC tag. 

954 :exception PyCMSError: 

955 """ 

956 try: 

957 # add an extra newline to preserve pyCMS compatibility 

958 if not isinstance(profile, ImageCmsProfile): 

959 profile = ImageCmsProfile(profile) 

960 return (profile.profile.manufacturer or "") + "\n" 

961 except (AttributeError, OSError, TypeError, ValueError) as v: 

962 raise PyCMSError(v) from v 

963 

964 

965def getProfileModel(profile: _CmsProfileCompatible) -> str: 

966 """ 

967 (pyCMS) Gets the model for the given profile. 

968 

969 If ``profile`` isn't a valid CmsProfile object or filename to a profile, a 

970 :exc:`PyCMSError` is raised. 

971 

972 If an error occurs while trying to obtain the model tag, 

973 a :exc:`PyCMSError` is raised. 

974 

975 Use this function to obtain the information stored in the profile's 

976 model tag. 

977 

978 :param profile: EITHER a valid CmsProfile object, OR a string of the 

979 filename of an ICC profile. 

980 :returns: A string containing the internal profile information stored in 

981 an ICC tag. 

982 :exception PyCMSError: 

983 """ 

984 

985 try: 

986 # add an extra newline to preserve pyCMS compatibility 

987 if not isinstance(profile, ImageCmsProfile): 

988 profile = ImageCmsProfile(profile) 

989 return (profile.profile.model or "") + "\n" 

990 except (AttributeError, OSError, TypeError, ValueError) as v: 

991 raise PyCMSError(v) from v 

992 

993 

994def getProfileDescription(profile: _CmsProfileCompatible) -> str: 

995 """ 

996 (pyCMS) Gets the description for the given profile. 

997 

998 If ``profile`` isn't a valid CmsProfile object or filename to a profile, a 

999 :exc:`PyCMSError` is raised. 

1000 

1001 If an error occurs while trying to obtain the description tag, 

1002 a :exc:`PyCMSError` is raised. 

1003 

1004 Use this function to obtain the information stored in the profile's 

1005 description tag. 

1006 

1007 :param profile: EITHER a valid CmsProfile object, OR a string of the 

1008 filename of an ICC profile. 

1009 :returns: A string containing the internal profile information stored in an 

1010 ICC tag. 

1011 :exception PyCMSError: 

1012 """ 

1013 

1014 try: 

1015 # add an extra newline to preserve pyCMS compatibility 

1016 if not isinstance(profile, ImageCmsProfile): 

1017 profile = ImageCmsProfile(profile) 

1018 return (profile.profile.profile_description or "") + "\n" 

1019 except (AttributeError, OSError, TypeError, ValueError) as v: 

1020 raise PyCMSError(v) from v 

1021 

1022 

1023def getDefaultIntent(profile: _CmsProfileCompatible) -> int: 

1024 """ 

1025 (pyCMS) Gets the default intent name for the given profile. 

1026 

1027 If ``profile`` isn't a valid CmsProfile object or filename to a profile, a 

1028 :exc:`PyCMSError` is raised. 

1029 

1030 If an error occurs while trying to obtain the default intent, a 

1031 :exc:`PyCMSError` is raised. 

1032 

1033 Use this function to determine the default (and usually best optimized) 

1034 rendering intent for this profile. Most profiles support multiple 

1035 rendering intents, but are intended mostly for one type of conversion. 

1036 If you wish to use a different intent than returned, use 

1037 ImageCms.isIntentSupported() to verify it will work first. 

1038 

1039 :param profile: EITHER a valid CmsProfile object, OR a string of the 

1040 filename of an ICC profile. 

1041 :returns: Integer 0-3 specifying the default rendering intent for this 

1042 profile. 

1043 

1044 ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) 

1045 ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 

1046 ImageCms.Intent.SATURATION = 2 

1047 ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 

1048 

1049 see the pyCMS documentation for details on rendering intents and what 

1050 they do. 

1051 :exception PyCMSError: 

1052 """ 

1053 

1054 try: 

1055 if not isinstance(profile, ImageCmsProfile): 

1056 profile = ImageCmsProfile(profile) 

1057 return profile.profile.rendering_intent 

1058 except (AttributeError, OSError, TypeError, ValueError) as v: 

1059 raise PyCMSError(v) from v 

1060 

1061 

1062def isIntentSupported( 

1063 profile: _CmsProfileCompatible, intent: Intent, direction: Direction 

1064) -> Literal[-1, 1]: 

1065 """ 

1066 (pyCMS) Checks if a given intent is supported. 

1067 

1068 Use this function to verify that you can use your desired 

1069 ``intent`` with ``profile``, and that ``profile`` can be used for the 

1070 input/output/proof profile as you desire. 

1071 

1072 Some profiles are created specifically for one "direction", can cannot 

1073 be used for others. Some profiles can only be used for certain 

1074 rendering intents, so it's best to either verify this before trying 

1075 to create a transform with them (using this function), or catch the 

1076 potential :exc:`PyCMSError` that will occur if they don't 

1077 support the modes you select. 

1078 

1079 :param profile: EITHER a valid CmsProfile object, OR a string of the 

1080 filename of an ICC profile. 

1081 :param intent: Integer (0-3) specifying the rendering intent you wish to 

1082 use with this profile 

1083 

1084 ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) 

1085 ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 

1086 ImageCms.Intent.SATURATION = 2 

1087 ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 

1088 

1089 see the pyCMS documentation for details on rendering intents and what 

1090 they do. 

1091 :param direction: Integer specifying if the profile is to be used for 

1092 input, output, or proof 

1093 

1094 INPUT = 0 (or use ImageCms.Direction.INPUT) 

1095 OUTPUT = 1 (or use ImageCms.Direction.OUTPUT) 

1096 PROOF = 2 (or use ImageCms.Direction.PROOF) 

1097 

1098 :returns: 1 if the intent/direction are supported, -1 if they are not. 

1099 :exception PyCMSError: 

1100 """ 

1101 

1102 try: 

1103 if not isinstance(profile, ImageCmsProfile): 

1104 profile = ImageCmsProfile(profile) 

1105 # FIXME: I get different results for the same data w. different 

1106 # compilers. Bug in LittleCMS or in the binding? 

1107 if profile.profile.is_intent_supported(intent, direction): 

1108 return 1 

1109 else: 

1110 return -1 

1111 except (AttributeError, OSError, TypeError, ValueError) as v: 

1112 raise PyCMSError(v) from v 

1113 

1114 

1115def versions() -> tuple[str, str | None, str, str]: 

1116 """ 

1117 (pyCMS) Fetches versions. 

1118 """ 

1119 

1120 deprecate( 

1121 "PIL.ImageCms.versions()", 

1122 12, 

1123 '(PIL.features.version("littlecms2"), sys.version, PIL.__version__)', 

1124 ) 

1125 return _VERSION, core.littlecms_version, sys.version.split()[0], __version__