diff --git a/Tests/images/xmp_tags_orientation.png b/Tests/images/xmp_tags_orientation.png new file mode 100644 index 000000000..c1be1665f Binary files /dev/null and b/Tests/images/xmp_tags_orientation.png differ diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index a2535b97f..cb1f28201 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -603,6 +603,11 @@ class TestFilePng: exif = im._getexif() assert exif[274] == 1 + def test_xmp_tags_orientation(self): + with Image.open("Tests/images/xmp_tags_orientation.png") as im: + exif = im.getexif() + assert exif[274] == 3 + def test_exif_save(self, tmp_path): with Image.open("Tests/images/exif.png") as im: test_file = str(tmp_path / "temp.png") diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 19ddcbba5..f85e5ed2e 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -35,6 +35,7 @@ import struct import sys import tempfile import warnings +import xml.etree.ElementTree from collections.abc import Callable, MutableMapping from pathlib import Path @@ -1297,10 +1298,30 @@ class Image: return tuple(extrema) return self.im.getextrema() + def _parse_xmp_tags(self): + if 0x0112 in self._exif: + return + + xmp_tags = self.info.get("XML:com.adobe.xmp") + if not xmp_tags: + return + + root = xml.etree.ElementTree.fromstring(xmp_tags) + for elem in root.iter(): + if elem.tag.endswith("}Description"): + break + else: + return + + orientation = elem.attrib.get("{http://ns.adobe.com/tiff/1.0/}Orientation") + if orientation: + self._exif[0x0112] = int(orientation) + def getexif(self): if self._exif is None: self._exif = Exif() self._exif.load(self.info.get("exif")) + self._parse_xmp_tags() return self._exif def getim(self): diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index ee9d52b4c..698462523 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -940,6 +940,7 @@ class PngImageFile(ImageFile.ImageFile): "".join(self.info["Raw profile type exif"].split("\n")[3:]) ) self._exif.load(exif_info) + self._parse_xmp_tags() return self._exif def _close__fp(self):