diff --git a/CHANGELOG.md b/CHANGELOG.md index c6dfaf8a8..cf359236c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Better ignore tiff directories that aren't part of the pyramid ([943](../../pull/943)) - Fix an issue with styling frames in ome tiffs ([945](../../pull/945)) - Better handle large user records in annotation elements ([949](../../pull/949)) +- Harden which output formats are supported ([950](../../pull/950)) ### Changes - Adjusted rest request logging rates for region endpoint ([948](../../pull/948)) diff --git a/large_image/tilesource/utilities.py b/large_image/tilesource/utilities.py index c134ea08d..9f19f021c 100644 --- a/large_image/tilesource/utilities.py +++ b/large_image/tilesource/utilities.py @@ -99,7 +99,19 @@ def _encodeImageBinary(image, encoding, jpegQuality, jpegSubsampling, tiffCompre elif encoding == 'PNG': params['compress_level'] = 2 output = io.BytesIO() - image.save(output, encoding, **params) + try: + image.save(output, encoding, **params) + except Exception: + retry = True + if image.mode not in {'RGB', 'L'}: + image = image.convert('RGB') + try: + image.convert('RGB').save(output, encoding, **params) + retry = False + except Exception: + pass + if retry: + image.convert('1').save(output, encoding, **params) return ImageBytes( output.getvalue(), mimetype=f'image/{encoding.lower().replace("tiled", "tiff")}' @@ -931,6 +943,10 @@ def addPILFormatsToOutputOptions(): # Call this to actual register the extensions PIL.Image.registered_extensions() for key, value in PIL.Image.MIME.items(): + # We don't support these formats; ICNS and ICO have fixed sizes; PALM + # and PDF can't be read back by PIL without extensions + if key in {'ICNS', 'ICO', 'PALM', 'PDF'}: + continue if key not in TileOutputMimeTypes and key in PIL.Image.SAVE: TileOutputMimeTypes[key] = value for key, value in PIL.Image.registered_extensions().items(): diff --git a/test/test_source_base.py b/test/test_source_base.py index 7270ab8f6..a7a1366e4 100644 --- a/test/test_source_base.py +++ b/test/test_source_base.py @@ -1,8 +1,10 @@ +import io import os import re import sys from pathlib import Path +import PIL.Image import pytest import large_image @@ -540,3 +542,24 @@ def testImageBytes(): assert 'ImageBytes' in repr(ib) assert ib._repr_jpeg_() is None assert ib._repr_png_() is None + + +@pytest.mark.parametrize('format', [ + format for format in large_image.constants.TileOutputMimeTypes + if format not in {'TILED'}]) +def testOutputFormats(format): + imagePath = datastore.fetch('sample_image.ptif') + testDir = os.path.dirname(os.path.realpath(__file__)) + imagePathRGBA = os.path.join(testDir, 'test_files', 'rgba_geotiff.tiff') + + ts = large_image.open(imagePath, encoding=format) + img = PIL.Image.open(io.BytesIO(ts.getTile(0, 0, 0))) + assert (img.width, img.height) == (256, 256) + img = PIL.Image.open(io.BytesIO(ts.getThumbnail(encoding=format)[0])) + assert (img.width, img.height) == (256, 53) + + ts = large_image.open(imagePathRGBA, encoding=format) + img = PIL.Image.open(io.BytesIO(ts.getTile(0, 0, 0))) + assert (img.width, img.height) == (256, 256) + img = PIL.Image.open(io.BytesIO(ts.getThumbnail(encoding=format)[0])) + assert (img.width, img.height) == (256, 256)