Pillow/Tests/test_file_jpeg.py

644 lines
24 KiB
Python
Raw Normal View History

from .helper import unittest, PillowTestCase, hopper
from .helper import djpeg_available, cjpeg_available
2014-06-10 13:10:47 +04:00
from io import BytesIO
import os
import sys
from PIL import Image
from PIL import ImageFile
from PIL import JpegImagePlugin
codecs = dir(Image.core)
2014-09-23 20:52:03 +04:00
TEST_FILE = "Tests/images/hopper.jpg"
2014-06-10 13:10:47 +04:00
class TestFileJpeg(PillowTestCase):
2014-05-21 15:32:24 +04:00
2014-06-10 13:10:47 +04:00
def setUp(self):
if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs:
self.skipTest("jpeg support not available")
2014-06-10 13:10:47 +04:00
def roundtrip(self, im, **options):
out = BytesIO()
im.save(out, "JPEG", **options)
2015-04-24 11:24:52 +03:00
test_bytes = out.tell()
2014-06-10 13:10:47 +04:00
out.seek(0)
im = Image.open(out)
2015-04-24 11:24:52 +03:00
im.bytes = test_bytes # for testing only
2014-06-10 13:10:47 +04:00
return im
2016-12-03 17:45:05 +03:00
def gen_random_image(self, size, mode='RGB'):
""" Generates a very hard to compress file
:param size: tuple
:param mode: optional image mode
2017-03-03 13:32:31 +03:00
2016-12-03 17:45:05 +03:00
"""
return Image.frombytes(mode, size,
os.urandom(size[0]*size[1]*len(mode)))
2017-03-03 13:32:31 +03:00
2014-06-10 13:10:47 +04:00
def test_sanity(self):
2014-05-21 15:32:24 +04:00
2014-06-10 13:10:47 +04:00
# internal version number
self.assertRegex(Image.core.jpeglib_version, r"\d+\.\d+$")
2014-09-23 20:52:03 +04:00
im = Image.open(TEST_FILE)
2014-06-10 13:10:47 +04:00
im.load()
self.assertEqual(im.mode, "RGB")
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "JPEG")
self.assertEqual(im.get_format_mimetype(), "image/jpeg")
2014-05-21 15:32:24 +04:00
2014-06-10 13:10:47 +04:00
def test_app(self):
# Test APP/COM reader (@PIL135)
2014-09-23 20:52:03 +04:00
im = Image.open(TEST_FILE)
2014-06-10 13:10:47 +04:00
self.assertEqual(
im.applist[0],
2014-09-23 20:52:03 +04:00
("APP0", b"JFIF\x00\x01\x01\x01\x00`\x00`\x00\x00"))
self.assertEqual(im.applist[1], (
"COM", b"File written by Adobe Photoshop\xa8 4.0\x00"))
2014-06-10 13:10:47 +04:00
self.assertEqual(len(im.applist), 2)
def test_cmyk(self):
# Test CMYK handling. Thanks to Tim and Charlie for test data,
# Michael for getting me to look one more time.
f = "Tests/images/pil_sample_cmyk.jpg"
im = Image.open(f)
# the source image has red pixels in the upper left corner.
c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))]
self.assertEqual(c, 0.0)
self.assertGreater(m, 0.8)
self.assertGreater(y, 0.8)
self.assertEqual(k, 0.0)
# the opposite corner is black
2014-08-27 11:57:40 +04:00
c, m, y, k = [x / 255.0 for x in im.getpixel((
im.size[0]-1, im.size[1]-1))]
2014-06-10 13:10:47 +04:00
self.assertGreater(k, 0.9)
# roundtrip, and check again
im = self.roundtrip(im)
c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))]
self.assertEqual(c, 0.0)
self.assertGreater(m, 0.8)
self.assertGreater(y, 0.8)
self.assertEqual(k, 0.0)
2014-08-27 11:57:40 +04:00
c, m, y, k = [x / 255.0 for x in im.getpixel((
im.size[0]-1, im.size[1]-1))]
2014-06-10 13:10:47 +04:00
self.assertGreater(k, 0.9)
def test_dpi(self):
def test(xdpi, ydpi=None):
2014-09-23 20:52:03 +04:00
im = Image.open(TEST_FILE)
2014-06-10 13:10:47 +04:00
im = self.roundtrip(im, dpi=(xdpi, ydpi or xdpi))
return im.info.get("dpi")
self.assertEqual(test(72), (72, 72))
self.assertEqual(test(300), (300, 300))
self.assertEqual(test(100, 200), (100, 200))
self.assertIsNone(test(0)) # square pixels
2014-06-10 13:10:47 +04:00
def test_icc(self):
# Test ICC support
im1 = Image.open("Tests/images/rgb.jpg")
icc_profile = im1.info["icc_profile"]
self.assertEqual(len(icc_profile), 3144)
# Roundtrip via physical file.
f = self.tempfile("temp.jpg")
im1.save(f, icc_profile=icc_profile)
im2 = Image.open(f)
self.assertEqual(im2.info.get("icc_profile"), icc_profile)
# Roundtrip via memory buffer.
2014-09-23 20:52:03 +04:00
im1 = self.roundtrip(hopper())
im2 = self.roundtrip(hopper(), icc_profile=icc_profile)
2014-06-10 13:10:47 +04:00
self.assert_image_equal(im1, im2)
self.assertFalse(im1.info.get("icc_profile"))
self.assertTrue(im2.info.get("icc_profile"))
def test_icc_big(self):
# Make sure that the "extra" support handles large blocks
def test(n):
# The ICC APP marker can store 65519 bytes per marker, so
# using a 4-byte test code should allow us to detect out of
# order issues.
icc_profile = (b"Test"*int(n/4+1))[:n]
self.assertEqual(len(icc_profile), n) # sanity
2014-09-23 20:52:03 +04:00
im1 = self.roundtrip(hopper(), icc_profile=icc_profile)
2014-06-10 13:10:47 +04:00
self.assertEqual(im1.info.get("icc_profile"), icc_profile or None)
test(0)
test(1)
test(3)
test(4)
test(5)
test(65533-14) # full JPEG marker block
test(65533-14+1) # full block plus one byte
test(ImageFile.MAXBLOCK) # full buffer block
test(ImageFile.MAXBLOCK+1) # full buffer block plus one byte
test(ImageFile.MAXBLOCK*4+3) # large block
def test_large_icc_meta(self):
# https://github.com/python-pillow/Pillow/issues/148
2017-06-29 12:21:19 +03:00
# Sometimes the meta data on the icc_profile block is bigger than
# Image.MAXBLOCK or the image size.
im = Image.open('Tests/images/icc_profile_big.jpg')
f = self.tempfile("temp.jpg")
icc_profile = im.info["icc_profile"]
# Should not raise IOError for image with icc larger than image size.
im.save(f, format='JPEG', progressive=True, quality=95,
icc_profile=icc_profile, optimize=True)
2014-06-10 13:10:47 +04:00
def test_optimize(self):
2014-09-23 20:52:03 +04:00
im1 = self.roundtrip(hopper())
im2 = self.roundtrip(hopper(), optimize=0)
im3 = self.roundtrip(hopper(), optimize=1)
2014-06-10 13:10:47 +04:00
self.assert_image_equal(im1, im2)
self.assert_image_equal(im1, im3)
2014-06-10 13:10:47 +04:00
self.assertGreaterEqual(im1.bytes, im2.bytes)
self.assertGreaterEqual(im1.bytes, im3.bytes)
2014-06-10 13:10:47 +04:00
def test_optimize_large_buffer(self):
# https://github.com/python-pillow/Pillow/issues/148
f = self.tempfile('temp.jpg')
# this requires ~ 1.5x Image.MAXBLOCK
im = Image.new("RGB", (4096, 4096), 0xff3333)
im.save(f, format="JPEG", optimize=True)
def test_progressive(self):
2014-09-23 20:52:03 +04:00
im1 = self.roundtrip(hopper())
im2 = self.roundtrip(hopper(), progressive=False)
im3 = self.roundtrip(hopper(), progressive=True)
self.assertFalse(im1.info.get("progressive"))
self.assertFalse(im2.info.get("progressive"))
self.assertTrue(im3.info.get("progressive"))
self.assert_image_equal(im1, im3)
self.assertGreaterEqual(im1.bytes, im3.bytes)
2014-06-10 13:10:47 +04:00
def test_progressive_large_buffer(self):
f = self.tempfile('temp.jpg')
# this requires ~ 1.5x Image.MAXBLOCK
im = Image.new("RGB", (4096, 4096), 0xff3333)
im.save(f, format="JPEG", progressive=True)
def test_progressive_large_buffer_highest_quality(self):
f = self.tempfile('temp.jpg')
2017-03-14 12:26:11 +03:00
im = self.gen_random_image((255, 255))
2014-06-10 13:10:47 +04:00
# this requires more bytes than pixels in the image
im.save(f, format="JPEG", progressive=True, quality=100)
def test_progressive_cmyk_buffer(self):
# Issue 2272, quality 90 cmyk image is tripping the large buffer bug.
f = BytesIO()
2017-03-14 12:26:11 +03:00
im = self.gen_random_image((256, 256), 'CMYK')
im.save(f, format='JPEG', progressive=True, quality=94)
2017-03-03 13:32:31 +03:00
2014-06-10 13:10:47 +04:00
def test_large_exif(self):
# https://github.com/python-pillow/Pillow/issues/148
f = self.tempfile('temp.jpg')
2014-09-23 20:52:03 +04:00
im = hopper()
2014-06-10 13:10:47 +04:00
im.save(f, 'JPEG', quality=90, exif=b"1"*65532)
2015-07-29 16:38:26 +03:00
def test_exif_typeerror(self):
im = Image.open('Tests/images/exif_typeerror.jpg')
# Should not raise a TypeError
im._getexif()
2015-10-07 12:24:15 +03:00
def test_exif_gps(self):
# Arrange
im = Image.open('Tests/images/exif_gps.jpg')
gps_index = 34853
expected_exif_gps = {
2015-12-30 02:56:47 +03:00
0: b'\x00\x00\x00\x01',
2: (4294967295, 1),
5: b'\x01',
2015-10-07 12:24:15 +03:00
30: 65535,
2015-12-30 03:45:48 +03:00
29: '1999:99:99 99:99:99'}
2015-10-07 12:24:15 +03:00
# Act
exif = im._getexif()
# Assert
self.assertEqual(exif[gps_index], expected_exif_gps)
def test_exif_rollback(self):
# rolling back exif support in 3.1 to pre-3.0 formatting.
# expected from 2.9, with b/u qualifiers switched for 3.2 compatibility
2016-02-05 01:57:13 +03:00
# this test passes on 2.9 and 3.1, but not 3.0
expected_exif = {34867: 4294967295,
2016-02-05 01:57:13 +03:00
258: (24, 24, 24),
36867: '2099:09:29 10:10:10',
34853: {0: b'\x00\x00\x00\x01',
2: (4294967295, 1),
5: b'\x01',
30: 65535,
29: '1999:99:99 99:99:99'},
296: 65535,
34665: 185,
41994: 65535,
514: 4294967295,
271: 'Make',
2016-02-05 01:57:13 +03:00
272: 'XXX-XXX',
305: 'PIL',
42034: ((1, 1), (1, 1), (1, 1), (1, 1)),
42035: 'LensMake',
34856: b'\xaa\xaa\xaa\xaa\xaa\xaa',
282: (4294967295, 1),
33434: (4294967295, 1)}
im = Image.open('Tests/images/exif_gps.jpg')
exif = im._getexif()
for tag, value in expected_exif.items():
self.assertEqual(value, exif[tag])
def test_exif_gps_typeerror(self):
im = Image.open('Tests/images/exif_gps_typeerror.jpg')
2015-07-29 16:38:26 +03:00
# Should not raise a TypeError
im._getexif()
2014-06-10 13:10:47 +04:00
def test_progressive_compat(self):
2014-09-23 20:52:03 +04:00
im1 = self.roundtrip(hopper())
self.assertFalse(im1.info.get("progressive"))
self.assertFalse(im1.info.get("progression"))
im2 = self.roundtrip(hopper(), progressive=0)
im3 = self.roundtrip(hopper(), progression=0) # compatibility
self.assertFalse(im2.info.get("progressive"))
self.assertFalse(im2.info.get("progression"))
self.assertFalse(im3.info.get("progressive"))
self.assertFalse(im3.info.get("progression"))
2014-09-23 20:52:03 +04:00
im2 = self.roundtrip(hopper(), progressive=1)
im3 = self.roundtrip(hopper(), progression=1) # compatibility
2014-06-10 13:10:47 +04:00
self.assert_image_equal(im1, im2)
self.assert_image_equal(im1, im3)
self.assertTrue(im2.info.get("progressive"))
self.assertTrue(im2.info.get("progression"))
self.assertTrue(im3.info.get("progressive"))
self.assertTrue(im3.info.get("progression"))
def test_quality(self):
2014-09-23 20:52:03 +04:00
im1 = self.roundtrip(hopper())
im2 = self.roundtrip(hopper(), quality=50)
2014-06-10 13:10:47 +04:00
self.assert_image(im1, im2.mode, im2.size)
self.assertGreaterEqual(im1.bytes, im2.bytes)
def test_smooth(self):
2014-09-23 20:52:03 +04:00
im1 = self.roundtrip(hopper())
im2 = self.roundtrip(hopper(), smooth=100)
2014-06-10 13:10:47 +04:00
self.assert_image(im1, im2.mode, im2.size)
def test_subsampling(self):
def getsampling(im):
layer = im.layer
return layer[0][1:3] + layer[1][1:3] + layer[2][1:3]
# experimental API
2014-09-23 20:52:03 +04:00
im = self.roundtrip(hopper(), subsampling=-1) # default
2014-06-10 13:10:47 +04:00
self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))
2014-09-23 20:52:03 +04:00
im = self.roundtrip(hopper(), subsampling=0) # 4:4:4
2014-06-10 13:10:47 +04:00
self.assertEqual(getsampling(im), (1, 1, 1, 1, 1, 1))
2014-09-23 20:52:03 +04:00
im = self.roundtrip(hopper(), subsampling=1) # 4:2:2
2014-06-10 13:10:47 +04:00
self.assertEqual(getsampling(im), (2, 1, 1, 1, 1, 1))
im = self.roundtrip(hopper(), subsampling=2) # 4:2:0
2014-06-10 13:10:47 +04:00
self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))
2014-09-23 20:52:03 +04:00
im = self.roundtrip(hopper(), subsampling=3) # default (undefined)
2014-06-10 13:10:47 +04:00
self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))
2014-09-23 20:52:03 +04:00
im = self.roundtrip(hopper(), subsampling="4:4:4")
2014-06-10 13:10:47 +04:00
self.assertEqual(getsampling(im), (1, 1, 1, 1, 1, 1))
2014-09-23 20:52:03 +04:00
im = self.roundtrip(hopper(), subsampling="4:2:2")
2014-06-10 13:10:47 +04:00
self.assertEqual(getsampling(im), (2, 1, 1, 1, 1, 1))
im = self.roundtrip(hopper(), subsampling="4:2:0")
self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))
2014-09-23 20:52:03 +04:00
im = self.roundtrip(hopper(), subsampling="4:1:1")
2014-06-10 13:10:47 +04:00
self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))
self.assertRaises(
2017-09-01 14:05:40 +03:00
TypeError, self.roundtrip, hopper(), subsampling="1:1:1")
2014-06-10 13:10:47 +04:00
def test_exif(self):
im = Image.open("Tests/images/pil_sample_rgb.jpg")
info = im._getexif()
self.assertEqual(info[305], 'Adobe Photoshop CS Macintosh')
def test_mp(self):
im = Image.open("Tests/images/pil_sample_rgb.jpg")
self.assertIsNone(im._getmp())
2014-06-10 13:10:47 +04:00
def test_quality_keep(self):
# RGB
2014-09-23 20:52:03 +04:00
im = Image.open("Tests/images/hopper.jpg")
2014-06-10 13:10:47 +04:00
f = self.tempfile('temp.jpg')
im.save(f, quality='keep')
# Grayscale
2014-09-23 20:52:03 +04:00
im = Image.open("Tests/images/hopper_gray.jpg")
f = self.tempfile('temp.jpg')
im.save(f, quality='keep')
# CMYK
im = Image.open("Tests/images/pil_sample_cmyk.jpg")
f = self.tempfile('temp.jpg')
im.save(f, quality='keep')
2014-06-10 13:10:47 +04:00
def test_junk_jpeg_header(self):
# https://github.com/python-pillow/Pillow/issues/630
filename = "Tests/images/junk_jpeg_header.jpg"
Image.open(filename)
2016-06-22 23:36:23 +03:00
def test_ff00_jpeg_header(self):
filename = "Tests/images/jpeg_ff00_header.jpg"
Image.open(filename)
2018-03-08 08:31:51 +03:00
def test_truncated_jpeg_should_read_all_the_data(self):
filename = "Tests/images/truncated_jpeg.jpg"
ImageFile.LOAD_TRUNCATED_IMAGES = True
im = Image.open(filename)
im.load()
ImageFile.LOAD_TRUNCATED_IMAGES = False
self.assertIsNotNone(im.getbbox())
def test_truncated_jpeg_throws_IOError(self):
filename = "Tests/images/truncated_jpeg.jpg"
im = Image.open(filename)
with self.assertRaises(IOError):
im.load()
def _n_qtables_helper(self, n, test_file):
im = Image.open(test_file)
f = self.tempfile('temp.jpg')
im.save(f, qtables=[[n]*64]*n)
im = Image.open(f)
self.assertEqual(len(im.quantization), n)
reloaded = self.roundtrip(im, qtables="keep")
self.assertEqual(im.quantization, reloaded.quantization)
2014-06-20 11:40:18 +04:00
def test_qtables(self):
2014-09-23 20:52:03 +04:00
im = Image.open("Tests/images/hopper.jpg")
2014-06-20 11:34:32 +04:00
qtables = im.quantization
reloaded = self.roundtrip(im, qtables=qtables, subsampling=0)
self.assertEqual(im.quantization, reloaded.quantization)
2014-08-28 18:18:54 +04:00
self.assert_image_similar(im, self.roundtrip(im, qtables='web_low'),
30)
self.assert_image_similar(im, self.roundtrip(im, qtables='web_high'),
30)
2014-06-20 12:09:59 +04:00
self.assert_image_similar(im, self.roundtrip(im, qtables='keep'), 30)
# valid bounds for baseline qtable
bounds_qtable = [int(s) for s in ("255 1 " * 32).split(None)]
self.roundtrip(im, qtables=[bounds_qtable])
2014-08-28 18:18:54 +04:00
# values from wizard.txt in jpeg9-a src package.
2014-06-20 12:09:59 +04:00
standard_l_qtable = [int(s) for s in """
16 11 10 16 24 40 51 61
12 12 14 19 26 58 60 55
14 13 16 24 40 57 69 56
14 17 22 29 51 87 80 62
18 22 37 56 68 109 103 77
24 35 55 64 81 104 113 92
49 64 78 87 103 121 120 101
72 92 95 98 112 100 103 99
""".split(None)]
2014-08-28 18:18:54 +04:00
standard_chrominance_qtable = [int(s) for s in """
2014-06-20 12:09:59 +04:00
17 18 24 47 99 99 99 99
18 21 26 66 99 99 99 99
24 26 56 99 99 99 99 99
47 66 99 99 99 99 99 99
99 99 99 99 99 99 99 99
99 99 99 99 99 99 99 99
99 99 99 99 99 99 99 99
99 99 99 99 99 99 99 99
""".split(None)]
# list of qtable lists
2014-08-28 18:18:54 +04:00
self.assert_image_similar(
im, self.roundtrip(
im, qtables=[standard_l_qtable, standard_chrominance_qtable]),
30)
2014-06-20 12:09:59 +04:00
# tuple of qtable lists
2014-08-28 18:18:54 +04:00
self.assert_image_similar(
im, self.roundtrip(
im, qtables=(standard_l_qtable, standard_chrominance_qtable)),
30)
2014-06-20 12:09:59 +04:00
# dict of qtable lists
self.assert_image_similar(im,
2015-07-03 09:22:56 +03:00
self.roundtrip(im, qtables={
2015-12-30 23:27:27 +03:00
0: standard_l_qtable,
1: standard_chrominance_qtable
2015-07-03 09:22:56 +03:00
}), 30)
self._n_qtables_helper(1, "Tests/images/hopper_gray.jpg")
self._n_qtables_helper(1, "Tests/images/pil_sample_rgb.jpg")
self._n_qtables_helper(2, "Tests/images/pil_sample_rgb.jpg")
self._n_qtables_helper(3, "Tests/images/pil_sample_rgb.jpg")
self._n_qtables_helper(1, "Tests/images/pil_sample_cmyk.jpg")
self._n_qtables_helper(2, "Tests/images/pil_sample_cmyk.jpg")
self._n_qtables_helper(3, "Tests/images/pil_sample_cmyk.jpg")
self._n_qtables_helper(4, "Tests/images/pil_sample_cmyk.jpg")
2014-09-25 01:15:17 +04:00
# not a sequence
self.assertRaises(ValueError, self.roundtrip, im, qtables='a')
2014-09-25 01:15:17 +04:00
# sequence wrong length
self.assertRaises(ValueError, self.roundtrip, im, qtables=[])
2014-09-25 01:15:17 +04:00
# sequence wrong length
self.assertRaises(ValueError,
2017-09-01 14:05:40 +03:00
self.roundtrip, im, qtables=[1, 2, 3, 4, 5])
2014-09-25 01:15:17 +04:00
# qtable entry not a sequence
self.assertRaises(ValueError, self.roundtrip, im, qtables=[1])
2014-09-25 01:15:17 +04:00
# qtable entry has wrong number of items
self.assertRaises(ValueError,
2017-09-01 14:05:40 +03:00
self.roundtrip, im, qtables=[[1, 2, 3, 4]])
2014-09-25 01:15:17 +04:00
@unittest.skipUnless(djpeg_available(), "djpeg not available")
def test_load_djpeg(self):
2014-09-23 20:52:03 +04:00
img = Image.open(TEST_FILE)
img.load_djpeg()
2014-09-23 20:52:03 +04:00
self.assert_image_similar(img, Image.open(TEST_FILE), 0)
@unittest.skipUnless(cjpeg_available(), "cjpeg not available")
def test_save_cjpeg(self):
2014-09-23 20:52:03 +04:00
img = Image.open(TEST_FILE)
tempfile = self.tempfile("temp.jpg")
JpegImagePlugin._save_cjpeg(img, 0, tempfile)
# Default save quality is 75%, so a tiny bit of difference is alright
self.assert_image_similar(img, Image.open(tempfile), 17)
def test_no_duplicate_0x1001_tag(self):
# Arrange
from PIL import ExifTags
tag_ids = {v: k for k, v in ExifTags.TAGS.items()}
# Assert
self.assertEqual(tag_ids['RelatedImageWidth'], 0x1001)
self.assertEqual(tag_ids['RelatedImageLength'], 0x1002)
def test_MAXBLOCK_scaling(self):
2016-12-03 17:45:05 +03:00
im = self.gen_random_image((512, 512))
f = self.tempfile("temp.jpeg")
im.save(f, quality=100, optimize=True)
reloaded = Image.open(f)
# none of these should crash
reloaded.save(f, quality='keep')
reloaded.save(f, quality='keep', progressive=True)
reloaded.save(f, quality='keep', optimize=True)
def test_bad_mpo_header(self):
""" Treat unknown MPO as JPEG """
# Arrange
# Act
# Shouldn't raise error
fn = "Tests/images/sugarshack_bad_mpo_header.jpg"
2017-09-01 14:05:40 +03:00
im = self.assert_warning(UserWarning, Image.open, fn)
# Assert
self.assertEqual(im.format, "JPEG")
def test_save_correct_modes(self):
out = BytesIO()
for mode in ['1', 'L', 'RGB', 'RGBX', 'CMYK', 'YCbCr']:
img = Image.new(mode, (20, 20))
img.save(out, "JPEG")
def test_save_wrong_modes(self):
# ref https://github.com/python-pillow/Pillow/issues/2005
out = BytesIO()
for mode in ['LA', 'La', 'RGBA', 'RGBa', 'P']:
img = Image.new(mode, (20, 20))
self.assertRaises(IOError, img.save, out, "JPEG")
def test_save_tiff_with_dpi(self):
# Arrange
outfile = self.tempfile("temp.tif")
im = Image.open("Tests/images/hopper.tif")
# Act
im.save(outfile, 'JPEG', dpi=im.info['dpi'])
# Assert
reloaded = Image.open(outfile)
reloaded.load()
self.assertEqual(im.info['dpi'], reloaded.info['dpi'])
2019-03-30 07:03:57 +03:00
def test_load_dpi_rounding(self):
# Round up
im = Image.open('Tests/images/iptc_roundUp.jpg')
self.assertEqual(im.info["dpi"], (44, 44))
# Round down
im = Image.open('Tests/images/iptc_roundDown.jpg')
self.assertEqual(im.info["dpi"], (2, 2))
def test_save_dpi_rounding(self):
outfile = self.tempfile("temp.jpg")
im = Image.open('Tests/images/hopper.jpg')
im.save(outfile, dpi=(72.2, 72.2))
reloaded = Image.open(outfile)
self.assertEqual(reloaded.info["dpi"], (72, 72))
im.save(outfile, dpi=(72.8, 72.8))
reloaded = Image.open(outfile)
self.assertEqual(reloaded.info["dpi"], (73, 73))
def test_dpi_tuple_from_exif(self):
2017-03-14 12:26:11 +03:00
# Arrange
# This Photoshop CC 2017 image has DPI in EXIF not metadata
# EXIF XResolution is (2000000, 10000)
2017-03-14 12:26:11 +03:00
im = Image.open("Tests/images/photoshop-200dpi.jpg")
# Act / Assert
self.assertEqual(im.info.get("dpi"), (200, 200))
def test_dpi_int_from_exif(self):
# Arrange
# This image has DPI in EXIF not metadata
# EXIF XResolution is 72
im = Image.open("Tests/images/exif-72dpi-int.jpg")
# Act / Assert
self.assertEqual(im.info.get("dpi"), (72, 72))
def test_dpi_from_dpcm_exif(self):
# Arrange
# This is photoshop-200dpi.jpg with EXIF resolution unit set to cm:
# exiftool -exif:ResolutionUnit=cm photoshop-200dpi.jpg
im = Image.open("Tests/images/exif-200dpcm.jpg")
# Act / Assert
self.assertEqual(im.info.get("dpi"), (508, 508))
def test_dpi_exif_zero_division(self):
# Arrange
# This is photoshop-200dpi.jpg with EXIF resolution set to 0/0:
# exiftool -XResolution=0/0 -YResolution=0/0 photoshop-200dpi.jpg
im = Image.open("Tests/images/exif-dpi-zerodivision.jpg")
# Act / Assert
# This should return the default, and not raise a ZeroDivisionError
self.assertEqual(im.info.get("dpi"), (72, 72))
def test_no_dpi_in_exif(self):
# Arrange
# This is photoshop-200dpi.jpg with resolution removed from EXIF:
# exiftool "-*resolution*"= photoshop-200dpi.jpg
im = Image.open("Tests/images/no-dpi-in-exif.jpg")
# Act / Assert
# "When the image resolution is unknown, 72 [dpi] is designated."
# http://www.exiv2.org/tags.html
self.assertEqual(im.info.get("dpi"), (72, 72))
2017-03-14 12:26:11 +03:00
def test_invalid_exif(self):
# This is no-dpi-in-exif with the tiff header of the exif block
# hexedited from MM * to FF FF FF FF
im = Image.open("Tests/images/invalid-exif.jpg")
# This should return the default, and not a SyntaxError or
# OSError for unidentified image.
self.assertEqual(im.info.get("dpi"), (72, 72))
2019-01-12 03:40:32 +03:00
def test_ifd_offset_exif(self):
# Arrange
# This image has been manually hexedited to have an IFD offset of 10,
# in contrast to normal 8
im = Image.open("Tests/images/exif-ifd-offset.jpg")
# Act / Assert
self.assertEqual(im._getexif()[306], '2017:03:13 23:03:09')
2019-03-06 02:28:45 +03:00
def test_photoshop(self):
im = Image.open("Tests/images/photoshop-200dpi.jpg")
self.assertEqual(im.info["photoshop"][0x03ed], {
'XResolution': 200.0,
'DisplayedUnitsX': 1,
'YResolution': 200.0,
'DisplayedUnitsY': 1,
})
@unittest.skipUnless(sys.platform.startswith('win32'), "Windows only")
class TestFileCloseW32(PillowTestCase):
def setUp(self):
if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs:
self.skipTest("jpeg support not available")
def test_fd_leak(self):
tmpfile = self.tempfile("temp.jpg")
with Image.open("Tests/images/hopper.jpg") as im:
im.save(tmpfile)
im = Image.open(tmpfile)
fp = im.fp
self.assertFalse(fp.closed)
self.assertRaises(WindowsError, os.remove, tmpfile)
im.load()
self.assertTrue(fp.closed)
# this should not fail, as load should have closed the file.
os.remove(tmpfile)