Merge pull request #5376 from radarhere/xmp

This commit is contained in:
Hugo van Kemenade 2021-04-01 15:38:11 +03:00 committed by GitHub
commit ef5f294d74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 44 additions and 31 deletions

View File

@ -805,6 +805,13 @@ class TestFileJpeg:
# Assert the entire file has not been read
assert 0 < buffer.max_pos < size
def test_getxmp(self):
with Image.open("Tests/images/xmp_test.jpg") as im:
xmp = im.getxmp()
assert isinstance(xmp, dict)
assert xmp["Description"]["Version"] == "10.4"
@pytest.mark.skipif(not is_win32(), reason="Windows only")
@skip_unless_feature("jpg")

View File

@ -1,9 +0,0 @@
from PIL import Image
def test_getxmp():
with Image.open("Tests/images/xmp_test.jpg") as im:
xmp = im.getxmp()
assert isinstance(xmp, dict)
assert xmp["Description"]["Version"] == "10.4"

View File

@ -112,6 +112,20 @@ separate histograms for each color channel, changing the tone of the image. The
``preserve_tone`` argument keeps the tone unchanged by using one luminance histogram
for all channels.
getxmp() for JPEG images
^^^^^^^^^^^^^^^^^^^^^^^^
A new method has been added to return
`XMP data <https://en.wikipedia.org/wiki/Extensible_Metadata_Platform>`_ for JPEG
images. It reads the XML data into a dictionary of names and values.
For example::
>>> from PIL import Image
>>> with Image.open("Tests/images/xmp_test.jpg") as im:
>>> print(im.getxmp())
{'RDF': {}, 'Description': {'Version': '10.4', 'ProcessVersion': '10.0', ...}, ...}
Security
========

View File

@ -528,7 +528,6 @@ class Image:
self.readonly = 0
self.pyaccess = None
self._exif = None
self._xmp = None
def __getattr__(self, name):
if name == "category":
@ -1340,27 +1339,6 @@ class Image:
return self._exif
def getxmp(self):
"""
Returns a dictionary containing the xmp tags for a given image.
:returns: XMP tags in a dictionary.
"""
if self._xmp is None:
self._xmp = {}
for segment, content in self.applist:
if segment == "APP1":
marker, xmp_tags = content.rsplit(b"\x00", 1)
if marker == b"http://ns.adobe.com/xap/1.0/":
root = xml.etree.ElementTree.fromstring(xmp_tags)
for element in root.findall(".//"):
self._xmp[element.tag.split("}")[1]] = {
child.split("}")[1]: value
for child, value in element.attrib.items()
}
return self._xmp
def getim(self):
"""
Returns a capsule that points to the internal image memory.

View File

@ -39,6 +39,7 @@ import subprocess
import sys
import tempfile
import warnings
import xml.etree.ElementTree
from . import Image, ImageFile, TiffImagePlugin
from ._binary import i16be as i16
@ -358,6 +359,7 @@ class JpegImageFile(ImageFile.ImageFile):
self.app = {} # compatibility
self.applist = []
self.icclist = []
self._xmp = None
while True:
@ -474,6 +476,27 @@ class JpegImageFile(ImageFile.ImageFile):
def _getmp(self):
return _getmp(self)
def getxmp(self):
"""
Returns a dictionary containing the XMP tags.
:returns: XMP tags in a dictionary.
"""
if self._xmp is None:
self._xmp = {}
for segment, content in self.applist:
if segment == "APP1":
marker, xmp_tags = content.rsplit(b"\x00", 1)
if marker == b"http://ns.adobe.com/xap/1.0/":
root = xml.etree.ElementTree.fromstring(xmp_tags)
for element in root.findall(".//"):
self._xmp[element.tag.split("}")[1]] = {
child.split("}")[1]: value
for child, value in element.attrib.items()
}
return self._xmp
def _getexif(self):
if "exif" not in self.info: