Line too long

This commit is contained in:
Andrew Murray 2018-06-24 22:32:25 +10:00
parent ce5d0e72b2
commit c2189235af
52 changed files with 805 additions and 381 deletions

View File

@ -153,7 +153,8 @@ class PillowTestCase(unittest.TestCase):
pass pass
raise e raise e
def assert_image_similar_tofile(self, a, filename, epsilon, msg=None, mode=None): def assert_image_similar_tofile(self, a, filename, epsilon, msg=None,
mode=None):
with Image.open(filename) as img: with Image.open(filename) as img:
if mode: if mode:
img = img.convert(mode) img = img.convert(mode)
@ -246,7 +247,8 @@ class PillowLeakTestCase(PillowTestCase):
mem = getrusage(RUSAGE_SELF).ru_maxrss mem = getrusage(RUSAGE_SELF).ru_maxrss
if sys.platform == 'darwin': if sys.platform == 'darwin':
# man 2 getrusage: # man 2 getrusage:
# ru_maxrss the maximum resident set size utilized (in bytes). # ru_maxrss
# This is the maximum resident set size utilized (in bytes).
return mem / 1024 # Kb return mem / 1024 # Kb
else: else:
# linux # linux

View File

@ -410,7 +410,8 @@ class TestGenerateColorLut3D(PillowTestCase):
self.assertEqual(lut.name, "Color 3D LUT") self.assertEqual(lut.name, "Color 3D LUT")
self.assertEqual(lut.table[:24], [ self.assertEqual(lut.table[:24], [
0.0, 0.0, 0.0, 0.0, 0.0, 0.25, 0.0, 0.125, 0.0, 0.5, 0.0, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.25, 0.0, 0.125, 0.0, 0.5, 0.0, 0.25,
0.0, 0.75, 0.0, 0.375, 0.0, 1.0, 0.0, 0.5, 0.0, 0.0, 0.25, 0.125]) 0.0, 0.75, 0.0, 0.375, 0.0, 1.0, 0.0, 0.5, 0.0, 0.0, 0.25, 0.125
])
def test_apply(self): def test_apply(self):
lut = ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b)) lut = ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b))

View File

@ -402,7 +402,8 @@ class TestFileGif(PillowTestCase):
def test_comment(self): def test_comment(self):
im = Image.open(TEST_GIF) im = Image.open(TEST_GIF)
self.assertEqual(im.info['comment'], b"File written by Adobe Photoshop\xa8 4.0") self.assertEqual(im.info['comment'],
b"File written by Adobe Photoshop\xa8 4.0")
out = self.tempfile('temp.gif') out = self.tempfile('temp.gif')
im = Image.new('L', (100, 100), '#000') im = Image.new('L', (100, 100), '#000')

View File

@ -126,7 +126,8 @@ class TestFileLibTiff(LibTiffTestCase):
im.tile[0][:3], ('tiff_adobe_deflate', (0, 0, 278, 374), 0)) im.tile[0][:3], ('tiff_adobe_deflate', (0, 0, 278, 374), 0))
im.load() im.load()
self.assert_image_equal_tofile(im, 'Tests/images/tiff_adobe_deflate.png') self.assert_image_equal_tofile(im,
'Tests/images/tiff_adobe_deflate.png')
def test_write_metadata(self): def test_write_metadata(self):
""" Test metadata writing through libtiff """ """ Test metadata writing through libtiff """
@ -217,7 +218,8 @@ class TestFileLibTiff(LibTiffTestCase):
if info.length == 0: if info.length == 0:
new_ifd[tag] = tuple(values[info.type] for _ in range(3)) new_ifd[tag] = tuple(values[info.type] for _ in range(3))
else: else:
new_ifd[tag] = tuple(values[info.type] for _ in range(info.length)) new_ifd[tag] = tuple(values[info.type]
for _ in range(info.length))
# Extra samples really doesn't make sense in this application. # Extra samples really doesn't make sense in this application.
del(new_ifd[338]) del(new_ifd[338])
@ -578,10 +580,14 @@ class TestFileLibTiff(LibTiffTestCase):
self.assertEqual(im.mode, "RGBA") self.assertEqual(im.mode, "RGBA")
self.assertEqual(im.size, (100, 40)) self.assertEqual(im.size, (100, 40))
self.assertEqual(im.tile, [('tiff_lzw', (0, 0, 100, 40), 0, ('RGBa;16N', 'tiff_lzw', False))]) self.assertEqual(
im.tile,
[('tiff_lzw', (0, 0, 100, 40), 0, ('RGBa;16N', 'tiff_lzw', False))]
)
im.load() im.load()
self.assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGBa_target.png") self.assert_image_equal_tofile(
im, "Tests/images/tiff_16bit_RGBa_target.png")
def test_gimp_tiff(self): def test_gimp_tiff(self):
# Read TIFF JPEG images from GIMP [@PIL168] # Read TIFF JPEG images from GIMP [@PIL168]
@ -607,7 +613,8 @@ class TestFileLibTiff(LibTiffTestCase):
im = Image.open("Tests/images/copyleft.tiff") im = Image.open("Tests/images/copyleft.tiff")
self.assertEqual(im.mode, 'RGB') self.assertEqual(im.mode, 'RGB')
self.assert_image_equal_tofile(im, "Tests/images/copyleft.png", mode='RGB') self.assert_image_equal_tofile(im, "Tests/images/copyleft.png",
mode='RGB')
def test_lzw(self): def test_lzw(self):
im = Image.open("Tests/images/hopper_lzw.tif") im = Image.open("Tests/images/hopper_lzw.tif")

View File

@ -20,7 +20,8 @@ class TestFilePdf(PillowTestCase):
self.assertTrue(os.path.isfile(outfile)) self.assertTrue(os.path.isfile(outfile))
self.assertGreater(os.path.getsize(outfile), 0) self.assertGreater(os.path.getsize(outfile), 0)
with PdfParser.PdfParser(outfile) as pdf: with PdfParser.PdfParser(outfile) as pdf:
if kwargs.get("append_images", False) or kwargs.get("append", False): if kwargs.get("append_images", False) or \
kwargs.get("append", False):
self.assertGreater(len(pdf.pages), 1) self.assertGreater(len(pdf.pages), 1)
else: else:
self.assertGreater(len(pdf.pages), 0) self.assertGreater(len(pdf.pages), 0)
@ -116,7 +117,9 @@ class TestFilePdf(PillowTestCase):
def test_pdf_open(self): def test_pdf_open(self):
# fail on a buffer full of null bytes # fail on a buffer full of null bytes
self.assertRaises(PdfParser.PdfFormatError, PdfParser.PdfParser, buf=bytearray(65536)) self.assertRaises(
PdfParser.PdfFormatError,
PdfParser.PdfParser, buf=bytearray(65536))
# make an empty PDF object # make an empty PDF object
with PdfParser.PdfParser() as empty_pdf: with PdfParser.PdfParser() as empty_pdf:
@ -153,7 +156,10 @@ class TestFilePdf(PillowTestCase):
im = hopper("RGB") im = hopper("RGB")
temp_dir = tempfile.mkdtemp() temp_dir = tempfile.mkdtemp()
try: try:
self.assertRaises(IOError, im.save, os.path.join(temp_dir, "nonexistent.pdf"), append=True) self.assertRaises(IOError,
im.save,
os.path.join(temp_dir, "nonexistent.pdf"),
append=True)
finally: finally:
os.rmdir(temp_dir) os.rmdir(temp_dir)
@ -204,7 +210,8 @@ class TestFilePdf(PillowTestCase):
# append two images # append two images
mode_CMYK = hopper("CMYK") mode_CMYK = hopper("CMYK")
mode_P = hopper("P") mode_P = hopper("P")
mode_CMYK.save(pdf_filename, append=True, save_all=True, append_images=[mode_P]) mode_CMYK.save(pdf_filename,
append=True, save_all=True, append_images=[mode_P])
# open the PDF again, check pages and info again # open the PDF again, check pages and info again
with PdfParser.PdfParser(pdf_filename) as pdf: with PdfParser.PdfParser(pdf_filename) as pdf:
@ -219,7 +226,9 @@ class TestFilePdf(PillowTestCase):
def test_pdf_info(self): def test_pdf_info(self):
# make a PDF file # make a PDF file
pdf_filename = self.helper_save_as_pdf("RGB", title="title", author="author", subject="subject", keywords="keywords", creator="creator", producer="producer") pdf_filename = self.helper_save_as_pdf(
"RGB", title="title", author="author", subject="subject",
keywords="keywords", creator="creator", producer="producer")
# open it, check pages and info # open it, check pages and info
with PdfParser.PdfParser(pdf_filename) as pdf: with PdfParser.PdfParser(pdf_filename) as pdf:

View File

@ -355,7 +355,8 @@ class TestFilePng(PillowTestCase):
broken_crc_chunk_data = chunk_data[:-1] + b'q' # break CRC broken_crc_chunk_data = chunk_data[:-1] + b'q' # break CRC
image_data = HEAD + broken_crc_chunk_data + TAIL image_data = HEAD + broken_crc_chunk_data + TAIL
self.assertRaises(SyntaxError, PngImagePlugin.PngImageFile, BytesIO(image_data)) self.assertRaises(SyntaxError, PngImagePlugin.PngImageFile,
BytesIO(image_data))
ImageFile.LOAD_TRUNCATED_IMAGES = True ImageFile.LOAD_TRUNCATED_IMAGES = True
try: try:
@ -371,7 +372,8 @@ class TestFilePng(PillowTestCase):
ImageFile.LOAD_TRUNCATED_IMAGES = True ImageFile.LOAD_TRUNCATED_IMAGES = True
try: try:
self.assertRaises(SyntaxError, PngImagePlugin.PngImageFile, BytesIO(image_data)) self.assertRaises(SyntaxError, PngImagePlugin.PngImageFile,
BytesIO(image_data))
finally: finally:
ImageFile.LOAD_TRUNCATED_IMAGES = False ImageFile.LOAD_TRUNCATED_IMAGES = False

View File

@ -444,7 +444,8 @@ class TestFileTiff(PillowTestCase):
for im in ims: for im in ims:
yield im yield im
mp = io.BytesIO() mp = io.BytesIO()
im.save(mp, format="TIFF", save_all=True, append_images=imGenerator(ims)) im.save(mp, format="TIFF", save_all=True,
append_images=imGenerator(ims))
mp.seek(0, os.SEEK_SET) mp.seek(0, os.SEEK_SET)
reread = Image.open(mp) reread = Image.open(mp)

View File

@ -16,7 +16,8 @@ class TestTTypeFontLeak(PillowLeakTestCase):
self._test_leak(lambda: draw.text((0, 0), "some text "*1024, # ~10k self._test_leak(lambda: draw.text((0, 0), "some text "*1024, # ~10k
font=font, fill="black")) font=font, fill="black"))
@unittest.skipIf(not features.check('freetype2'), "Test requires freetype2") @unittest.skipIf(not features.check('freetype2'),
"Test requires freetype2")
def test_leak(self): def test_leak(self):
ttype = ImageFont.truetype('Tests/fonts/FreeMono.ttf', 20) ttype = ImageFont.truetype('Tests/fonts/FreeMono.ttf', 20)
self._test_font(ttype) self._test_font(ttype)

View File

@ -294,7 +294,8 @@ int main(int argc, char* argv[])
compiler = ccompiler.new_compiler() compiler = ccompiler.new_compiler()
compiler.add_include_dir(sysconfig.get_python_inc()) compiler.add_include_dir(sysconfig.get_python_inc())
libdir = sysconfig.get_config_var('LIBDIR') or sysconfig.get_python_inc().replace('include', 'libs') libdir = (sysconfig.get_config_var('LIBDIR') or
sysconfig.get_python_inc().replace('include', 'libs'))
print(libdir) print(libdir)
compiler.add_library_dir(libdir) compiler.add_library_dir(libdir)
objects = compiler.compile(['embed_pil.c']) objects = compiler.compile(['embed_pil.c'])

View File

@ -15,9 +15,11 @@ class TestImageArray(PillowTestCase):
self.assertEqual(test("L"), (3, (100, 128), '|u1', 12800)) self.assertEqual(test("L"), (3, (100, 128), '|u1', 12800))
# FIXME: wrong? # FIXME: wrong?
self.assertEqual(test("I"), (3, (100, 128), Image._ENDIAN + 'i4', 51200)) self.assertEqual(test("I"), (3, (100, 128),
Image._ENDIAN + 'i4', 51200))
# FIXME: wrong? # FIXME: wrong?
self.assertEqual(test("F"), (3, (100, 128), Image._ENDIAN + 'f4', 51200)) self.assertEqual(test("F"), (3, (100, 128),
Image._ENDIAN + 'f4', 51200))
self.assertEqual(test("LA"), (3, (100, 128, 2), '|u1', 25600)) self.assertEqual(test("LA"), (3, (100, 128, 2), '|u1', 25600))
self.assertEqual(test("RGB"), (3, (100, 128, 3), '|u1', 38400)) self.assertEqual(test("RGB"), (3, (100, 128, 3), '|u1', 38400))

View File

@ -193,7 +193,8 @@ class TestImageConvert(PillowTestCase):
if converted_im.mode == 'RGB': if converted_im.mode == 'RGB':
self.assert_image_similar(converted_im, target, 3) self.assert_image_similar(converted_im, target, 3)
else: else:
self.assert_image_similar(converted_im, target.getchannel(0), 1) self.assert_image_similar(converted_im,
target.getchannel(0), 1)
matrix_convert('RGB') matrix_convert('RGB')
matrix_convert('L') matrix_convert('L')

View File

@ -288,10 +288,14 @@ class CoreResampleAlphaCorrectTest(PillowTestCase):
def test_dirty_pixels_rgba(self): def test_dirty_pixels_rgba(self):
case = self.make_dirty_case('RGBA', (255, 255, 0, 128), (0, 0, 255, 0)) case = self.make_dirty_case('RGBA', (255, 255, 0, 128), (0, 0, 255, 0))
self.run_dirty_case(case.resize((20, 20), Image.BOX), (255, 255, 0)) self.run_dirty_case(case.resize((20, 20), Image.BOX), (255, 255, 0))
self.run_dirty_case(case.resize((20, 20), Image.BILINEAR), (255, 255, 0)) self.run_dirty_case(case.resize((20, 20), Image.BILINEAR),
self.run_dirty_case(case.resize((20, 20), Image.HAMMING), (255, 255, 0)) (255, 255, 0))
self.run_dirty_case(case.resize((20, 20), Image.BICUBIC), (255, 255, 0)) self.run_dirty_case(case.resize((20, 20), Image.HAMMING),
self.run_dirty_case(case.resize((20, 20), Image.LANCZOS), (255, 255, 0)) (255, 255, 0))
self.run_dirty_case(case.resize((20, 20), Image.BICUBIC),
(255, 255, 0))
self.run_dirty_case(case.resize((20, 20), Image.LANCZOS),
(255, 255, 0))
def test_dirty_pixels_la(self): def test_dirty_pixels_la(self):
case = self.make_dirty_case('LA', (255, 128), (0, 0)) case = self.make_dirty_case('LA', (255, 128), (0, 0))
@ -367,23 +371,28 @@ class CoreResampleCoefficientsTest(PillowTestCase):
im = Image.new('RGBA', (1280, 1280), (0x20, 0x40, 0x60, 0xff)) im = Image.new('RGBA', (1280, 1280), (0x20, 0x40, 0x60, 0xff))
histogram = im.resize((256, 256), Image.BICUBIC).histogram() histogram = im.resize((256, 256), Image.BICUBIC).histogram()
self.assertEqual(histogram[0x100 * 0 + 0x20], 0x10000) # first channel # first channel
self.assertEqual(histogram[0x100 * 1 + 0x40], 0x10000) # second channel self.assertEqual(histogram[0x100 * 0 + 0x20], 0x10000)
self.assertEqual(histogram[0x100 * 2 + 0x60], 0x10000) # third channel # second channel
self.assertEqual(histogram[0x100 * 3 + 0xff], 0x10000) # fourth channel self.assertEqual(histogram[0x100 * 1 + 0x40], 0x10000)
# third channel
self.assertEqual(histogram[0x100 * 2 + 0x60], 0x10000)
# fourth channel
self.assertEqual(histogram[0x100 * 3 + 0xff], 0x10000)
class CoreResampleBoxTest(PillowTestCase): class CoreResampleBoxTest(PillowTestCase):
def test_wrong_arguments(self): def test_wrong_arguments(self):
im = hopper() im = hopper()
for resample in (Image.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING, for resample in (Image.NEAREST, Image.BOX, Image.BILINEAR,
Image.BICUBIC, Image.LANCZOS): Image.HAMMING, Image.BICUBIC, Image.LANCZOS):
im.resize((32, 32), resample, (0, 0, im.width, im.height)) im.resize((32, 32), resample, (0, 0, im.width, im.height))
im.resize((32, 32), resample, (20, 20, im.width, im.height)) im.resize((32, 32), resample, (20, 20, im.width, im.height))
im.resize((32, 32), resample, (20, 20, 20, 100)) im.resize((32, 32), resample, (20, 20, 20, 100))
im.resize((32, 32), resample, (20, 20, 100, 20)) im.resize((32, 32), resample, (20, 20, 100, 20))
with self.assertRaisesRegex(TypeError, "must be sequence of length 4"): with self.assertRaisesRegex(TypeError,
"must be sequence of length 4"):
im.resize((32, 32), resample, (im.width, im.height)) im.resize((32, 32), resample, (im.width, im.height))
with self.assertRaisesRegex(ValueError, "can't be negative"): with self.assertRaisesRegex(ValueError, "can't be negative"):

View File

@ -95,8 +95,9 @@ class TestImageRotate(PillowTestCase):
im = hopper() im = hopper()
self.rotate(im, im.mode, 45, center=(0, 0)) self.rotate(im, im.mode, 45, center=(0, 0))
self.rotate(im, im.mode, 45, translate=(im.size[0]/2, 0)) self.rotate(im, im.mode, 45, translate=(im.size[0]/2, 0))
self.rotate(im, im.mode, 45, center=(0, 0), translate=(im.size[0]/2, 0)) self.rotate(im, im.mode, 45, center=(0, 0),
translate=(im.size[0]/2, 0))
def test_rotate_no_fill(self): def test_rotate_no_fill(self):
im = Image.new('RGB', (100, 100), 'green') im = Image.new('RGB', (100, 100), 'green')
target = Image.open('Tests/images/rotate_45_no_fill.png') target = Image.open('Tests/images/rotate_45_no_fill.png')

View File

@ -43,8 +43,9 @@ class TestToQImage(PillowQtTestCase, PillowTestCase):
if mode == '1': if mode == '1':
# BW appears to not save correctly on QT4 and QT5 # BW appears to not save correctly on QT4 and QT5
# kicks out errors on console: # kicks out errors on console:
# libpng warning: Invalid color type/bit depth combination in IHDR # libpng warning: Invalid color type/bit depth combination
# libpng error: Invalid IHDR data # in IHDR
# libpng error: Invalid IHDR data
continue continue
# Test saving the file # Test saving the file

View File

@ -286,53 +286,104 @@ class TestImageCms(PillowTestCase):
self.assertEqual(truncate_tuple(tup1), truncate_tuple(tup2)) self.assertEqual(truncate_tuple(tup1), truncate_tuple(tup2))
self.assertEqual(p.attributes, 4294967296) self.assertEqual(p.attributes, 4294967296)
assert_truncated_tuple_equal(p.blue_colorant, ((0.14306640625, 0.06060791015625, 0.7140960693359375), (0.1558847490315394, 0.06603820639433387, 0.06060791015625))) assert_truncated_tuple_equal(
assert_truncated_tuple_equal(p.blue_primary, ((0.14306641366715667, 0.06060790921083026, 0.7140960805782015), (0.15588475410450106, 0.06603820408959558, 0.06060790921083026))) p.blue_colorant,
((0.14306640625, 0.06060791015625, 0.7140960693359375),
(0.1558847490315394, 0.06603820639433387, 0.06060791015625)))
assert_truncated_tuple_equal(
p.blue_primary,
((0.14306641366715667, 0.06060790921083026, 0.7140960805782015),
(0.15588475410450106, 0.06603820408959558, 0.06060790921083026)))
assert_truncated_tuple_equal(p.chromatic_adaptation, (((1.04791259765625, 0.0229339599609375, -0.050201416015625), (0.02960205078125, 0.9904632568359375, -0.0170745849609375), (-0.009246826171875, 0.0150604248046875, 0.7517852783203125)), ((1.0267159024652783, 0.022470062342089134, 0.0229339599609375), (0.02951378324103937, 0.9875098886387147, 0.9904632568359375), (-0.012205438066465256, 0.01987915407854985, 0.0150604248046875)))) assert_truncated_tuple_equal(p.chromatic_adaptation, (((1.04791259765625, 0.0229339599609375, -0.050201416015625), (0.02960205078125, 0.9904632568359375, -0.0170745849609375), (-0.009246826171875, 0.0150604248046875, 0.7517852783203125)), ((1.0267159024652783, 0.022470062342089134, 0.0229339599609375), (0.02951378324103937, 0.9875098886387147, 0.9904632568359375), (-0.012205438066465256, 0.01987915407854985, 0.0150604248046875))))
self.assertIsNone(p.chromaticity) self.assertIsNone(p.chromaticity)
self.assertEqual(p.clut, {0: (False, False, True), 1: (False, False, True), 2: (False, False, True), 3: (False, False, True)}) self.assertEqual(p.clut, {
0: (False, False, True),
1: (False, False, True),
2: (False, False, True),
3: (False, False, True)
})
self.assertEqual(p.color_space, 'RGB') self.assertEqual(p.color_space, 'RGB')
self.assertIsNone(p.colorant_table) self.assertIsNone(p.colorant_table)
self.assertIsNone(p.colorant_table_out) self.assertIsNone(p.colorant_table_out)
self.assertIsNone(p.colorimetric_intent) self.assertIsNone(p.colorimetric_intent)
self.assertEqual(p.connection_space, 'XYZ ') self.assertEqual(p.connection_space, 'XYZ ')
self.assertEqual(p.copyright, 'Copyright International Color Consortium, 2009') self.assertEqual(p.copyright,
self.assertEqual(p.creation_date, datetime.datetime(2009, 2, 27, 21, 36, 31)) 'Copyright International Color Consortium, 2009')
self.assertEqual(p.creation_date,
datetime.datetime(2009, 2, 27, 21, 36, 31))
self.assertEqual(p.device_class, 'mntr') self.assertEqual(p.device_class, 'mntr')
assert_truncated_tuple_equal(p.green_colorant, ((0.3851470947265625, 0.7168731689453125, 0.097076416015625), (0.32119769927720654, 0.5978443449048152, 0.7168731689453125))) assert_truncated_tuple_equal(
assert_truncated_tuple_equal(p.green_primary, ((0.3851470888162112, 0.7168731974161346, 0.09707641738998518), (0.32119768793686687, 0.5978443567149709, 0.7168731974161346))) p.green_colorant,
((0.3851470947265625, 0.7168731689453125, 0.097076416015625),
(0.32119769927720654, 0.5978443449048152, 0.7168731689453125)))
assert_truncated_tuple_equal(
p.green_primary,
((0.3851470888162112, 0.7168731974161346, 0.09707641738998518),
(0.32119768793686687, 0.5978443567149709, 0.7168731974161346)))
self.assertEqual(p.header_flags, 0) self.assertEqual(p.header_flags, 0)
self.assertEqual(p.header_manufacturer, '\x00\x00\x00\x00') self.assertEqual(p.header_manufacturer, '\x00\x00\x00\x00')
self.assertEqual(p.header_model, '\x00\x00\x00\x00') self.assertEqual(p.header_model, '\x00\x00\x00\x00')
self.assertEqual(p.icc_measurement_condition, {'backing': (0.0, 0.0, 0.0), 'flare': 0.0, 'geo': 'unknown', 'observer': 1, 'illuminant_type': 'D65'}) self.assertEqual(p.icc_measurement_condition, {
'backing': (0.0, 0.0, 0.0),
'flare': 0.0,
'geo': 'unknown',
'observer': 1,
'illuminant_type': 'D65'
})
self.assertEqual(p.icc_version, 33554432) self.assertEqual(p.icc_version, 33554432)
self.assertIsNone(p.icc_viewing_condition) self.assertIsNone(p.icc_viewing_condition)
self.assertEqual(p.intent_supported, {0: (True, True, True), 1: (True, True, True), 2: (True, True, True), 3: (True, True, True)}) self.assertEqual(p.intent_supported, {
0: (True, True, True),
1: (True, True, True),
2: (True, True, True),
3: (True, True, True)
})
self.assertTrue(p.is_matrix_shaper) self.assertTrue(p.is_matrix_shaper)
self.assertEqual(p.luminance, ((0.0, 80.0, 0.0), (0.0, 1.0, 80.0))) self.assertEqual(p.luminance, ((0.0, 80.0, 0.0), (0.0, 1.0, 80.0)))
self.assertIsNone(p.manufacturer) self.assertIsNone(p.manufacturer)
assert_truncated_tuple_equal(p.media_black_point, ((0.012054443359375, 0.0124969482421875, 0.01031494140625), (0.34573304157549234, 0.35842450765864337, 0.0124969482421875))) assert_truncated_tuple_equal(
assert_truncated_tuple_equal(p.media_white_point, ((0.964202880859375, 1.0, 0.8249053955078125), (0.3457029219802284, 0.3585375327567059, 1.0))) p.media_black_point,
assert_truncated_tuple_equal((p.media_white_point_temperature,), (5000.722328847392,)) ((0.012054443359375, 0.0124969482421875, 0.01031494140625),
self.assertEqual(p.model, 'IEC 61966-2-1 Default RGB Colour Space - sRGB') (0.34573304157549234, 0.35842450765864337, 0.0124969482421875)))
assert_truncated_tuple_equal(
p.media_white_point,
((0.964202880859375, 1.0, 0.8249053955078125),
(0.3457029219802284, 0.3585375327567059, 1.0)))
assert_truncated_tuple_equal(
(p.media_white_point_temperature,),
(5000.722328847392,))
self.assertEqual(p.model,
'IEC 61966-2-1 Default RGB Colour Space - sRGB')
self.assertEqual(p.pcs, 'XYZ') self.assertEqual(p.pcs, 'XYZ')
self.assertIsNone(p.perceptual_rendering_intent_gamut) self.assertIsNone(p.perceptual_rendering_intent_gamut)
self.assertEqual(p.product_copyright, 'Copyright International Color Consortium, 2009') self.assertEqual(p.product_copyright,
'Copyright International Color Consortium, 2009')
self.assertEqual(p.product_desc, 'sRGB IEC61966-2-1 black scaled') self.assertEqual(p.product_desc, 'sRGB IEC61966-2-1 black scaled')
self.assertEqual(p.product_description, 'sRGB IEC61966-2-1 black scaled') self.assertEqual(p.product_description,
'sRGB IEC61966-2-1 black scaled')
self.assertEqual(p.product_manufacturer, '') self.assertEqual(p.product_manufacturer, '')
self.assertEqual(p.product_model, 'IEC 61966-2-1 Default RGB Colour Space - sRGB') self.assertEqual(
self.assertEqual(p.profile_description, 'sRGB IEC61966-2-1 black scaled') p.product_model, 'IEC 61966-2-1 Default RGB Colour Space - sRGB')
self.assertEqual(p.profile_id, b')\xf8=\xde\xaf\xf2U\xaexB\xfa\xe4\xca\x839\r') self.assertEqual(
assert_truncated_tuple_equal(p.red_colorant, ((0.436065673828125, 0.2224884033203125, 0.013916015625), (0.6484536316398539, 0.3308524880306778, 0.2224884033203125))) p.profile_description, 'sRGB IEC61966-2-1 black scaled')
assert_truncated_tuple_equal(p.red_primary, ((0.43606566581047446, 0.22248840582960838, 0.013916015621759925), (0.6484536250319214, 0.3308524944738204, 0.22248840582960838))) self.assertEqual(
p.profile_id, b')\xf8=\xde\xaf\xf2U\xaexB\xfa\xe4\xca\x839\r')
assert_truncated_tuple_equal(
p.red_colorant,
((0.436065673828125, 0.2224884033203125, 0.013916015625),
(0.6484536316398539, 0.3308524880306778, 0.2224884033203125)))
assert_truncated_tuple_equal(
p.red_primary,
((0.43606566581047446, 0.22248840582960838, 0.013916015621759925),
(0.6484536250319214, 0.3308524944738204, 0.22248840582960838)))
self.assertEqual(p.rendering_intent, 0) self.assertEqual(p.rendering_intent, 0)
self.assertIsNone(p.saturation_rendering_intent_gamut) self.assertIsNone(p.saturation_rendering_intent_gamut)
self.assertIsNone(p.screening_description) self.assertIsNone(p.screening_description)
self.assertIsNone(p.target) self.assertIsNone(p.target)
self.assertEqual(p.technology, 'CRT ') self.assertEqual(p.technology, 'CRT ')
self.assertEqual(p.version, 2.0) self.assertEqual(p.version, 2.0)
self.assertEqual(p.viewing_condition, 'Reference Viewing Condition in IEC 61966-2-1') self.assertEqual(p.viewing_condition,
'Reference Viewing Condition in IEC 61966-2-1')
self.assertEqual(p.xcolor_space, 'RGB ') self.assertEqual(p.xcolor_space, 'RGB ')
def test_profile_typesafety(self): def test_profile_typesafety(self):
@ -346,7 +397,8 @@ class TestImageCms(PillowTestCase):
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
ImageCms.ImageCmsProfile(1).tobytes() ImageCms.ImageCmsProfile(1).tobytes()
def assert_aux_channel_preserved(self, mode, transform_in_place, preserved_channel): def assert_aux_channel_preserved(self, mode,
transform_in_place, preserved_channel):
def create_test_image(): def create_test_image():
# set up test image with something interesting in the tested aux # set up test image with something interesting in the tested aux
# channel. # channel.
@ -379,29 +431,35 @@ class TestImageCms(PillowTestCase):
# create some transform, it doesn't matter which one # create some transform, it doesn't matter which one
source_profile = ImageCms.createProfile("sRGB") source_profile = ImageCms.createProfile("sRGB")
destination_profile = ImageCms.createProfile("sRGB") destination_profile = ImageCms.createProfile("sRGB")
t = ImageCms.buildTransform(source_profile, destination_profile, inMode=mode, outMode=mode) t = ImageCms.buildTransform(
source_profile, destination_profile, inMode=mode, outMode=mode)
# apply transform # apply transform
if transform_in_place: if transform_in_place:
ImageCms.applyTransform(source_image, t, inPlace=True) ImageCms.applyTransform(source_image, t, inPlace=True)
result_image = source_image result_image = source_image
else: else:
result_image = ImageCms.applyTransform(source_image, t, inPlace=False) result_image = ImageCms.applyTransform(
source_image, t, inPlace=False)
result_image_aux = result_image.getchannel(preserved_channel) result_image_aux = result_image.getchannel(preserved_channel)
self.assert_image_equal(source_image_aux, result_image_aux) self.assert_image_equal(source_image_aux, result_image_aux)
def test_preserve_auxiliary_channels_rgba(self): def test_preserve_auxiliary_channels_rgba(self):
self.assert_aux_channel_preserved(mode='RGBA', transform_in_place=False, preserved_channel='A') self.assert_aux_channel_preserved(mode='RGBA',
transform_in_place=False, preserved_channel='A')
def test_preserve_auxiliary_channels_rgba_in_place(self): def test_preserve_auxiliary_channels_rgba_in_place(self):
self.assert_aux_channel_preserved(mode='RGBA', transform_in_place=True, preserved_channel='A') self.assert_aux_channel_preserved(mode='RGBA',
transform_in_place=True, preserved_channel='A')
def test_preserve_auxiliary_channels_rgbx(self): def test_preserve_auxiliary_channels_rgbx(self):
self.assert_aux_channel_preserved(mode='RGBX', transform_in_place=False, preserved_channel='X') self.assert_aux_channel_preserved(mode='RGBX',
transform_in_place=False, preserved_channel='X')
def test_preserve_auxiliary_channels_rgbx_in_place(self): def test_preserve_auxiliary_channels_rgbx_in_place(self):
self.assert_aux_channel_preserved(mode='RGBX', transform_in_place=True, preserved_channel='X') self.assert_aux_channel_preserved(mode='RGBX',
transform_in_place=True, preserved_channel='X')
def test_auxiliary_channels_isolated(self): def test_auxiliary_channels_isolated(self):
# test data in aux channels does not affect non-aux channels # test data in aux channels does not affect non-aux channels
@ -422,20 +480,29 @@ class TestImageCms(PillowTestCase):
source_profile = ImageCms.createProfile(src_format[1]) source_profile = ImageCms.createProfile(src_format[1])
destination_profile = ImageCms.createProfile(dst_format[1]) destination_profile = ImageCms.createProfile(dst_format[1])
source_image = src_format[3] source_image = src_format[3]
test_transform = ImageCms.buildTransform(source_profile, destination_profile, inMode=src_format[0], outMode=dst_format[0]) test_transform = ImageCms.buildTransform(
source_profile, destination_profile,
inMode=src_format[0], outMode=dst_format[0])
# test conversion from aux-ful source # test conversion from aux-ful source
if transform_in_place: if transform_in_place:
test_image = source_image.copy() test_image = source_image.copy()
ImageCms.applyTransform(test_image, test_transform, inPlace=True) ImageCms.applyTransform(
test_image, test_transform, inPlace=True)
else: else:
test_image = ImageCms.applyTransform(source_image, test_transform, inPlace=False) test_image = ImageCms.applyTransform(
source_image, test_transform, inPlace=False)
# reference conversion from aux-less source # reference conversion from aux-less source
reference_transform = ImageCms.buildTransform(source_profile, destination_profile, inMode=src_format[2], outMode=dst_format[2]) reference_transform = ImageCms.buildTransform(
reference_image = ImageCms.applyTransform(source_image.convert(src_format[2]), reference_transform) source_profile, destination_profile,
inMode=src_format[2], outMode=dst_format[2])
reference_image = ImageCms.applyTransform(
source_image.convert(src_format[2]),
reference_transform)
self.assert_image_equal(test_image.convert(dst_format[2]), reference_image) self.assert_image_equal(test_image.convert(dst_format[2]),
reference_image)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -31,7 +31,8 @@ class TestImageColor(PillowTestCase):
# case insensitivity # case insensitivity
self.assertEqual(ImageColor.getrgb("#DEF"), ImageColor.getrgb("#def")) self.assertEqual(ImageColor.getrgb("#DEF"), ImageColor.getrgb("#def"))
self.assertEqual(ImageColor.getrgb("#CDEF"), ImageColor.getrgb("#cdef")) self.assertEqual(ImageColor.getrgb("#CDEF"),
ImageColor.getrgb("#cdef"))
self.assertEqual(ImageColor.getrgb("#DEFDEF"), self.assertEqual(ImageColor.getrgb("#DEFDEF"),
ImageColor.getrgb("#defdef")) ImageColor.getrgb("#defdef"))
self.assertEqual(ImageColor.getrgb("#CDEFCDEF"), self.assertEqual(ImageColor.getrgb("#CDEFCDEF"),
@ -80,18 +81,23 @@ class TestImageColor(PillowTestCase):
self.assertEqual((255, 0, 0), ImageColor.getrgb("hsv(0,100%,100%)")) self.assertEqual((255, 0, 0), ImageColor.getrgb("hsv(0,100%,100%)"))
self.assertEqual((255, 0, 0), ImageColor.getrgb("hsv(360,100%,100%)")) self.assertEqual((255, 0, 0), ImageColor.getrgb("hsv(360,100%,100%)"))
self.assertEqual((0, 255, 255), ImageColor.getrgb("hsv(180,100%,100%)")) self.assertEqual((0, 255, 255),
ImageColor.getrgb("hsv(180,100%,100%)"))
# alternate format # alternate format
self.assertEqual(ImageColor.getrgb("hsb(0,100%,50%)"), self.assertEqual(ImageColor.getrgb("hsb(0,100%,50%)"),
ImageColor.getrgb("hsv(0,100%,50%)")) ImageColor.getrgb("hsv(0,100%,50%)"))
# floats # floats
self.assertEqual((254, 3, 3), ImageColor.getrgb("hsl(0.1,99.2%,50.3%)")) self.assertEqual((254, 3, 3),
self.assertEqual((255, 0, 0), ImageColor.getrgb("hsl(360.,100.0%,50%)")) ImageColor.getrgb("hsl(0.1,99.2%,50.3%)"))
self.assertEqual((255, 0, 0),
ImageColor.getrgb("hsl(360.,100.0%,50%)"))
self.assertEqual((253, 2, 2), ImageColor.getrgb("hsv(0.1,99.2%,99.3%)")) self.assertEqual((253, 2, 2),
self.assertEqual((255, 0, 0), ImageColor.getrgb("hsv(360.,100.0%,100%)")) ImageColor.getrgb("hsv(0.1,99.2%,99.3%)"))
self.assertEqual((255, 0, 0),
ImageColor.getrgb("hsv(360.,100.0%,100%)"))
# case insensitivity # case insensitivity
self.assertEqual(ImageColor.getrgb("RGB(255,0,0)"), self.assertEqual(ImageColor.getrgb("RGB(255,0,0)"),

View File

@ -45,8 +45,9 @@ class TestImageEnhance(PillowTestCase):
for op in ['Color', 'Brightness', 'Contrast', 'Sharpness']: for op in ['Color', 'Brightness', 'Contrast', 'Sharpness']:
for amount in [0, 0.5, 1.0]: for amount in [0, 0.5, 1.0]:
self._check_alpha(getattr(ImageEnhance, op)(original).enhance(amount), self._check_alpha(
original, op, amount) getattr(ImageEnhance, op)(original).enhance(amount),
original, op, amount)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -215,12 +215,16 @@ class TestPyDecoder(PillowTestCase):
buf = BytesIO(b'\x00'*255) buf = BytesIO(b'\x00'*255)
im = MockImageFile(buf) im = MockImageFile(buf)
im.tile = [("MOCK", (xoff, yoff, xoff+xsize + 100, yoff+ysize), 32, None)] im.tile = [
("MOCK", (xoff, yoff, xoff+xsize + 100, yoff+ysize), 32, None)
]
d = self.get_decoder() d = self.get_decoder()
self.assertRaises(ValueError, im.load) self.assertRaises(ValueError, im.load)
im.tile = [("MOCK", (xoff, yoff, xoff+xsize, yoff+ysize + 100), 32, None)] im.tile = [
("MOCK", (xoff, yoff, xoff+xsize, yoff+ysize + 100), 32, None)
]
self.assertRaises(ValueError, im.load) self.assertRaises(ValueError, im.load)
def test_no_format(self): def test_no_format(self):

View File

@ -67,8 +67,11 @@ class TestImageFont(PillowTestCase):
} }
def setUp(self): def setUp(self):
freetype_version = tuple(ImageFont.core.freetype2_version.split('.'))[:2] freetype_version = tuple(
self.metrics = self.METRICS.get(freetype_version, self.METRICS['Default']) ImageFont.core.freetype2_version.split('.')
)[:2]
self.metrics = self.METRICS.get(freetype_version,
self.METRICS['Default'])
def get_font(self): def get_font(self):
return ImageFont.truetype(FONT_PATH, FONT_SIZE, return ImageFont.truetype(FONT_PATH, FONT_SIZE,
@ -202,7 +205,8 @@ class TestImageFont(PillowTestCase):
target_img = Image.open(target) target_img = Image.open(target)
# Epsilon ~.5 fails with FreeType 2.7 # Epsilon ~.5 fails with FreeType 2.7
self.assert_image_similar(im, target_img, self.metrics['multiline']) self.assert_image_similar(im, target_img,
self.metrics['multiline'])
def test_unknown_align(self): def test_unknown_align(self):
im = Image.new(mode='RGB', size=(300, 100)) im = Image.new(mode='RGB', size=(300, 100))
@ -427,7 +431,8 @@ class TestImageFont(PillowTestCase):
# Make a copy of FreeTypeFont so we can patch the original # Make a copy of FreeTypeFont so we can patch the original
free_type_font = copy.deepcopy(ImageFont.FreeTypeFont) free_type_font = copy.deepcopy(ImageFont.FreeTypeFont)
with SimplePatcher(ImageFont, '_FreeTypeFont', free_type_font): with SimplePatcher(ImageFont, '_FreeTypeFont', free_type_font):
def loadable_font(filepath, size, index, encoding, *args, **kwargs): def loadable_font(filepath, size, index, encoding,
*args, **kwargs):
if filepath == path_to_fake: if filepath == path_to_fake:
return ImageFont._FreeTypeFont(FONT_PATH, size, index, return ImageFont._FreeTypeFont(FONT_PATH, size, index,
encoding, *args, **kwargs) encoding, *args, **kwargs)

View File

@ -19,7 +19,8 @@ class TestImageFontBitmap(PillowTestCase):
font='Tests/fonts/DejaVuSans-bitmap.ttf', size=24) font='Tests/fonts/DejaVuSans-bitmap.ttf', size=24)
size_outline = font_outline.getsize(text) size_outline = font_outline.getsize(text)
size_bitmap = font_bitmap.getsize(text) size_bitmap = font_bitmap.getsize(text)
size_final = max(size_outline[0], size_bitmap[0]), max(size_outline[1], size_bitmap[1]) size_final = (max(size_outline[0], size_bitmap[0]),
max(size_outline[1], size_bitmap[1]))
im_bitmap = Image.new('RGB', size_final, (255, 255, 255)) im_bitmap = Image.new('RGB', size_final, (255, 255, 255))
im_outline = im_bitmap.copy() im_outline = im_bitmap.copy()
draw_bitmap = ImageDraw.Draw(im_bitmap) draw_bitmap = ImageDraw.Draw(im_bitmap)

View File

@ -30,7 +30,8 @@ class TestImagecomplextext(PillowTestCase):
self.assert_image_similar(im, target_img, .5) self.assert_image_similar(im, target_img, .5)
def test_y_offset(self): def test_y_offset(self):
ttf = ImageFont.truetype("Tests/fonts/NotoNastaliqUrdu-Regular.ttf", FONT_SIZE) ttf = ImageFont.truetype("Tests/fonts/NotoNastaliqUrdu-Regular.ttf",
FONT_SIZE)
im = Image.new(mode='RGB', size=(300, 100)) im = Image.new(mode='RGB', size=(300, 100))
draw = ImageDraw.Draw(im) draw = ImageDraw.Draw(im)
@ -70,7 +71,8 @@ class TestImagecomplextext(PillowTestCase):
im = Image.new(mode='RGB', size=(300, 100)) im = Image.new(mode='RGB', size=(300, 100))
draw = ImageDraw.Draw(im) draw = ImageDraw.Draw(im)
draw.text((0, 0), 'سلطنة عمان Oman', font=ttf, fill=500, direction='ltr') draw.text((0, 0), 'سلطنة عمان Oman',
font=ttf, fill=500, direction='ltr')
target = 'Tests/images/test_direction_ltr.png' target = 'Tests/images/test_direction_ltr.png'
target_img = Image.open(target) target_img = Image.open(target)
@ -82,7 +84,8 @@ class TestImagecomplextext(PillowTestCase):
im = Image.new(mode='RGB', size=(300, 100)) im = Image.new(mode='RGB', size=(300, 100))
draw = ImageDraw.Draw(im) draw = ImageDraw.Draw(im)
draw.text((0, 0), 'Oman سلطنة عمان', font=ttf, fill=500, direction='rtl') draw.text((0, 0), 'Oman سلطنة عمان',
font=ttf, fill=500, direction='rtl')
target = 'Tests/images/test_direction_ltr.png' target = 'Tests/images/test_direction_ltr.png'
target_img = Image.open(target) target_img = Image.open(target)
@ -120,7 +123,8 @@ class TestImagecomplextext(PillowTestCase):
im = Image.new(mode='RGB', size=(300, 100)) im = Image.new(mode='RGB', size=(300, 100))
draw = ImageDraw.Draw(im) draw = ImageDraw.Draw(im)
draw.text((0, 0), 'اللغة العربية', font=ttf, fill=500, features=['-fina', '-init', '-medi']) draw.text((0, 0), 'اللغة العربية', font=ttf, fill=500,
features=['-fina', '-init', '-medi'])
target = 'Tests/images/test_arabictext_features.png' target = 'Tests/images/test_arabictext_features.png'
target_img = Image.open(target) target_img = Image.open(target)

View File

@ -265,8 +265,9 @@ class MorphTests(PillowTestCase):
# Act / Assert # Act / Assert
with self.assertRaises(Exception) as e: with self.assertRaises(Exception) as e:
lb.build_lut() lb.build_lut()
self.assertEqual(str(e.exception), self.assertEqual(
'Syntax error in pattern "a pattern with a syntax error"') str(e.exception),
'Syntax error in pattern "a pattern with a syntax error"')
def test_load_invalid_mrl(self): def test_load_invalid_mrl(self):
# Arrange # Arrange

View File

@ -34,7 +34,8 @@ class TestLibPack(PillowTestCase):
self.assert_pack("1", "1;R", b'\xaa', 0, X, 0, X, 0, X, 0, X) self.assert_pack("1", "1;R", b'\xaa', 0, X, 0, X, 0, X, 0, X)
self.assert_pack("1", "1;IR", b'\xaa', X, 0, X, 0, X, 0, X, 0) self.assert_pack("1", "1;IR", b'\xaa', X, 0, X, 0, X, 0, X, 0)
self.assert_pack("1", "L", b'\xff\x00\x00\xff\x00\x00', X, 0, 0, X, 0, 0) self.assert_pack(
"1", "L", b'\xff\x00\x00\xff\x00\x00', X, 0, 0, X, 0, 0)
def test_L(self): def test_L(self):
self.assert_pack("L", "L", 1, 1, 2, 3, 4) self.assert_pack("L", "L", 1, 1, 2, 3, 4)
@ -80,8 +81,10 @@ class TestLibPack(PillowTestCase):
"RGBA", "RGBA", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) "RGBA", "RGBA", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12))
self.assert_pack( self.assert_pack(
"RGBA", "RGBA;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) "RGBA", "RGBA;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12))
self.assert_pack("RGBA", "RGB", 3, (1, 2, 3, 14), (4, 5, 6, 15), (7, 8, 9, 16)) self.assert_pack(
self.assert_pack("RGBA", "BGR", 3, (3, 2, 1, 14), (6, 5, 4, 15), (9, 8, 7, 16)) "RGBA", "RGB", 3, (1, 2, 3, 14), (4, 5, 6, 15), (7, 8, 9, 16))
self.assert_pack(
"RGBA", "BGR", 3, (3, 2, 1, 14), (6, 5, 4, 15), (9, 8, 7, 16))
self.assert_pack( self.assert_pack(
"RGBA", "BGRA", 4, "RGBA", "BGRA", 4,
(3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12)) (3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12))
@ -90,10 +93,14 @@ class TestLibPack(PillowTestCase):
self.assert_pack( self.assert_pack(
"RGBA", "BGRa", 4, "RGBA", "BGRa", 4,
(191, 127, 63, 4), (223, 191, 159, 8), (233, 212, 191, 12)) (191, 127, 63, 4), (223, 191, 159, 8), (233, 212, 191, 12))
self.assert_pack("RGBA", "R", 1, (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0)) self.assert_pack(
self.assert_pack("RGBA", "G", 1, (6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9)) "RGBA", "R", 1, (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0))
self.assert_pack("RGBA", "B", 1, (6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9)) self.assert_pack(
self.assert_pack("RGBA", "A", 1, (6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3)) "RGBA", "G", 1, (6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9))
self.assert_pack(
"RGBA", "B", 1, (6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9))
self.assert_pack(
"RGBA", "A", 1, (6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3))
def test_RGBa(self): def test_RGBa(self):
self.assert_pack( self.assert_pack(
@ -104,10 +111,14 @@ class TestLibPack(PillowTestCase):
"RGBa", "aBGR", 4, (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9)) "RGBa", "aBGR", 4, (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9))
def test_RGBX(self): def test_RGBX(self):
self.assert_pack("RGBX", "RGBX", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) self.assert_pack(
self.assert_pack("RGBX", "RGBX;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) "RGBX", "RGBX", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12))
self.assert_pack("RGBX", "RGB", 3, (1, 2, 3, X), (4, 5, 6, X), (7, 8, 9, X)) self.assert_pack(
self.assert_pack("RGBX", "BGR", 3, (3, 2, 1, X), (6, 5, 4, X), (9, 8, 7, X)) "RGBX", "RGBX;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12))
self.assert_pack(
"RGBX", "RGB", 3, (1, 2, 3, X), (4, 5, 6, X), (7, 8, 9, X))
self.assert_pack(
"RGBX", "BGR", 3, (3, 2, 1, X), (6, 5, 4, X), (9, 8, 7, X))
self.assert_pack( self.assert_pack(
"RGBX", "BGRX", "RGBX", "BGRX",
b'\x01\x02\x03\x00\x05\x06\x07\x00\t\n\x0b\x00', b'\x01\x02\x03\x00\x05\x06\x07\x00\t\n\x0b\x00',
@ -116,23 +127,30 @@ class TestLibPack(PillowTestCase):
"RGBX", "XBGR", "RGBX", "XBGR",
b'\x00\x02\x03\x04\x00\x06\x07\x08\x00\n\x0b\x0c', b'\x00\x02\x03\x04\x00\x06\x07\x08\x00\n\x0b\x0c',
(4, 3, 2, X), (8, 7, 6, X), (12, 11, 10, X)) (4, 3, 2, X), (8, 7, 6, X), (12, 11, 10, X))
self.assert_pack("RGBX", "R", 1, (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0)) self.assert_pack("RGBX", "R", 1,
self.assert_pack("RGBX", "G", 1, (6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9)) (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0))
self.assert_pack("RGBX", "B", 1, (6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9)) self.assert_pack("RGBX", "G", 1,
self.assert_pack("RGBX", "X", 1, (6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3)) (6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9))
self.assert_pack("RGBX", "B", 1,
(6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9))
self.assert_pack("RGBX", "X", 1,
(6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3))
def test_CMYK(self): def test_CMYK(self):
self.assert_pack("CMYK", "CMYK", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) self.assert_pack("CMYK", "CMYK", 4,
(1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12))
self.assert_pack( self.assert_pack(
"CMYK", "CMYK;I", 4, "CMYK", "CMYK;I", 4,
(254, 253, 252, 251), (250, 249, 248, 247), (246, 245, 244, 243)) (254, 253, 252, 251), (250, 249, 248, 247), (246, 245, 244, 243))
self.assert_pack( self.assert_pack(
"CMYK", "CMYK;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) "CMYK", "CMYK;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12))
self.assert_pack("CMYK", "K", 1, (6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3)) self.assert_pack("CMYK", "K", 1,
(6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3))
def test_YCbCr(self): def test_YCbCr(self):
self.assert_pack("YCbCr", "YCbCr", 3, (1, 2, 3), (4, 5, 6), (7, 8, 9)) self.assert_pack("YCbCr", "YCbCr", 3, (1, 2, 3), (4, 5, 6), (7, 8, 9))
self.assert_pack("YCbCr", "YCbCr;L", 3, (1, 4, 7), (2, 5, 8), (3, 6, 9)) self.assert_pack("YCbCr", "YCbCr;L", 3,
(1, 4, 7), (2, 5, 8), (3, 6, 9))
self.assert_pack( self.assert_pack(
"YCbCr", "YCbCrX", "YCbCr", "YCbCrX",
b'\x01\x02\x03\xff\x05\x06\x07\xff\t\n\x0b\xff', b'\x01\x02\x03\xff\x05\x06\x07\xff\t\n\x0b\xff',
@ -141,9 +159,12 @@ class TestLibPack(PillowTestCase):
"YCbCr", "YCbCrK", "YCbCr", "YCbCrK",
b'\x01\x02\x03\xff\x05\x06\x07\xff\t\n\x0b\xff', b'\x01\x02\x03\xff\x05\x06\x07\xff\t\n\x0b\xff',
(1, 2, 3), (5, 6, 7), (9, 10, 11)) (1, 2, 3), (5, 6, 7), (9, 10, 11))
self.assert_pack("YCbCr", "Y", 1, (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0)) self.assert_pack("YCbCr", "Y", 1,
self.assert_pack("YCbCr", "Cb", 1, (6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9)) (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0))
self.assert_pack("YCbCr", "Cr", 1, (6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9)) self.assert_pack("YCbCr", "Cb", 1,
(6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9))
self.assert_pack("YCbCr", "Cr", 1,
(6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9))
def test_LAB(self): def test_LAB(self):
self.assert_pack( self.assert_pack(
@ -269,8 +290,10 @@ class TestLibUnpack(PillowTestCase):
self.assert_unpack("RGB", "RGBX", 4, (1, 2, 3), (5, 6, 7), (9, 10, 11)) self.assert_unpack("RGB", "RGBX", 4, (1, 2, 3), (5, 6, 7), (9, 10, 11))
self.assert_unpack("RGB", "RGBX;L", 4, (1, 4, 7), (2, 5, 8), (3, 6, 9)) self.assert_unpack("RGB", "RGBX;L", 4, (1, 4, 7), (2, 5, 8), (3, 6, 9))
self.assert_unpack("RGB", "BGRX", 4, (3, 2, 1), (7, 6, 5), (11, 10, 9)) self.assert_unpack("RGB", "BGRX", 4, (3, 2, 1), (7, 6, 5), (11, 10, 9))
self.assert_unpack("RGB", "XRGB", 4, (2, 3, 4), (6, 7, 8), (10, 11, 12)) self.assert_unpack(
self.assert_unpack("RGB", "XBGR", 4, (4, 3, 2), (8, 7, 6), (12, 11, 10)) "RGB", "XRGB", 4, (2, 3, 4), (6, 7, 8), (10, 11, 12))
self.assert_unpack(
"RGB", "XBGR", 4, (4, 3, 2), (8, 7, 6), (12, 11, 10))
self.assert_unpack( self.assert_unpack(
"RGB", "YCC;P", "RGB", "YCC;P",
b'D]\x9c\x82\x1a\x91\xfaOC\xe7J\x12', # random data b'D]\x9c\x82\x1a\x91\xfaOC\xe7J\x12', # random data
@ -280,7 +303,8 @@ class TestLibUnpack(PillowTestCase):
self.assert_unpack("RGB", "B", 1, (0, 0, 1), (0, 0, 2), (0, 0, 3)) self.assert_unpack("RGB", "B", 1, (0, 0, 1), (0, 0, 2), (0, 0, 3))
def test_RGBA(self): def test_RGBA(self):
self.assert_unpack("RGBA", "LA", 2, (1, 1, 1, 2), (3, 3, 3, 4), (5, 5, 5, 6)) self.assert_unpack(
"RGBA", "LA", 2, (1, 1, 1, 2), (3, 3, 3, 4), (5, 5, 5, 6))
self.assert_unpack( self.assert_unpack(
"RGBA", "LA;16B", 4, (1, 1, 1, 3), (5, 5, 5, 7), (9, 9, 9, 11)) "RGBA", "LA;16B", 4, (1, 1, 1, 3), (5, 5, 5, 7), (9, 9, 9, 11))
self.assert_unpack( self.assert_unpack(
@ -322,9 +346,12 @@ class TestLibUnpack(PillowTestCase):
"RGBA", "RGBA;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) "RGBA", "RGBA;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12))
self.assert_unpack("RGBA", "RGBA;15", 2, (8, 131, 0, 0), (24, 0, 8, 0)) self.assert_unpack("RGBA", "RGBA;15", 2, (8, 131, 0, 0), (24, 0, 8, 0))
self.assert_unpack("RGBA", "BGRA;15", 2, (0, 131, 8, 0), (8, 0, 24, 0)) self.assert_unpack("RGBA", "BGRA;15", 2, (0, 131, 8, 0), (8, 0, 24, 0))
self.assert_unpack("RGBA", "RGBA;4B", 2, (17, 0, 34, 0), (51, 0, 68, 0)) self.assert_unpack(
self.assert_unpack("RGBA", "RGBA;16L", 8, (2, 4, 6, 8), (10, 12, 14, 16)) "RGBA", "RGBA;4B", 2, (17, 0, 34, 0), (51, 0, 68, 0))
self.assert_unpack("RGBA", "RGBA;16B", 8, (1, 3, 5, 7), (9, 11, 13, 15)) self.assert_unpack(
"RGBA", "RGBA;16L", 8, (2, 4, 6, 8), (10, 12, 14, 16))
self.assert_unpack(
"RGBA", "RGBA;16B", 8, (1, 3, 5, 7), (9, 11, 13, 15))
self.assert_unpack( self.assert_unpack(
"RGBA", "BGRA", 4, (3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12)) "RGBA", "BGRA", 4, (3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12))
self.assert_unpack( self.assert_unpack(
@ -335,10 +362,14 @@ class TestLibUnpack(PillowTestCase):
"RGBA", "YCCA;P", "RGBA", "YCCA;P",
b']bE\x04\xdd\xbej\xed57T\xce\xac\xce:\x11', # random data b']bE\x04\xdd\xbej\xed57T\xce\xac\xce:\x11', # random data
(0, 161, 0, 4), (255, 255, 255, 237), (27, 158, 0, 206), (0, 118, 0, 17)) (0, 161, 0, 4), (255, 255, 255, 237), (27, 158, 0, 206), (0, 118, 0, 17))
self.assert_unpack("RGBA", "R", 1, (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0)) self.assert_unpack(
self.assert_unpack("RGBA", "G", 1, (0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0)) "RGBA", "R", 1, (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0))
self.assert_unpack("RGBA", "B", 1, (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0)) self.assert_unpack(
self.assert_unpack("RGBA", "A", 1, (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3)) "RGBA", "G", 1, (0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0))
self.assert_unpack(
"RGBA", "B", 1, (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0))
self.assert_unpack(
"RGBA", "A", 1, (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3))
def test_RGBa(self): def test_RGBa(self):
self.assert_unpack( self.assert_unpack(
@ -351,10 +382,13 @@ class TestLibUnpack(PillowTestCase):
"RGBa", "aBGR", 4, (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9)) "RGBa", "aBGR", 4, (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9))
def test_RGBX(self): def test_RGBX(self):
self.assert_unpack("RGBX", "RGB", 3, (1, 2, 3, X), (4, 5, 6, X), (7, 8, 9, X)) self.assert_unpack("RGBX", "RGB", 3,
self.assert_unpack("RGBX", "RGB;L", 3, (1, 4, 7, X), (2, 5, 8, X), (3, 6, 9, X)) (1, 2, 3, X), (4, 5, 6, X), (7, 8, 9, X))
self.assert_unpack("RGBX", "RGB;L", 3,
(1, 4, 7, X), (2, 5, 8, X), (3, 6, 9, X))
self.assert_unpack("RGBX", "RGB;16B", 6, (1, 3, 5, X), (7, 9, 11, X)) self.assert_unpack("RGBX", "RGB;16B", 6, (1, 3, 5, X), (7, 9, 11, X))
self.assert_unpack("RGBX", "BGR", 3, (3, 2, 1, X), (6, 5, 4, X), (9, 8, 7, X)) self.assert_unpack("RGBX", "BGR", 3,
(3, 2, 1, X), (6, 5, 4, X), (9, 8, 7, X))
self.assert_unpack("RGBX", "RGB;15", 2, (8, 131, 0, X), (24, 0, 8, X)) self.assert_unpack("RGBX", "RGB;15", 2, (8, 131, 0, X), (24, 0, 8, X))
self.assert_unpack("RGBX", "BGR;15", 2, (0, 131, 8, X), (8, 0, 24, X)) self.assert_unpack("RGBX", "BGR;15", 2, (0, 131, 8, X), (8, 0, 24, X))
self.assert_unpack("RGBX", "RGB;4B", 2, (17, 0, 34, X), (51, 0, 68, X)) self.assert_unpack("RGBX", "RGB;4B", 2, (17, 0, 34, X), (51, 0, 68, X))
@ -366,19 +400,28 @@ class TestLibUnpack(PillowTestCase):
"RGBX", "RGBXXX", 6, (1, 2, 3, 4), (7, 8, 9, 10), (13, 14, 15, 16)) "RGBX", "RGBXXX", 6, (1, 2, 3, 4), (7, 8, 9, 10), (13, 14, 15, 16))
self.assert_unpack( self.assert_unpack(
"RGBX", "RGBX;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) "RGBX", "RGBX;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12))
self.assert_unpack("RGBX", "RGBX;16L", 8, (2, 4, 6, 8), (10, 12, 14, 16)) self.assert_unpack("RGBX", "RGBX;16L", 8,
self.assert_unpack("RGBX", "RGBX;16B", 8, (1, 3, 5, 7), (9, 11, 13, 15)) (2, 4, 6, 8), (10, 12, 14, 16))
self.assert_unpack("RGBX", "BGRX", 4, (3, 2, 1, X), (7, 6, 5, X), (11, 10, 9, X)) self.assert_unpack("RGBX", "RGBX;16B", 8,
self.assert_unpack("RGBX", "XRGB", 4, (2, 3, 4, X), (6, 7, 8, X), (10, 11, 12, X)) (1, 3, 5, 7), (9, 11, 13, 15))
self.assert_unpack("RGBX", "XBGR", 4, (4, 3, 2, X), (8, 7, 6, X), (12, 11, 10, X)) self.assert_unpack("RGBX", "BGRX", 4,
(3, 2, 1, X), (7, 6, 5, X), (11, 10, 9, X))
self.assert_unpack("RGBX", "XRGB", 4,
(2, 3, 4, X), (6, 7, 8, X), (10, 11, 12, X))
self.assert_unpack("RGBX", "XBGR", 4,
(4, 3, 2, X), (8, 7, 6, X), (12, 11, 10, X))
self.assert_unpack( self.assert_unpack(
"RGBX", "YCC;P", "RGBX", "YCC;P",
b'D]\x9c\x82\x1a\x91\xfaOC\xe7J\x12', # random data b'D]\x9c\x82\x1a\x91\xfaOC\xe7J\x12', # random data
(127, 102, 0, X), (192, 227, 0, X), (213, 255, 170, X), (98, 255, 133, X)) (127, 102, 0, X), (192, 227, 0, X), (213, 255, 170, X), (98, 255, 133, X))
self.assert_unpack("RGBX", "R", 1, (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0)) self.assert_unpack("RGBX", "R", 1,
self.assert_unpack("RGBX", "G", 1, (0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0)) (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0))
self.assert_unpack("RGBX", "B", 1, (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0)) self.assert_unpack("RGBX", "G", 1,
self.assert_unpack("RGBX", "X", 1, (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3)) (0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0))
self.assert_unpack("RGBX", "B", 1,
(0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0))
self.assert_unpack("RGBX", "X", 1,
(0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3))
def test_CMYK(self): def test_CMYK(self):
self.assert_unpack( self.assert_unpack(
@ -392,10 +435,14 @@ class TestLibUnpack(PillowTestCase):
(254, 253, 252, 251), (250, 249, 248, 247), (246, 245, 244, 243)) (254, 253, 252, 251), (250, 249, 248, 247), (246, 245, 244, 243))
self.assert_unpack( self.assert_unpack(
"CMYK", "CMYK;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) "CMYK", "CMYK;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12))
self.assert_unpack("CMYK", "C", 1, (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0)) self.assert_unpack("CMYK", "C", 1,
self.assert_unpack("CMYK", "M", 1, (0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0)) (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0))
self.assert_unpack("CMYK", "Y", 1, (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0)) self.assert_unpack("CMYK", "M", 1,
self.assert_unpack("CMYK", "K", 1, (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3)) (0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0))
self.assert_unpack("CMYK", "Y", 1,
(0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0))
self.assert_unpack("CMYK", "K", 1,
(0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3))
self.assert_unpack( self.assert_unpack(
"CMYK", "C;I", 1, (254, 0, 0, 0), (253, 0, 0, 0), (252, 0, 0, 0)) "CMYK", "C;I", 1, (254, 0, 0, 0), (253, 0, 0, 0), (252, 0, 0, 0))
self.assert_unpack( self.assert_unpack(
@ -451,7 +498,8 @@ class TestLibUnpack(PillowTestCase):
if sys.byteorder == 'little': if sys.byteorder == 'little':
self.assert_unpack("I", "I", 4, 0x04030201, 0x08070605) self.assert_unpack("I", "I", 4, 0x04030201, 0x08070605)
self.assert_unpack("I", "I;16N", 2, 0x0201, 0x0403) self.assert_unpack("I", "I;16N", 2, 0x0201, 0x0403)
self.assert_unpack("I", "I;16NS", b'\x83\x01\x01\x83', 0x0183, -31999) self.assert_unpack("I", "I;16NS",
b'\x83\x01\x01\x83', 0x0183, -31999)
self.assert_unpack("I", "I;32N", 4, 0x04030201, 0x08070605) self.assert_unpack("I", "I;32N", 4, 0x04030201, 0x08070605)
self.assert_unpack( self.assert_unpack(
"I", "I;32NS", "I", "I;32NS",
@ -459,7 +507,8 @@ class TestLibUnpack(PillowTestCase):
else: else:
self.assert_unpack("I", "I", 4, 0x01020304, 0x05060708) self.assert_unpack("I", "I", 4, 0x01020304, 0x05060708)
self.assert_unpack("I", "I;16N", 2, 0x0102, 0x0304) self.assert_unpack("I", "I;16N", 2, 0x0102, 0x0304)
self.assert_unpack("I", "I;16NS", b'\x83\x01\x01\x83', -31999, 0x0183) self.assert_unpack("I", "I;16NS",
b'\x83\x01\x01\x83', -31999, 0x0183)
self.assert_unpack("I", "I;32N", 4, 0x01020304, 0x05060708) self.assert_unpack("I", "I;32N", 4, 0x01020304, 0x05060708)
self.assert_unpack( self.assert_unpack(
"I", "I;32NS", "I", "I;32NS",
@ -483,14 +532,18 @@ class TestLibUnpack(PillowTestCase):
if sys.byteorder == 'little': if sys.byteorder == 'little':
self.assert_unpack("F", "F;16N", 2, 0x0201, 0x0403) self.assert_unpack("F", "F;16N", 2, 0x0201, 0x0403)
self.assert_unpack("F", "F;16NS", b'\x83\x01\x01\x83', 0x0183, -31999) self.assert_unpack(
"F", "F;16NS",
b'\x83\x01\x01\x83', 0x0183, -31999)
self.assert_unpack("F", "F;32N", 4, 67305984, 134678016) self.assert_unpack("F", "F;32N", 4, 67305984, 134678016)
self.assert_unpack( self.assert_unpack(
"F", "F;32NS", "F", "F;32NS",
b'\x83\x00\x00\x01\x01\x00\x00\x83', 16777348, -2097152000) b'\x83\x00\x00\x01\x01\x00\x00\x83', 16777348, -2097152000)
else: else:
self.assert_unpack("F", "F;16N", 2, 0x0102, 0x0304) self.assert_unpack("F", "F;16N", 2, 0x0102, 0x0304)
self.assert_unpack("F", "F;16NS", b'\x83\x01\x01\x83', -31999, 0x0183) self.assert_unpack(
"F", "F;16NS",
b'\x83\x01\x01\x83', -31999, 0x0183)
self.assert_unpack("F", "F;32N", 4, 0x01020304, 0x05060708) self.assert_unpack("F", "F;32N", 4, 0x01020304, 0x05060708)
self.assert_unpack( self.assert_unpack(
"F", "F;32NS", "F", "F;32NS",

View File

@ -4,7 +4,8 @@ import sys
from PIL import Image from PIL import Image
@unittest.skipIf(sys.platform.startswith('win32'), "Win32 does not call map_buffer") @unittest.skipIf(sys.platform.startswith('win32'),
"Win32 does not call map_buffer")
class TestMap(PillowTestCase): class TestMap(PillowTestCase):
def test_overflow(self): def test_overflow(self):
# There is the potential to overflow comparisons in map.c # There is the potential to overflow comparisons in map.c

View File

@ -124,7 +124,9 @@ class TestNumpy(PillowTestCase):
def test_save_tiff_uint16(self): def test_save_tiff_uint16(self):
# Tests that we're getting the pixel value in the right byte order. # Tests that we're getting the pixel value in the right byte order.
pixel_value = 0x1234 pixel_value = 0x1234
a = numpy.array([pixel_value] * TEST_IMAGE_SIZE[0] * TEST_IMAGE_SIZE[1], dtype=numpy.uint16) a = numpy.array(
[pixel_value] * TEST_IMAGE_SIZE[0] * TEST_IMAGE_SIZE[1],
dtype=numpy.uint16)
a.shape = TEST_IMAGE_SIZE a.shape = TEST_IMAGE_SIZE
img = Image.fromarray(a) img = Image.fromarray(a)

View File

@ -1,6 +1,8 @@
from helper import unittest, PillowTestCase from helper import unittest, PillowTestCase
from PIL.PdfParser import IndirectObjectDef, IndirectReference, PdfBinary, PdfDict, PdfFormatError, PdfName, PdfParser, PdfStream, decode_text, encode_text, pdf_repr from PIL.PdfParser import IndirectObjectDef, IndirectReference, PdfBinary, \
PdfDict, PdfFormatError, PdfName, PdfParser, \
PdfStream, decode_text, encode_text, pdf_repr
class TestPdfParser(PillowTestCase): class TestPdfParser(PillowTestCase):
@ -22,23 +24,35 @@ class TestPdfParser(PillowTestCase):
self.assertNotEqual(IndirectObjectDef(1, 2), (1, 2)) self.assertNotEqual(IndirectObjectDef(1, 2), (1, 2))
def test_parsing(self): def test_parsing(self):
self.assertEqual(PdfParser.interpret_name(b"Name#23Hash"), b"Name#Hash") self.assertEqual(PdfParser.interpret_name(b"Name#23Hash"),
b"Name#Hash")
self.assertEqual(PdfParser.interpret_name(b"Name#23Hash", as_text=True), "Name#Hash") self.assertEqual(PdfParser.interpret_name(b"Name#23Hash", as_text=True), "Name#Hash")
self.assertEqual(PdfParser.get_value(b"1 2 R ", 0), (IndirectReference(1, 2), 5)) self.assertEqual(PdfParser.get_value(b"1 2 R ", 0),
(IndirectReference(1, 2), 5))
self.assertEqual(PdfParser.get_value(b"true[", 0), (True, 4)) self.assertEqual(PdfParser.get_value(b"true[", 0), (True, 4))
self.assertEqual(PdfParser.get_value(b"false%", 0), (False, 5)) self.assertEqual(PdfParser.get_value(b"false%", 0), (False, 5))
self.assertEqual(PdfParser.get_value(b"null<", 0), (None, 4)) self.assertEqual(PdfParser.get_value(b"null<", 0), (None, 4))
self.assertEqual(PdfParser.get_value(b"%cmt\n %cmt\n 123\n", 0), (123, 15)) self.assertEqual(PdfParser.get_value(b"%cmt\n %cmt\n 123\n", 0),
self.assertEqual(PdfParser.get_value(b"<901FA3>", 0), (b"\x90\x1F\xA3", 8)) (123, 15))
self.assertEqual(PdfParser.get_value(b"asd < 9 0 1 f A > qwe", 3), (b"\x90\x1F\xA0", 17)) self.assertEqual(PdfParser.get_value(b"<901FA3>", 0),
(b"\x90\x1F\xA3", 8))
self.assertEqual(PdfParser.get_value(b"asd < 9 0 1 f A > qwe", 3),
(b"\x90\x1F\xA0", 17))
self.assertEqual(PdfParser.get_value(b"(asd)", 0), (b"asd", 5)) self.assertEqual(PdfParser.get_value(b"(asd)", 0), (b"asd", 5))
self.assertEqual(PdfParser.get_value(b"(asd(qwe)zxc)zzz(aaa)", 0), (b"asd(qwe)zxc", 13)) self.assertEqual(PdfParser.get_value(b"(asd(qwe)zxc)zzz(aaa)", 0),
self.assertEqual(PdfParser.get_value(b"(Two \\\nwords.)", 0), (b"Two words.", 14)) (b"asd(qwe)zxc", 13))
self.assertEqual(PdfParser.get_value(b"(Two\nlines.)", 0), (b"Two\nlines.", 12)) self.assertEqual(PdfParser.get_value(b"(Two \\\nwords.)", 0),
self.assertEqual(PdfParser.get_value(b"(Two\r\nlines.)", 0), (b"Two\nlines.", 13)) (b"Two words.", 14))
self.assertEqual(PdfParser.get_value(b"(Two\\nlines.)", 0), (b"Two\nlines.", 13)) self.assertEqual(PdfParser.get_value(b"(Two\nlines.)", 0),
self.assertEqual(PdfParser.get_value(b"(One\\(paren).", 0), (b"One(paren", 12)) (b"Two\nlines.", 12))
self.assertEqual(PdfParser.get_value(b"(One\\)paren).", 0), (b"One)paren", 12)) self.assertEqual(PdfParser.get_value(b"(Two\r\nlines.)", 0),
(b"Two\nlines.", 13))
self.assertEqual(PdfParser.get_value(b"(Two\\nlines.)", 0),
(b"Two\nlines.", 13))
self.assertEqual(PdfParser.get_value(b"(One\\(paren).", 0),
(b"One(paren", 12))
self.assertEqual(PdfParser.get_value(b"(One\\)paren).", 0),
(b"One)paren", 12))
self.assertEqual(PdfParser.get_value(b"(\\0053)", 0), (b"\x053", 7)) self.assertEqual(PdfParser.get_value(b"(\\0053)", 0), (b"\x053", 7))
self.assertEqual(PdfParser.get_value(b"(\\053)", 0), (b"\x2B", 6)) self.assertEqual(PdfParser.get_value(b"(\\053)", 0), (b"\x2B", 6))
self.assertEqual(PdfParser.get_value(b"(\\53)", 0), (b"\x2B", 5)) self.assertEqual(PdfParser.get_value(b"(\\53)", 0), (b"\x2B", 5))
@ -65,23 +79,30 @@ class TestPdfParser(PillowTestCase):
def test_pdf_repr(self): def test_pdf_repr(self):
self.assertEqual(bytes(IndirectReference(1, 2)), b"1 2 R") self.assertEqual(bytes(IndirectReference(1, 2)), b"1 2 R")
self.assertEqual(bytes(IndirectObjectDef(*IndirectReference(1, 2))), b"1 2 obj") self.assertEqual(bytes(IndirectObjectDef(*IndirectReference(1, 2))),
b"1 2 obj")
self.assertEqual(bytes(PdfName(b"Name#Hash")), b"/Name#23Hash") self.assertEqual(bytes(PdfName(b"Name#Hash")), b"/Name#23Hash")
self.assertEqual(bytes(PdfName("Name#Hash")), b"/Name#23Hash") self.assertEqual(bytes(PdfName("Name#Hash")), b"/Name#23Hash")
self.assertEqual(bytes(PdfDict({b"Name": IndirectReference(1, 2)})), b"<<\n/Name 1 2 R\n>>") self.assertEqual(bytes(PdfDict({b"Name": IndirectReference(1, 2)})),
self.assertEqual(bytes(PdfDict({"Name": IndirectReference(1, 2)})), b"<<\n/Name 1 2 R\n>>") b"<<\n/Name 1 2 R\n>>")
self.assertEqual(bytes(PdfDict({"Name": IndirectReference(1, 2)})),
b"<<\n/Name 1 2 R\n>>")
self.assertEqual(pdf_repr(IndirectReference(1, 2)), b"1 2 R") self.assertEqual(pdf_repr(IndirectReference(1, 2)), b"1 2 R")
self.assertEqual(pdf_repr(IndirectObjectDef(*IndirectReference(1, 2))), b"1 2 obj") self.assertEqual(pdf_repr(IndirectObjectDef(*IndirectReference(1, 2))),
b"1 2 obj")
self.assertEqual(pdf_repr(PdfName(b"Name#Hash")), b"/Name#23Hash") self.assertEqual(pdf_repr(PdfName(b"Name#Hash")), b"/Name#23Hash")
self.assertEqual(pdf_repr(PdfName("Name#Hash")), b"/Name#23Hash") self.assertEqual(pdf_repr(PdfName("Name#Hash")), b"/Name#23Hash")
self.assertEqual(pdf_repr(PdfDict({b"Name": IndirectReference(1, 2)})), b"<<\n/Name 1 2 R\n>>") self.assertEqual(pdf_repr(PdfDict({b"Name": IndirectReference(1, 2)})),
self.assertEqual(pdf_repr(PdfDict({"Name": IndirectReference(1, 2)})), b"<<\n/Name 1 2 R\n>>") b"<<\n/Name 1 2 R\n>>")
self.assertEqual(pdf_repr(PdfDict({"Name": IndirectReference(1, 2)})),
b"<<\n/Name 1 2 R\n>>")
self.assertEqual(pdf_repr(123), b"123") self.assertEqual(pdf_repr(123), b"123")
self.assertEqual(pdf_repr(True), b"true") self.assertEqual(pdf_repr(True), b"true")
self.assertEqual(pdf_repr(False), b"false") self.assertEqual(pdf_repr(False), b"false")
self.assertEqual(pdf_repr(None), b"null") self.assertEqual(pdf_repr(None), b"null")
self.assertEqual(pdf_repr(b"a)/b\\(c"), br"(a\)/b\\\(c)") self.assertEqual(pdf_repr(b"a)/b\\(c"), br"(a\)/b\\\(c)")
self.assertEqual(pdf_repr([123, True, {"a": PdfName(b"b")}]), b"[ 123 true <<\n/a /b\n>> ]") self.assertEqual(pdf_repr([123, True, {"a": PdfName(b"b")}]),
b"[ 123 true <<\n/a /b\n>> ]")
self.assertEqual(pdf_repr(PdfBinary(b"\x90\x1F\xA0")), b"<901FA0>") self.assertEqual(pdf_repr(PdfBinary(b"\x90\x1F\xA0")), b"<901FA0>")

View File

@ -424,7 +424,8 @@ class pil_build_ext(build_ext):
best_path = None best_path = None
for name in os.listdir(program_files): for name in os.listdir(program_files):
if name.startswith('OpenJPEG '): if name.startswith('OpenJPEG '):
version = tuple(int(x) for x in name[9:].strip().split('.')) version = tuple(int(x) for x in
name[9:].strip().split('.'))
if version > best_version: if version > best_version:
best_version = version best_version = version
best_path = os.path.join(program_files, name) best_path = os.path.join(program_files, name)
@ -501,7 +502,8 @@ class pil_build_ext(build_ext):
# possible. # possible.
_add_directory(self.compiler.include_dirs, best_path, 0) _add_directory(self.compiler.include_dirs, best_path, 0)
feature.jpeg2000 = 'openjp2' feature.jpeg2000 = 'openjp2'
feature.openjpeg_version = '.'.join(str(x) for x in best_version) feature.openjpeg_version = '.'.join(str(x) for x in
best_version)
if feature.want('imagequant'): if feature.want('imagequant'):
_dbg('Looking for imagequant') _dbg('Looking for imagequant')
@ -516,7 +518,8 @@ class pil_build_ext(build_ext):
if _find_include_file(self, 'tiff.h'): if _find_include_file(self, 'tiff.h'):
if _find_library_file(self, "tiff"): if _find_library_file(self, "tiff"):
feature.tiff = "tiff" feature.tiff = "tiff"
if sys.platform == "win32" and _find_library_file(self, "libtiff"): if (sys.platform == "win32" and
_find_library_file(self, "libtiff")):
feature.tiff = "libtiff" feature.tiff = "libtiff"
if (sys.platform == "darwin" and if (sys.platform == "darwin" and
_find_library_file(self, "libtiff")): _find_library_file(self, "libtiff")):
@ -528,14 +531,16 @@ class pil_build_ext(build_ext):
# look for freetype2 include files # look for freetype2 include files
freetype_version = 0 freetype_version = 0
for subdir in self.compiler.include_dirs: for subdir in self.compiler.include_dirs:
_dbg('Checking for include file %s in %s', ("ft2build.h", subdir)) _dbg('Checking for include file %s in %s',
("ft2build.h", subdir))
if os.path.isfile(os.path.join(subdir, "ft2build.h")): if os.path.isfile(os.path.join(subdir, "ft2build.h")):
_dbg('Found %s in %s', ("ft2build.h", subdir)) _dbg('Found %s in %s', ("ft2build.h", subdir))
freetype_version = 21 freetype_version = 21
subdir = os.path.join(subdir, "freetype2") subdir = os.path.join(subdir, "freetype2")
break break
subdir = os.path.join(subdir, "freetype2") subdir = os.path.join(subdir, "freetype2")
_dbg('Checking for include file %s in %s', ("ft2build.h", subdir)) _dbg('Checking for include file %s in %s',
("ft2build.h", subdir))
if os.path.isfile(os.path.join(subdir, "ft2build.h")): if os.path.isfile(os.path.join(subdir, "ft2build.h")):
_dbg('Found %s in %s', ("ft2build.h", subdir)) _dbg('Found %s in %s', ("ft2build.h", subdir))
freetype_version = 21 freetype_version = 21

View File

@ -60,7 +60,14 @@ class BmpImageFile(ImageFile.ImageFile):
format_description = "Windows Bitmap" format_description = "Windows Bitmap"
format = "BMP" format = "BMP"
# --------------------------------------------------- BMP Compression values # --------------------------------------------------- BMP Compression values
COMPRESSIONS = {'RAW': 0, 'RLE8': 1, 'RLE4': 2, 'BITFIELDS': 3, 'JPEG': 4, 'PNG': 5} COMPRESSIONS = {
'RAW': 0,
'RLE8': 1,
'RLE4': 2,
'BITFIELDS': 3,
'JPEG': 4,
'PNG': 5
}
RAW, RLE8, RLE4, BITFIELDS, JPEG, PNG = 0, 1, 2, 3, 4, 5 RAW, RLE8, RLE4, BITFIELDS, JPEG, PNG = 0, 1, 2, 3, 4, 5
def _bitmap(self, header=0, offset=0): def _bitmap(self, header=0, offset=0):
@ -69,10 +76,13 @@ class BmpImageFile(ImageFile.ImageFile):
if header: if header:
seek(header) seek(header)
file_info = {} file_info = {}
file_info['header_size'] = i32(read(4)) # read bmp header size @offset 14 (this is part of the header size) # read bmp header size @offset 14 (this is part of the header size)
file_info['header_size'] = i32(read(4))
file_info['direction'] = -1 file_info['direction'] = -1
# --------------------- If requested, read header at a specific position # --------------------- If requested, read header at a specific position
header_data = ImageFile._safe_read(self.fp, file_info['header_size'] - 4) # read the rest of the bmp header, without its size # read the rest of the bmp header, without its size
header_data = ImageFile._safe_read(self.fp,
file_info['header_size'] - 4)
# --------------------------------------------------- IBM OS/2 Bitmap v1 # --------------------------------------------------- IBM OS/2 Bitmap v1
# ------ This format has different offsets because of width/height types # ------ This format has different offsets because of width/height types
if file_info['header_size'] == 12: if file_info['header_size'] == 12:
@ -88,12 +98,16 @@ class BmpImageFile(ImageFile.ImageFile):
file_info['y_flip'] = i8(header_data[7]) == 0xff file_info['y_flip'] = i8(header_data[7]) == 0xff
file_info['direction'] = 1 if file_info['y_flip'] else -1 file_info['direction'] = 1 if file_info['y_flip'] else -1
file_info['width'] = i32(header_data[0:4]) file_info['width'] = i32(header_data[0:4])
file_info['height'] = i32(header_data[4:8]) if not file_info['y_flip'] else 2**32 - i32(header_data[4:8]) file_info['height'] = (i32(header_data[4:8])
if not file_info['y_flip']
else 2**32 - i32(header_data[4:8]))
file_info['planes'] = i16(header_data[8:10]) file_info['planes'] = i16(header_data[8:10])
file_info['bits'] = i16(header_data[10:12]) file_info['bits'] = i16(header_data[10:12])
file_info['compression'] = i32(header_data[12:16]) file_info['compression'] = i32(header_data[12:16])
file_info['data_size'] = i32(header_data[16:20]) # byte size of pixel data # byte size of pixel data
file_info['pixels_per_meter'] = (i32(header_data[20:24]), i32(header_data[24:28])) file_info['data_size'] = i32(header_data[16:20])
file_info['pixels_per_meter'] = (i32(header_data[20:24]),
i32(header_data[24:28]))
file_info['colors'] = i32(header_data[28:32]) file_info['colors'] = i32(header_data[28:32])
file_info['palette_padding'] = 4 file_info['palette_padding'] = 4
self.info["dpi"] = tuple( self.info["dpi"] = tuple(
@ -101,21 +115,32 @@ class BmpImageFile(ImageFile.ImageFile):
file_info['pixels_per_meter'])) file_info['pixels_per_meter']))
if file_info['compression'] == self.BITFIELDS: if file_info['compression'] == self.BITFIELDS:
if len(header_data) >= 52: if len(header_data) >= 52:
for idx, mask in enumerate(['r_mask', 'g_mask', 'b_mask', 'a_mask']): for idx, mask in enumerate(['r_mask',
'g_mask',
'b_mask',
'a_mask']):
file_info[mask] = i32(header_data[36+idx*4:40+idx*4]) file_info[mask] = i32(header_data[36+idx*4:40+idx*4])
else: else:
# 40 byte headers only have the three components in the bitfields masks, # 40 byte headers only have the three components in the
# bitfields masks,
# ref: https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx # ref: https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
# See also https://github.com/python-pillow/Pillow/issues/1293 # See also https://github.com/python-pillow/Pillow/issues/1293
# There is a 4th component in the RGBQuad, in the alpha location, but it # There is a 4th component in the RGBQuad, in the alpha
# is listed as a reserved component, and it is not generally an alpha channel # location, but it is listed as a reserved component,
# and it is not generally an alpha channel
file_info['a_mask'] = 0x0 file_info['a_mask'] = 0x0
for mask in ['r_mask', 'g_mask', 'b_mask']: for mask in ['r_mask', 'g_mask', 'b_mask']:
file_info[mask] = i32(read(4)) file_info[mask] = i32(read(4))
file_info['rgb_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask']) file_info['rgb_mask'] = (file_info['r_mask'],
file_info['rgba_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'], file_info['a_mask']) file_info['g_mask'],
file_info['b_mask'])
file_info['rgba_mask'] = (file_info['r_mask'],
file_info['g_mask'],
file_info['b_mask'],
file_info['a_mask'])
else: else:
raise IOError("Unsupported BMP header type (%d)" % file_info['header_size']) raise IOError("Unsupported BMP header type (%d)" %
file_info['header_size'])
# ------------------ Special case : header is reported 40, which # ------------------ Special case : header is reported 40, which
# ---------------------- is shorter than real size for bpp >= 16 # ---------------------- is shorter than real size for bpp >= 16
self.size = file_info['width'], file_info['height'] self.size = file_info['width'], file_info['height']
@ -127,11 +152,15 @@ class BmpImageFile(ImageFile.ImageFile):
# ----------------------- Check bit depth for unusual unsupported values # ----------------------- Check bit depth for unusual unsupported values
self.mode, raw_mode = BIT2MODE.get(file_info['bits'], (None, None)) self.mode, raw_mode = BIT2MODE.get(file_info['bits'], (None, None))
if self.mode is None: if self.mode is None:
raise IOError("Unsupported BMP pixel depth (%d)" % file_info['bits']) raise IOError("Unsupported BMP pixel depth (%d)"
% file_info['bits'])
# ----------------- Process BMP with Bitfields compression (not palette) # ----------------- Process BMP with Bitfields compression (not palette)
if file_info['compression'] == self.BITFIELDS: if file_info['compression'] == self.BITFIELDS:
SUPPORTED = { SUPPORTED = {
32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0), (0xff000000, 0xff0000, 0xff00, 0x0)], 32: [(0xff0000, 0xff00, 0xff, 0x0),
(0xff0000, 0xff00, 0xff, 0xff000000),
(0x0, 0x0, 0x0, 0x0),
(0xff000000, 0xff0000, 0xff00, 0x0)],
24: [(0xff0000, 0xff00, 0xff)], 24: [(0xff0000, 0xff00, 0xff)],
16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)] 16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]
} }
@ -145,11 +174,15 @@ class BmpImageFile(ImageFile.ImageFile):
(16, (0x7c00, 0x3e0, 0x1f)): "BGR;15" (16, (0x7c00, 0x3e0, 0x1f)): "BGR;15"
} }
if file_info['bits'] in SUPPORTED: if file_info['bits'] in SUPPORTED:
if file_info['bits'] == 32 and file_info['rgba_mask'] in SUPPORTED[file_info['bits']]: if file_info['bits'] == 32 and \
file_info['rgba_mask'] in SUPPORTED[file_info['bits']]:
raw_mode = MASK_MODES[(file_info['bits'], file_info['rgba_mask'])] raw_mode = MASK_MODES[(file_info['bits'], file_info['rgba_mask'])]
self.mode = "RGBA" if raw_mode in ("BGRA",) else self.mode self.mode = "RGBA" if raw_mode in ("BGRA",) else self.mode
elif file_info['bits'] in (24, 16) and file_info['rgb_mask'] in SUPPORTED[file_info['bits']]: elif (file_info['bits'] in (24, 16) and
raw_mode = MASK_MODES[(file_info['bits'], file_info['rgb_mask'])] file_info['rgb_mask'] in SUPPORTED[file_info['bits']]):
raw_mode = MASK_MODES[
(file_info['bits'], file_info['rgb_mask'])
]
else: else:
raise IOError("Unsupported BMP bitfields layout") raise IOError("Unsupported BMP bitfields layout")
else: else:
@ -158,17 +191,20 @@ class BmpImageFile(ImageFile.ImageFile):
if file_info['bits'] == 32 and header == 22: # 32-bit .cur offset if file_info['bits'] == 32 and header == 22: # 32-bit .cur offset
raw_mode, self.mode = "BGRA", "RGBA" raw_mode, self.mode = "BGRA", "RGBA"
else: else:
raise IOError("Unsupported BMP compression (%d)" % file_info['compression']) raise IOError("Unsupported BMP compression (%d)" %
file_info['compression'])
# ---------------- Once the header is processed, process the palette/LUT # ---------------- Once the header is processed, process the palette/LUT
if self.mode == "P": # Paletted for 1, 4 and 8 bit images if self.mode == "P": # Paletted for 1, 4 and 8 bit images
# ----------------------------------------------------- 1-bit images # ----------------------------------------------------- 1-bit images
if not (0 < file_info['colors'] <= 65536): if not (0 < file_info['colors'] <= 65536):
raise IOError("Unsupported BMP Palette size (%d)" % file_info['colors']) raise IOError("Unsupported BMP Palette size (%d)" %
file_info['colors'])
else: else:
padding = file_info['palette_padding'] padding = file_info['palette_padding']
palette = read(padding * file_info['colors']) palette = read(padding * file_info['colors'])
greyscale = True greyscale = True
indices = (0, 255) if file_info['colors'] == 2 else list(range(file_info['colors'])) indices = (0, 255) if file_info['colors'] == 2 else \
list(range(file_info['colors']))
# ------------------ Check if greyscale and ignore palette if so # ------------------ Check if greyscale and ignore palette if so
for ind, val in enumerate(indices): for ind, val in enumerate(indices):
rgb = palette[ind*padding:ind*padding + 3] rgb = palette[ind*padding:ind*padding + 3]
@ -180,13 +216,19 @@ class BmpImageFile(ImageFile.ImageFile):
raw_mode = self.mode raw_mode = self.mode
else: else:
self.mode = "P" self.mode = "P"
self.palette = ImagePalette.raw("BGRX" if padding == 4 else "BGR", palette) self.palette = ImagePalette.raw(
"BGRX" if padding == 4 else "BGR", palette)
# ----------------------------- Finally set the tile data for the plugin # ----------------------------- Finally set the tile data for the plugin
self.info['compression'] = file_info['compression'] self.info['compression'] = file_info['compression']
self.tile = [('raw', (0, 0, file_info['width'], file_info['height']), offset or self.fp.tell(), self.tile = [
(raw_mode, ((file_info['width'] * file_info['bits'] + 31) >> 3) & (~3), file_info['direction']) ('raw',
)] (0, 0, file_info['width'], file_info['height']),
offset or self.fp.tell(),
(raw_mode,
((file_info['width'] * file_info['bits'] + 31) >> 3) & (~3),
file_info['direction']))
]
def _open(self): def _open(self):
""" Open file, check magic number and read header """ """ Open file, check magic number and read header """

View File

@ -142,7 +142,8 @@ class DdsImageFile(ImageFile.ImageFile):
# ignoring flags which pertain to volume textures and cubemaps # ignoring flags which pertain to volume textures and cubemaps
dxt10 = BytesIO(self.fp.read(20)) dxt10 = BytesIO(self.fp.read(20))
dxgi_format, dimension = struct.unpack("<II", dxt10.read(8)) dxgi_format, dimension = struct.unpack("<II", dxt10.read(8))
if dxgi_format in (DXGI_FORMAT_BC7_TYPELESS, DXGI_FORMAT_BC7_UNORM): if dxgi_format in (DXGI_FORMAT_BC7_TYPELESS,
DXGI_FORMAT_BC7_UNORM):
self.pixel_format = "BC7" self.pixel_format = "BC7"
n = 7 n = 7
elif dxgi_format == DXGI_FORMAT_BC7_UNORM_SRGB: elif dxgi_format == DXGI_FORMAT_BC7_UNORM_SRGB:

View File

@ -9,7 +9,8 @@ Full text of the CC0 license:
Independence War 2: Edge Of Chaos - Texture File Format - 16 October 2001 Independence War 2: Edge Of Chaos - Texture File Format - 16 October 2001
The textures used for 3D objects in Independence War 2: Edge Of Chaos are in a The textures used for 3D objects in Independence War 2: Edge Of Chaos are in a
packed custom format called FTEX. This file format uses file extensions FTC and FTU. packed custom format called FTEX. This file format uses file extensions FTC
and FTU.
* FTC files are compressed textures (using standard texture compression). * FTC files are compressed textures (using standard texture compression).
* FTU files are not compressed. * FTU files are not compressed.
Texture File Format Texture File Format
@ -24,18 +25,21 @@ Where:
* The "magic" number is "FTEX". * The "magic" number is "FTEX".
* "width" and "height" are the dimensions of the texture. * "width" and "height" are the dimensions of the texture.
* "mipmap_count" is the number of mipmaps in the texture. * "mipmap_count" is the number of mipmaps in the texture.
* "format_count" is the number of texture formats (different versions of the same texture) in this file. * "format_count" is the number of texture formats (different versions of the
same texture) in this file.
{format_directory} = format_count * { u32:format, u32:where } {format_directory} = format_count * { u32:format, u32:where }
The format value is 0 for DXT1 compressed textures and 1 for 24-bit RGB uncompressed textures. The format value is 0 for DXT1 compressed textures and 1 for 24-bit RGB
uncompressed textures.
The texture data for a format starts at the position "where" in the file. The texture data for a format starts at the position "where" in the file.
Each set of texture data in the file has the following structure: Each set of texture data in the file has the following structure:
{data} = format_count * { u32:mipmap_size, mipmap_size * { u8 } } {data} = format_count * { u32:mipmap_size, mipmap_size * { u8 } }
* "mipmap_size" is the number of bytes in that mip level. For compressed textures this is the * "mipmap_size" is the number of bytes in that mip level. For compressed
size of the texture data compressed with DXT1. For 24 bit uncompressed textures, this is 3 * width * height. textures this is the size of the texture data compressed with DXT1. For 24 bit
Following this are the image bytes for that mipmap level. uncompressed textures, this is 3 * width * height. Following this are the image
bytes for that mipmap level.
Note: All data is stored in little-Endian (Intel) byte order. Note: All data is stored in little-Endian (Intel) byte order.
""" """
@ -62,7 +66,8 @@ class FtexImageFile(ImageFile.ImageFile):
self.mode = "RGB" self.mode = "RGB"
# Only support single-format files. I don't know of any multi-format file. # Only support single-format files.
# I don't know of any multi-format file.
assert format_count == 1 assert format_count == 1
format, where = struct.unpack("<2i", self.fp.read(8)) format, where = struct.unpack("<2i", self.fp.read(8))
@ -77,7 +82,8 @@ class FtexImageFile(ImageFile.ImageFile):
elif format == FORMAT_UNCOMPRESSED: elif format == FORMAT_UNCOMPRESSED:
self.tile = [("raw", (0, 0) + self.size, 0, ('RGB', 0, 1))] self.tile = [("raw", (0, 0) + self.size, 0, ('RGB', 0, 1))]
else: else:
raise ValueError("Invalid texture compression format: %r" % (format)) raise ValueError(
"Invalid texture compression format: %r" % (format))
self.fp.close() self.fp.close()
self.fp = BytesIO(data) self.fp = BytesIO(data)

View File

@ -29,7 +29,8 @@ from ._binary import i32be as i32
def _accept(prefix): def _accept(prefix):
return len(prefix) >= 8 and i32(prefix[:4]) >= 20 and i32(prefix[4:8]) in (1, 2) return len(prefix) >= 8 and \
i32(prefix[:4]) >= 20 and i32(prefix[4:8]) in (1, 2)
## ##
@ -54,7 +55,8 @@ class GbrImageFile(ImageFile.ImageFile):
if width <= 0 or height <= 0: if width <= 0 or height <= 0:
raise SyntaxError("not a GIMP brush") raise SyntaxError("not a GIMP brush")
if color_depth not in (1, 4): if color_depth not in (1, 4):
raise SyntaxError("Unsupported GIMP brush color depth: %s" % color_depth) raise SyntaxError(
"Unsupported GIMP brush color depth: %s" % color_depth)
if version == 1: if version == 1:
comment_length = header_size-20 comment_length = header_size-20

View File

@ -397,7 +397,8 @@ def _write_multiple_frames(im, fp, palette):
im_frames = [] im_frames = []
frame_count = 0 frame_count = 0
for imSequence in itertools.chain([im], im.encoderinfo.get("append_images", [])): for imSequence in itertools.chain([im],
im.encoderinfo.get("append_images", [])):
for im_frame in ImageSequence.Iterator(imSequence): for im_frame in ImageSequence.Iterator(imSequence):
# a copy is required here since seek can still mutate the image # a copy is required here since seek can still mutate the image
im_frame = _normalize_mode(im_frame.copy()) im_frame = _normalize_mode(im_frame.copy())
@ -413,17 +414,19 @@ def _write_multiple_frames(im, fp, palette):
if im_frames: if im_frames:
# delta frame # delta frame
previous = im_frames[-1] previous = im_frames[-1]
if _get_palette_bytes(im_frame) == _get_palette_bytes(previous['im']): if _get_palette_bytes(im_frame) == \
_get_palette_bytes(previous['im']):
delta = ImageChops.subtract_modulo(im_frame, delta = ImageChops.subtract_modulo(im_frame,
previous['im']) previous['im'])
else: else:
delta = ImageChops.subtract_modulo(im_frame.convert('RGB'), delta = ImageChops.subtract_modulo(
previous['im'].convert('RGB')) im_frame.convert('RGB'), previous['im'].convert('RGB'))
bbox = delta.getbbox() bbox = delta.getbbox()
if not bbox: if not bbox:
# This frame is identical to the previous frame # This frame is identical to the previous frame
if duration: if duration:
previous['encoderinfo']['duration'] += encoderinfo['duration'] previous['encoderinfo']['duration'] += \
encoderinfo['duration']
continue continue
else: else:
bbox = None bbox = None
@ -525,7 +528,8 @@ def _write_local_header(fp, im, offset, flags):
o8(transparency) + # transparency index o8(transparency) + # transparency index
o8(0)) o8(0))
if "comment" in im.encoderinfo and 1 <= len(im.encoderinfo["comment"]) <= 255: if "comment" in im.encoderinfo and \
1 <= len(im.encoderinfo["comment"]) <= 255:
fp.write(b"!" + fp.write(b"!" +
o8(254) + # extension intro o8(254) + # extension intro
o8(len(im.encoderinfo["comment"])) + o8(len(im.encoderinfo["comment"])) +
@ -691,7 +695,8 @@ def _get_global_header(im, info):
for extensionKey in ["transparency", "duration", "loop", "comment"]: for extensionKey in ["transparency", "duration", "loop", "comment"]:
if info and extensionKey in info: if info and extensionKey in info:
if ((extensionKey == "duration" and info[extensionKey] == 0) or if ((extensionKey == "duration" and info[extensionKey] == 0) or
(extensionKey == "comment" and not (1 <= len(info[extensionKey]) <= 255))): (extensionKey == "comment" and
not (1 <= len(info[extensionKey]) <= 255))):
continue continue
version = b"89a" version = b"89a"
break break

View File

@ -305,10 +305,10 @@ def profileToProfile(
:param renderingIntent: Integer (0-3) specifying the rendering intent you :param renderingIntent: Integer (0-3) specifying the rendering intent you
wish to use for the transform wish to use for the transform
INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT)
INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) ImageCms.INTENT_SATURATION = 2
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3
see the pyCMS documentation for details on rendering intents and what see the pyCMS documentation for details on rendering intents and what
they do. they do.
@ -424,10 +424,10 @@ def buildTransform(
:param renderingIntent: Integer (0-3) specifying the rendering intent you :param renderingIntent: Integer (0-3) specifying the rendering intent you
wish to use for the transform wish to use for the transform
INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT)
INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) ImageCms.INTENT_SATURATION = 2
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3
see the pyCMS documentation for details on rendering intents and what see the pyCMS documentation for details on rendering intents and what
they do. they do.
@ -512,20 +512,20 @@ def buildProofTransform(
:param renderingIntent: Integer (0-3) specifying the rendering intent you :param renderingIntent: Integer (0-3) specifying the rendering intent you
wish to use for the input->proof (simulated) transform wish to use for the input->proof (simulated) transform
INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT)
INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) ImageCms.INTENT_SATURATION = 2
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3
see the pyCMS documentation for details on rendering intents and what see the pyCMS documentation for details on rendering intents and what
they do. they do.
:param proofRenderingIntent: Integer (0-3) specifying the rendering intent you :param proofRenderingIntent: Integer (0-3) specifying the rendering intent
wish to use for proof->output transform you wish to use for proof->output transform
INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT)
INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) ImageCms.INTENT_SATURATION = 2
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3
see the pyCMS documentation for details on rendering intents and what see the pyCMS documentation for details on rendering intents and what
they do. they do.
@ -875,10 +875,10 @@ def getDefaultIntent(profile):
:returns: Integer 0-3 specifying the default rendering intent for this :returns: Integer 0-3 specifying the default rendering intent for this
profile. profile.
INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT)
INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) ImageCms.INTENT_SATURATION = 2
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3
see the pyCMS documentation for details on rendering intents and what see the pyCMS documentation for details on rendering intents and what
they do. they do.
@ -913,15 +913,15 @@ def isIntentSupported(profile, intent, direction):
:param intent: Integer (0-3) specifying the rendering intent you wish to :param intent: Integer (0-3) specifying the rendering intent you wish to
use with this profile use with this profile
INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT)
INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) ImageCms.INTENT_SATURATION = 2
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3
see the pyCMS documentation for details on rendering intents and what see the pyCMS documentation for details on rendering intents and what
they do. they do.
:param direction: Integer specifying if the profile is to be used for input, :param direction: Integer specifying if the profile is to be used for
output, or proof input, output, or proof
INPUT = 0 (or use ImageCms.DIRECTION_INPUT) INPUT = 0 (or use ImageCms.DIRECTION_INPUT)
OUTPUT = 1 (or use ImageCms.DIRECTION_OUTPUT) OUTPUT = 1 (or use ImageCms.DIRECTION_OUTPUT)

View File

@ -217,7 +217,8 @@ class ImageDraw(object):
ink = fill ink = fill
if ink is not None: if ink is not None:
try: try:
mask, offset = font.getmask2(text, self.fontmode, *args, **kwargs) mask, offset = font.getmask2(text, self.fontmode,
*args, **kwargs)
xy = xy[0] + offset[0], xy[1] + offset[1] xy = xy[0] + offset[0], xy[1] + offset[1]
except AttributeError: except AttributeError:
try: try:

View File

@ -51,7 +51,8 @@ class Color(_Enhance):
if 'A' in image.getbands(): if 'A' in image.getbands():
self.intermediate_mode = 'LA' self.intermediate_mode = 'LA'
self.degenerate = image.convert(self.intermediate_mode).convert(image.mode) self.degenerate = image.convert(
self.intermediate_mode).convert(image.mode)
class Contrast(_Enhance): class Contrast(_Enhance):

View File

@ -166,8 +166,9 @@ class ImageFile(Image.Image):
if use_mmap: if use_mmap:
# try memory mapping # try memory mapping
decoder_name, extents, offset, args = self.tile[0] decoder_name, extents, offset, args = self.tile[0]
if decoder_name == "raw" and len(args) >= 3 and args[0] == self.mode \ if decoder_name == "raw" and len(args) >= 3 and \
and args[0] in Image._MAPMODES: args[0] == self.mode and \
args[0] in Image._MAPMODES:
try: try:
if hasattr(Image.core, "map"): if hasattr(Image.core, "map"):
# use built-in mapper WIN32 only # use built-in mapper WIN32 only
@ -180,12 +181,14 @@ class ImageFile(Image.Image):
# use mmap, if possible # use mmap, if possible
import mmap import mmap
with open(self.filename, "r") as fp: with open(self.filename, "r") as fp:
self.map = mmap.mmap(fp.fileno(), 0, access=mmap.ACCESS_READ) self.map = mmap.mmap(fp.fileno(), 0,
access=mmap.ACCESS_READ)
self.im = Image.core.map_buffer( self.im = Image.core.map_buffer(
self.map, self.size, decoder_name, extents, offset, args self.map, self.size, decoder_name, extents,
) offset, args)
readonly = 1 readonly = 1
# After trashing self.im, we might need to reload the palette data. # After trashing self.im,
# we might need to reload the palette data.
if self.palette: if self.palette:
self.palette.dirty = 1 self.palette.dirty = 1
except (AttributeError, EnvironmentError, ImportError): except (AttributeError, EnvironmentError, ImportError):
@ -217,7 +220,8 @@ class ImageFile(Image.Image):
while True: while True:
try: try:
s = read(self.decodermaxblock) s = read(self.decodermaxblock)
except (IndexError, struct.error): # truncated png/gif except (IndexError, struct.error):
# truncated png/gif
if LOAD_TRUNCATED_IMAGES: if LOAD_TRUNCATED_IMAGES:
break break
else: else:
@ -229,7 +233,8 @@ class ImageFile(Image.Image):
else: else:
self.tile = [] self.tile = []
raise IOError("image file is truncated " raise IOError("image file is truncated "
"(%d bytes not processed)" % len(b)) "(%d bytes not processed)" %
len(b))
b = b + s b = b + s
n, err_code = decoder.decode(b) n, err_code = decoder.decode(b)
@ -588,10 +593,12 @@ class PyDecoder(object):
""" """
Override to perform the decoding process. Override to perform the decoding process.
:param buffer: A bytes object with the data to be decoded. If `handles_eof` :param buffer: A bytes object with the data to be decoded.
is set, then `buffer` will be empty and `self.fd` will be set. If `handles_eof` is set, then `buffer` will be empty and `self.fd`
:returns: A tuple of (bytes consumed, errcode). If finished with decoding will be set.
return <0 for the bytes consumed. Err codes are from `ERRORS` :returns: A tuple of (bytes consumed, errcode).
If finished with decoding return <0 for the bytes consumed.
Err codes are from `ERRORS`
""" """
raise NotImplementedError() raise NotImplementedError()
@ -650,8 +657,8 @@ class PyDecoder(object):
Convenience method to set the internal image from a stream of raw data Convenience method to set the internal image from a stream of raw data
:param data: Bytes to be set :param data: Bytes to be set
:param rawmode: The rawmode to be used for the decoder. If not specified, :param rawmode: The rawmode to be used for the decoder.
it will default to the mode of the image If not specified, it will default to the mode of the image
:returns: None :returns: None
""" """

View File

@ -141,7 +141,8 @@ class FreeTypeFont(object):
self.layout_engine = layout_engine self.layout_engine = layout_engine
if isPath(font): if isPath(font):
self.font = core.getfont(font, size, index, encoding, layout_engine=layout_engine) self.font = core.getfont(font, size, index, encoding,
layout_engine=layout_engine)
else: else:
self.font_bytes = font.read() self.font_bytes = font.read()
self.font = core.getfont( self.font = core.getfont(
@ -175,9 +176,11 @@ class FreeTypeFont(object):
return self.font.getsize(text)[1] return self.font.getsize(text)[1]
def getmask(self, text, mode="", direction=None, features=None): def getmask(self, text, mode="", direction=None, features=None):
return self.getmask2(text, mode, direction=direction, features=features)[0] return self.getmask2(text, mode, direction=direction,
features=features)[0]
def getmask2(self, text, mode="", fill=Image.core.fill, direction=None, features=None, *args, **kwargs): def getmask2(self, text, mode="", fill=Image.core.fill, direction=None,
features=None, *args, **kwargs):
size, offset = self.font.getsize(text, direction, features) size, offset = self.font.getsize(text, direction, features)
im = fill("L", size, 0) im = fill("L", size, 0)
self.font.render(text, im.id, mode == "1", direction, features) self.font.render(text, im.id, mode == "1", direction, features)
@ -194,12 +197,13 @@ class FreeTypeFont(object):
:return: A FreeTypeFont object. :return: A FreeTypeFont object.
""" """
return FreeTypeFont(font=self.path if font is None else font, return FreeTypeFont(
size=self.size if size is None else size, font=self.path if font is None else font,
index=self.index if index is None else index, size=self.size if size is None else size,
encoding=self.encoding if encoding is None else encoding, index=self.index if index is None else index,
layout_engine=self.layout_engine if layout_engine is None else layout_engine encoding=self.encoding if encoding is None else encoding,
) layout_engine=self.layout_engine if layout_engine is None else layout_engine
)
class TransposedFont(object): class TransposedFont(object):
@ -303,12 +307,16 @@ def truetype(font=None, size=10, index=0, encoding="",
for walkfilename in walkfilenames: for walkfilename in walkfilenames:
if ext and walkfilename == ttf_filename: if ext and walkfilename == ttf_filename:
fontpath = os.path.join(walkroot, walkfilename) fontpath = os.path.join(walkroot, walkfilename)
return FreeTypeFont(fontpath, size, index, encoding, layout_engine) return FreeTypeFont(fontpath, size, index,
elif not ext and os.path.splitext(walkfilename)[0] == ttf_filename: encoding, layout_engine)
elif (not ext and
os.path.splitext(walkfilename)[0] == ttf_filename):
fontpath = os.path.join(walkroot, walkfilename) fontpath = os.path.join(walkroot, walkfilename)
if os.path.splitext(fontpath)[1] == '.ttf': if os.path.splitext(fontpath)[1] == '.ttf':
return FreeTypeFont(fontpath, size, index, encoding, layout_engine) return FreeTypeFont(fontpath, size, index,
if not ext and first_font_with_a_different_extension is None: encoding, layout_engine)
if not ext \
and first_font_with_a_different_extension is None:
first_font_with_a_different_extension = fontpath first_font_with_a_different_extension = fontpath
if first_font_with_a_different_extension: if first_font_with_a_different_extension:
return FreeTypeFont(first_font_with_a_different_extension, size, return FreeTypeFont(first_font_with_a_different_extension, size,

View File

@ -42,7 +42,8 @@ def getmode(mode):
for m, (basemode, basetype, bands) in Image._MODEINFO.items(): for m, (basemode, basetype, bands) in Image._MODEINFO.items():
modes[m] = ModeDescriptor(m, bands, basemode, basetype) modes[m] = ModeDescriptor(m, bands, basemode, basetype)
# extra experimental modes # extra experimental modes
modes["RGBa"] = ModeDescriptor("RGBa", ("R", "G", "B", "a"), "RGB", "L") modes["RGBa"] = ModeDescriptor("RGBa",
("R", "G", "B", "a"), "RGB", "L")
modes["LA"] = ModeDescriptor("LA", ("L", "A"), "L", "L") modes["LA"] = ModeDescriptor("LA", ("L", "A"), "L", "L")
modes["La"] = ModeDescriptor("La", ("L", "a"), "L", "L") modes["La"] = ModeDescriptor("La", ("L", "a"), "L", "L")
modes["PA"] = ModeDescriptor("PA", ("P", "A"), "RGB", "L") modes["PA"] = ModeDescriptor("PA", ("P", "A"), "RGB", "L")

View File

@ -119,7 +119,8 @@ def align8to32(bytes, width, mode):
new_data = [] new_data = []
for i in range(len(bytes) // bytes_per_line): for i in range(len(bytes) // bytes_per_line):
new_data.append(bytes[i*bytes_per_line:(i+1)*bytes_per_line] + b'\x00' * extra_padding) new_data.append(bytes[i*bytes_per_line:(i+1)*bytes_per_line]
+ b'\x00' * extra_padding)
return b''.join(new_data) return b''.join(new_data)

View File

@ -195,7 +195,8 @@ class PhotoImage(object):
# Pypy is using a ffi cdata element # Pypy is using a ffi cdata element
# (Pdb) self.tk.interp # (Pdb) self.tk.interp
# <cdata 'Tcl_Interp *' 0x3061b50> # <cdata 'Tcl_Interp *' 0x3061b50>
_imagingtk.tkinit(int(ffi.cast("uintptr_t", tk.interp)), 1) _imagingtk.tkinit(
int(ffi.cast("uintptr_t", tk.interp)), 1)
else: else:
_imagingtk.tkinit(tk.interpaddr(), 1) _imagingtk.tkinit(tk.interpaddr(), 1)
except AttributeError: except AttributeError:

View File

@ -270,7 +270,8 @@ def _save(im, fp, filename):
Image.register_open(Jpeg2KImageFile.format, Jpeg2KImageFile, _accept) Image.register_open(Jpeg2KImageFile.format, Jpeg2KImageFile, _accept)
Image.register_save(Jpeg2KImageFile.format, _save) Image.register_save(Jpeg2KImageFile.format, _save)
Image.register_extensions(Jpeg2KImageFile.format, [".jp2", ".j2k", ".jpc", ".jpf", ".jpx", ".j2c"]) Image.register_extensions(Jpeg2KImageFile.format,
[".jp2", ".j2k", ".jpc", ".jpf", ".jpx", ".j2c"])
Image.register_mime(Jpeg2KImageFile.format, 'image/jp2') Image.register_mime(Jpeg2KImageFile.format, 'image/jp2')
Image.register_mime(Jpeg2KImageFile.format, 'image/jpx') Image.register_mime(Jpeg2KImageFile.format, 'image/jpx')

View File

@ -799,6 +799,7 @@ def jpeg_factory(fp=None, filename=None):
Image.register_open(JpegImageFile.format, jpeg_factory, _accept) Image.register_open(JpegImageFile.format, jpeg_factory, _accept)
Image.register_save(JpegImageFile.format, _save) Image.register_save(JpegImageFile.format, _save)
Image.register_extensions(JpegImageFile.format, [".jfif", ".jpe", ".jpg", ".jpeg"]) Image.register_extensions(JpegImageFile.format,
[".jfif", ".jpe", ".jpg", ".jpeg"])
Image.register_mime(JpegImageFile.format, "image/jpeg") Image.register_mime(JpegImageFile.format, "image/jpeg")

View File

@ -126,8 +126,9 @@ class MspDecoder(ImageFile.PyDecoder):
continue continue
row = self.fd.read(rowlen) row = self.fd.read(rowlen)
if len(row) != rowlen: if len(row) != rowlen:
raise IOError("Truncated MSP file, expected %d bytes on row %s", raise IOError(
(rowlen, x)) "Truncated MSP file, expected %d bytes on row %s",
(rowlen, x))
idx = 0 idx = 0
while idx < rowlen: while idx < rowlen:
runtype = i8(row[idx]) runtype = i8(row[idx])

View File

@ -98,7 +98,8 @@ def _save(im, fp, filename, save_all=False):
try: try:
im_numberOfPages = im.n_frames im_numberOfPages = im.n_frames
except AttributeError: except AttributeError:
# Image format does not have n_frames. It is a single frame image # Image format does not have n_frames.
# It is a single frame image
pass pass
numberOfPages += im_numberOfPages numberOfPages += im_numberOfPages
for i in range(im_numberOfPages): for i in range(im_numberOfPages):
@ -115,9 +116,9 @@ def _save(im, fp, filename, save_all=False):
for imSequence in ims: for imSequence in ims:
im_pages = ImageSequence.Iterator(imSequence) if save_all else [imSequence] im_pages = ImageSequence.Iterator(imSequence) if save_all else [imSequence]
for im in im_pages: for im in im_pages:
# FIXME: Should replace ASCIIHexDecode with RunLengthDecode (packbits) # FIXME: Should replace ASCIIHexDecode with RunLengthDecode
# or LZWDecode (tiff/lzw compression). Note that PDF 1.2 also supports # (packbits) or LZWDecode (tiff/lzw compression). Note that
# Flatedecode (zip compression). # PDF 1.2 also supports Flatedecode (zip compression).
bits = 8 bits = 8
params = None params = None
@ -135,7 +136,12 @@ def _save(im, fp, filename, save_all=False):
elif im.mode == "P": elif im.mode == "P":
filter = "ASCIIHexDecode" filter = "ASCIIHexDecode"
palette = im.im.getpalette("RGB") palette = im.im.getpalette("RGB")
colorspace = [PdfParser.PdfName("Indexed"), PdfParser.PdfName("DeviceRGB"), 255, PdfParser.PdfBinary(palette)] colorspace = [
PdfParser.PdfName("Indexed"),
PdfParser.PdfName("DeviceRGB"),
255,
PdfParser.PdfBinary(palette)
]
procset = "ImageI" # indexed color procset = "ImageI" # indexed color
elif im.mode == "RGB": elif im.mode == "RGB":
filter = "DCTDecode" filter = "DCTDecode"
@ -166,7 +172,8 @@ def _save(im, fp, filename, save_all=False):
elif filter == "FlateDecode": elif filter == "FlateDecode":
ImageFile._save(im, op, [("zip", (0, 0)+im.size, 0, im.mode)]) ImageFile._save(im, op, [("zip", (0, 0)+im.size, 0, im.mode)])
elif filter == "RunLengthDecode": elif filter == "RunLengthDecode":
ImageFile._save(im, op, [("packbits", (0, 0)+im.size, 0, im.mode)]) ImageFile._save(im, op,
[("packbits", (0, 0)+im.size, 0, im.mode)])
else: else:
raise ValueError("unsupported PDF filter (%s)" % filter) raise ValueError("unsupported PDF filter (%s)" % filter)
@ -175,26 +182,37 @@ def _save(im, fp, filename, save_all=False):
width, height = im.size width, height = im.size
existing_pdf.write_obj(image_refs[pageNumber], stream=op.getvalue(), existing_pdf.write_obj(image_refs[pageNumber],
Type=PdfParser.PdfName("XObject"), stream=op.getvalue(),
Subtype=PdfParser.PdfName("Image"), Type=PdfParser.PdfName("XObject"),
Width=width, # * 72.0 / resolution, Subtype=PdfParser.PdfName("Image"),
Height=height, # * 72.0 / resolution, Width=width, # * 72.0 / resolution,
Filter=PdfParser.PdfName(filter), Height=height, # * 72.0 / resolution,
BitsPerComponent=bits, Filter=PdfParser.PdfName(filter),
DecodeParams=params, BitsPerComponent=bits,
ColorSpace=colorspace) DecodeParams=params,
ColorSpace=colorspace)
# #
# page # page
existing_pdf.write_page(page_refs[pageNumber], existing_pdf.write_page(page_refs[pageNumber],
Resources=PdfParser.PdfDict( Resources=PdfParser.PdfDict(
ProcSet=[PdfParser.PdfName("PDF"), PdfParser.PdfName(procset)], ProcSet=[
XObject=PdfParser.PdfDict(image=image_refs[pageNumber])), PdfParser.PdfName("PDF"),
MediaBox=[0, 0, int(width * 72.0 / resolution), int(height * 72.0 / resolution)], PdfParser.PdfName(procset)
Contents=contents_refs[pageNumber] ],
) XObject=PdfParser.PdfDict(
image=image_refs[pageNumber]
)
),
MediaBox=[
0,
0,
int(width * 72.0 / resolution),
int(height * 72.0 / resolution)
],
Contents=contents_refs[pageNumber])
# #
# page contents # page contents
@ -204,7 +222,8 @@ def _save(im, fp, filename, save_all=False):
int(width * 72.0 / resolution), int(width * 72.0 / resolution),
int(height * 72.0 / resolution))) int(height * 72.0 / resolution)))
existing_pdf.write_obj(contents_refs[pageNumber], stream=page_contents) existing_pdf.write_obj(contents_refs[pageNumber],
stream=page_contents)
pageNumber += 1 pageNumber += 1

View File

@ -20,7 +20,8 @@ else: # Python 2.x
return s # pragma: no cover return s # pragma: no cover
# see 7.9.2.2 Text String Type on page 86 and D.3 PDFDocEncoding Character Set on page 656 # see 7.9.2.2 Text String Type on page 86 and D.3 PDFDocEncoding Character Set
# on page 656
def encode_text(s): def encode_text(s):
return codecs.BOM_UTF16_BE + s.encode("utf_16_be") return codecs.BOM_UTF16_BE + s.encode("utf_16_be")
@ -80,7 +81,8 @@ def decode_text(b):
class PdfFormatError(RuntimeError): class PdfFormatError(RuntimeError):
"""An error that probably indicates a syntactic or semantic error in the PDF file structure""" """An error that probably indicates a syntactic or semantic error in the
PDF file structure"""
pass pass
@ -89,7 +91,8 @@ def check_format_condition(condition, error_message):
raise PdfFormatError(error_message) raise PdfFormatError(error_message)
class IndirectReference(collections.namedtuple("IndirectReferenceTuple", ["object_id", "generation"])): class IndirectReference(collections.namedtuple("IndirectReferenceTuple",
["object_id", "generation"])):
def __str__(self): def __str__(self):
return "%s %s R" % self return "%s %s R" % self
@ -97,7 +100,9 @@ class IndirectReference(collections.namedtuple("IndirectReferenceTuple", ["objec
return self.__str__().encode("us-ascii") return self.__str__().encode("us-ascii")
def __eq__(self, other): def __eq__(self, other):
return other.__class__ is self.__class__ and other.object_id == self.object_id and other.generation == self.generation return other.__class__ is self.__class__ and \
other.object_id == self.object_id and \
other.generation == self.generation
def __ne__(self, other): def __ne__(self, other):
return not (self == other) return not (self == other)
@ -143,19 +148,26 @@ class XrefTable:
elif key in self.deleted_entries: elif key in self.deleted_entries:
generation = self.deleted_entries[key] generation = self.deleted_entries[key]
else: else:
raise IndexError("object ID " + str(key) + " cannot be deleted because it doesn't exist") raise IndexError("object ID " + str(key) +
" cannot be deleted because it doesn't exist")
def __contains__(self, key): def __contains__(self, key):
return key in self.existing_entries or key in self.new_entries return key in self.existing_entries or key in self.new_entries
def __len__(self): def __len__(self):
return len(set(self.existing_entries.keys()) | set(self.new_entries.keys()) | set(self.deleted_entries.keys())) return len(set(self.existing_entries.keys()) |
set(self.new_entries.keys()) |
set(self.deleted_entries.keys()))
def keys(self): def keys(self):
return (set(self.existing_entries.keys()) - set(self.deleted_entries.keys())) | set(self.new_entries.keys()) return (
set(self.existing_entries.keys()) -
set(self.deleted_entries.keys())
) | set(self.new_entries.keys())
def write(self, f): def write(self, f):
keys = sorted(set(self.new_entries.keys()) | set(self.deleted_entries.keys())) keys = sorted(set(self.new_entries.keys()) |
set(self.deleted_entries.keys()))
deleted_keys = sorted(set(self.deleted_entries.keys())) deleted_keys = sorted(set(self.deleted_entries.keys()))
startxref = f.tell() startxref = f.tell()
f.write(b"xref\n") f.write(b"xref\n")
@ -172,10 +184,12 @@ class XrefTable:
else: else:
contiguous_keys = keys contiguous_keys = keys
keys = None keys = None
f.write(make_bytes("%d %d\n" % (contiguous_keys[0], len(contiguous_keys)))) f.write(make_bytes("%d %d\n" %
(contiguous_keys[0], len(contiguous_keys))))
for object_id in contiguous_keys: for object_id in contiguous_keys:
if object_id in self.new_entries: if object_id in self.new_entries:
f.write(make_bytes("%010d %05d n \n" % self.new_entries[object_id])) f.write(make_bytes("%010d %05d n \n" %
self.new_entries[object_id]))
else: else:
this_deleted_object_id = deleted_keys.pop(0) this_deleted_object_id = deleted_keys.pop(0)
check_format_condition(object_id == this_deleted_object_id, check_format_condition(object_id == this_deleted_object_id,
@ -186,7 +200,9 @@ class XrefTable:
next_in_linked_list = deleted_keys[0] next_in_linked_list = deleted_keys[0]
except IndexError: except IndexError:
next_in_linked_list = 0 next_in_linked_list = 0
f.write(make_bytes("%010d %05d f \n" % (next_in_linked_list, self.deleted_entries[object_id]))) f.write(make_bytes("%010d %05d f \n" %
(next_in_linked_list,
self.deleted_entries[object_id])))
return startxref return startxref
@ -203,7 +219,8 @@ class PdfName:
return self.name.decode("us-ascii") return self.name.decode("us-ascii")
def __eq__(self, other): def __eq__(self, other):
return (isinstance(other, PdfName) and other.name == self.name) or other == self.name return (isinstance(other, PdfName) and other.name == self.name) or \
other == self.name
def __hash__(self): def __hash__(self):
return hash(self.name) return hash(self.name)
@ -313,7 +330,9 @@ class PdfStream:
expected_length = self.dictionary.Length expected_length = self.dictionary.Length
return zlib.decompress(self.buf, bufsize=int(expected_length)) return zlib.decompress(self.buf, bufsize=int(expected_length))
else: else:
raise NotImplementedError("stream filter %s unknown/unsupported" % repr(self.dictionary.Filter)) raise NotImplementedError(
"stream filter %s unknown/unsupported" %
repr(self.dictionary.Filter))
def pdf_repr(x): def pdf_repr(x):
@ -323,7 +342,8 @@ def pdf_repr(x):
return b"false" return b"false"
elif x is None: elif x is None:
return b"null" return b"null"
elif isinstance(x, PdfName) or isinstance(x, PdfDict) or isinstance(x, PdfArray) or isinstance(x, PdfBinary): elif (isinstance(x, PdfName) or isinstance(x, PdfDict) or
isinstance(x, PdfArray) or isinstance(x, PdfBinary)):
return bytes(x) return bytes(x)
elif isinstance(x, int): elif isinstance(x, int):
return str(x).encode("us-ascii") return str(x).encode("us-ascii")
@ -331,10 +351,15 @@ def pdf_repr(x):
return bytes(PdfDict(x)) return bytes(PdfDict(x))
elif isinstance(x, list): elif isinstance(x, list):
return bytes(PdfArray(x)) return bytes(PdfArray(x))
elif (py3 and isinstance(x, str)) or (not py3 and isinstance(x, unicode)): elif ((py3 and isinstance(x, str)) or
(not py3 and isinstance(x, unicode))):
return pdf_repr(encode_text(x)) return pdf_repr(encode_text(x))
elif isinstance(x, bytes): elif isinstance(x, bytes):
return b"(" + x.replace(b"\\", b"\\\\").replace(b"(", b"\\(").replace(b")", b"\\)") + b")" # XXX escape more chars? handle binary garbage # XXX escape more chars? handle binary garbage
x = x.replace(b"\\", b"\\\\")
x = x.replace(b"(", b"\\(")
x = x.replace(b")", b"\\)")
return b"(" + x + b")"
else: else:
return bytes(x) return bytes(x)
@ -344,10 +369,13 @@ class PdfParser:
Supports PDF up to 1.4 Supports PDF up to 1.4
""" """
def __init__(self, filename=None, f=None, buf=None, start_offset=0, mode="rb"): def __init__(self, filename=None, f=None,
# type: (PdfParser, str, file, Union[bytes, bytearray], int, str) -> None buf=None, start_offset=0, mode="rb"):
# type: (PdfParser, str, file, Union[bytes, bytearray], int, str)
# -> None
if buf and f: if buf and f:
raise RuntimeError("specify buf or f or filename, but not both buf and f") raise RuntimeError(
"specify buf or f or filename, but not both buf and f")
self.filename = filename self.filename = filename
self.buf = buf self.buf = buf
self.f = f self.f = f
@ -473,7 +501,8 @@ class PdfParser:
if self.info: if self.info:
trailer_dict[b"Info"] = self.info_ref trailer_dict[b"Info"] = self.info_ref
self.last_xref_section_offset = start_xref self.last_xref_section_offset = start_xref
self.f.write(b"trailer\n" + bytes(PdfDict(trailer_dict)) + make_bytes("\nstartxref\n%d\n%%%%EOF" % start_xref)) self.f.write(b"trailer\n" + bytes(PdfDict(trailer_dict)) +
make_bytes("\nstartxref\n%d\n%%%%EOF" % start_xref))
def write_page(self, ref, *objs, **dict_obj): def write_page(self, ref, *objs, **dict_obj):
if isinstance(ref, int): if isinstance(ref, int):
@ -535,13 +564,18 @@ class PdfParser:
else: else:
self.info = PdfDict(self.read_indirect(self.info_ref)) self.info = PdfDict(self.read_indirect(self.info_ref))
check_format_condition(b"Type" in self.root, "/Type missing in Root") check_format_condition(b"Type" in self.root, "/Type missing in Root")
check_format_condition(self.root[b"Type"] == b"Catalog", "/Type in Root is not /Catalog") check_format_condition(self.root[b"Type"] == b"Catalog",
"/Type in Root is not /Catalog")
check_format_condition(b"Pages" in self.root, "/Pages missing in Root") check_format_condition(b"Pages" in self.root, "/Pages missing in Root")
check_format_condition(isinstance(self.root[b"Pages"], IndirectReference), "/Pages in Root is not an indirect reference") check_format_condition(isinstance(self.root[b"Pages"],
IndirectReference),
"/Pages in Root is not an indirect reference")
self.pages_ref = self.root[b"Pages"] self.pages_ref = self.root[b"Pages"]
self.page_tree_root = self.read_indirect(self.pages_ref) self.page_tree_root = self.read_indirect(self.pages_ref)
self.pages = self.linearize_page_tree(self.page_tree_root) self.pages = self.linearize_page_tree(self.page_tree_root)
# save the original list of page references in case the user modifies, adds or deletes some pages and we need to rewrite the pages and their list # save the original list of page references
# in case the user modifies, adds or deletes some pages
# and we need to rewrite the pages and their list
self.orig_pages = self.pages[:] self.orig_pages = self.pages[:]
def next_object_id(self, offset=None): def next_object_id(self, offset=None):
@ -562,10 +596,14 @@ class PdfParser:
whitespace_mandatory = whitespace + b"+" whitespace_mandatory = whitespace + b"+"
newline_only = br"[\r\n]+" newline_only = br"[\r\n]+"
newline = whitespace_optional + newline_only + whitespace_optional newline = whitespace_optional + newline_only + whitespace_optional
re_trailer_end = re.compile(whitespace_mandatory + br"trailer" + whitespace_optional + br"\<\<(.*\>\>)" + newline re_trailer_end = re.compile(
+ br"startxref" + newline + br"([0-9]+)" + newline + br"%%EOF" + whitespace_optional + br"$", re.DOTALL) whitespace_mandatory + br"trailer" + whitespace_optional +
re_trailer_prev = re.compile(whitespace_optional + br"trailer" + whitespace_optional + br"\<\<(.*?\>\>)" + newline br"\<\<(.*\>\>)" + newline + br"startxref" + newline + br"([0-9]+)" +
+ br"startxref" + newline + br"([0-9]+)" + newline + br"%%EOF" + whitespace_optional, re.DOTALL) newline + br"%%EOF" + whitespace_optional + br"$", re.DOTALL)
re_trailer_prev = re.compile(
whitespace_optional + br"trailer" + whitespace_optional +
br"\<\<(.*?\>\>)" + newline + br"startxref" + newline + br"([0-9]+)" +
newline + br"%%EOF" + whitespace_optional, re.DOTALL)
def read_trailer(self): def read_trailer(self):
search_start_offset = len(self.buf) - 16384 search_start_offset = len(self.buf) - 16384
@ -589,19 +627,26 @@ class PdfParser:
self.read_prev_trailer(self.trailer_dict[b"Prev"]) self.read_prev_trailer(self.trailer_dict[b"Prev"])
def read_prev_trailer(self, xref_section_offset): def read_prev_trailer(self, xref_section_offset):
trailer_offset = self.read_xref_table(xref_section_offset=xref_section_offset) trailer_offset = self.read_xref_table(
m = self.re_trailer_prev.search(self.buf[trailer_offset:trailer_offset+16384]) xref_section_offset=xref_section_offset)
m = self.re_trailer_prev.search(
self.buf[trailer_offset:trailer_offset+16384])
check_format_condition(m, "previous trailer not found") check_format_condition(m, "previous trailer not found")
trailer_data = m.group(1) trailer_data = m.group(1)
check_format_condition(int(m.group(2)) == xref_section_offset, "xref section offset in previous trailer doesn't match what was expected") check_format_condition(int(m.group(2)) == xref_section_offset,
"xref section offset in previous trailer "
"doesn't match what was expected")
trailer_dict = self.interpret_trailer(trailer_data) trailer_dict = self.interpret_trailer(trailer_data)
if b"Prev" in trailer_dict: if b"Prev" in trailer_dict:
self.read_prev_trailer(trailer_dict[b"Prev"]) self.read_prev_trailer(trailer_dict[b"Prev"])
re_whitespace_optional = re.compile(whitespace_optional) re_whitespace_optional = re.compile(whitespace_optional)
re_name = re.compile(whitespace_optional + br"/([!-$&'*-.0-;=?-Z\\^-z|~]+)(?=" + delimiter_or_ws + br")") re_name = re.compile(
whitespace_optional + br"/([!-$&'*-.0-;=?-Z\\^-z|~]+)(?=" +
delimiter_or_ws + br")")
re_dict_start = re.compile(whitespace_optional + br"\<\<") re_dict_start = re.compile(whitespace_optional + br"\<\<")
re_dict_end = re.compile(whitespace_optional + br"\>\>" + whitespace_optional) re_dict_end = re.compile(
whitespace_optional + br"\>\>" + whitespace_optional)
@classmethod @classmethod
def interpret_trailer(cls, trailer_data): def interpret_trailer(cls, trailer_data):
@ -611,13 +656,21 @@ class PdfParser:
m = cls.re_name.match(trailer_data, offset) m = cls.re_name.match(trailer_data, offset)
if not m: if not m:
m = cls.re_dict_end.match(trailer_data, offset) m = cls.re_dict_end.match(trailer_data, offset)
check_format_condition(m and m.end() == len(trailer_data), "name not found in trailer, remaining data: " + repr(trailer_data[offset:])) check_format_condition(
m and m.end() == len(trailer_data),
"name not found in trailer, remaining data: " +
repr(trailer_data[offset:]))
break break
key = cls.interpret_name(m.group(1)) key = cls.interpret_name(m.group(1))
value, offset = cls.get_value(trailer_data, m.end()) value, offset = cls.get_value(trailer_data, m.end())
trailer[key] = value trailer[key] = value
check_format_condition(b"Size" in trailer and isinstance(trailer[b"Size"], int), "/Size not in trailer or not an integer") check_format_condition(
check_format_condition(b"Root" in trailer and isinstance(trailer[b"Root"], IndirectReference), "/Root not in trailer or not an indirect reference") b"Size" in trailer and isinstance(trailer[b"Size"], int),
"/Size not in trailer or not an integer")
check_format_condition(
b"Root" in trailer and
isinstance(trailer[b"Root"], IndirectReference),
"/Root not in trailer or not an indirect reference")
return trailer return trailer
re_hashes_in_name = re.compile(br"([^#]*)(#([0-9a-fA-F]{2}))?") re_hashes_in_name = re.compile(br"([^#]*)(#([0-9a-fA-F]{2}))?")
@ -627,7 +680,8 @@ class PdfParser:
name = b"" name = b""
for m in cls.re_hashes_in_name.finditer(raw): for m in cls.re_hashes_in_name.finditer(raw):
if m.group(3): if m.group(3):
name += m.group(1) + bytearray.fromhex(m.group(3).decode("us-ascii")) name += m.group(1) + \
bytearray.fromhex(m.group(3).decode("us-ascii"))
else: else:
name += m.group(1) name += m.group(1)
if as_text: if as_text:
@ -635,21 +689,37 @@ class PdfParser:
else: else:
return bytes(name) return bytes(name)
re_null = re.compile(whitespace_optional + br"null(?=" + delimiter_or_ws + br")") re_null = re.compile(
re_true = re.compile(whitespace_optional + br"true(?=" + delimiter_or_ws + br")") whitespace_optional + br"null(?=" + delimiter_or_ws + br")")
re_false = re.compile(whitespace_optional + br"false(?=" + delimiter_or_ws + br")") re_true = re.compile(
re_int = re.compile(whitespace_optional + br"([-+]?[0-9]+)(?=" + delimiter_or_ws + br")") whitespace_optional + br"true(?=" + delimiter_or_ws + br")")
re_real = re.compile(whitespace_optional + br"([-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+))(?=" + delimiter_or_ws + br")") re_false = re.compile(
whitespace_optional + br"false(?=" + delimiter_or_ws + br")")
re_int = re.compile(
whitespace_optional + br"([-+]?[0-9]+)(?=" + delimiter_or_ws + br")")
re_real = re.compile(
whitespace_optional + br"([-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+))(?=" +
delimiter_or_ws + br")")
re_array_start = re.compile(whitespace_optional + br"\[") re_array_start = re.compile(whitespace_optional + br"\[")
re_array_end = re.compile(whitespace_optional + br"]") re_array_end = re.compile(whitespace_optional + br"]")
re_string_hex = re.compile(whitespace_optional + br"\<(" + whitespace_or_hex + br"*)\>") re_string_hex = re.compile(
whitespace_optional + br"\<(" + whitespace_or_hex + br"*)\>")
re_string_lit = re.compile(whitespace_optional + br"\(") re_string_lit = re.compile(whitespace_optional + br"\(")
re_indirect_reference = re.compile(whitespace_optional + br"([-+]?[0-9]+)" + whitespace_mandatory + br"([-+]?[0-9]+)" + whitespace_mandatory + br"R(?=" + delimiter_or_ws + br")") re_indirect_reference = re.compile(
re_indirect_def_start = re.compile(whitespace_optional + br"([-+]?[0-9]+)" + whitespace_mandatory + br"([-+]?[0-9]+)" + whitespace_mandatory + br"obj(?=" + delimiter_or_ws + br")") whitespace_optional + br"([-+]?[0-9]+)" + whitespace_mandatory +
re_indirect_def_end = re.compile(whitespace_optional + br"endobj(?=" + delimiter_or_ws + br")") br"([-+]?[0-9]+)" + whitespace_mandatory + br"R(?=" + delimiter_or_ws +
re_comment = re.compile(br"(" + whitespace_optional + br"%[^\r\n]*" + newline + br")*") br")")
re_indirect_def_start = re.compile(
whitespace_optional + br"([-+]?[0-9]+)" + whitespace_mandatory +
br"([-+]?[0-9]+)" + whitespace_mandatory + br"obj(?=" +
delimiter_or_ws + br")")
re_indirect_def_end = re.compile(
whitespace_optional + br"endobj(?=" + delimiter_or_ws + br")")
re_comment = re.compile(
br"(" + whitespace_optional + br"%[^\r\n]*" + newline + br")*")
re_stream_start = re.compile(whitespace_optional + br"stream\r?\n") re_stream_start = re.compile(whitespace_optional + br"stream\r?\n")
re_stream_end = re.compile(whitespace_optional + br"endstream(?=" + delimiter_or_ws + br")") re_stream_end = re.compile(
whitespace_optional + br"endstream(?=" + delimiter_or_ws + br")")
@classmethod @classmethod
def get_value(cls, data, offset, expect_indirect=None, max_nesting=-1): def get_value(cls, data, offset, expect_indirect=None, max_nesting=-1):
@ -660,21 +730,34 @@ class PdfParser:
offset = m.end() offset = m.end()
m = cls.re_indirect_def_start.match(data, offset) m = cls.re_indirect_def_start.match(data, offset)
if m: if m:
check_format_condition(int(m.group(1)) > 0, "indirect object definition: object ID must be greater than 0") check_format_condition(
check_format_condition(int(m.group(2)) >= 0, "indirect object definition: generation must be non-negative") int(m.group(1)) > 0,
check_format_condition(expect_indirect is None or expect_indirect == IndirectReference(int(m.group(1)), int(m.group(2))), "indirect object definition: object ID must be greater than 0")
check_format_condition(
int(m.group(2)) >= 0,
"indirect object definition: generation must be non-negative")
check_format_condition(
expect_indirect is None or expect_indirect ==
IndirectReference(int(m.group(1)), int(m.group(2))),
"indirect object definition different than expected") "indirect object definition different than expected")
object, offset = cls.get_value(data, m.end(), max_nesting=max_nesting-1) object, offset = cls.get_value(
data, m.end(), max_nesting=max_nesting-1)
if offset is None: if offset is None:
return object, None return object, None
m = cls.re_indirect_def_end.match(data, offset) m = cls.re_indirect_def_end.match(data, offset)
check_format_condition(m, "indirect object definition end not found") check_format_condition(
m, "indirect object definition end not found")
return object, m.end() return object, m.end()
check_format_condition(not expect_indirect, "indirect object definition not found") check_format_condition(
not expect_indirect, "indirect object definition not found")
m = cls.re_indirect_reference.match(data, offset) m = cls.re_indirect_reference.match(data, offset)
if m: if m:
check_format_condition(int(m.group(1)) > 0, "indirect object reference: object ID must be greater than 0") check_format_condition(
check_format_condition(int(m.group(2)) >= 0, "indirect object reference: generation must be non-negative") int(m.group(1)) > 0,
"indirect object reference: object ID must be greater than 0")
check_format_condition(
int(m.group(2)) >= 0,
"indirect object reference: generation must be non-negative")
return IndirectReference(int(m.group(1)), int(m.group(2))), m.end() return IndirectReference(int(m.group(1)), int(m.group(2))), m.end()
m = cls.re_dict_start.match(data, offset) m = cls.re_dict_start.match(data, offset)
if m: if m:
@ -682,10 +765,12 @@ class PdfParser:
result = {} result = {}
m = cls.re_dict_end.match(data, offset) m = cls.re_dict_end.match(data, offset)
while not m: while not m:
key, offset = cls.get_value(data, offset, max_nesting=max_nesting-1) key, offset = cls.get_value(
data, offset, max_nesting=max_nesting-1)
if offset is None: if offset is None:
return result, None return result, None
value, offset = cls.get_value(data, offset, max_nesting=max_nesting-1) value, offset = cls.get_value(
data, offset, max_nesting=max_nesting-1)
result[key] = value result[key] = value
if offset is None: if offset is None:
return result, None return result, None
@ -696,7 +781,9 @@ class PdfParser:
try: try:
stream_len = int(result[b"Length"]) stream_len = int(result[b"Length"])
except (TypeError, KeyError, ValueError): except (TypeError, KeyError, ValueError):
raise PdfFormatError("bad or missing Length in stream dict (%r)" % result.get(b"Length", None)) raise PdfFormatError(
"bad or missing Length in stream dict (%r)" %
result.get(b"Length", None))
stream_data = data[m.end():m.end() + stream_len] stream_data = data[m.end():m.end() + stream_len]
m = cls.re_stream_end.match(data, m.end() + stream_len) m = cls.re_stream_end.match(data, m.end() + stream_len)
check_format_condition(m, "stream end not found") check_format_condition(m, "stream end not found")
@ -711,7 +798,8 @@ class PdfParser:
result = [] result = []
m = cls.re_array_end.match(data, offset) m = cls.re_array_end.match(data, offset)
while not m: while not m:
value, offset = cls.get_value(data, offset, max_nesting=max_nesting-1) value, offset = cls.get_value(
data, offset, max_nesting=max_nesting-1)
result.append(value) result.append(value)
if offset is None: if offset is None:
return result, None return result, None
@ -734,18 +822,25 @@ class PdfParser:
return int(m.group(1)), m.end() return int(m.group(1)), m.end()
m = cls.re_real.match(data, offset) m = cls.re_real.match(data, offset)
if m: if m:
return float(m.group(1)), m.end() # XXX Decimal instead of float??? # XXX Decimal instead of float???
return float(m.group(1)), m.end()
m = cls.re_string_hex.match(data, offset) m = cls.re_string_hex.match(data, offset)
if m: if m:
hex_string = bytearray([b for b in m.group(1) if b in b"0123456789abcdefABCDEF"]) # filter out whitespace # filter out whitespace
hex_string = bytearray([
b for b in m.group(1)
if b in b"0123456789abcdefABCDEF"
])
if len(hex_string) % 2 == 1: if len(hex_string) % 2 == 1:
hex_string.append(ord(b"0")) # append a 0 if the length is not even - yes, at the end # append a 0 if the length is not even - yes, at the end
hex_string.append(ord(b"0"))
return bytearray.fromhex(hex_string.decode("us-ascii")), m.end() return bytearray.fromhex(hex_string.decode("us-ascii")), m.end()
m = cls.re_string_lit.match(data, offset) m = cls.re_string_lit.match(data, offset)
if m: if m:
return cls.get_literal_string(data, m.end()) return cls.get_literal_string(data, m.end())
# return None, offset # fallback (only for debugging) # return None, offset # fallback (only for debugging)
raise PdfFormatError("unrecognized object: " + repr(data[offset:offset+32])) raise PdfFormatError(
"unrecognized object: " + repr(data[offset:offset+32]))
re_lit_str_token = re.compile(br"(\\[nrtbf()\\])|(\\[0-9]{1,3})|(\\(\r\n|\r|\n))|(\r\n|\r|\n)|(\()|(\))") re_lit_str_token = re.compile(br"(\\[nrtbf()\\])|(\\[0-9]{1,3})|(\\(\r\n|\r|\n))|(\r\n|\r|\n)|(\()|(\))")
escaped_chars = { escaped_chars = {
@ -792,19 +887,24 @@ class PdfParser:
offset = m.end() offset = m.end()
raise PdfFormatError("unfinished literal string") raise PdfFormatError("unfinished literal string")
re_xref_section_start = re.compile(whitespace_optional + br"xref" + newline) re_xref_section_start = re.compile(
re_xref_subsection_start = re.compile(whitespace_optional + br"([0-9]+)" + whitespace_mandatory + br"([0-9]+)" + whitespace_optional + newline_only) whitespace_optional + br"xref" + newline)
re_xref_subsection_start = re.compile(
whitespace_optional + br"([0-9]+)" + whitespace_mandatory +
br"([0-9]+)" + whitespace_optional + newline_only)
re_xref_entry = re.compile(br"([0-9]{10}) ([0-9]{5}) ([fn])( \r| \n|\r\n)") re_xref_entry = re.compile(br"([0-9]{10}) ([0-9]{5}) ([fn])( \r| \n|\r\n)")
def read_xref_table(self, xref_section_offset): def read_xref_table(self, xref_section_offset):
subsection_found = False subsection_found = False
m = self.re_xref_section_start.match(self.buf, xref_section_offset + self.start_offset) m = self.re_xref_section_start.match(
self.buf, xref_section_offset + self.start_offset)
check_format_condition(m, "xref section start not found") check_format_condition(m, "xref section start not found")
offset = m.end() offset = m.end()
while True: while True:
m = self.re_xref_subsection_start.match(self.buf, offset) m = self.re_xref_subsection_start.match(self.buf, offset)
if not m: if not m:
check_format_condition(subsection_found, "xref subsection start not found") check_format_condition(
subsection_found, "xref subsection start not found")
break break
subsection_found = True subsection_found = True
offset = m.end() offset = m.end()
@ -818,22 +918,31 @@ class PdfParser:
generation = int(m.group(2)) generation = int(m.group(2))
if not is_free: if not is_free:
new_entry = (int(m.group(1)), generation) new_entry = (int(m.group(1)), generation)
check_format_condition(i not in self.xref_table or self.xref_table[i] == new_entry, "xref entry duplicated (and not identical)") check_format_condition(
i not in self.xref_table or
self.xref_table[i] == new_entry,
"xref entry duplicated (and not identical)")
self.xref_table[i] = new_entry self.xref_table[i] = new_entry
return offset return offset
def read_indirect(self, ref, max_nesting=-1): def read_indirect(self, ref, max_nesting=-1):
offset, generation = self.xref_table[ref[0]] offset, generation = self.xref_table[ref[0]]
check_format_condition(generation == ref[1], "expected to find generation %s for object ID %s in xref table, instead found generation %s at offset %s" check_format_condition(
generation == ref[1],
"expected to find generation %s for object ID %s in xref table, "
"instead found generation %s at offset %s"
% (ref[1], ref[0], generation, offset)) % (ref[1], ref[0], generation, offset))
value = self.get_value(self.buf, offset + self.start_offset, expect_indirect=IndirectReference(*ref), max_nesting=max_nesting)[0] value = self.get_value(self.buf, offset + self.start_offset,
expect_indirect=IndirectReference(*ref),
max_nesting=max_nesting)[0]
self.cached_objects[ref] = value self.cached_objects[ref] = value
return value return value
def linearize_page_tree(self, node=None): def linearize_page_tree(self, node=None):
if node is None: if node is None:
node = self.page_tree_root node = self.page_tree_root
check_format_condition(node[b"Type"] == b"Pages", "/Type of page tree node is not /Pages") check_format_condition(
node[b"Type"] == b"Pages", "/Type of page tree node is not /Pages")
pages = [] pages = []
for kid in node[b"Kids"]: for kid in node[b"Kids"]:
kid_object = self.read_indirect(kid) kid_object = self.read_indirect(kid)

View File

@ -142,7 +142,8 @@ class ChunkStream(object):
def crc(self, cid, data): def crc(self, cid, data):
"Read and verify checksum" "Read and verify checksum"
# Skip CRC checks for ancillary chunks if allowed to load truncated images # Skip CRC checks for ancillary chunks if allowed to load truncated
# images
# 5th byte of first char is 1 [specs, section 5.4] # 5th byte of first char is 1 [specs, section 5.4]
if ImageFile.LOAD_TRUNCATED_IMAGES and (i8(cid[0]) >> 5 & 1): if ImageFile.LOAD_TRUNCATED_IMAGES and (i8(cid[0]) >> 5 & 1):
self.crc_skip(cid, data) self.crc_skip(cid, data)
@ -301,8 +302,8 @@ class PngStream(ChunkStream):
def check_text_memory(self, chunklen): def check_text_memory(self, chunklen):
self.text_memory += chunklen self.text_memory += chunklen
if self.text_memory > MAX_TEXT_MEMORY: if self.text_memory > MAX_TEXT_MEMORY:
raise ValueError("Too much memory used in text chunks: %s>MAX_TEXT_MEMORY" % raise ValueError("Too much memory used in text chunks: "
self.text_memory) "%s>MAX_TEXT_MEMORY" % self.text_memory)
def chunk_iCCP(self, pos, length): def chunk_iCCP(self, pos, length):

View File

@ -83,7 +83,8 @@ class PpmImageFile(ImageFile.ImageFile):
if s not in b_whitespace: if s not in b_whitespace:
break break
if s == b"": if s == b"":
raise ValueError("File does not extend beyond magic number") raise ValueError(
"File does not extend beyond magic number")
if s != b"#": if s != b"#":
break break
s = self.fp.readline() s = self.fp.readline()

View File

@ -222,6 +222,7 @@ Image.register_save(SgiImageFile.format, _save)
Image.register_mime(SgiImageFile.format, "image/sgi") Image.register_mime(SgiImageFile.format, "image/sgi")
Image.register_mime(SgiImageFile.format, "image/rgb") Image.register_mime(SgiImageFile.format, "image/rgb")
Image.register_extensions(SgiImageFile.format, [".bw", ".rgb", ".rgba", ".sgi"]) Image.register_extensions(SgiImageFile.format,
[".bw", ".rgb", ".rgba", ".sgi"])
# End of file # End of file

View File

@ -94,7 +94,8 @@ class SunImageFile(ImageFile.ImageFile):
raise SyntaxError("Unsupported Palette Type") raise SyntaxError("Unsupported Palette Type")
offset = offset + palette_length offset = offset + palette_length
self.palette = ImagePalette.raw("RGB;L", self.fp.read(palette_length)) self.palette = ImagePalette.raw("RGB;L",
self.fp.read(palette_length))
if self.mode == "L": if self.mode == "L":
self.mode = "P" self.mode = "P"
rawmode = rawmode.replace('L', 'P') rawmode = rawmode.replace('L', 'P')

View File

@ -431,7 +431,8 @@ class ImageFileDirectory_v2(MutableMapping):
* self.tagtype = {} * self.tagtype = {}
* Key: numerical tiff tag number * Key: numerical tiff tag number
* Value: integer corresponding to the data type from `~PIL.TiffTags.TYPES` * Value: integer corresponding to the data type from
~PIL.TiffTags.TYPES`
.. versionadded:: 3.0.0 .. versionadded:: 3.0.0
""" """

View File

@ -23,7 +23,8 @@ from collections import namedtuple
class TagInfo(namedtuple("_TagInfo", "value name type length enum")): class TagInfo(namedtuple("_TagInfo", "value name type length enum")):
__slots__ = [] __slots__ = []
def __new__(cls, value=None, name="unknown", type=None, length=None, enum=None): def __new__(cls, value=None, name="unknown",
type=None, length=None, enum=None):
return super(TagInfo, cls).__new__( return super(TagInfo, cls).__new__(
cls, value, name, type, length, enum or {}) cls, value, name, type, length, enum or {})
@ -72,8 +73,8 @@ TAGS_V2 = {
257: ("ImageLength", LONG, 1), 257: ("ImageLength", LONG, 1),
258: ("BitsPerSample", SHORT, 0), 258: ("BitsPerSample", SHORT, 0),
259: ("Compression", SHORT, 1, 259: ("Compression", SHORT, 1,
{"Uncompressed": 1, "CCITT 1d": 2, "Group 3 Fax": 3, "Group 4 Fax": 4, {"Uncompressed": 1, "CCITT 1d": 2, "Group 3 Fax": 3,
"LZW": 5, "JPEG": 6, "PackBits": 32773}), "Group 4 Fax": 4, "LZW": 5, "JPEG": 6, "PackBits": 32773}),
262: ("PhotometricInterpretation", SHORT, 1, 262: ("PhotometricInterpretation", SHORT, 1,
{"WhiteIsZero": 0, "BlackIsZero": 1, "RGB": 2, "RGB Palette": 3, {"WhiteIsZero": 0, "BlackIsZero": 1, "RGB": 2, "RGB Palette": 3,

View File

@ -22,7 +22,8 @@
from __future__ import print_function from __future__ import print_function
from . import Image, ImageFile from . import Image, ImageFile
from ._binary import i16le as word, si16le as short, i32le as dword, si32le as _long from ._binary import i16le as word, si16le as short, \
i32le as dword, si32le as _long
from ._util import py3 from ._util import py3