|
21 | 21 | import os |
22 | 22 | import io |
23 | 23 | import struct |
| 24 | +import time |
24 | 25 |
|
25 | 26 | FILE_UNKNOWN = "Sorry, don't know how to get size for this file." |
26 | 27 |
|
@@ -119,7 +120,7 @@ def get_image_metadata_from_bytesio(input, size, file_path=None): |
119 | 120 | """ |
120 | 121 | height = -1 |
121 | 122 | width = -1 |
122 | | - data = input.read(26) |
| 123 | + data = input.read(28) |
123 | 124 | msg = " raised while trying to decode as JPEG." |
124 | 125 |
|
125 | 126 | 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): |
245 | 246 | break |
246 | 247 | except Exception as e: |
247 | 248 | 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") |
248 | 292 | elif size >= 2: |
249 | 293 | # see http://en.wikipedia.org/wiki/ICO_(file_format) |
250 | 294 | imgtype = 'ICO' |
|
0 commit comments