Skip to content

Commit ef5f294

Browse files
authored
Merge pull request #5376 from radarhere/xmp
2 parents 43c4172 + ae7110a commit ef5f294

File tree

5 files changed

+44
-31
lines changed

5 files changed

+44
-31
lines changed

Tests/test_file_jpeg.py

+7
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,13 @@ def read(n=-1):
805805
# Assert the entire file has not been read
806806
assert 0 < buffer.max_pos < size
807807

808+
def test_getxmp(self):
809+
with Image.open("Tests/images/xmp_test.jpg") as im:
810+
xmp = im.getxmp()
811+
812+
assert isinstance(xmp, dict)
813+
assert xmp["Description"]["Version"] == "10.4"
814+
808815

809816
@pytest.mark.skipif(not is_win32(), reason="Windows only")
810817
@skip_unless_feature("jpg")

Tests/test_image_getxmp.py

-9
This file was deleted.

docs/releasenotes/8.2.0.rst

+14
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,20 @@ separate histograms for each color channel, changing the tone of the image. The
112112
``preserve_tone`` argument keeps the tone unchanged by using one luminance histogram
113113
for all channels.
114114

115+
getxmp() for JPEG images
116+
^^^^^^^^^^^^^^^^^^^^^^^^
117+
118+
A new method has been added to return
119+
`XMP data <https://en.wikipedia.org/wiki/Extensible_Metadata_Platform>`_ for JPEG
120+
images. It reads the XML data into a dictionary of names and values.
121+
122+
For example::
123+
124+
>>> from PIL import Image
125+
>>> with Image.open("Tests/images/xmp_test.jpg") as im:
126+
>>> print(im.getxmp())
127+
{'RDF': {}, 'Description': {'Version': '10.4', 'ProcessVersion': '10.0', ...}, ...}
128+
115129
Security
116130
========
117131

src/PIL/Image.py

-22
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,6 @@ def __init__(self):
528528
self.readonly = 0
529529
self.pyaccess = None
530530
self._exif = None
531-
self._xmp = None
532531

533532
def __getattr__(self, name):
534533
if name == "category":
@@ -1340,27 +1339,6 @@ def getexif(self):
13401339

13411340
return self._exif
13421341

1343-
def getxmp(self):
1344-
"""
1345-
Returns a dictionary containing the xmp tags for a given image.
1346-
:returns: XMP tags in a dictionary.
1347-
"""
1348-
1349-
if self._xmp is None:
1350-
self._xmp = {}
1351-
1352-
for segment, content in self.applist:
1353-
if segment == "APP1":
1354-
marker, xmp_tags = content.rsplit(b"\x00", 1)
1355-
if marker == b"http://ns.adobe.com/xap/1.0/":
1356-
root = xml.etree.ElementTree.fromstring(xmp_tags)
1357-
for element in root.findall(".//"):
1358-
self._xmp[element.tag.split("}")[1]] = {
1359-
child.split("}")[1]: value
1360-
for child, value in element.attrib.items()
1361-
}
1362-
return self._xmp
1363-
13641342
def getim(self):
13651343
"""
13661344
Returns a capsule that points to the internal image memory.

src/PIL/JpegImagePlugin.py

+23
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import sys
4040
import tempfile
4141
import warnings
42+
import xml.etree.ElementTree
4243

4344
from . import Image, ImageFile, TiffImagePlugin
4445
from ._binary import i16be as i16
@@ -358,6 +359,7 @@ def _open(self):
358359
self.app = {} # compatibility
359360
self.applist = []
360361
self.icclist = []
362+
self._xmp = None
361363

362364
while True:
363365

@@ -474,6 +476,27 @@ def _getexif(self):
474476
def _getmp(self):
475477
return _getmp(self)
476478

479+
def getxmp(self):
480+
"""
481+
Returns a dictionary containing the XMP tags.
482+
:returns: XMP tags in a dictionary.
483+
"""
484+
485+
if self._xmp is None:
486+
self._xmp = {}
487+
488+
for segment, content in self.applist:
489+
if segment == "APP1":
490+
marker, xmp_tags = content.rsplit(b"\x00", 1)
491+
if marker == b"http://ns.adobe.com/xap/1.0/":
492+
root = xml.etree.ElementTree.fromstring(xmp_tags)
493+
for element in root.findall(".//"):
494+
self._xmp[element.tag.split("}")[1]] = {
495+
child.split("}")[1]: value
496+
for child, value in element.attrib.items()
497+
}
498+
return self._xmp
499+
477500

478501
def _getexif(self):
479502
if "exif" not in self.info:

0 commit comments

Comments
 (0)