Skip to content

Commit 1ed40d7

Browse files
committed
Add partial support for avif
From the spec, this only covers the most common format for AVIF images. I can't do more because the alternatives are speced behind a paywall. AVIF: https://aomediacodec.github.io/av1-avif/v1.1.0.html Points to HEIF: https://www.iso.org/standard/66067.html
1 parent fb25377 commit 1ed40d7

File tree

1 file changed

+45
-1
lines changed

1 file changed

+45
-1
lines changed

get_image_size.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import os
2222
import io
2323
import struct
24+
import time
2425

2526
FILE_UNKNOWN = "Sorry, don't know how to get size for this file."
2627

@@ -119,7 +120,7 @@ def get_image_metadata_from_bytesio(input, size, file_path=None):
119120
"""
120121
height = -1
121122
width = -1
122-
data = input.read(26)
123+
data = input.read(28)
123124
msg = " raised while trying to decode as JPEG."
124125

125126
if (size >= 10) and data[:6] in (b'GIF87a', b'GIF89a'):
@@ -245,6 +246,49 @@ def get_image_metadata_from_bytesio(input, size, file_path=None):
245246
break
246247
except Exception as e:
247248
raise UnknownImageFormat(str(e))
249+
elif (size >= 28) and data[4:8] == b'ftyp' and \
250+
any(bytes(brand) in (b'avif', b'avis') for brand in zip(*((iter(data[8:]),) * 4))):
251+
MSG = 'Failed to parse image as avif. '
252+
HEADER_PATH = ('meta', 'iprp', 'ipco', 'ispe')
253+
MAX_HEAD_SEARCH = 2 << 10 # Don't waste time if missed 'mdat' & it's processing mdat as part of the header
254+
try:
255+
header_size = struct.unpack('>I', data[0:4])[0]
256+
input.seek(header_size, 0)
257+
258+
def _find(what, up_to_bytes):
259+
while input.tell() < up_to_bytes:
260+
block_size_bytes = input.read(4)
261+
# https://docs.python.org/3/library/io.html#io.RawIOBase.read
262+
if block_size_bytes is None:
263+
time.sleep(0.1)
264+
continue
265+
if not block_size_bytes:
266+
raise UnknownImageFormat(MSG + "Reached end of file")
267+
268+
block_size = struct.unpack('>I', block_size_bytes)[0]
269+
block_type = input.read(4).decode('ascii')
270+
if block_type == 'mdat':
271+
raise UnknownImageFormat(MSG + "Reached image data without finding '{}' data".format(HEADER_PATH[-1]))
272+
if block_type == what:
273+
return input.tell() + block_size
274+
275+
input.seek(block_size - 8, 1)
276+
277+
raise UnknownImageFormat(MSG + "Reached end of block but couldn't find '{}'".format(what))
278+
279+
sub_block_end = _find(HEADER_PATH[0], MAX_HEAD_SEARCH)
280+
input.seek(4, 1) # Jumping over meta's version and flags.
281+
for find in HEADER_PATH[1:]:
282+
sub_block_end = _find(find, sub_block_end)
283+
284+
input.seek(4, 1) # Jumping over ispe's version and flags.
285+
width = struct.unpack('>I', input.read(4))[0]
286+
height = struct.unpack('>I', input.read(4))[0]
287+
288+
except struct.error:
289+
raise UnknownImageFormat(MSG + "See cause exception for details")
290+
except UnicodeDecodeError:
291+
raise UnknownImageFormat(MSG + "Header name not ASCII convertible")
248292
elif size >= 2:
249293
# see http://en.wikipedia.org/wiki/ICO_(file_format)
250294
imgtype = 'ICO'

0 commit comments

Comments
 (0)