diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index cdd6f00c3..de140710f 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -309,7 +309,7 @@ class TestImageCms(PillowTestCase): 2: (False, False, True), 3: (False, False, True) }) - self.assertEqual(p.color_space, 'RGB') + self.assertIsNone(p.colorant_table) self.assertIsNone(p.colorant_table_out) self.assertIsNone(p.colorimetric_intent) @@ -361,16 +361,9 @@ class TestImageCms(PillowTestCase): (5000.722328847392,)) self.assertEqual(p.model, 'IEC 61966-2-1 Default RGB Colour Space - sRGB') - self.assertEqual(p.pcs, 'XYZ') + self.assertIsNone(p.perceptual_rendering_intent_gamut) - self.assertEqual(p.product_copyright, - 'Copyright International Color Consortium, 2009') - self.assertEqual(p.product_desc, 'sRGB IEC61966-2-1 black scaled') - self.assertEqual(p.product_description, - 'sRGB IEC61966-2-1 black scaled') - self.assertEqual(p.product_manufacturer, '') - self.assertEqual( - p.product_model, 'IEC 61966-2-1 Default RGB Colour Space - sRGB') + self.assertEqual( p.profile_description, 'sRGB IEC61966-2-1 black scaled') self.assertEqual( @@ -393,6 +386,40 @@ class TestImageCms(PillowTestCase): 'Reference Viewing Condition in IEC 61966-2-1') self.assertEqual(p.xcolor_space, 'RGB ') + def test_deprecations(self): + self.skip_missing() + o = ImageCms.getOpenProfile(SRGB) + p = o.profile + + def helper_deprecated(attr, expected): + result = self.assert_warning(DeprecationWarning, getattr, p, attr) + self.assertEqual(result, expected) + + # p.color_space + helper_deprecated("color_space", "RGB") + + # p.pcs + helper_deprecated("pcs", "XYZ") + + # p.product_copyright + helper_deprecated( + "product_copyright", "Copyright International Color Consortium, 2009" + ) + + # p.product_desc + helper_deprecated("product_desc", "sRGB IEC61966-2-1 black scaled") + + # p.product_description + helper_deprecated("product_description", "sRGB IEC61966-2-1 black scaled") + + # p.product_manufacturer + helper_deprecated("product_manufacturer", "") + + # p.product_model + helper_deprecated( + "product_model", "IEC 61966-2-1 Default RGB Colour Space - sRGB" + ) + def test_profile_typesafety(self): """ Profile init type safety diff --git a/docs/deprecations.rst b/docs/deprecations.rst index fb5acfa8e..ae29b4254 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -79,6 +79,26 @@ PILLOW_VERSION constant ``PILLOW_VERSION`` has been deprecated and will be removed in the next major release. Use ``__version__`` instead. +ImageCms.CmsProfile attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 3.2.0 + +Some attributes in ``ImageCms.CmsProfile`` are deprecated. From 6.0.0, they issue a +``DeprecationWarning``: + +======================== =============================== +Deprecated Use instead +======================== =============================== +``color_space`` Padded ``xcolor_space`` +``pcs`` Padded ``connection_space`` +``product_copyright`` Unicode ``copyright`` +``product_desc`` Unicode ``profile_description`` +``product_description`` Unicode ``profile_description`` +``product_manufacturer`` Unicode ``manufacturer`` +``product_model`` Unicode ``model`` +======================== =============================== + Removed features ---------------- diff --git a/docs/reference/ImageCms.rst b/docs/reference/ImageCms.rst index 35f4acee6..ea6334708 100644 --- a/docs/reference/ImageCms.rst +++ b/docs/reference/ImageCms.rst @@ -132,21 +132,21 @@ can be easily displayed in a chromaticity diagram, for example). .. py:attribute:: manufacturer - The (english) display string for the device manufacturer (see + The (English) display string for the device manufacturer (see 9.2.22 of ICC.1:2010). :type: :py:class:`unicode` or ``None`` .. py:attribute:: model - The (english) display string for the device model of the device + The (English) display string for the device model of the device for which this profile is created (see 9.2.23 of ICC.1:2010). :type: :py:class:`unicode` or ``None`` .. py:attribute:: profile_description - The (english) display string for the profile description (see + The (English) display string for the profile description (see 9.2.41 of ICC.1:2010). :type: :py:class:`unicode` or ``None`` @@ -269,14 +269,14 @@ can be easily displayed in a chromaticity diagram, for example). .. py:attribute:: viewing_condition - The (english) display string for the viewing conditions (see + The (English) display string for the viewing conditions (see 9.2.48 of ICC.1:2010). :type: :py:class:`unicode` or ``None`` .. py:attribute:: screening_description - The (english) display string for the screening conditions. + The (English) display string for the screening conditions. This tag was available in ICC 3.2, but it is removed from version 4. diff --git a/docs/releasenotes/6.0.0.rst b/docs/releasenotes/6.0.0.rst index b75c6e89a..9b5ec2d4f 100644 --- a/docs/releasenotes/6.0.0.rst +++ b/docs/releasenotes/6.0.0.rst @@ -99,6 +99,24 @@ version. Use ``PIL.__version__`` instead. +ImageCms.CmsProfile attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Some attributes in ``ImageCms.CmsProfile`` have been deprecated since Pillow 3.2.0. From +6.0.0, they issue a ``DeprecationWarning``: + +======================== =============================== +Deprecated Use instead +======================== =============================== +``color_space`` Padded ``xcolor_space`` +``pcs`` Padded ``connection_space`` +``product_copyright`` Unicode ``copyright`` +``product_desc`` Unicode ``profile_description`` +``product_description`` Unicode ``profile_description`` +``product_manufacturer`` Unicode ``manufacturer`` +``product_model`` Unicode ``model`` +======================== =============================== + MIME type improvements ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index 2cd6f33b5..c75ad6a66 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -686,11 +686,11 @@ def getProfileName(profile): # // 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 + model = profile.profile.model + manufacturer = profile.profile.manufacturer if not (model or manufacturer): - return profile.profile.product_description + "\n" + return (profile.profile.profile_description or "") + "\n" if not manufacturer or len(model) > 30: return model + "\n" return "%s - %s\n" % (model, manufacturer) @@ -727,8 +727,8 @@ def getProfileInfo(profile): # 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 + description = profile.profile.profile_description + cpright = profile.profile.copyright arr = [] for elt in (description, cpright): if elt: @@ -762,7 +762,7 @@ def getProfileCopyright(profile): # add an extra newline to preserve pyCMS compatibility if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) - return profile.profile.product_copyright + "\n" + return (profile.profile.copyright or "") + "\n" except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) @@ -790,7 +790,7 @@ def getProfileManufacturer(profile): # add an extra newline to preserve pyCMS compatibility if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) - return profile.profile.product_manufacturer + "\n" + return (profile.profile.manufacturer or "") + "\n" except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) @@ -819,7 +819,7 @@ def getProfileModel(profile): # add an extra newline to preserve pyCMS compatibility if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) - return profile.profile.product_model + "\n" + return (profile.profile.model or "") + "\n" except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) @@ -848,7 +848,7 @@ def getProfileDescription(profile): # add an extra newline to preserve pyCMS compatibility if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) - return profile.profile.product_description + "\n" + return (profile.profile.profile_description or "") + "\n" except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) diff --git a/src/_imagingcms.c b/src/_imagingcms.c index 5432a0117..2c9f3aa68 100644 --- a/src/_imagingcms.c +++ b/src/_imagingcms.c @@ -736,12 +736,12 @@ _xyz3_py(cmsCIEXYZ* XYZ) cmsXYZ2xyY(&xyY[2], &XYZ[2]); return Py_BuildValue("(((d,d,d),(d,d,d),(d,d,d)),((d,d,d),(d,d,d),(d,d,d)))", - XYZ[0].X, XYZ[0].Y, XYZ[0].Z, - XYZ[1].X, XYZ[1].Y, XYZ[1].Z, - XYZ[2].X, XYZ[2].Y, XYZ[2].Z, - xyY[0].x, xyY[0].y, xyY[0].Y, - xyY[1].x, xyY[1].y, xyY[1].Y, - xyY[2].x, xyY[2].y, xyY[2].Y); + XYZ[0].X, XYZ[0].Y, XYZ[0].Z, + XYZ[1].X, XYZ[1].Y, XYZ[1].Z, + XYZ[2].X, XYZ[2].Y, XYZ[2].Z, + xyY[0].x, xyY[0].y, xyY[0].Y, + xyY[1].x, xyY[1].y, xyY[1].Y, + xyY[2].x, xyY[2].y, xyY[2].Y); } static PyObject* @@ -784,9 +784,9 @@ _profile_read_ciexyy_triple(CmsProfileObject* self, cmsTagSignature info) /* Note: lcms does all the heavy lifting and error checking (nr of channels == 3). */ return Py_BuildValue("((d,d,d),(d,d,d),(d,d,d)),", - triple->Red.x, triple->Red.y, triple->Red.Y, - triple->Green.x, triple->Green.y, triple->Green.Y, - triple->Blue.x, triple->Blue.y, triple->Blue.Y); + triple->Red.x, triple->Red.y, triple->Red.Y, + triple->Green.x, triple->Green.y, triple->Green.Y, + triple->Blue.x, triple->Blue.y, triple->Blue.Y); } static PyObject* @@ -818,12 +818,12 @@ _profile_read_named_color_list(CmsProfileObject* self, cmsTagSignature info) for (i = 0; i < n; i++) { PyObject* str; cmsNamedColorInfo(ncl, i, name, NULL, NULL, NULL, NULL); - str = PyUnicode_FromString(name); - if (str == NULL) { - Py_DECREF(result); - Py_INCREF(Py_None); - return Py_None; - } + str = PyUnicode_FromString(name); + if (str == NULL) { + Py_DECREF(result); + Py_INCREF(Py_None); + return Py_None; + } PyList_SET_ITEM(result, i, str); } @@ -845,9 +845,9 @@ static cmsBool _calculate_rgb_primaries(CmsProfileObject* self, cmsCIEXYZTRIPLE* // transform from our profile to XYZ using doubles for highest precision hTransform = cmsCreateTransform(self->profile, TYPE_RGB_DBL, - hXYZ, TYPE_XYZ_DBL, - INTENT_RELATIVE_COLORIMETRIC, - cmsFLAGS_NOCACHE | cmsFLAGS_NOOPTIMIZE); + hXYZ, TYPE_XYZ_DBL, + INTENT_RELATIVE_COLORIMETRIC, + cmsFLAGS_NOCACHE | cmsFLAGS_NOOPTIMIZE); cmsCloseProfile(hXYZ); if (hTransform == NULL) return 0; @@ -886,31 +886,31 @@ _is_intent_supported(CmsProfileObject* self, int clut) n = cmsGetSupportedIntents(INTENTS, - intent_ids, - intent_descs); + intent_ids, + intent_descs); for (i = 0; i < n; i++) { int intent = (int) intent_ids[i]; PyObject* id; - PyObject* entry; + PyObject* entry; - /* Only valid for ICC Intents (otherwise we read invalid memory in lcms cmsio1.c). */ - if (!(intent == INTENT_PERCEPTUAL || intent == INTENT_RELATIVE_COLORIMETRIC - || intent == INTENT_SATURATION || intent == INTENT_ABSOLUTE_COLORIMETRIC)) - continue; + /* Only valid for ICC Intents (otherwise we read invalid memory in lcms cmsio1.c). */ + if (!(intent == INTENT_PERCEPTUAL || intent == INTENT_RELATIVE_COLORIMETRIC + || intent == INTENT_SATURATION || intent == INTENT_ABSOLUTE_COLORIMETRIC)) + continue; - id = PyInt_FromLong((long) intent); - entry = Py_BuildValue("(OOO)", - _check_intent(clut, self->profile, intent, LCMS_USED_AS_INPUT) ? Py_True : Py_False, - _check_intent(clut, self->profile, intent, LCMS_USED_AS_OUTPUT) ? Py_True : Py_False, - _check_intent(clut, self->profile, intent, LCMS_USED_AS_PROOF) ? Py_True : Py_False); - if (id == NULL || entry == NULL) { - Py_XDECREF(id); - Py_XDECREF(entry); - Py_XDECREF(result); - Py_INCREF(Py_None); - return Py_None; - } - PyDict_SetItem(result, id, entry); + id = PyInt_FromLong((long) intent); + entry = Py_BuildValue("(OOO)", + _check_intent(clut, self->profile, intent, LCMS_USED_AS_INPUT) ? Py_True : Py_False, + _check_intent(clut, self->profile, intent, LCMS_USED_AS_OUTPUT) ? Py_True : Py_False, + _check_intent(clut, self->profile, intent, LCMS_USED_AS_PROOF) ? Py_True : Py_False); + if (id == NULL || entry == NULL) { + Py_XDECREF(id); + Py_XDECREF(entry); + Py_XDECREF(result); + Py_INCREF(Py_None); + return Py_None; + } + PyDict_SetItem(result, id, entry); } return result; } @@ -967,6 +967,8 @@ _profile_getattr(CmsProfileObject* self, cmsInfoType field) static PyObject* cms_profile_getattr_product_desc(CmsProfileObject* self, void* closure) { + PyErr_WarnEx(PyExc_DeprecationWarning, + "product_desc is deprecated. Use Unicode profile_description instead.", 1); // description was Description != 'Copyright' || or "%s - %s" (manufacturer, model) in 1.x return _profile_getattr(self, cmsInfoDescription); } @@ -976,24 +978,32 @@ cms_profile_getattr_product_desc(CmsProfileObject* self, void* closure) static PyObject* cms_profile_getattr_product_description(CmsProfileObject* self, void* closure) { + PyErr_WarnEx(PyExc_DeprecationWarning, + "product_description is deprecated. Use Unicode profile_description instead.", 1); return _profile_getattr(self, cmsInfoDescription); } static PyObject* cms_profile_getattr_product_model(CmsProfileObject* self, void* closure) { + PyErr_WarnEx(PyExc_DeprecationWarning, + "product_model is deprecated. Use Unicode model instead.", 1); return _profile_getattr(self, cmsInfoModel); } static PyObject* cms_profile_getattr_product_manufacturer(CmsProfileObject* self, void* closure) { + PyErr_WarnEx(PyExc_DeprecationWarning, + "product_manufacturer is deprecated. Use Unicode manufacturer instead.", 1); return _profile_getattr(self, cmsInfoManufacturer); } static PyObject* cms_profile_getattr_product_copyright(CmsProfileObject* self, void* closure) { + PyErr_WarnEx(PyExc_DeprecationWarning, + "product_copyright is deprecated. Use Unicode copyright instead.", 1); return _profile_getattr(self, cmsInfoCopyright); } @@ -1006,12 +1016,16 @@ cms_profile_getattr_rendering_intent(CmsProfileObject* self, void* closure) static PyObject* cms_profile_getattr_pcs(CmsProfileObject* self, void* closure) { + PyErr_WarnEx(PyExc_DeprecationWarning, + "pcs is deprecated. Use padded connection_space instead.", 1); return PyUnicode_DecodeFSDefault(findICmode(cmsGetPCS(self->profile))); } static PyObject* cms_profile_getattr_color_space(CmsProfileObject* self, void* closure) { + PyErr_WarnEx(PyExc_DeprecationWarning, + "color_space is deprecated. Use padded xcolor_space instead.", 1); return PyUnicode_DecodeFSDefault(findICmode(cmsGetColorSpace(self->profile))); } @@ -1071,7 +1085,7 @@ cms_profile_getattr_creation_date(CmsProfileObject* self, void* closure) } return PyDateTime_FromDateAndTime(1900 + ct.tm_year, ct.tm_mon, ct.tm_mday, - ct.tm_hour, ct.tm_min, ct.tm_sec, 0); + ct.tm_hour, ct.tm_min, ct.tm_sec, 0); } static PyObject* @@ -1296,7 +1310,7 @@ cms_profile_getattr_green_primary(CmsProfileObject* self, void* closure) result = _calculate_rgb_primaries(self, &primaries); if (! result) { Py_INCREF(Py_None); - return Py_None; + return Py_None; } return _xyz_py(&primaries.Green); @@ -1312,7 +1326,7 @@ cms_profile_getattr_blue_primary(CmsProfileObject* self, void* closure) result = _calculate_rgb_primaries(self, &primaries); if (! result) { Py_INCREF(Py_None); - return Py_None; + return Py_None; } return _xyz_py(&primaries.Blue); @@ -1395,11 +1409,11 @@ cms_profile_getattr_icc_measurement_condition (CmsProfileObject* self, void* clo geo = "unknown"; return Py_BuildValue("{s:i,s:(ddd),s:s,s:d,s:s}", - "observer", mc->Observer, - "backing", mc->Backing.X, mc->Backing.Y, mc->Backing.Z, - "geo", geo, - "flare", mc->Flare, - "illuminant_type", _illu_map(mc->IlluminantType)); + "observer", mc->Observer, + "backing", mc->Backing.X, mc->Backing.Y, mc->Backing.Z, + "geo", geo, + "flare", mc->Flare, + "illuminant_type", _illu_map(mc->IlluminantType)); } static PyObject* @@ -1420,9 +1434,9 @@ cms_profile_getattr_icc_viewing_condition (CmsProfileObject* self, void* closure } return Py_BuildValue("{s:(ddd),s:(ddd),s:s}", - "illuminant", vc->IlluminantXYZ.X, vc->IlluminantXYZ.Y, vc->IlluminantXYZ.Z, - "surround", vc->SurroundXYZ.X, vc->SurroundXYZ.Y, vc->SurroundXYZ.Z, - "illuminant_type", _illu_map(vc->IlluminantType)); + "illuminant", vc->IlluminantXYZ.X, vc->IlluminantXYZ.Y, vc->IlluminantXYZ.Z, + "surround", vc->SurroundXYZ.X, vc->SurroundXYZ.Y, vc->SurroundXYZ.Z, + "illuminant_type", _illu_map(vc->IlluminantType)); }