Allow get_child_images to access JPEG thumbnails

This commit is contained in:
Andrew Murray 2022-12-06 19:30:53 +11:00
parent 8ada23ed04
commit c2a42655e1
5 changed files with 51 additions and 33 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -415,6 +415,13 @@ class TestFileJpeg:
info = im._getexif()
assert info[305] == "Adobe Photoshop CS Macintosh"
def test_get_child_images(self):
with Image.open("Tests/images/flower.jpg") as im:
ims = im.get_child_images()
assert len(ims) == 1
assert_image_equal_tofile(ims[0], "Tests/images/flower_thumbnail.png")
def test_mp(self):
with Image.open("Tests/images/pil_sample_rgb.jpg") as im:
assert im._getmp() is None

View File

@ -1454,6 +1454,49 @@ class Image:
self._exif._loaded = False
self.getexif()
def get_child_images(self):
child_images = []
exif = self.getexif()
ifds = []
if ExifTags.Base.SubIFDs in exif:
subifd_offsets = exif[ExifTags.Base.SubIFDs]
if subifd_offsets:
if not isinstance(subifd_offsets, tuple):
subifd_offsets = (subifd_offsets,)
for subifd_offset in subifd_offsets:
ifds.append((exif._get_ifd_dict(subifd_offset), subifd_offset))
ifd1 = exif.get_ifd(ExifTags.IFD.IFD1)
if ifd1 and ifd1.get(513):
ifds.append((ifd1, exif._info.next))
offset = None
for ifd, ifd_offset in ifds:
current_offset = self.fp.tell()
if offset is None:
offset = current_offset
fp = self.fp
thumbnailOffset = ifd.get(513)
if thumbnailOffset is not None:
try:
thumbnailOffset += self._exif_offset
except AttributeError:
pass
self.fp.seek(thumbnailOffset)
data = self.fp.read(ifd.get(514))
fp = io.BytesIO(data)
with open(fp) as im:
if thumbnailOffset is None:
im._frame_pos = [ifd_offset]
im._seek(0)
im.load()
child_images.append(im)
if offset is not None:
self.fp.seek(offset)
return child_images
def getim(self):
"""
Returns a capsule that points to the internal image memory.

View File

@ -89,6 +89,7 @@ def APP(self, marker):
if "exif" not in self.info:
# extract EXIF information (incomplete)
self.info["exif"] = s # FIXME: value will change
self._exif_offset = self.fp.tell() - n + 6
elif marker == 0xFFE2 and s[:5] == b"FPXR\0":
# extract FlashPix information (incomplete)
self.info["flashpix"] = s # FIXME: value will change

View File

@ -1153,39 +1153,6 @@ class TiffImageFile(ImageFile.ImageFile):
"""Return the current frame number"""
return self.__frame
def get_child_images(self):
if SUBIFD not in self.tag_v2:
return []
child_images = []
exif = self.getexif()
offset = None
for im_offset in self.tag_v2[SUBIFD]:
# reset buffered io handle in case fp
# was passed to libtiff, invalidating the buffer
current_offset = self._fp.tell()
if offset is None:
offset = current_offset
fp = self._fp
ifd = exif._get_ifd_dict(im_offset)
jpegInterchangeFormat = ifd.get(513)
if jpegInterchangeFormat is not None:
fp.seek(jpegInterchangeFormat)
jpeg_data = fp.read(ifd.get(514))
fp = io.BytesIO(jpeg_data)
with Image.open(fp) as im:
if jpegInterchangeFormat is None:
im._frame_pos = [im_offset]
im._seek(0)
im.load()
child_images.append(im)
if offset is not None:
self._fp.seek(offset)
return child_images
def getxmp(self):
"""
Returns a dictionary containing the XMP tags.