diff --git a/Tests/images/balloon.jpf b/Tests/images/balloon.jpf new file mode 100644 index 000000000..767eab5dd Binary files /dev/null and b/Tests/images/balloon.jpf differ diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index 8f027b651..db61b301b 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -39,6 +39,12 @@ class TestFileJpeg2k(PillowTestCase): self.assertEqual(im.mode, 'RGB') self.assertEqual(im.size, (640, 480)) self.assertEqual(im.format, 'JPEG2000') + self.assertEqual(im.get_format_mimetype(), 'image/jp2') + + def test_jpf(self): + im = Image.open('Tests/images/balloon.jpf') + self.assertEqual(im.format, 'JPEG2000') + self.assertEqual(im.get_format_mimetype(), 'image/jpx') def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index be52b3bd1..408a76444 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -86,6 +86,7 @@ class TestFilePng(PillowTestCase): self.assertEqual(im.mode, "RGB") self.assertEqual(im.size, (128, 128)) self.assertEqual(im.format, "PNG") + self.assertEqual(im.get_format_mimetype(), 'image/png') hopper("1").save(test_file) Image.open(test_file) @@ -589,6 +590,8 @@ class TestFilePng(PillowTestCase): "WebP support not installed with animation") def test_apng(self): im = Image.open("Tests/images/iss634.apng") + self.assertEqual(im.get_format_mimetype(), 'image/apng') + expected = Image.open("Tests/images/iss634.webp") self.assert_image_similar(im, expected, 0.23) diff --git a/Tests/test_file_sgi.py b/Tests/test_file_sgi.py index aa33c9d93..76f8a3841 100644 --- a/Tests/test_file_sgi.py +++ b/Tests/test_file_sgi.py @@ -12,6 +12,7 @@ class TestFileSgi(PillowTestCase): im = Image.open(test_file) self.assert_image_equal(im, hopper()) + self.assertEqual(im.get_format_mimetype(), 'image/rgb') def test_rgb16(self): test_file = "Tests/images/hopper16.rgb" @@ -26,6 +27,7 @@ class TestFileSgi(PillowTestCase): im = Image.open(test_file) self.assert_image_similar(im, hopper('L'), 2) + self.assertEqual(im.get_format_mimetype(), 'image/sgi') def test_rgba(self): # Created with ImageMagick: @@ -35,6 +37,7 @@ class TestFileSgi(PillowTestCase): im = Image.open(test_file) target = Image.open('Tests/images/transparent.png') self.assert_image_equal(im, target) + self.assertEqual(im.get_format_mimetype(), 'image/sgi') def test_rle(self): # Created with ImageMagick: diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 915557a57..bcc910853 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -79,6 +79,8 @@ class ImageFile(Image.Image): self._min_frame = 0 + self.custom_mimetype = None + self.tile = None self.readonly = 1 # until we know better @@ -120,7 +122,7 @@ class ImageFile(Image.Image): def get_format_mimetype(self): if self.format is None: return - return Image.MIME.get(self.format.upper()) + return self.custom_mimetype or Image.MIME.get(self.format.upper()) def verify(self): "Check file integrity" diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index 172162c9a..090337252 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -57,10 +57,11 @@ def _parse_codestream(fp): def _parse_jp2_header(fp): """Parse the JP2 header box to extract size, component count and - color space information, returning a PIL (size, mode) tuple.""" + color space information, returning a (size, mode, mimetype) tuple.""" # Find the JP2 header box header = None + mimetype = None while True: lbox, tbox = struct.unpack('>I4s', fp.read(8)) if lbox == 1: @@ -75,6 +76,10 @@ def _parse_jp2_header(fp): if tbox == b'jp2h': header = fp.read(lbox - hlen) break + elif tbox == b'ftyp': + if fp.read(4) == b'jpx ': + mimetype = 'image/jpx' + fp.seek(lbox - hlen - 4, os.SEEK_CUR) else: fp.seek(lbox - hlen, os.SEEK_CUR) @@ -145,7 +150,7 @@ def _parse_jp2_header(fp): if size is None or mode is None: raise SyntaxError("Malformed jp2 header") - return (size, mode) + return (size, mode, mimetype) ## # Image plugin for JPEG2000 images. @@ -165,7 +170,8 @@ class Jpeg2KImageFile(ImageFile.ImageFile): if sig == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a': self.codec = "jp2" - self._size, self.mode = _parse_jp2_header(self.fp) + header = _parse_jp2_header(self.fp) + self._size, self.mode, self.custom_mimetype = header else: raise SyntaxError('not a JPEG 2000 file') @@ -281,4 +287,3 @@ Image.register_extensions(Jpeg2KImageFile.format, [".jp2", ".j2k", ".jpc", ".jpf", ".jpx", ".j2c"]) Image.register_mime(Jpeg2KImageFile.format, 'image/jp2') -Image.register_mime(Jpeg2KImageFile.format, 'image/jpx') diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 04161a56c..d8ef56d73 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -296,6 +296,7 @@ class PngStream(ChunkStream): self.im_mode = None self.im_tile = None self.im_palette = None + self.im_custom_mimetype = None self.text_memory = 0 @@ -529,6 +530,7 @@ class PngStream(ChunkStream): # APNG chunks def chunk_acTL(self, pos, length): s = ImageFile._safe_read(self.fp, length) + self.im_custom_mimetype = 'image/apng' return s def chunk_fcTL(self, pos, length): @@ -594,6 +596,7 @@ class PngImageFile(ImageFile.ImageFile): self.info = self.png.im_info self._text = None self.tile = self.png.im_tile + self.custom_mimetype = self.png.im_custom_mimetype if self.png.im_palette: rawmode, data = self.png.im_palette @@ -908,4 +911,3 @@ Image.register_save(PngImageFile.format, _save) Image.register_extensions(PngImageFile.format, [".png", ".apng"]) Image.register_mime(PngImageFile.format, "image/png") -Image.register_mime(PngImageFile.format, "image/apng") diff --git a/src/PIL/SgiImagePlugin.py b/src/PIL/SgiImagePlugin.py index 88df35135..91753380d 100644 --- a/src/PIL/SgiImagePlugin.py +++ b/src/PIL/SgiImagePlugin.py @@ -98,6 +98,8 @@ class SgiImageFile(ImageFile.ImageFile): self._size = xsize, ysize self.mode = rawmode.split(";")[0] + if self.mode == 'RGB': + self.custom_mimetype = 'image/rgb' # orientation -1 : scanlines begins at the bottom-left corner orientation = -1 @@ -220,7 +222,6 @@ Image.register_decoder("SGI16", SGI16Decoder) Image.register_open(SgiImageFile.format, SgiImageFile, _accept) Image.register_save(SgiImageFile.format, _save) Image.register_mime(SgiImageFile.format, "image/sgi") -Image.register_mime(SgiImageFile.format, "image/rgb") Image.register_extensions(SgiImageFile.format, [".bw", ".rgb", ".rgba", ".sgi"])