mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-10-29 23:17:49 +03:00 
			
		
		
		
	Merge pull request #5455 from radarhere/xmp
This commit is contained in:
		
						commit
						b5c4b9afe0
					
				|  | @ -828,7 +828,23 @@ class TestFileJpeg: | ||||||
|             xmp = im.getxmp() |             xmp = im.getxmp() | ||||||
| 
 | 
 | ||||||
|             assert isinstance(xmp, dict) |             assert isinstance(xmp, dict) | ||||||
|             assert xmp["Description"]["Version"] == "10.4" | 
 | ||||||
|  |             description = xmp["xmpmeta"]["RDF"]["Description"] | ||||||
|  |             assert description["DerivedFrom"] == { | ||||||
|  |                 "documentID": "8367D410E636EA95B7DE7EBA1C43A412", | ||||||
|  |                 "originalDocumentID": "8367D410E636EA95B7DE7EBA1C43A412", | ||||||
|  |             } | ||||||
|  |             assert description["Look"]["Description"]["Group"]["Alt"]["li"] == { | ||||||
|  |                 "lang": "x-default", | ||||||
|  |                 "text": "Profiles", | ||||||
|  |             } | ||||||
|  |             assert description["ToneCurve"]["Seq"]["li"] == ["0, 0", "255, 255"] | ||||||
|  | 
 | ||||||
|  |             # Attribute | ||||||
|  |             assert description["Version"] == "10.4" | ||||||
|  | 
 | ||||||
|  |         with Image.open("Tests/images/hopper.jpg") as im: | ||||||
|  |             assert im.getxmp() == {} | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @pytest.mark.skipif(not is_win32(), reason="Windows only") | @pytest.mark.skipif(not is_win32(), reason="Windows only") | ||||||
|  |  | ||||||
|  | @ -651,6 +651,16 @@ class TestFilePng: | ||||||
|         with Image.open(out) as reloaded: |         with Image.open(out) as reloaded: | ||||||
|             assert len(reloaded.png.im_palette[1]) == 3 |             assert len(reloaded.png.im_palette[1]) == 3 | ||||||
| 
 | 
 | ||||||
|  |     def test_xmp(self): | ||||||
|  |         with Image.open("Tests/images/color_snakes.png") as im: | ||||||
|  |             xmp = im.getxmp() | ||||||
|  | 
 | ||||||
|  |             assert isinstance(xmp, dict) | ||||||
|  | 
 | ||||||
|  |             description = xmp["xmpmeta"]["RDF"]["Description"] | ||||||
|  |             assert description["PixelXDimension"] == "10" | ||||||
|  |             assert description["subject"]["Seq"] is None | ||||||
|  | 
 | ||||||
|     def test_exif(self): |     def test_exif(self): | ||||||
|         # With an EXIF chunk |         # With an EXIF chunk | ||||||
|         with Image.open("Tests/images/exif.png") as im: |         with Image.open("Tests/images/exif.png") as im: | ||||||
|  |  | ||||||
|  | @ -643,6 +643,16 @@ class TestFileTiff: | ||||||
|         with Image.open(outfile) as reloaded: |         with Image.open(outfile) as reloaded: | ||||||
|             assert "icc_profile" not in reloaded.info |             assert "icc_profile" not in reloaded.info | ||||||
| 
 | 
 | ||||||
|  |     def test_xmp(self): | ||||||
|  |         with Image.open("Tests/images/lab.tif") as im: | ||||||
|  |             xmp = im.getxmp() | ||||||
|  | 
 | ||||||
|  |             assert isinstance(xmp, dict) | ||||||
|  | 
 | ||||||
|  |             description = xmp["xmpmeta"]["RDF"]["Description"] | ||||||
|  |             assert description[0]["format"] == "image/tiff" | ||||||
|  |             assert description[3]["BitsPerSample"]["Seq"]["li"] == ["8", "8", "8"] | ||||||
|  | 
 | ||||||
|     def test_close_on_load_exclusive(self, tmp_path): |     def test_close_on_load_exclusive(self, tmp_path): | ||||||
|         # similar to test_fd_leak, but runs on unixlike os |         # similar to test_fd_leak, but runs on unixlike os | ||||||
|         tmpfile = str(tmp_path / "temp.tif") |         tmpfile = str(tmp_path / "temp.tif") | ||||||
|  |  | ||||||
|  | @ -1318,6 +1318,33 @@ class Image: | ||||||
|             return tuple(extrema) |             return tuple(extrema) | ||||||
|         return self.im.getextrema() |         return self.im.getextrema() | ||||||
| 
 | 
 | ||||||
|  |     def _getxmp(self, xmp_tags): | ||||||
|  |         def get_name(tag): | ||||||
|  |             return tag.split("}")[1] | ||||||
|  | 
 | ||||||
|  |         def get_value(element): | ||||||
|  |             value = {get_name(k): v for k, v in element.attrib.items()} | ||||||
|  |             children = list(element) | ||||||
|  |             if children: | ||||||
|  |                 for child in children: | ||||||
|  |                     name = get_name(child.tag) | ||||||
|  |                     child_value = get_value(child) | ||||||
|  |                     if name in value: | ||||||
|  |                         if not isinstance(value[name], list): | ||||||
|  |                             value[name] = [value[name]] | ||||||
|  |                         value[name].append(child_value) | ||||||
|  |                     else: | ||||||
|  |                         value[name] = child_value | ||||||
|  |             elif value: | ||||||
|  |                 if element.text: | ||||||
|  |                     value["text"] = element.text | ||||||
|  |             else: | ||||||
|  |                 return element.text | ||||||
|  |             return value | ||||||
|  | 
 | ||||||
|  |         root = xml.etree.ElementTree.fromstring(xmp_tags) | ||||||
|  |         return {get_name(root.tag): get_value(root)} | ||||||
|  | 
 | ||||||
|     def getexif(self): |     def getexif(self): | ||||||
|         if self._exif is None: |         if self._exif is None: | ||||||
|             self._exif = Exif() |             self._exif = Exif() | ||||||
|  | @ -1338,15 +1365,15 @@ class Image: | ||||||
|         if 0x0112 not in self._exif: |         if 0x0112 not in self._exif: | ||||||
|             xmp_tags = self.info.get("XML:com.adobe.xmp") |             xmp_tags = self.info.get("XML:com.adobe.xmp") | ||||||
|             if xmp_tags: |             if xmp_tags: | ||||||
|                 root = xml.etree.ElementTree.fromstring(xmp_tags) |                 xmp = self._getxmp(xmp_tags) | ||||||
|                 for elem in root.iter(): |                 if ( | ||||||
|                     if elem.tag.endswith("}Description"): |                     "xmpmeta" in xmp | ||||||
|                         orientation = elem.attrib.get( |                     and "RDF" in xmp["xmpmeta"] | ||||||
|                             "{http://ns.adobe.com/tiff/1.0/}Orientation" |                     and "Description" in xmp["xmpmeta"]["RDF"] | ||||||
|                         ) |                 ): | ||||||
|                         if orientation: |                     description = xmp["xmpmeta"]["RDF"]["Description"] | ||||||
|                             self._exif[0x0112] = int(orientation) |                     if "Orientation" in description: | ||||||
|                         break |                         self._exif[0x0112] = int(description["Orientation"]) | ||||||
| 
 | 
 | ||||||
|         return self._exif |         return self._exif | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -40,7 +40,6 @@ import subprocess | ||||||
| import sys | import sys | ||||||
| import tempfile | import tempfile | ||||||
| import warnings | import warnings | ||||||
| import xml.etree.ElementTree |  | ||||||
| 
 | 
 | ||||||
| from . import Image, ImageFile, TiffImagePlugin | from . import Image, ImageFile, TiffImagePlugin | ||||||
| from ._binary import i16be as i16 | from ._binary import i16be as i16 | ||||||
|  | @ -362,7 +361,6 @@ class JpegImageFile(ImageFile.ImageFile): | ||||||
|         self.app = {}  # compatibility |         self.app = {}  # compatibility | ||||||
|         self.applist = [] |         self.applist = [] | ||||||
|         self.icclist = [] |         self.icclist = [] | ||||||
|         self._xmp = None |  | ||||||
| 
 | 
 | ||||||
|         while True: |         while True: | ||||||
| 
 | 
 | ||||||
|  | @ -485,20 +483,12 @@ class JpegImageFile(ImageFile.ImageFile): | ||||||
|         :returns: XMP tags in a dictionary. |         :returns: XMP tags in a dictionary. | ||||||
|         """ |         """ | ||||||
| 
 | 
 | ||||||
|         if self._xmp is None: |  | ||||||
|             self._xmp = {} |  | ||||||
| 
 |  | ||||||
|         for segment, content in self.applist: |         for segment, content in self.applist: | ||||||
|             if segment == "APP1": |             if segment == "APP1": | ||||||
|                 marker, xmp_tags = content.rsplit(b"\x00", 1) |                 marker, xmp_tags = content.rsplit(b"\x00", 1) | ||||||
|                 if marker == b"http://ns.adobe.com/xap/1.0/": |                 if marker == b"http://ns.adobe.com/xap/1.0/": | ||||||
|                     root = xml.etree.ElementTree.fromstring(xmp_tags) |                     return self._getxmp(xmp_tags) | ||||||
|                     for element in root.findall(".//"): |         return {} | ||||||
|                         self._xmp[element.tag.split("}")[1]] = { |  | ||||||
|                             child.split("}")[1]: value |  | ||||||
|                             for child, value in element.attrib.items() |  | ||||||
|                         } |  | ||||||
|         return self._xmp |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def _getexif(self): | def _getexif(self): | ||||||
|  |  | ||||||
|  | @ -978,6 +978,17 @@ class PngImageFile(ImageFile.ImageFile): | ||||||
| 
 | 
 | ||||||
|         return super().getexif() |         return super().getexif() | ||||||
| 
 | 
 | ||||||
|  |     def getxmp(self): | ||||||
|  |         """ | ||||||
|  |         Returns a dictionary containing the XMP tags. | ||||||
|  |         :returns: XMP tags in a dictionary. | ||||||
|  |         """ | ||||||
|  |         return ( | ||||||
|  |             self._getxmp(self.info["XML:com.adobe.xmp"]) | ||||||
|  |             if "XML:com.adobe.xmp" in self.info | ||||||
|  |             else {} | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|     def _close__fp(self): |     def _close__fp(self): | ||||||
|         try: |         try: | ||||||
|             if self.__fp != self.fp: |             if self.__fp != self.fp: | ||||||
|  |  | ||||||
|  | @ -1105,6 +1105,13 @@ class TiffImageFile(ImageFile.ImageFile): | ||||||
|         """Return the current frame number""" |         """Return the current frame number""" | ||||||
|         return self.__frame |         return self.__frame | ||||||
| 
 | 
 | ||||||
|  |     def getxmp(self): | ||||||
|  |         """ | ||||||
|  |         Returns a dictionary containing the XMP tags. | ||||||
|  |         :returns: XMP tags in a dictionary. | ||||||
|  |         """ | ||||||
|  |         return self._getxmp(self.tag_v2[700]) if 700 in self.tag_v2 else {} | ||||||
|  | 
 | ||||||
|     def load(self): |     def load(self): | ||||||
|         if self.tile and self.use_load_libtiff: |         if self.tile and self.use_load_libtiff: | ||||||
|             return self._load_libtiff() |             return self._load_libtiff() | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user