mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-09-21 03:18:57 +03:00
Added getxmp() for PNG
This commit is contained in:
parent
ae3bdf87f0
commit
cd31dae0d1
|
@ -830,15 +830,22 @@ class TestFileJpeg:
|
||||||
assert isinstance(xmp, dict)
|
assert isinstance(xmp, dict)
|
||||||
|
|
||||||
description = xmp["xmpmeta"]["RDF"]["Description"]
|
description = xmp["xmpmeta"]["RDF"]["Description"]
|
||||||
assert description["DerivedFrom"] is None
|
assert description["DerivedFrom"] == {
|
||||||
assert (
|
"documentID": "8367D410E636EA95B7DE7EBA1C43A412",
|
||||||
description["Look"]["Description"]["Group"]["Alt"]["li"] == "Profiles"
|
"originalDocumentID": "8367D410E636EA95B7DE7EBA1C43A412",
|
||||||
)
|
}
|
||||||
|
assert description["Look"]["Description"]["Group"]["Alt"]["li"] == {
|
||||||
|
"lang": "x-default",
|
||||||
|
"text": "Profiles",
|
||||||
|
}
|
||||||
assert description["ToneCurve"]["Seq"]["li"] == ["0, 0", "255, 255"]
|
assert description["ToneCurve"]["Seq"]["li"] == ["0, 0", "255, 255"]
|
||||||
|
|
||||||
# Attribute
|
# Attribute
|
||||||
assert description["Version"] == "10.4"
|
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")
|
||||||
@skip_unless_feature("jpg")
|
@skip_unless_feature("jpg")
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -1317,6 +1317,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()
|
||||||
|
@ -1332,15 +1359,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
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,6 @@ import io
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
import xml.etree.ElementTree
|
|
||||||
|
|
||||||
from . import Image
|
from . import Image
|
||||||
from ._util import isPath
|
from ._util import isPath
|
||||||
|
@ -310,30 +309,6 @@ class ImageFile(Image.Image):
|
||||||
|
|
||||||
return self.tell() != frame
|
return self.tell() != frame
|
||||||
|
|
||||||
def _getxmp(self, xmp_tags):
|
|
||||||
def get_name(tag):
|
|
||||||
return tag.split("}")[1]
|
|
||||||
|
|
||||||
def get_value(element):
|
|
||||||
children = list(element)
|
|
||||||
if children:
|
|
||||||
value = {get_name(k): v for k, v in element.attrib.items()}
|
|
||||||
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
|
|
||||||
return value
|
|
||||||
else:
|
|
||||||
return element.text
|
|
||||||
|
|
||||||
root = xml.etree.ElementTree.fromstring(xmp_tags)
|
|
||||||
self._xmp[get_name(root.tag)] = get_value(root)
|
|
||||||
|
|
||||||
|
|
||||||
class StubImageFile(ImageFile):
|
class StubImageFile(ImageFile):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -361,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:
|
||||||
|
|
||||||
|
@ -484,15 +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/":
|
||||||
self._getxmp(xmp_tags)
|
return self._getxmp(xmp_tags)
|
||||||
return self._xmp
|
return {}
|
||||||
|
|
||||||
|
|
||||||
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:
|
||||||
|
|
|
@ -1032,7 +1032,6 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||||
self.__fp = self.fp
|
self.__fp = self.fp
|
||||||
self._frame_pos = []
|
self._frame_pos = []
|
||||||
self._n_frames = None
|
self._n_frames = None
|
||||||
self._xmp = None
|
|
||||||
|
|
||||||
logger.debug("*** TiffImageFile._open ***")
|
logger.debug("*** TiffImageFile._open ***")
|
||||||
logger.debug(f"- __first: {self.__first}")
|
logger.debug(f"- __first: {self.__first}")
|
||||||
|
@ -1107,13 +1106,7 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||||
Returns a dictionary containing the XMP tags.
|
Returns a dictionary containing the XMP tags.
|
||||||
:returns: XMP tags in a dictionary.
|
:returns: XMP tags in a dictionary.
|
||||||
"""
|
"""
|
||||||
|
return self._getxmp(self.tag_v2[700]) if 700 in self.tag_v2 else {}
|
||||||
if self._xmp is None:
|
|
||||||
self._xmp = {}
|
|
||||||
|
|
||||||
if 700 in self.tag_v2:
|
|
||||||
self._getxmp(self.tag_v2[700])
|
|
||||||
return self._xmp
|
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
if self.tile and self.use_load_libtiff:
|
if self.tile and self.use_load_libtiff:
|
||||||
|
|
Loading…
Reference in New Issue
Block a user