mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-08-09 06:44:45 +03:00
webp: handle ImageFile.LOAD_TRUNCATED_IMAGES
When set, don't raise an error on truncated content.
This commit is contained in:
parent
b4fcc44eb9
commit
3b736c5a0f
|
@ -7,6 +7,11 @@ _VALID_WEBP_MODES = ("RGB", "RGBA")
|
||||||
|
|
||||||
_VP8_MODES_BY_IDENTIFIER = (b"VP8 ", b"VP8X", b"VP8L")
|
_VP8_MODES_BY_IDENTIFIER = (b"VP8 ", b"VP8X", b"VP8L")
|
||||||
|
|
||||||
|
_INFO_CHUNKS = {
|
||||||
|
b'EXIF': 'exif',
|
||||||
|
b'ICCP': 'icc_profile',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix):
|
def _accept(prefix):
|
||||||
is_riff_file_format = prefix[:4] == b"RIFF"
|
is_riff_file_format = prefix[:4] == b"RIFF"
|
||||||
|
@ -96,9 +101,8 @@ class WebPImageFile(ImageFile.ImageFile):
|
||||||
while True:
|
while True:
|
||||||
|
|
||||||
chunk_header = self.fp.read(8)
|
chunk_header = self.fp.read(8)
|
||||||
if 8 != len(chunk_header):
|
if len(chunk_header) < 8:
|
||||||
if first_chunk:
|
# EOF.
|
||||||
raise SyntaxError("not a WebP file")
|
|
||||||
break
|
break
|
||||||
|
|
||||||
chunk_fourcc = chunk_header[0:4]
|
chunk_fourcc = chunk_header[0:4]
|
||||||
|
@ -125,26 +129,19 @@ class WebPImageFile(ImageFile.ImageFile):
|
||||||
elif b'ALPH' == chunk_fourcc:
|
elif b'ALPH' == chunk_fourcc:
|
||||||
mode = 'RGBA'
|
mode = 'RGBA'
|
||||||
|
|
||||||
elif b'EXIF' == chunk_fourcc:
|
elif chunk_fourcc in _INFO_CHUNKS:
|
||||||
exif = self.fp.read(chunk_size)
|
data = self.fp.read(chunk_size)
|
||||||
if chunk_size != len(exif):
|
if len(data) < chunk_size:
|
||||||
raise SyntaxError("bad EXIF chunk")
|
if ImageFile.LOAD_TRUNCATED_IMAGES:
|
||||||
self.info["exif"] = exif
|
# Simulate EOF.
|
||||||
|
break
|
||||||
|
msg = "image file is truncated: incomplete %s chunk" % chunk_fourcc
|
||||||
|
raise IOError(msg)
|
||||||
|
self.info[_INFO_CHUNKS[chunk_fourcc]] = data
|
||||||
chunk_size = 0
|
chunk_size = 0
|
||||||
|
|
||||||
elif b'ICCP' == chunk_fourcc:
|
# Skip to next chunk.
|
||||||
icc_profile = self.fp.read(chunk_size)
|
self.fp.seek(chunk_size, os.SEEK_CUR)
|
||||||
if chunk_size != len(icc_profile):
|
|
||||||
raise SyntaxError("bad ICCP chunk")
|
|
||||||
self.info["icc_profile"] = icc_profile
|
|
||||||
chunk_size = 0
|
|
||||||
|
|
||||||
if chunk_size > 0:
|
|
||||||
# Skip to next chunk.
|
|
||||||
pos = self.fp.tell()
|
|
||||||
self.fp.seek(chunk_size, os.SEEK_CUR)
|
|
||||||
if self.fp.tell() != (pos + chunk_size):
|
|
||||||
raise SyntaxError("not a WebP file")
|
|
||||||
|
|
||||||
first_chunk = False
|
first_chunk = False
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
from helper import unittest, PillowTestCase, hopper
|
from helper import unittest, PillowTestCase, hopper
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image, ImageFile
|
||||||
|
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from PIL import _webp
|
from PIL import _webp
|
||||||
|
@ -72,6 +74,40 @@ class TestFileWebp(PillowTestCase):
|
||||||
target = hopper("RGB")
|
target = hopper("RGB")
|
||||||
self.assert_image_similar(image, target, 12)
|
self.assert_image_similar(image, target, 12)
|
||||||
|
|
||||||
|
def test_truncated(self):
|
||||||
|
assert False == ImageFile.LOAD_TRUNCATED_IMAGES
|
||||||
|
with open('Tests/images/flower.webp', 'rb') as fp:
|
||||||
|
full_data = fp.read()
|
||||||
|
# Truncate in the middle (VP8 chunk).
|
||||||
|
half_data = full_data[:len(full_data)//2]
|
||||||
|
im = Image.open(BytesIO(half_data))
|
||||||
|
self.assertRaises(IOError, im.load)
|
||||||
|
im = Image.open(BytesIO(half_data))
|
||||||
|
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
||||||
|
try:
|
||||||
|
im.load()
|
||||||
|
finally:
|
||||||
|
ImageFile.LOAD_TRUNCATED_IMAGES = False
|
||||||
|
original = Image.open(BytesIO(full_data))
|
||||||
|
width, height = original.size
|
||||||
|
# Check we decoded at least part of the image (top).
|
||||||
|
top_area = (0, 0, width-1, 31)
|
||||||
|
self.assert_image_equal(im.crop(top_area), original.crop(top_area))
|
||||||
|
# Bottom should be blank.
|
||||||
|
bottom_area = (width-33, height-33, width-1, height-1)
|
||||||
|
self.assert_image_equal(im.crop(bottom_area), Image.new(original.mode, (32, 32)))
|
||||||
|
# Truncate at the end (EXIF chunk).
|
||||||
|
most_data = full_data[:-4*1024]
|
||||||
|
self.assertRaises(IOError, Image.open, BytesIO(most_data))
|
||||||
|
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
||||||
|
try:
|
||||||
|
im = Image.open(BytesIO(most_data))
|
||||||
|
finally:
|
||||||
|
ImageFile.LOAD_TRUNCATED_IMAGES = False
|
||||||
|
im.load()
|
||||||
|
# Check we decoded the whole image.
|
||||||
|
self.assert_image_equal(im, original)
|
||||||
|
|
||||||
def test_info_compression(self):
|
def test_info_compression(self):
|
||||||
for name, compression in (
|
for name, compression in (
|
||||||
('flower2' , 'lossy'),
|
('flower2' , 'lossy'),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user