From 954c7ca699e3e799da7e9f45540c48c394cbbf97 Mon Sep 17 00:00:00 2001 From: Joey Payne Date: Mon, 25 Jan 2016 11:39:33 -0700 Subject: [PATCH 1/2] Switched to python 3. --- __init__.py | 2 +- bmpimageplugin.py | 11 +++++----- ico_plugin.py | 33 ++++++++++++++--------------- pe.py | 54 +++++++++++++++++++++++------------------------ 4 files changed, 48 insertions(+), 52 deletions(-) diff --git a/__init__.py b/__init__.py index 9004c8c..866ed65 100644 --- a/__init__.py +++ b/__init__.py @@ -1,3 +1,3 @@ __all__ = ['pe'] -from pe import * +from .pe import * diff --git a/bmpimageplugin.py b/bmpimageplugin.py index 968254a..4733b10 100644 --- a/bmpimageplugin.py +++ b/bmpimageplugin.py @@ -29,7 +29,7 @@ __version__ = "0.7" from PIL import Image, ImageFile, ImagePalette, _binary from PIL.BmpImagePlugin import * #This is a hack to override the default bmp plugin for PIL -from cStringIO import StringIO +from io import BytesIO import math i8 = _binary.i8 @@ -132,7 +132,7 @@ class BmpImageFile(ImageFile.ImageFile): elif bits == 16 and mask == (0x007c00, 0x0003e0, 0x00001f): rawmode = "BGR;15" else: - print bits, map(hex, mask) + print(bits, map(hex, mask)) raise IOError("Unsupported BMP bitfields layout") elif compression != 0: raise IOError("Unsupported BMP compression (%d)" % compression) @@ -204,7 +204,7 @@ class DibImageFile(BmpImageFile): def to_bitmapimage(self, header): self.fp.seek(header['offset']) d = bytearray(self.fp.read(header['size'])) - print self.info + print(self.info) dpi = (96,96) ppm = tuple(map(lambda x: int(x * 39.3701), dpi)) @@ -213,7 +213,7 @@ class DibImageFile(BmpImageFile): dib_size = i32(str(d[:4])) offset = 14 + dib_size - data = StringIO() + data = BytesIO() data.write(b'BM'+ o32(14+len(d)) + o32(0) + @@ -262,7 +262,6 @@ def _save(im, fp, filename, check=0): green_mask = 0x0000ff00 blue_mask = 0x000000ff alpha_mask = 0xff000000 - # bitmap header fp.write(b"BM" + # file type (magic) o32(offset+image+16) + # file size @@ -295,7 +294,7 @@ def _save(im, fp, filename, check=0): o32(green_mask) + # green channel mask o32(blue_mask) + # blue channel mask o32(alpha_mask) + # alpha channel mask - 'BGRs' + # Color Space + b'BGRs' + # Color Space o8(0)*0x24 + # ciexyztriple color space endpoints o32(0) + # red gamma o32(0) + # green gamma diff --git a/ico_plugin.py b/ico_plugin.py index 1aeed0f..b7bcb27 100644 --- a/ico_plugin.py +++ b/ico_plugin.py @@ -25,9 +25,8 @@ __version__ = "0.1" from PIL import Image, ImageFile, BmpImagePlugin, PngImagePlugin, _binary -import bmpimageplugin -from bmpimageplugin import * -from cStringIO import StringIO +from .bmpimageplugin import * +from io import BytesIO, StringIO from math import log, ceil i8 = _binary.i8 @@ -63,7 +62,7 @@ class IcoFile: self.nb_items = i16(s[4:]) # Get headers for each item - for i in xrange(self.nb_items): + for i in range(self.nb_items): s = buf.read(16) icon_header = { @@ -137,18 +136,18 @@ class IcoFile: if data[:8] == PngImagePlugin._MAGIC: # png frame - s = StringIO() + s = BytesIO() s.write(self.buf.read(header['size'])) s.seek(0) self.buf.seek(header['offset']) im = Image.open(s) else: # XOR + AND mask bmp frame - s = StringIO() + s = BytesIO() s.write(self.buf.read(header['size'])) s.seek(0) self.buf.seek(header['offset']) - im = bmpimageplugin.DibImageFile(s) + im = DibImageFile(s) #im = im.to_bitmapimage(header) #print im.tile # change tile dimension to only encompass XOR image @@ -272,7 +271,7 @@ SAVE = { } def get_data(im): - s = StringIO() + s = BytesIO() if im.format != 'DIB': im.save(s, im.format) else: @@ -284,9 +283,9 @@ def get_data(im): bmp_f.seek(10) offset = i32(bmp_f.read(4)) dib_size = i32(bmp_f.read(4)) - dib = o32(dib_size)+bytearray(bmp_f.read(36)) + dib = bytearray(o32(dib_size)+bytearray(bmp_f.read(36))) dib[:4] = o32(40) - dib[8:12] = o32(i32(str(dib[8:12]))*2) + dib[8:12] = o32(i32(dib[8:12])*2) dib[16:20] = o32(0) dib = dib[:40] bmp_f.seek(offset) @@ -303,7 +302,7 @@ def _save(image, fp, filename, check=0): data = None images = [] if image.format != 'ICO': - bmp_f = StringIO() + bmp_f = BytesIO() image.save(bmp_f, format='BMP') bmp_f.seek(0) im2 = Image.open(bmp_f) @@ -336,13 +335,13 @@ def _save(image, fp, filename, check=0): #write matte data. Taken from imagemagick scanline_pad = (((im.size[0]+31) & ~31)-im.size[0]) >> 3 row_len = im.size[0]*4 - for y in reversed(xrange(im.size[1])): + for y in reversed(range(im.size[1])): d = data[40:] #BGRA row_pixels = d[row_len*y:row_len*y+row_len] bit=0 byte=0 - for x in xrange(im.size[0]): + for x in range(im.size[0]): p=row_pixels[x] byte<<=1 @@ -353,7 +352,7 @@ def _save(image, fp, filename, check=0): byte=0 if not bit == 0: matte_data+=o8(byte<<(8-bit)) - for i in xrange(scanline_pad): + for i in range(scanline_pad): matte_data+=o8(0) fp.write(o8(im.size[0]) + # width @@ -378,13 +377,13 @@ def _save(image, fp, filename, check=0): #write matte data. Taken from imagemagick scanline_pad = (((im.size[0]+31) & ~31)-im.size[0]) >> 3 row_len = im.size[0]*4 - for y in reversed(xrange(im.size[1])): + for y in reversed(range(im.size[1])): d = data[40:] #BGRA row_pixels = d[row_len*y:row_len*y+row_len] bit=0 byte=0 - for x in xrange(im.size[0]): + for x in range(im.size[0]): p=row_pixels[x] byte<<=1 @@ -395,7 +394,7 @@ def _save(image, fp, filename, check=0): byte=0 if not bit == 0: fp.write(o8(byte<<(8-bit))) - for i in xrange(scanline_pad): + for i in range(scanline_pad): fp.write(o8(0)) diff --git a/pe.py b/pe.py index 1b7a36b..887fd1f 100644 --- a/pe.py +++ b/pe.py @@ -1,18 +1,18 @@ import os import struct -from cStringIO import StringIO +from io import BytesIO -from ico_plugin import * +from .ico_plugin import * def resize(image, size, format=None): - output = StringIO() + output = BytesIO() back = Image.new('RGBA', size, (0,0,0,0)) image.thumbnail(size, Image.ANTIALIAS) offset = [0,0] if image.size[0] >= image.size[1]: - offset[1] = back.size[1]/2-image.size[1]/2 + offset[1] = int(back.size[1]/2-image.size[1]/2) else: - offset[0] = back.size[0]/2-image.size[0]/2 + offset[0] = int(back.size[0]/2-image.size[0]/2) back.paste(image, tuple(offset)) format = format or image.format back.save(output, format) @@ -166,7 +166,7 @@ name_dictionary = {'PEHeader_Machine': { 0x10000000:'IMAGE_SCN_MEM_SHARED', 0x20000000:'IMAGE_SCN_MEM_EXECUTE', 0x40000000:'IMAGE_SCN_MEM_READ', - 0x80000000L:'IMAGE_SCN_MEM_WRITE', + 0x80000000:'IMAGE_SCN_MEM_WRITE', }, } @@ -195,9 +195,8 @@ def read_bytes(file_data, offset, number_of_bytes, endian=None, string_data=None endian = endian or DEFAULT_ENDIAN endian = endian_symbols[endian] - data = str(file_data[offset:offset+number_of_bytes]) + data = bytes(file_data[offset:offset+number_of_bytes]) if len(data) != number_of_bytes: - #print 'data out of bounds:', 'offset', hex(offset), 'data', data, 'data_len', len(data), 'num_bytes', number_of_bytes, 'total', hex(len(file_data)) return 0, u'' return struct.unpack(endian+struct_symbols[number_of_bytes], data)[0], data @@ -305,12 +304,9 @@ class Printable(object): return u', '.join(vals) def __repr__(self): - return unicode(self) + return str(self) def __str__(self): - return unicode(self).encode('utf-8') - - def __unicode__(self): return u'{} [{}]'.format(self.__class__.__name__, self._dict_string()) @@ -385,7 +381,7 @@ class Structure(Printable): bit_length = len(bin(int_value))-2 characteristics = {} - for i in xrange(bit_length): + for i in range(bit_length): set_bit = test_bit(int_value, i) char_name = field_name_dict.get(set_bit, '') if set_bit != 0 and char_name: @@ -726,13 +722,14 @@ class OptionalHeader(Structure): """Parses the Structure from the file data.""" self = cls(**cls_args) self._file_data = file_data - magic, _ = read_bytes(file_data, self.absolute_offset, 2) + magic, x = read_bytes(file_data, self.absolute_offset, 2) if magic == _32BIT_MAGIC: self._fields = self._fields_32 elif magic == _32BIT_PLUS_MAGIC: self._fields = self._fields_32_plus else: + print(magic, _32BIT_MAGIC, _32BIT_PLUS_MAGIC) raise PEFormatError('Magic for Optional Header is invalid.') for field_name, field_info in self._fields.items(): @@ -859,9 +856,9 @@ class ResourceDirectoryString(Structure): size = field_info['size'] self.size += size data = u'' - for i in xrange(size): + for i in range(size): val, dat = read_bytes(file_data, absolute_offset+i*2,2) - data += dat + data += str(dat, 'utf-8') setattr(self, field_name, Structure(offset=field_info['offset'], size=size, @@ -983,7 +980,7 @@ class IconHeader(Structure): self.entries = [] entry_offset = 0 self.total_size = self.size - for i in xrange(self.ImageCount.value): + for i in range(self.ImageCount.value): entry = IconEntry.parse_from_data(file_data, absolute_offset=self.absolute_offset+self.size+entry_offset, offset=entry_offset) entry.number = i + 1 self.entries.append(entry) @@ -1015,7 +1012,7 @@ class GroupHeader(Structure): entry_offset = 0 self.total_size = self.size for icon_entry in icon_header.entries: - group_entry = GroupEntry.parse_from_data(bytearray(''), absolute_offset=self.absolute_offset+self.size+entry_offset, offset=entry_offset) + group_entry = GroupEntry.parse_from_data(bytearray(b''), absolute_offset=self.absolute_offset+self.size+entry_offset, offset=entry_offset) group_entry._file_data = self._file_data group_entry.copy_from(icon_entry) group_entry.number = icon_entry.number @@ -1036,7 +1033,7 @@ class GroupHeader(Structure): self.entries = [] entry_offset = 0 self.total_size = self.size - for i in xrange(self.ResourceCount.value): + for i in range(self.ResourceCount.value): entry = GroupEntry.parse_from_data(file_data, absolute_offset=self.absolute_offset+self.size+entry_offset, offset=entry_offset) entry.number = i + 1 self.entries.append(entry) @@ -1078,7 +1075,7 @@ class IconEntry(Structure): offset = 6 #Default icon header size offset += self.size * len(group_entries) - for i in xrange(group_entry.number-1): + for i in range(group_entry.number-1): offset += group_entries[i].DataSize.value return offset @@ -1141,7 +1138,7 @@ class PEFile(Printable): Right now this only reads the .rsrc section. """ - signature = 'MZ' + signature = b'MZ' dos_header = None def __init__(self, file_path, endian='little'): @@ -1162,18 +1159,19 @@ class PEFile(Printable): section_offset = self.pe_header.size+self.pe_header.absolute_offset+self.pe_header.SizeOfOptionalHeader.value self.sections = {} - for section_number in xrange(number_of_sections): + for section_number in range(number_of_sections): section_header = SectionHeader.parse_from_data(self.pe_file_data, absolute_offset=section_offset) section_offset += section_size - self.sections[section_header.Name.data.strip('\x00')] = section_header + header_name = str(section_header.Name.data, 'utf-8').strip('\x00') + self.sections[header_name] = section_header if section_header.PointerToLineNumbers.value != 0: - print '{} section contains line number COFF table, which is not implemented yet.'.format(section_header.Name) + print('{} section contains line number COFF table, which is not implemented yet.'.format(section_header.Name)) if section_header.PointerToRelocations.value != 0: - print '{} section contains relocation table, which is not implemented yet.'.format(section_header.Name) + print('{} section contains relocation table, which is not implemented yet.'.format(section_header.Name)) - if section_header.Name.data == '.rsrc\x00\x00\x00': + if section_header.Name.data == b'.rsrc\x00\x00\x00': current_table_pointer = section_header.PointerToRawData.value current_resource_directory_table = ResourceDirectoryTable.parse_from_data(self.pe_file_data, absolute_offset=current_table_pointer, _section_header=section_header, type=None) self.resource_directory_table = current_resource_directory_table @@ -1188,7 +1186,7 @@ class PEFile(Printable): num_id_entries = resource_directory_table.NumberOfIDEntries.value current_offset = resource_directory_table.absolute_offset + resource_directory_table.size - for i in xrange(num_name_entries): + for i in range(num_name_entries): name_entry = ResourceDirectoryEntryName.parse_from_data(self.pe_file_data, absolute_offset=current_offset, _section_header=section_header) current_offset += name_entry.size @@ -1209,7 +1207,7 @@ class PEFile(Printable): resource_directory_table.name_entries.append(name_entry) - for i in xrange(num_id_entries): + for i in range(num_id_entries): id_entry = ResourceDirectoryEntryID.parse_from_data(self.pe_file_data, absolute_offset=current_offset, _section_header=section_header) current_offset += id_entry.size From e3c92ebd67b4fd24bfc10018c6eb525ef6e3e921 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sat, 14 Sep 2019 10:44:17 -0600 Subject: [PATCH 2/2] Fix image replacement --- bmpimageplugin.py | 326 ------------------------------------ ico_plugin.py | 410 ---------------------------------------------- pe.py | 5 +- 3 files changed, 3 insertions(+), 738 deletions(-) delete mode 100644 bmpimageplugin.py delete mode 100644 ico_plugin.py diff --git a/bmpimageplugin.py b/bmpimageplugin.py deleted file mode 100644 index 4733b10..0000000 --- a/bmpimageplugin.py +++ /dev/null @@ -1,326 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# BMP file handler -# -# Windows (and OS/2) native bitmap storage format. -# -# history: -# 1995-09-01 fl Created -# 1996-04-30 fl Added save -# 1997-08-27 fl Fixed save of 1-bit images -# 1998-03-06 fl Load P images as L where possible -# 1998-07-03 fl Load P images as 1 where possible -# 1998-12-29 fl Handle small palettes -# 2002-12-30 fl Fixed load of 1-bit palette images -# 2003-04-21 fl Fixed load of 1-bit monochrome images -# 2003-04-23 fl Added limited support for BI_BITFIELDS compression -# -# Copyright (c) 1997-2003 by Secret Labs AB -# Copyright (c) 1995-2003 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# - - -__version__ = "0.7" - - -from PIL import Image, ImageFile, ImagePalette, _binary -from PIL.BmpImagePlugin import * #This is a hack to override the default bmp plugin for PIL -from io import BytesIO -import math - -i8 = _binary.i8 -i16 = _binary.i16le -i32 = _binary.i32le -o8 = _binary.o8 -o16 = _binary.o16le -o32 = _binary.o32le - -# -# -------------------------------------------------------------------- -# Read BMP file - -BIT2MODE = { - # bits => mode, rawmode - 1: ("P", "P;1"), - 4: ("P", "P;4"), - 8: ("P", "P"), - 16: ("RGB", "BGR;15"), - 24: ("RGB", "BGR"), - 32: ("RGBA", "BGRA") -} - - -def _accept(prefix): - return prefix[:2] == b"BM" - - -## -# Image plugin for the Windows BMP format. - -class BmpImageFile(ImageFile.ImageFile): - - format = "BMP" - format_description = "Windows Bitmap" - - def _bitmap(self, header=0, offset=0): - if header: - self.fp.seek(header) - - read = self.fp.read - - if not offset: - offset = self.fp.tell() - # CORE/INFO - s = read(4) - s = s + ImageFile._safe_read(self.fp, i32(s)-4) - - if len(s) == 12: - - # OS/2 1.0 CORE - bits = i16(s[10:]) - self.size = i16(s[4:]), i16(s[6:]) - compression = 0 - lutsize = 3 - colors = 0 - direction = -1 - - elif len(s) in [40, 64, 108, 124]: - - # WIN 3.1 or OS/2 2.0 INFO - bits = i16(s[14:]) - self.size = i32(s[4:]), i32(s[8:]) - compression = i32(s[16:]) - pxperm = (i32(s[24:]), i32(s[28:])) # Pixels per meter - lutsize = 4 - colors = i32(s[32:]) - direction = -1 - if i8(s[11]) == 0xff: - # upside-down storage - self.size = self.size[0], 2**32 - self.size[1] - direction = 0 - - self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), - pxperm)) - - else: - raise IOError("Unsupported BMP header type (%d)" % len(s)) - - if (self.size[0]*self.size[1]) > 2**31: - # Prevent DOS for > 2gb images - raise IOError("Unsupported BMP Size: (%dx%d)" % self.size) - - if not colors: - colors = 1 << bits - - # MODE - try: - self.mode, rawmode = BIT2MODE[bits] - except KeyError: - raise IOError("Unsupported BMP pixel depth (%d)" % bits) - - if compression == 3: - # BI_BITFIELDS compression - mask = i32(s[0x36-14:]), i32(s[0x3a-14:]), i32(s[0x3E-14:]), i32(s[0x42-14:]) - if bits == 32 and mask == (0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000): - rawmode = "BGRA" - elif bits == 16 and mask == (0x00f800, 0x0007e0, 0x00001f): - rawmode = "BGR;16" - elif bits == 16 and mask == (0x007c00, 0x0003e0, 0x00001f): - rawmode = "BGR;15" - else: - print(bits, map(hex, mask)) - raise IOError("Unsupported BMP bitfields layout") - elif compression != 0: - raise IOError("Unsupported BMP compression (%d)" % compression) - - # LUT - if self.mode == "P": - palette = [] - greyscale = 1 - if colors == 2: - indices = (0, 255) - elif colors > 2**16 or colors <= 0: # We're reading a i32. - raise IOError("Unsupported BMP Palette size (%d)" % colors) - else: - indices = list(range(colors)) - for i in indices: - rgb = read(lutsize)[:3] - if rgb != o8(i)*3: - greyscale = 0 - palette.append(rgb) - if greyscale: - if colors == 2: - self.mode = rawmode = "1" - else: - self.mode = rawmode = "L" - else: - self.mode = "P" - self.palette = ImagePalette.raw( - "BGR", b"".join(palette) - ) - - if not offset: - offset = self.fp.tell() - - self.tile = [("raw", - (0, 0) + self.size, - offset, - (rawmode, ((self.size[0]*bits+31) >> 3) & (~3), - direction))] - - self.info["compression"] = compression - - def _open(self): - - # HEAD - s = self.fp.read(14) - if s[:2] != b"BM": - raise SyntaxError("Not a BMP file") - offset = i32(s[10:]) - - self._bitmap(offset=offset) - -class DibImageFile(BmpImageFile): - - format = "DIB" - format_description = "Windows Bitmap" - - def __init__(self, buf, *args, **kwargs): - BmpImageFile.__init__(self, buf, *args, **kwargs) - buf.seek(0) - self.buf = buf.read() - buf.seek(0) - - def _open(self): - self._bitmap() - - def get_file_data(self): - return self.buf - - def to_bitmapimage(self, header): - self.fp.seek(header['offset']) - d = bytearray(self.fp.read(header['size'])) - print(self.info) - dpi = (96,96) - ppm = tuple(map(lambda x: int(x * 39.3701), dpi)) - - d[24:28] = o32(ppm[0]) - d[28:32] = o32(ppm[1]) - - dib_size = i32(str(d[:4])) - offset = 14 + dib_size - data = BytesIO() - data.write(b'BM'+ - o32(14+len(d)) + - o32(0) + - o32(offset)) - - data.write(d) - data.seek(0) - new_image = Image.open(data) - return new_image - -# -# -------------------------------------------------------------------- -# Write BMP file - -SAVE = { - "1": ("1", 1, 2), - "L": ("L", 8, 256), - "P": ("P", 8, 256), - "RGB": ("BGR", 24, 0), - "RGBA": ("BGRA", 32, 0), -} - - -def _save(im, fp, filename, check=0): - try: - rawmode, bits, colors = SAVE[im.mode] - except KeyError: - raise IOError("cannot write mode %s as BMP" % im.mode) - - if check: - return check - - info = im.encoderinfo - - dpi = info.get("dpi", (96, 96)) - - # 1 meter == 39.3701 inches - ppm = tuple(map(lambda x: int(x * 39.3701), dpi)) - - stride = ((im.size[0]*bits+7)//8+3) & (~3) - header = 108 if im.mode == 'RGBA' else 40 # or 64 for OS/2 version 2 - offset = 14 + header + colors*4 - image = stride * im.size[1] - - red_mask = 0x00ff0000 - green_mask = 0x0000ff00 - blue_mask = 0x000000ff - alpha_mask = 0xff000000 - # bitmap header - fp.write(b"BM" + # file type (magic) - o32(offset+image+16) + # file size - o32(0) + # reserved - o32(offset+16)) # image data offset - - width,height = im.size - - # bitmap info header - fp.write(o32(header+16) + # info header size - o32(width) + # width - o32(height) + # height - o16(1) + # planes - o16(bits) + # depth - o32(3) + # compression (0=uncompressed) - o32(image) + # size of bitmap - o32(ppm[0]) + o32(ppm[1]) + # resolution - o32(colors) + # colors used - o32(colors) # colors important - #o32(red_mask) + # red channel ma - #o32(green_mask) + # green channel mask - #o32(blue_mask) + # blue channel mask - #o32(alpha_mask) - ) - # This was commented out because although it works, some - # decoders do not support images with a BI_BITFIELDS compression - # - if im.mode == 'RGBA': - fp.write(o32(red_mask) + # red channel mask - o32(green_mask) + # green channel mask - o32(blue_mask) + # blue channel mask - o32(alpha_mask) + # alpha channel mask - b'BGRs' + # Color Space - o8(0)*0x24 + # ciexyztriple color space endpoints - o32(0) + # red gamma - o32(0) + # green gamma - o32(0) # blue gamma - ) - - fp.write(bytearray(16)) - - if im.mode == "1": - for i in (0, 255): - fp.write(o8(i) * 4) - elif im.mode == "L": - for i in range(256): - fp.write(o8(i) * 4) - elif im.mode == "P": - fp.write(im.im.getpalette("RGB", "BGRX")) - - #fp.write( - ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, - (rawmode, stride, -1))]) - -# -# -------------------------------------------------------------------- -# Registry - -Image.register_open(BmpImageFile.format, BmpImageFile, _accept) -Image.register_save(BmpImageFile.format, _save) - -Image.register_extension(BmpImageFile.format, ".bmp") diff --git a/ico_plugin.py b/ico_plugin.py deleted file mode 100644 index b7bcb27..0000000 --- a/ico_plugin.py +++ /dev/null @@ -1,410 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# Windows Icon support for PIL -# -# History: -# 96-05-27 fl Created -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1996. -# -# See the README file for information on usage and redistribution. -# - -# This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis -# . -# https://code.google.com/p/casadebender/wiki/Win32IconImagePlugin -# -# Icon format references: -# * http://en.wikipedia.org/wiki/ICO_(file_format) -# * http://msdn.microsoft.com/en-us/library/ms997538.aspx - - -__version__ = "0.1" - -from PIL import Image, ImageFile, BmpImagePlugin, PngImagePlugin, _binary -from .bmpimageplugin import * -from io import BytesIO, StringIO -from math import log, ceil - -i8 = _binary.i8 -i16 = _binary.i16le -i32 = _binary.i32le -o8 = _binary.o8 -o16 = _binary.o16le -o32 = _binary.o32le - -_MAGIC = b"\0\0\1\0" - - -def _accept(prefix): - return prefix[:4] == _MAGIC - - -class IcoFile: - format = 'ICO' - def __init__(self, buf): - """ - Parse image from file-like object containing ico file data - """ - - # check magic - s = buf.read(6) - if not _accept(s): - raise SyntaxError("not an ICO file") - - self.buf = buf - self.entries = [] - - # Number of items in file - self.nb_items = i16(s[4:]) - - # Get headers for each item - for i in range(self.nb_items): - s = buf.read(16) - - icon_header = { - 'width': i8(s[0]), - 'height': i8(s[1]), - 'nb_color': i8(s[2]), # No. of colors in image (0 if >=8bpp) - 'reserved': i8(s[3]), - 'planes': i16(s[4:]), - 'bpp': i16(s[6:]), - 'size': i32(s[8:]), - 'offset': i32(s[12:]) - } - - # See Wikipedia - for j in ('width', 'height'): - if not icon_header[j]: - icon_header[j] = 256 - - # See Wikipedia notes about color depth. - # We need this just to differ images with equal sizes - icon_header['color_depth'] = (icon_header['bpp'] or - (icon_header['nb_color'] != 0 and - ceil(log(icon_header['nb_color'], - 2))) or 256) - - icon_header['dim'] = (icon_header['width'], icon_header['height']) - icon_header['square'] = (icon_header['width'] * - icon_header['height']) - - self.entries.append(icon_header) - - self.entries = sorted(self.entries, key=lambda x: x['color_depth']) - # ICO images are usually squares - # self.entries = sorted(self.entries, key=lambda x: x['width']) - self.entries = sorted(self.entries, key=lambda x: x['square']) - self.entries.reverse() - - def sizes(self): - """ - Get a list of all available icon sizes and color depths. - """ - return set((h['width'], h['height']) for h in self.entries) - - def getimage(self, size, bpp=False): - """ - Get an image from the icon - """ - for (i, h) in enumerate(self.entries): - if size == h['dim'] and (bpp is False or bpp == h['color_depth']): - return self.frame(i) - return self.frame(0) - - def get_images(self): - images = [] - for i, header in enumerate(self.entries): - f = self.frame(i) - if f.format: - images.append(self.frame(i)) - return images - - def frame(self, idx): - """ - Get an image from frame idx - """ - - header = self.entries[idx] - - self.buf.seek(header['offset']) - data = self.buf.read(8) - self.buf.seek(header['offset']) - - if data[:8] == PngImagePlugin._MAGIC: - # png frame - s = BytesIO() - s.write(self.buf.read(header['size'])) - s.seek(0) - self.buf.seek(header['offset']) - im = Image.open(s) - else: - # XOR + AND mask bmp frame - s = BytesIO() - s.write(self.buf.read(header['size'])) - s.seek(0) - self.buf.seek(header['offset']) - im = DibImageFile(s) - #im = im.to_bitmapimage(header) - #print im.tile - # change tile dimension to only encompass XOR image - im.size = (im.size[0], int(im.size[1] / 2)) - #print im.size - d, e, o, a = im.tile[0] - im.tile[0] = d, (0, 0) + im.size, o, a - - # figure out where AND mask image starts - mode = a[0] - bpp = 8 - for k in BmpImagePlugin.BIT2MODE.keys(): - if mode == BmpImagePlugin.BIT2MODE[k][1]: - bpp = k - break - - if 32 == bpp: - # 32-bit color depth icon image allows semitransparent areas - # PIL's DIB format ignores transparency bits, recover them. - # The DIB is packed in BGRX byte order where X is the alpha - # channel. - - # Back up to start of bmp data - self.buf.seek(o) - # extract every 4th byte (eg. 3,7,11,15,...) - alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4] - - # convert to an 8bpp grayscale image - mask = Image.frombuffer( - 'L', # 8bpp - im.size, # (w, h) - alpha_bytes, # source chars - 'raw', # raw decoder - ('L', 0, -1) # 8bpp inverted, unpadded, reversed - ) - else: - # get AND image from end of bitmap - w = im.size[0] - if (w % 32) > 0: - # bitmap row data is aligned to word boundaries - w += 32 - (im.size[0] % 32) - - # the total mask data is - # padded row size * height / bits per char - - and_mask_offset = o + int(im.size[0] * im.size[1] * - (bpp / 8.0)) - total_bytes = int((w * im.size[1]) / 8) - - self.buf.seek(and_mask_offset) - maskData = self.buf.read(total_bytes) - - # convert raw data to image - mask = Image.frombuffer( - '1', # 1 bpp - im.size, # (w, h) - maskData, # source chars - 'raw', # raw decoder - ('1;I', int(w/8), -1) # 1bpp inverted, padded, reversed - ) - - # now we have two images, im is XOR image and mask is AND image - - if im.mode != 'RGBA': - im = im.convert('RGBA') - im.putalpha(mask) - - return im - - -## -# Image plugin for Windows Icon files. - -class IcoImageFile(ImageFile.ImageFile): - """ - PIL read-only image support for Microsoft Windows .ico files. - - By default the largest resolution image in the file will be loaded. This - can be changed by altering the 'size' attribute before calling 'load'. - - The info dictionary has a key 'sizes' that is a list of the sizes available - in the icon file. - - Handles classic, XP and Vista icon formats. - - This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis - . - https://code.google.com/p/casadebender/wiki/Win32IconImagePlugin - """ - format = "ICO" - format_description = "Windows Icon" - - def _open(self): - self.ico = IcoFile(self.fp) - self.info['sizes'] = self.ico.sizes() - self.size = self.ico.entries[0]['dim'] - self.load() - - def load(self): - im = self.ico.getimage(self.size) - # if tile is PNG, it won't really be loaded yet - im.load() - self.im = im.im - self.mode = im.mode - self.size = im.size - - def get_images(self): - return self.ico.get_images() - - def load_seek(self): - # Flage the ImageFile.Parser so that it - # just does all the decode at the end. - pass - -SAVE = { - "1": ("1", 1, 2), - "L": ("L", 8, 256), - "P": ("P", 8, 256), - "RGB": ("BGR", 24, 0), - "RGBA": ("BGRA", 32, 0), -} - -def get_data(im): - s = BytesIO() - if im.format != 'DIB': - im.save(s, im.format) - else: - s.write(im.buf) - s.seek(0) - - if im.format == 'BMP': - bmp_f = s - bmp_f.seek(10) - offset = i32(bmp_f.read(4)) - dib_size = i32(bmp_f.read(4)) - dib = bytearray(o32(dib_size)+bytearray(bmp_f.read(36))) - dib[:4] = o32(40) - dib[8:12] = o32(i32(dib[8:12])*2) - dib[16:20] = o32(0) - dib = dib[:40] - bmp_f.seek(offset) - data = bytearray(bmp_f.read()) - data = dib+data - else: - data = bytearray(s.read()) - - return data - - - -def _save(image, fp, filename, check=0): - data = None - images = [] - if image.format != 'ICO': - bmp_f = BytesIO() - image.save(bmp_f, format='BMP') - bmp_f.seek(0) - im2 = Image.open(bmp_f) - if im2.format: - images.append(im2) - else: - images = image.get_images() - - number_of_images = len(images) - - header_size = 6+16*number_of_images - - # ico header - fp.write(o16(0) + # reserved - o16(1) + # file type - o16(number_of_images)) # number of images - - current_offset = header_size - for i, im in enumerate(images): - - data = get_data(im) - - try: - rawmode, bits, colors = SAVE[im.mode] - except KeyError: - raise IOError("cannot write mode %s as BMP" % im.mode) - matte_data = bytearray() - if im.format == 'BMP': - - #write matte data. Taken from imagemagick - scanline_pad = (((im.size[0]+31) & ~31)-im.size[0]) >> 3 - row_len = im.size[0]*4 - for y in reversed(range(im.size[1])): - d = data[40:] - #BGRA - row_pixels = d[row_len*y:row_len*y+row_len] - bit=0 - byte=0 - for x in range(im.size[0]): - p=row_pixels[x] - - byte<<=1 - bit+=1 - if bit == 8: - matte_data+=o8(byte) - bit=0 - byte=0 - if not bit == 0: - matte_data+=o8(byte<<(8-bit)) - for i in range(scanline_pad): - matte_data+=o8(0) - - fp.write(o8(im.size[0]) + # width - o8(im.size[1]) + # height - o8(0) + # color palette - o8(0) + # reserved - o16(1) + # planes - o16(bits) + # depth - o32(len(data+matte_data)) + # size of image in bytes - o32(current_offset) # offset - - ) - current_offset += len(data+matte_data) - - for im in images: - - data = get_data(im) - fp.write(data) - - if im.format == 'BMP': - - #write matte data. Taken from imagemagick - scanline_pad = (((im.size[0]+31) & ~31)-im.size[0]) >> 3 - row_len = im.size[0]*4 - for y in reversed(range(im.size[1])): - d = data[40:] - #BGRA - row_pixels = d[row_len*y:row_len*y+row_len] - bit=0 - byte=0 - for x in range(im.size[0]): - p=row_pixels[x] - - byte<<=1 - bit+=1 - if bit == 8: - fp.write(o8(byte)) - bit=0 - byte=0 - if not bit == 0: - fp.write(o8(byte<<(8-bit))) - for i in range(scanline_pad): - fp.write(o8(0)) - - - fp.flush() - - -# -# -------------------------------------------------------------------- - -Image.register_open(IcoFile.format, IcoImageFile, _accept) -Image.register_save(IcoFile.format, _save) - -Image.register_extension(IcoFile.format, '.ico') diff --git a/pe.py b/pe.py index 6172ee8..0a6cfcf 100644 --- a/pe.py +++ b/pe.py @@ -2,7 +2,7 @@ import os import struct from io import BytesIO -from .ico_plugin import * +from PIL import Image def resize(image, size, format=None): output = BytesIO() @@ -28,7 +28,7 @@ def resize(image, size, format=None): back.paste(image, tuple(offset)) format = format or image.format - back.save(output, format) + back.save(output, format, sizes=[size]) contents = output.getvalue() output.close() return contents @@ -1279,6 +1279,7 @@ class PEFile(Printable): #9662 is the exact length of the icon in nw.exe extra_size = icon_file_size-new_icon_size + if extra_size < 0: extra_size = 0 icon_data = bytearray(i_data) + bytearray(extra_size)