From 37f22ebfcd490308036778c68a2feff316dd9a83 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Wed, 10 Oct 2012 17:11:13 -0500 Subject: [PATCH 01/61] py3k: Use string methods instead of string module First, we go for the obvious stuff. The string module methods are gone in 3.0, so we translate them to the appropriate methods on the string class. --- PIL/BdfFontFile.py | 23 +++++++++++------------ PIL/BmpImagePlugin.py | 3 +-- PIL/EpsImagePlugin.py | 8 ++++---- PIL/FliImagePlugin.py | 3 +-- PIL/GimpGradientFile.py | 5 ++--- PIL/GimpPaletteFile.py | 6 +++--- PIL/IcnsImagePlugin.py | 4 ++-- PIL/ImImagePlugin.py | 6 +++--- PIL/Image.py | 24 ++++++++++++------------ PIL/ImageCms.py | 3 +-- PIL/ImageColor.py | 35 ++++++++++++++--------------------- PIL/ImageFile.py | 4 ++-- PIL/ImageFont.py | 4 ++-- PIL/JpegImagePlugin.py | 3 +-- PIL/OleFileIO.py | 4 ++-- PIL/PSDraw.py | 5 ++--- PIL/PaletteFile.py | 6 ++---- PIL/PcfFontFile.py | 4 +--- PIL/PngImagePlugin.py | 14 +++++++------- PIL/TarIO.py | 5 ++--- PIL/TiffImagePlugin.py | 14 +++++++------- PIL/XVThumbImagePlugin.py | 3 +-- PIL/XbmImagePlugin.py | 4 ++-- PIL/XpmImagePlugin.py | 12 ++++++------ Sane/sane.py | 3 +-- Scripts/explode.py | 14 +++++++------- Scripts/gifmaker.py | 1 - Scripts/pildriver.py | 7 +++---- 28 files changed, 102 insertions(+), 125 deletions(-) diff --git a/PIL/BdfFontFile.py b/PIL/BdfFontFile.py index 47b5f05c8..33c237e0c 100644 --- a/PIL/BdfFontFile.py +++ b/PIL/BdfFontFile.py @@ -20,7 +20,6 @@ import Image import FontFile -import string # -------------------------------------------------------------------- # parse X Bitmap Distribution Format (BDF) @@ -50,7 +49,7 @@ def bdf_char(f): return None if s[:9] == "STARTCHAR": break - id = string.strip(s[9:]) + id = s[9:].strip() # load symbol properties props = {} @@ -58,7 +57,7 @@ def bdf_char(f): s = f.readline() if not s or s[:6] == "BITMAP": break - i = string.find(s, " ") + i = s.find(" ") props[s[:i]] = s[i+1:-1] # load bitmap @@ -68,10 +67,10 @@ def bdf_char(f): if not s or s[:7] == "ENDCHAR": break bitmap.append(s[:-1]) - bitmap = string.join(bitmap, "") + bitmap = "".join(bitmap) - [x, y, l, d] = map(int, string.split(props["BBX"])) - [dx, dy] = map(int, string.split(props["DWIDTH"])) + [x, y, l, d] = map(int, props["BBX"].split()) + [dx, dy] = map(int, props["DWIDTH"].split()) bbox = (dx, dy), (l, -d-y, x+l, -d), (0, 0, x, y) @@ -103,21 +102,21 @@ class BdfFontFile(FontFile.FontFile): s = fp.readline() if not s or s[:13] == "ENDPROPERTIES": break - i = string.find(s, " ") + i = s.find(" ") props[s[:i]] = s[i+1:-1] if s[:i] in ["COMMENT", "COPYRIGHT"]: - if string.find(s, "LogicalFontDescription") < 0: + if s.find("LogicalFontDescription") < 0: comments.append(s[i+1:-1]) - font = string.split(props["FONT"], "-") + font = props["FONT"].split("-") - font[4] = bdf_slant[string.upper(font[4])] - font[11] = bdf_spacing[string.upper(font[11])] + font[4] = bdf_slant[font[4].upper()] + font[11] = bdf_spacing[font[11].upper()] ascent = int(props["FONT_ASCENT"]) descent = int(props["FONT_DESCENT"]) - fontname = string.join(font[1:], ";") + fontname = ";".join(font[1:]) # print "#", fontname # for i in comments: diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index 09c0a2141..c2c3f6d49 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -27,7 +27,6 @@ __version__ = "0.7" -import string import Image, ImageFile, ImagePalette @@ -146,7 +145,7 @@ class BmpImageFile(ImageFile.ImageFile): else: self.mode = "P" self.palette = ImagePalette.raw( - "BGR", string.join(palette, "") + "BGR", "".join(palette) ) if not offset: diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py index 837447bca..f304a1fe2 100644 --- a/PIL/EpsImagePlugin.py +++ b/PIL/EpsImagePlugin.py @@ -20,7 +20,7 @@ __version__ = "0.5" -import re, string +import re import Image, ImageFile # @@ -55,7 +55,7 @@ def Ghostscript(tile, size, fp): "-sOutputFile=%s" % file,# output file "- >/dev/null 2>/dev/null"] - command = string.join(command) + command = " ".join(command) # push data through ghostscript try: @@ -183,7 +183,7 @@ class EpsImageFile(ImageFile.ImageFile): # Note: The DSC spec says that BoundingBox # fields should be integers, but some drivers # put floating point values there anyway. - box = map(int, map(float, string.split(v))) + box = map(int, map(float, v.split())) self.size = box[2] - box[0], box[3] - box[1] self.tile = [("eps", (0,0) + self.size, offset, (length, box))] @@ -231,7 +231,7 @@ class EpsImageFile(ImageFile.ImageFile): if s[:11] == "%ImageData:": [x, y, bi, mo, z3, z4, en, id] =\ - string.split(s[11:], maxsplit=7) + s[11:].split(maxsplit=7) x = int(x); y = int(y) diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py index dac8c435d..94c99bcc6 100644 --- a/PIL/FliImagePlugin.py +++ b/PIL/FliImagePlugin.py @@ -19,7 +19,6 @@ __version__ = "0.2" import Image, ImageFile, ImagePalette -import string def i16(c): @@ -84,7 +83,7 @@ class FliImageFile(ImageFile.ImageFile): self._palette(palette, 0) palette = map(lambda (r,g,b): chr(r)+chr(g)+chr(b), palette) - self.palette = ImagePalette.raw("RGB", string.join(palette, "")) + self.palette = ImagePalette.raw("RGB", "".join(palette)) # set things up to decode first frame self.frame = -1 diff --git a/PIL/GimpGradientFile.py b/PIL/GimpGradientFile.py index 1a850dc53..9a9f9c421 100644 --- a/PIL/GimpGradientFile.py +++ b/PIL/GimpGradientFile.py @@ -14,7 +14,6 @@ # from math import pi, log, sin, sqrt -import string # -------------------------------------------------------------------- # Stuff to translate curve segments to palette values (derived from @@ -87,7 +86,7 @@ class GradientFile: # add to palette palette.append(r + g + b + a) - return string.join(palette, ""), "RGBA" + return "".join(palette), "RGBA" ## # File handler for GIMP's gradient format. @@ -105,7 +104,7 @@ class GimpGradientFile(GradientFile): for i in range(count): - s = string.split(fp.readline()) + s = fp.readline().split() w = map(float, s[:11]) x0, x1 = w[0], w[2] diff --git a/PIL/GimpPaletteFile.py b/PIL/GimpPaletteFile.py index aba85b71f..b39ca7891 100644 --- a/PIL/GimpPaletteFile.py +++ b/PIL/GimpPaletteFile.py @@ -14,7 +14,7 @@ # See the README file for information on usage and redistribution. # -import re, string +import re ## # File handler for GIMP's palette format. @@ -44,7 +44,7 @@ class GimpPaletteFile: if len(s) > 100: raise SyntaxError, "bad palette file" - v = tuple(map(int, string.split(s)[:3])) + v = tuple(map(int, s.split()[:3])) if len(v) != 3: raise ValueError, "bad palette entry" @@ -53,7 +53,7 @@ class GimpPaletteFile: i = i + 1 - self.palette = string.join(self.palette, "") + self.palette = "".join(self.palette) def getpalette(self): diff --git a/PIL/IcnsImagePlugin.py b/PIL/IcnsImagePlugin.py index 4d68fa232..fdfe51110 100644 --- a/PIL/IcnsImagePlugin.py +++ b/PIL/IcnsImagePlugin.py @@ -15,7 +15,7 @@ # import Image, ImageFile -import string, struct +import struct HEADERSIZE = 8 @@ -68,7 +68,7 @@ def read_32(fobj, (start, length), size): "Error reading channel [%r left]" % bytesleft ) band = Image.frombuffer( - "L", size, string.join(data, ""), "raw", "L", 0, 1 + "L", size, "".join(data), "raw", "L", 0, 1 ) im.im.putband(band.im, band_ix) return {"RGB": im} diff --git a/PIL/ImImagePlugin.py b/PIL/ImImagePlugin.py index a76dcf04e..788a26229 100644 --- a/PIL/ImImagePlugin.py +++ b/PIL/ImImagePlugin.py @@ -28,7 +28,7 @@ __version__ = "0.7" -import re, string +import re import Image, ImageFile, ImagePalette @@ -158,8 +158,8 @@ class ImImageFile(ImageFile.ImageFile): # Convert value as appropriate if k in [FRAMES, SCALE, SIZE]: - v = string.replace(v, "*", ",") - v = tuple(map(number, string.split(v, ","))) + v = v.replace("*", ",") + v = tuple(map(number, v.split(","))) if len(v) == 1: v = v[0] elif k == MODE and OPEN.has_key(v): diff --git a/PIL/Image.py b/PIL/Image.py index aa87fb472..8038e6481 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -67,7 +67,7 @@ except ImportError, v: import ImageMode import ImagePalette -import os, string, sys +import os, sys # type stuff from types import IntType, StringType, TupleType @@ -544,7 +544,7 @@ class Image: if s < 0: raise RuntimeError("encoder error %d in tostring" % s) - return string.join(data, "") + return "".join(data) ## # Returns the image converted to an X11 bitmap. This method @@ -561,9 +561,9 @@ class Image: if self.mode != "1": raise ValueError("not a bitmap") data = self.tostring("xbm") - return string.join(["#define %s_width %d\n" % (name, self.size[0]), + return "".join(["#define %s_width %d\n" % (name, self.size[0]), "#define %s_height %d\n"% (name, self.size[1]), - "static char %s_bits[] = {\n" % name, data, "};"], "") + "static char %s_bits[] = {\n" % name, data, "};"]) ## # Loads this image with pixel data from a string. @@ -1233,7 +1233,7 @@ class Image: palette = ImagePalette.raw(data.rawmode, data.palette) else: if not isStringType(data): - data = string.join(map(chr, data), "") + data = "".join(map(chr, data)) palette = ImagePalette.raw(rawmode, data) self.mode = "P" self.palette = palette @@ -1408,7 +1408,7 @@ class Image: preinit() - ext = string.lower(os.path.splitext(filename)[1]) + ext = os.path.splitext(filename)[1].lower() if not format: try: @@ -1421,10 +1421,10 @@ class Image: raise KeyError(ext) # unknown extension try: - save_handler = SAVE[string.upper(format)] + save_handler = SAVE[format.upper()] except KeyError: init() - save_handler = SAVE[string.upper(format)] # unknown format + save_handler = SAVE[format.upper()] # unknown format if isStringType(fp): import __builtin__ @@ -2093,7 +2093,7 @@ def merge(mode, bands): # reject images having another format. def register_open(id, factory, accept=None): - id = string.upper(id) + id = id.upper() ID.append(id) OPEN[id] = factory, accept @@ -2105,7 +2105,7 @@ def register_open(id, factory, accept=None): # @param mimetype The image MIME type for this format. def register_mime(id, mimetype): - MIME[string.upper(id)] = mimetype + MIME[id.upper()] = mimetype ## # Registers an image save function. This function should not be @@ -2115,7 +2115,7 @@ def register_mime(id, mimetype): # @param driver A function to save images in this format. def register_save(id, driver): - SAVE[string.upper(id)] = driver + SAVE[id.upper()] = driver ## # Registers an image extension. This function should not be @@ -2125,7 +2125,7 @@ def register_save(id, driver): # @param extension An extension used for this format. def register_extension(id, extension): - EXTENSION[string.lower(extension)] = string.upper(id) + EXTENSION[extension.lower()] = id.upper() # -------------------------------------------------------------------- diff --git a/PIL/ImageCms.py b/PIL/ImageCms.py index b8a6dca71..d5f7b975c 100644 --- a/PIL/ImageCms.py +++ b/PIL/ImageCms.py @@ -770,7 +770,6 @@ if __name__ == "__main__": # create a cheap manual from the __doc__ strings for the functions above import ImageCms - import string print __doc__ for f in dir(pyCMS): @@ -779,7 +778,7 @@ if __name__ == "__main__": try: exec ("doc = ImageCms.%s.__doc__" %(f)) - if string.find(doc, "pyCMS") >= 0: + if "pyCMS" in doc: # so we don't get the __doc__ string for imported modules print doc except AttributeError: diff --git a/PIL/ImageColor.py b/PIL/ImageColor.py index c3cca46db..8d3354a3c 100644 --- a/PIL/ImageColor.py +++ b/PIL/ImageColor.py @@ -18,15 +18,8 @@ # import Image -import re, string +import re -try: - x = int("a", 16) -except TypeError: - # python 1.5.2 doesn't support int(x,b) - str2int = string.atoi -else: - str2int = int ## # Convert color string to RGB tuple. @@ -43,7 +36,7 @@ def getrgb(color): except KeyError: try: # fall back on case-insensitive lookup - rgb = colormap[string.lower(color)] + rgb = colormap[color.lower()] except KeyError: rgb = None # found color in cache @@ -56,30 +49,30 @@ def getrgb(color): m = re.match("#\w\w\w$", color) if m: return ( - str2int(color[1]*2, 16), - str2int(color[2]*2, 16), - str2int(color[3]*2, 16) + int(color[1]*2, 16), + int(color[2]*2, 16), + int(color[3]*2, 16) ) m = re.match("#\w\w\w\w\w\w$", color) if m: return ( - str2int(color[1:3], 16), - str2int(color[3:5], 16), - str2int(color[5:7], 16) + int(color[1:3], 16), + int(color[3:5], 16), + int(color[5:7], 16) ) m = re.match("rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color) if m: return ( - str2int(m.group(1)), - str2int(m.group(2)), - str2int(m.group(3)) + int(m.group(1)), + int(m.group(2)), + int(m.group(3)) ) m = re.match("rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color) if m: return ( - int((str2int(m.group(1)) * 255) / 100.0 + 0.5), - int((str2int(m.group(2)) * 255) / 100.0 + 0.5), - int((str2int(m.group(3)) * 255) / 100.0 + 0.5) + int((int(m.group(1)) * 255) / 100.0 + 0.5), + int((int(m.group(2)) * 255) / 100.0 + 0.5), + int((int(m.group(3)) * 255) / 100.0 + 0.5) ) m = re.match("hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color) if m: diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index 8a97c1b5b..e093f6f56 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -28,7 +28,7 @@ # import Image -import traceback, string, os +import traceback, os MAXBLOCK = 65536 @@ -525,4 +525,4 @@ def _safe_read(fp, size): break data.append(block) size = size - len(block) - return string.join(data, "") + return "".join(data) diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index 3ea2f2f8e..5531c8b47 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -26,7 +26,7 @@ # import Image -import os, string, sys +import os, sys class _imagingft_not_installed: # module placeholder @@ -99,7 +99,7 @@ class ImageFont: # read PILfont header if file.readline() != "PILfont\n": raise SyntaxError("Not a PILfont file") - d = string.split(file.readline(), ";") + d = file.readline().split(";") self.info = [] # FIXME: should be a dictionary while True: s = file.readline() diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 933abf3c2..3c4d5bd4e 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -35,7 +35,6 @@ __version__ = "0.6" import array, struct -import string import Image, ImageFile def i16(c,o=0): @@ -154,7 +153,7 @@ def SOF(self, marker): profile = [] for p in self.icclist: profile.append(p[14:]) - icc_profile = string.join(profile, "") + icc_profile = "".join(profile) else: icc_profile = None # wrong number of fragments self.info["icc_profile"] = icc_profile diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index 36e598a9b..1792c9558 100644 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -36,7 +36,7 @@ # See the README file for information on usage and redistribution. # -import string, StringIO +import StringIO def i16(c, o = 0): @@ -105,7 +105,7 @@ class _OleStream(StringIO.StringIO): data.append(fp.read(sectorsize)) sect = fat[sect] - data = string.join(data, "") + data = "".join(data) # print len(data), size diff --git a/PIL/PSDraw.py b/PIL/PSDraw.py index 7309e17b3..bc423fc8c 100644 --- a/PIL/PSDraw.py +++ b/PIL/PSDraw.py @@ -16,7 +16,6 @@ # import EpsImagePlugin -import string ## # Simple Postscript graphics interface. @@ -71,8 +70,8 @@ class PSDraw: self.fp.write("%d %d M %d %d 0 Vr\n" % box) def text(self, xy, text): - text = string.joinfields(string.splitfields(text, "("), "\\(") - text = string.joinfields(string.splitfields(text, ")"), "\\)") + text = "\\(".join(text.split("(")) + text = "\\)".join(text.split(")")) xy = xy + (text,) self.fp.write("%d %d M (%s) S\n" % xy) diff --git a/PIL/PaletteFile.py b/PIL/PaletteFile.py index 3bbd91327..99098e8e9 100644 --- a/PIL/PaletteFile.py +++ b/PIL/PaletteFile.py @@ -13,8 +13,6 @@ # See the README file for information on usage and redistribution. # -import string - ## # File handler for Teragon-style palette files. @@ -37,7 +35,7 @@ class PaletteFile: if len(s) > 100: raise SyntaxError, "bad palette file" - v = map(int, string.split(s)) + v = map(int, s.split()) try: [i, r, g, b] = v except ValueError: @@ -47,7 +45,7 @@ class PaletteFile: if 0 <= i <= 255: self.palette[i] = chr(r) + chr(g) + chr(b) - self.palette = string.join(self.palette, "") + self.palette = "".join(self.palette) def getpalette(self): diff --git a/PIL/PcfFontFile.py b/PIL/PcfFontFile.py index bec39e3a0..92e96eb53 100644 --- a/PIL/PcfFontFile.py +++ b/PIL/PcfFontFile.py @@ -19,8 +19,6 @@ import Image import FontFile -import string - # -------------------------------------------------------------------- # declarations @@ -55,7 +53,7 @@ def b32(c): return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) def sz(s, o): - return s[o:string.index(s, "\0", o)] + return s[o:s.index("\0", o)] ## # Font file plugin for the X11 PCF format. diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index 0ee8589a5..4e58c2bf7 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -33,7 +33,7 @@ __version__ = "0.9" -import re, string +import re import Image, ImageFile, ImagePalette, zlib @@ -189,7 +189,7 @@ class PngStream(ChunkStream): # Null separator 1 byte (null character) # Compression method 1 byte (0) # Compressed profile n bytes (zlib with deflate compression) - i = string.find(s, chr(0)) + i = s.find(chr(0)) if Image.DEBUG: print "iCCP profile name", s[:i] print "Compression method", ord(s[i]) @@ -243,7 +243,7 @@ class PngStream(ChunkStream): # transparency s = ImageFile._safe_read(self.fp, len) if self.im_mode == "P": - i = string.find(s, chr(0)) + i = s.find(chr(0)) if i >= 0: self.im_info["transparency"] = i elif self.im_mode == "L": @@ -277,7 +277,7 @@ class PngStream(ChunkStream): # text s = ImageFile._safe_read(self.fp, len) try: - k, v = string.split(s, "\0", 1) + k, v = s.split("\0", 1) except ValueError: k = s; v = "" # fallback for broken tEXt tags if k: @@ -288,7 +288,7 @@ class PngStream(ChunkStream): # compressed text s = ImageFile._safe_read(self.fp, len) - k, v = string.split(s, "\0", 1) + k, v = s.split("\0", 1) comp_method = ord(v[0]) if comp_method != 0: raise SyntaxError("Unknown compression method %s in zTXt chunk" % comp_method) @@ -443,7 +443,7 @@ _OUTMODES = { def putchunk(fp, cid, *data): "Write a PNG chunk (including CRC field)" - data = string.join(data, "") + data = "".join(data) fp.write(o32(len(data)) + cid) fp.write(data) @@ -593,7 +593,7 @@ def getchunks(im, **params): self.data.append(chunk) def append(fp, cid, *data): - data = string.join(data, "") + data = "".join(data) hi, lo = Image.core.crc32(data, Image.core.crc32(cid)) crc = o16(hi) + o16(lo) fp.append((cid, data, crc)) diff --git a/PIL/TarIO.py b/PIL/TarIO.py index d5de729cd..332b30716 100644 --- a/PIL/TarIO.py +++ b/PIL/TarIO.py @@ -15,7 +15,6 @@ # import ContainerIO -import string ## # A file object that provides read access to a given member of a TAR @@ -40,13 +39,13 @@ class TarIO(ContainerIO.ContainerIO): raise IOError, "unexpected end of tar file" name = s[:100] - i = string.find(name, chr(0)) + i = name.find(chr(0)) if i == 0: raise IOError, "cannot find subfile" if i > 0: name = name[:i] - size = string.atoi(s[124:136], 8) + size = int(s[124:136], 8) if file == name: break diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 8a0fde9cd..a7f78627d 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -44,7 +44,7 @@ __version__ = "1.3.5" import Image, ImageFile import ImagePalette -import array, string, sys +import array, sys II = "II" # little-endian (intel-style) MM = "MM" # big-endian (motorola-style) @@ -422,14 +422,14 @@ class ImageFileDirectory: if typ == 1: # byte data - data = value = string.join(map(chr, value), "") + data = value = "".join(map(chr, value)) elif typ == 7: # untyped data - data = value = string.join(value, "") + data = value = "".join(value) elif type(value[0]) is type(""): # string data typ = 2 - data = value = string.join(value, "\0") + "\0" + data = value = "\0".join(value) + "\0" else: # integer data if tag == STRIPOFFSETS: @@ -444,9 +444,9 @@ class ImageFileDirectory: if v >= 65536: typ = 4 if typ == 3: - data = string.join(map(o16, value), "") + data = "".join(map(o16, value)) else: - data = string.join(map(o32, value), "") + data = "".join(map(o32, value)) if Image.DEBUG: import TiffTags @@ -705,7 +705,7 @@ class TiffImageFile(ImageFile.ImageFile): if self.mode == "P": palette = map(lambda a: chr(a / 256), self.tag[COLORMAP]) - self.palette = ImagePalette.raw("RGB;L", string.join(palette, "")) + self.palette = ImagePalette.raw("RGB;L", "".join(palette)) # # -------------------------------------------------------------------- # Write TIFF files diff --git a/PIL/XVThumbImagePlugin.py b/PIL/XVThumbImagePlugin.py index ab1fb5d7a..91e30e9b8 100644 --- a/PIL/XVThumbImagePlugin.py +++ b/PIL/XVThumbImagePlugin.py @@ -19,7 +19,6 @@ __version__ = "0.1" -import string import Image, ImageFile, ImagePalette # standard color palette for thumbnails (RGB332) @@ -56,7 +55,7 @@ class XVThumbImageFile(ImageFile.ImageFile): break # parse header line (already read) - s = string.split(s.strip()) + s = s.strip().split() self.mode = "P" self.size = int(s[0]), int(s[1]) diff --git a/PIL/XbmImagePlugin.py b/PIL/XbmImagePlugin.py index a8cf1026d..ef7e05eba 100644 --- a/PIL/XbmImagePlugin.py +++ b/PIL/XbmImagePlugin.py @@ -21,7 +21,7 @@ __version__ = "0.6" -import re, string +import re import Image, ImageFile # XBM header @@ -36,7 +36,7 @@ xbm_head = re.compile( ) def _accept(prefix): - return string.lstrip(prefix)[:7] == "#define" + return prefix.lstrip()[:7] == "#define" ## # Image plugin for X11 bitmaps. diff --git a/PIL/XpmImagePlugin.py b/PIL/XpmImagePlugin.py index a3f40f02c..36cb2ada6 100644 --- a/PIL/XpmImagePlugin.py +++ b/PIL/XpmImagePlugin.py @@ -18,7 +18,7 @@ __version__ = "0.2" -import re, string +import re import Image, ImageFile, ImagePalette # XPM header @@ -72,7 +72,7 @@ class XpmImageFile(ImageFile.ImageFile): s = s[:-1] c = ord(s[1]) - s = string.split(s[2:-2]) + s = s[2:-2].split() for i in range(0, len(s), 2): @@ -84,7 +84,7 @@ class XpmImageFile(ImageFile.ImageFile): self.info["transparency"] = c elif rgb[0] == "#": # FIXME: handle colour names (see ImagePalette.py) - rgb = string.atoi(rgb[1:], 16) + rgb = int(rgb[1:], 16) palette[c] = chr((rgb >> 16) & 255) +\ chr((rgb >> 8) & 255) +\ chr(rgb & 255) @@ -99,7 +99,7 @@ class XpmImageFile(ImageFile.ImageFile): raise ValueError, "cannot read this XPM file" self.mode = "P" - self.palette = ImagePalette.raw("RGB", string.join(palette, "")) + self.palette = ImagePalette.raw("RGB", "".join(palette)) self.tile = [("raw", (0, 0)+self.size, self.fp.tell(), ("P", 0, 1))] @@ -113,11 +113,11 @@ class XpmImageFile(ImageFile.ImageFile): s = [None] * ysize for i in range(ysize): - s[i] = string.ljust(self.fp.readline()[1:xsize+1], xsize) + s[i] = self.fp.readline()[1:xsize+1].ljust(xsize) self.fp = None - return string.join(s, "") + return "".join(s) # # Registry diff --git a/Sane/sane.py b/Sane/sane.py index 27be5a259..bc212e470 100644 --- a/Sane/sane.py +++ b/Sane/sane.py @@ -46,7 +46,6 @@ class Option: """ def __init__(self, args, scanDev): - import string self.scanDev = scanDev # needed to get current value of this option self.index, self.name = args[0], args[1] self.title, self.desc = args[2], args[3] @@ -57,7 +56,7 @@ class Option: if x=='-': return '_' else: return x if type(self.name)!=type(''): self.py_name=str(self.name) - else: self.py_name=string.join(map(f, self.name), '') + else: self.py_name=''.join(map(f, self.name)) def is_active(self): return _sane.OPTION_IS_ACTIVE(self.cap) diff --git a/Scripts/explode.py b/Scripts/explode.py index a336f0699..6081f751a 100644 --- a/Scripts/explode.py +++ b/Scripts/explode.py @@ -6,7 +6,7 @@ # from PIL import Image -import os, string, sys +import os, sys class Interval: @@ -18,18 +18,18 @@ class Interval: self.hilo = [] - for s in string.split(interval, ","): - if not string.strip(s): + for s in interval.split(","): + if not s.strip(): continue try: - v = string.atoi(s) + v = int(s) if v < 0: lo, hi = 0, -v else: lo = hi = v except ValueError: - i = string.find(s, "-") - lo, hi = string.atoi(s[:i]), string.atoi(s[i+1:]) + i = s.find("-") + lo, hi = int(s[:i]), int(s[i+1:]) self.hilo.append((hi, lo)) @@ -69,7 +69,7 @@ if not sys.argv[2:]: infile = sys.argv[1] outfile = sys.argv[2] -frames = Interval(string.join(sys.argv[3:], ",")) +frames = Interval(",".join(sys.argv[3:])) try: # check if outfile contains a placeholder diff --git a/Scripts/gifmaker.py b/Scripts/gifmaker.py index 95524eacc..e3b6701cf 100644 --- a/Scripts/gifmaker.py +++ b/Scripts/gifmaker.py @@ -40,7 +40,6 @@ # from PIL import Image, ImageChops -import string from PIL.GifImagePlugin import getheader, getdata diff --git a/Scripts/pildriver.py b/Scripts/pildriver.py index 5dd575a3f..19c83dbc7 100644 --- a/Scripts/pildriver.py +++ b/Scripts/pildriver.py @@ -49,7 +49,6 @@ of its upper-left-hand corner and displays the cropped portion. # from PIL import Image -import string class PILDriver: @@ -206,7 +205,7 @@ class PILDriver: Process the top image with the given filter. """ import ImageFilter - filter = eval("ImageFilter." + string.upper(self.do_pop())) + filter = eval("ImageFilter." + self.do_pop().upper()) image = self.do_pop() self.push(image.filter(filter)) @@ -314,7 +313,7 @@ class PILDriver: Transpose the top image. """ - transpose = string.upper(self.do_pop()) + transpose = self.do_pop().upper() image = self.do_pop() self.push(image.transpose(transpose)) @@ -515,7 +514,7 @@ if __name__ == '__main__': except EOFError: print "\nPILDriver says goodbye." break - driver.execute(string.split(line)) + driver.execute(line.split()) print driver.stack # The following sets edit modes for GNU EMACS From 78575798d768a256cacace3be30a48efb04b0812 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Wed, 10 Oct 2012 22:52:53 -0500 Subject: [PATCH 02/61] py3k: Update exception usage to modern syntax --- PIL/ArgImagePlugin.py | 40 +++++++++++++++++++-------------------- PIL/BdfFontFile.py | 2 +- PIL/CurImagePlugin.py | 2 +- PIL/DcxImagePlugin.py | 2 +- PIL/EpsImagePlugin.py | 16 ++++++++-------- PIL/FliImagePlugin.py | 4 ++-- PIL/FpxImagePlugin.py | 8 ++++---- PIL/GbrImagePlugin.py | 4 ++-- PIL/GifImagePlugin.py | 6 +++--- PIL/GimpGradientFile.py | 4 ++-- PIL/GimpPaletteFile.py | 6 +++--- PIL/IcnsImagePlugin.py | 6 +++--- PIL/IcoImagePlugin.py | 2 +- PIL/ImImagePlugin.py | 18 +++++++++--------- PIL/Image.py | 4 ++-- PIL/ImageCms.py | 20 ++++++++++---------- PIL/ImageFile.py | 18 +++++++++--------- PIL/ImageMath.py | 8 ++++---- PIL/ImageOps.py | 2 +- PIL/ImagePalette.py | 4 ++-- PIL/ImageStat.py | 4 ++-- PIL/ImageTk.py | 4 ++-- PIL/ImtImagePlugin.py | 2 +- PIL/IptcImagePlugin.py | 6 +++--- PIL/MicImagePlugin.py | 6 +++--- PIL/MpegImagePlugin.py | 2 +- PIL/MspImagePlugin.py | 6 +++--- PIL/OleFileIO.py | 8 ++++---- PIL/PaletteFile.py | 2 +- PIL/PalmImagePlugin.py | 2 +- PIL/PcdImagePlugin.py | 2 +- PIL/PcfFontFile.py | 4 ++-- PIL/PcxImagePlugin.py | 8 ++++---- PIL/PixarImagePlugin.py | 2 +- PIL/PngImagePlugin.py | 12 ++++++------ PIL/PpmImagePlugin.py | 4 ++-- PIL/PsdImagePlugin.py | 6 +++--- PIL/SpiderImagePlugin.py | 14 +++++++------- PIL/SunImagePlugin.py | 4 ++-- PIL/TarIO.py | 4 ++-- PIL/TgaImagePlugin.py | 6 +++--- PIL/TiffImagePlugin.py | 14 +++++++------- PIL/XVThumbImagePlugin.py | 4 ++-- PIL/XbmImagePlugin.py | 2 +- PIL/XpmImagePlugin.py | 10 +++++----- Sane/sane.py | 16 ++++++++-------- Scripts/pilconvert.py | 4 ++-- Scripts/pilfile.py | 6 +++--- Scripts/pilprint.py | 4 ++-- selftest.py | 2 +- 50 files changed, 173 insertions(+), 173 deletions(-) diff --git a/PIL/ArgImagePlugin.py b/PIL/ArgImagePlugin.py index 815fcea33..3507e0c1f 100644 --- a/PIL/ArgImagePlugin.py +++ b/PIL/ArgImagePlugin.py @@ -60,14 +60,14 @@ class ArgStream(ChunkStream): # assertions if self.count != 0: - raise SyntaxError, "misplaced AHDR chunk" + raise SyntaxError("misplaced AHDR chunk") s = self.fp.read(bytes) self.size = i32(s), i32(s[4:]) try: self.mode, self.rawmode = _MODES[(ord(s[8]), ord(s[9]))] except: - raise SyntaxError, "unknown ARG mode" + raise SyntaxError("unknown ARG mode") if Image.DEBUG: print "AHDR size", self.size @@ -80,7 +80,7 @@ class ArgStream(ChunkStream): # assertions if self.count != 0: - raise SyntaxError, "misplaced AFRM chunk" + raise SyntaxError("misplaced AFRM chunk") self.show = 1 self.id = 0 @@ -107,7 +107,7 @@ class ArgStream(ChunkStream): # assertions if self.count != 0: - raise SyntaxError, "misplaced ADEF chunk" + raise SyntaxError("misplaced ADEF chunk") self.show = 0 self.id = 0 @@ -130,7 +130,7 @@ class ArgStream(ChunkStream): # assertions if self.count == 0: - raise SyntaxError, "misplaced NAME chunk" + raise SyntaxError("misplaced NAME chunk") name = self.fp.read(bytes) self.names[self.id] = name @@ -145,7 +145,7 @@ class ArgStream(ChunkStream): self.eof = 1 - raise EOFError, "end of ARG file" + raise EOFError("end of ARG file") def __getmodesize(self, s, full=1): @@ -154,13 +154,13 @@ class ArgStream(ChunkStream): try: mode, rawmode = _MODES[(ord(s[8]), ord(s[9]))] except: - raise SyntaxError, "unknown image mode" + raise SyntaxError("unknown image mode") if full: if ord(s[12]): pass # interlace not yet supported if ord(s[11]): - raise SyntaxError, "unknown filter category" + raise SyntaxError("unknown filter category") return size, mode, rawmode @@ -169,7 +169,7 @@ class ArgStream(ChunkStream): # assertions if self.count == 0: - raise SyntaxError, "misplaced PAST chunk" + raise SyntaxError("misplaced PAST chunk") if self.repair is not None: # we must repair the target image before we @@ -206,7 +206,7 @@ class ArgStream(ChunkStream): # assertions if self.count == 0: - raise SyntaxError, "misplaced BLNK chunk" + raise SyntaxError("misplaced BLNK chunk") s = self.fp.read(bytes) size, mode, rawmode = self.__getmodesize(s, 0) @@ -223,7 +223,7 @@ class ArgStream(ChunkStream): # assertions if self.count == 0: - raise SyntaxError, "misplaced IHDR chunk" + raise SyntaxError("misplaced IHDR chunk") # image header s = self.fp.read(bytes) @@ -243,7 +243,7 @@ class ArgStream(ChunkStream): # assertions if self.count == 0: - raise SyntaxError, "misplaced DHDR chunk" + raise SyntaxError("misplaced DHDR chunk") s = self.fp.read(bytes) @@ -276,7 +276,7 @@ class ArgStream(ChunkStream): # assertions if self.count == 0: - raise SyntaxError, "misplaced JHDR chunk" + raise SyntaxError("misplaced JHDR chunk") # image header s = self.fp.read(bytes) @@ -296,7 +296,7 @@ class ArgStream(ChunkStream): # assertions if self.count == 0: - raise SyntaxError, "misplaced UHDR chunk" + raise SyntaxError("misplaced UHDR chunk") # image header s = self.fp.read(bytes) @@ -321,7 +321,7 @@ class ArgStream(ChunkStream): if n < 0: # end of image if e < 0: - raise IOError, "decoder error %d" % e + raise IOError("decoder error %d" % e) else: self.data = self.data[n:] @@ -386,7 +386,7 @@ class ArgStream(ChunkStream): "SYNC -- reset decoder" if self.count != 0: - raise SyntaxError, "misplaced sYNC chunk" + raise SyntaxError("misplaced sYNC chunk") s = self.fp.read(bytes) self.__reset() @@ -418,7 +418,7 @@ class ArgImageFile(ImageFile.ImageFile): ) if self.fp.read(8) != MAGIC: - raise SyntaxError, "not an ARG file" + raise SyntaxError("not an ARG file") self.arg = ArgStream(self.fp) @@ -427,7 +427,7 @@ class ArgImageFile(ImageFile.ImageFile): cid, offset, bytes = self.arg.read() if cid != "AHDR": - raise SyntaxError, "expected an AHDR chunk" + raise SyntaxError("expected an AHDR chunk") s = self.arg.call(cid, offset, bytes) @@ -452,7 +452,7 @@ class ArgImageFile(ImageFile.ImageFile): def seek(self, frame): if self.arg.eof: - raise EOFError, "end of animation" + raise EOFError("end of animation") self.fp = self.arg.fp @@ -464,7 +464,7 @@ class ArgImageFile(ImageFile.ImageFile): cid, offset, bytes = self.arg.read() if self.arg.eof: - raise EOFError, "end of animation" + raise EOFError("end of animation") try: s = self.arg.call(cid, offset, bytes) diff --git a/PIL/BdfFontFile.py b/PIL/BdfFontFile.py index 33c237e0c..8652ed0b6 100644 --- a/PIL/BdfFontFile.py +++ b/PIL/BdfFontFile.py @@ -93,7 +93,7 @@ class BdfFontFile(FontFile.FontFile): s = fp.readline() if s[:13] != "STARTFONT 2.1": - raise SyntaxError, "not a valid BDF file" + raise SyntaxError("not a valid BDF file") props = {} comments = [] diff --git a/PIL/CurImagePlugin.py b/PIL/CurImagePlugin.py index 6a126e701..98b971c0a 100644 --- a/PIL/CurImagePlugin.py +++ b/PIL/CurImagePlugin.py @@ -50,7 +50,7 @@ class CurImageFile(BmpImagePlugin.BmpImageFile): # check magic s = self.fp.read(6) if not _accept(s): - raise SyntaxError, "not an CUR file" + raise SyntaxError("not an CUR file") # pick the largest cursor in the file m = "" diff --git a/PIL/DcxImagePlugin.py b/PIL/DcxImagePlugin.py index 32da7d831..60c022f8e 100644 --- a/PIL/DcxImagePlugin.py +++ b/PIL/DcxImagePlugin.py @@ -48,7 +48,7 @@ class DcxImageFile(PcxImageFile): # Header s = self.fp.read(4) if i32(s) != MAGIC: - raise SyntaxError, "not a DCX file" + raise SyntaxError("not a DCX file") # Component directory self._offset = [] diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py index f304a1fe2..bef485258 100644 --- a/PIL/EpsImagePlugin.py +++ b/PIL/EpsImagePlugin.py @@ -146,7 +146,7 @@ class EpsImageFile(ImageFile.ImageFile): length = i32(s[8:]) fp.seek(offset) else: - raise SyntaxError, "not an EPS file" + raise SyntaxError("not an EPS file") fp.seek(offset) @@ -163,7 +163,7 @@ class EpsImageFile(ImageFile.ImageFile): while s: if len(s) > 255: - raise SyntaxError, "not an EPS file" + raise SyntaxError("not an EPS file") if s[-2:] == '\r\n': s = s[:-2] @@ -172,8 +172,8 @@ class EpsImageFile(ImageFile.ImageFile): try: m = split.match(s) - except re.error, v: - raise SyntaxError, "not an EPS file" + except re.error as v: + raise SyntaxError("not an EPS file") if m: k, v = m.group(1, 2) @@ -207,7 +207,7 @@ class EpsImageFile(ImageFile.ImageFile): # tools mistakenly put in the Comments section pass else: - raise IOError, "bad EPS header" + raise IOError("bad EPS header") s = fp.readline() @@ -221,7 +221,7 @@ class EpsImageFile(ImageFile.ImageFile): while s[0] == "%": if len(s) > 255: - raise SyntaxError, "not an EPS file" + raise SyntaxError("not an EPS file") if s[-2:] == '\r\n': s = s[:-2] @@ -278,7 +278,7 @@ class EpsImageFile(ImageFile.ImageFile): break if not box: - raise IOError, "cannot determine EPS bounding box" + raise IOError("cannot determine EPS bounding box") def load(self): # Load EPS via Ghostscript @@ -308,7 +308,7 @@ def _save(im, fp, filename, eps=1): elif im.mode == "CMYK": operator = (8, 4, "false 4 colorimage") else: - raise ValueError, "image mode is not supported" + raise ValueError("image mode is not supported") if eps: # diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py index 94c99bcc6..9f85a0ae4 100644 --- a/PIL/FliImagePlugin.py +++ b/PIL/FliImagePlugin.py @@ -50,7 +50,7 @@ class FliImageFile(ImageFile.ImageFile): if not (magic in [0xAF11, 0xAF12] and i16(s[14:16]) in [0, 3] and # flags s[20:22] == '\x00\x00'): # reserved - raise SyntaxError, "not an FLI/FLC file" + raise SyntaxError("not an FLI/FLC file") # image characteristics self.mode = "P" @@ -112,7 +112,7 @@ class FliImageFile(ImageFile.ImageFile): def seek(self, frame): if frame != self.frame + 1: - raise ValueError, "cannot seek to frame %d" % frame + raise ValueError("cannot seek to frame %d" % frame) self.frame = frame # move to next frame diff --git a/PIL/FpxImagePlugin.py b/PIL/FpxImagePlugin.py index b78fe8c70..3fd6e8bfa 100644 --- a/PIL/FpxImagePlugin.py +++ b/PIL/FpxImagePlugin.py @@ -60,10 +60,10 @@ class FpxImageFile(ImageFile.ImageFile): try: self.ole = OleFileIO(self.fp) except IOError: - raise SyntaxError, "not an FPX file; invalid OLE file" + raise SyntaxError("not an FPX file; invalid OLE file") if self.ole.root.clsid != "56616700-C154-11CE-8553-00AA00A1F95B": - raise SyntaxError, "not an FPX file; bad root CLSID" + raise SyntaxError("not an FPX file; bad root CLSID") self._open_index(1) @@ -143,7 +143,7 @@ class FpxImageFile(ImageFile.ImageFile): # print size, self.mode, self.rawmode if size != self.size: - raise IOError, "subimage mismatch" + raise IOError("subimage mismatch") # get tile descriptors fp.seek(28 + offset) @@ -198,7 +198,7 @@ class FpxImageFile(ImageFile.ImageFile): self.tile_prefix = self.jpeg[jpeg_tables] else: - raise IOError, "unknown/invalid compression" + raise IOError("unknown/invalid compression") x = x + xtile if x >= xsize: diff --git a/PIL/GbrImagePlugin.py b/PIL/GbrImagePlugin.py index c71a0f77f..db48bf968 100644 --- a/PIL/GbrImagePlugin.py +++ b/PIL/GbrImagePlugin.py @@ -34,13 +34,13 @@ class GbrImageFile(ImageFile.ImageFile): header_size = i32(self.fp.read(4)) version = i32(self.fp.read(4)) if header_size < 20 or version != 1: - raise SyntaxError, "not a GIMP brush" + raise SyntaxError("not a GIMP brush") width = i32(self.fp.read(4)) height = i32(self.fp.read(4)) bytes = i32(self.fp.read(4)) if width <= 0 or height <= 0 or bytes != 1: - raise SyntaxError, "not a GIMP brush" + raise SyntaxError("not a GIMP brush") comment = self.fp.read(header_size - 20)[:-1] diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 4d03493b5..fa3b66643 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -69,7 +69,7 @@ class GifImageFile(ImageFile.ImageFile): # Screen s = self.fp.read(13) if s[:6] not in ["GIF87a", "GIF89a"]: - raise SyntaxError, "not a GIF file" + raise SyntaxError("not a GIF file") self.info["version"] = s[:6] @@ -106,7 +106,7 @@ class GifImageFile(ImageFile.ImageFile): self.__fp.seek(self.__rewind) if frame != self.__frame + 1: - raise ValueError, "cannot seek to frame %d" % frame + raise ValueError("cannot seek to frame %d" % frame) self.__frame = frame self.tile = [] @@ -201,7 +201,7 @@ class GifImageFile(ImageFile.ImageFile): if not self.tile: # self.__fp = None - raise EOFError, "no more images in GIF file" + raise EOFError("no more images in GIF file") self.mode = "L" if self.palette: diff --git a/PIL/GimpGradientFile.py b/PIL/GimpGradientFile.py index 9a9f9c421..1e9baa28d 100644 --- a/PIL/GimpGradientFile.py +++ b/PIL/GimpGradientFile.py @@ -96,7 +96,7 @@ class GimpGradientFile(GradientFile): def __init__(self, fp): if fp.readline()[:13] != "GIMP Gradient": - raise SyntaxError, "not a GIMP gradient file" + raise SyntaxError("not a GIMP gradient file") count = int(fp.readline()) @@ -116,7 +116,7 @@ class GimpGradientFile(GradientFile): cspace = int(s[12]) if cspace != 0: - raise IOError, "cannot handle HSV colour space" + raise IOError("cannot handle HSV colour space") gradient.append((x0, x1, xm, rgb0, rgb1, segment)) diff --git a/PIL/GimpPaletteFile.py b/PIL/GimpPaletteFile.py index b39ca7891..a0887a31a 100644 --- a/PIL/GimpPaletteFile.py +++ b/PIL/GimpPaletteFile.py @@ -28,7 +28,7 @@ class GimpPaletteFile: self.palette = map(lambda i: chr(i)*3, range(256)) if fp.readline()[:12] != "GIMP Palette": - raise SyntaxError, "not a GIMP palette file" + raise SyntaxError("not a GIMP palette file") i = 0 @@ -42,11 +42,11 @@ class GimpPaletteFile: if re.match("\w+:|#", s): continue if len(s) > 100: - raise SyntaxError, "bad palette file" + raise SyntaxError("bad palette file") v = tuple(map(int, s.split()[:3])) if len(v) != 3: - raise ValueError, "bad palette entry" + raise ValueError("bad palette entry") if 0 <= i <= 255: self.palette[i] = chr(v[0]) + chr(v[1]) + chr(v[2]) diff --git a/PIL/IcnsImagePlugin.py b/PIL/IcnsImagePlugin.py index fdfe51110..9f427019b 100644 --- a/PIL/IcnsImagePlugin.py +++ b/PIL/IcnsImagePlugin.py @@ -27,7 +27,7 @@ def read_32t(fobj, (start, length), (width, height)): fobj.seek(start) sig = fobj.read(4) if sig != '\x00\x00\x00\x00': - raise SyntaxError, 'Unknown signature, expecting 0x00000000' + raise SyntaxError('Unknown signature, expecting 0x00000000') return read_32(fobj, (start + 4, length - 4), (width, height)) def read_32(fobj, (start, length), size): @@ -111,7 +111,7 @@ class IcnsFile: self.fobj = fobj sig, filesize = nextheader(fobj) if sig != 'icns': - raise SyntaxError, 'not an icns file' + raise SyntaxError('not an icns file') i = HEADERSIZE while i < filesize: sig, blocksize = nextheader(fobj) @@ -133,7 +133,7 @@ class IcnsFile: def bestsize(self): sizes = self.itersizes() if not sizes: - raise SyntaxError, "No 32bit icon resources found" + raise SyntaxError("No 32bit icon resources found") return max(sizes) def dataforsize(self, size): diff --git a/PIL/IcoImagePlugin.py b/PIL/IcoImagePlugin.py index 94846dc0c..65e2fff26 100644 --- a/PIL/IcoImagePlugin.py +++ b/PIL/IcoImagePlugin.py @@ -48,7 +48,7 @@ class IcoImageFile(BmpImagePlugin.BmpImageFile): # check magic s = self.fp.read(6) if not _accept(s): - raise SyntaxError, "not an ICO file" + raise SyntaxError("not an ICO file") # pick the largest icon in the file m = "" diff --git a/PIL/ImImagePlugin.py b/PIL/ImImagePlugin.py index 788a26229..a7272cc1a 100644 --- a/PIL/ImImagePlugin.py +++ b/PIL/ImImagePlugin.py @@ -113,7 +113,7 @@ class ImImageFile(ImageFile.ImageFile): # 100 bytes, this is (probably) not a text header. if not "\n" in self.fp.read(100): - raise SyntaxError, "not an IM file" + raise SyntaxError("not an IM file") self.fp.seek(0) n = 0 @@ -140,7 +140,7 @@ class ImImageFile(ImageFile.ImageFile): s = s + self.fp.readline() if len(s) > 100: - raise SyntaxError, "not an IM file" + raise SyntaxError("not an IM file") if s[-2:] == '\r\n': s = s[:-2] @@ -149,8 +149,8 @@ class ImImageFile(ImageFile.ImageFile): try: m = split.match(s) - except re.error, v: - raise SyntaxError, "not an IM file" + except re.error as v: + raise SyntaxError("not an IM file") if m: @@ -180,10 +180,10 @@ class ImImageFile(ImageFile.ImageFile): else: - raise SyntaxError, "Syntax error in IM header: " + s + raise SyntaxError("Syntax error in IM header: " + s) if not n: - raise SyntaxError, "Not an IM file" + raise SyntaxError("Not an IM file") # Basic attributes self.size = self.info[SIZE] @@ -193,7 +193,7 @@ class ImImageFile(ImageFile.ImageFile): while s and s[0] != chr(26): s = self.fp.read(1) if not s: - raise SyntaxError, "File truncated" + raise SyntaxError("File truncated") if self.info.has_key(LUT): # convert lookup table to palette or lut attribute @@ -253,7 +253,7 @@ class ImImageFile(ImageFile.ImageFile): def seek(self, frame): if frame < 0 or frame >= self.info[FRAMES]: - raise EOFError, "seek outside sequence" + raise EOFError("seek outside sequence") if self.frame == frame: return @@ -304,7 +304,7 @@ def _save(im, fp, filename, check=0): try: type, rawmode = SAVE[im.mode] except KeyError: - raise ValueError, "Cannot save %s images as IM" % im.mode + raise ValueError("Cannot save %s images as IM" % im.mode) try: frames = im.encoderinfo["frames"] diff --git a/PIL/Image.py b/PIL/Image.py index 8038e6481..1123c9750 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -52,7 +52,7 @@ try: # them. Note that other modules should not refer to _imaging # directly; import Image and use the Image.core variable instead. import _imaging as core -except ImportError, v: +except ImportError as v: core = _imaging_not_installed() if str(v)[:20] == "Module use of python" and warnings: # The _imaging C module is present, but not compiled for @@ -356,7 +356,7 @@ def init(): except ImportError: if DEBUG: print "Image: failed to import", - print f, ":", sys.exc_value + print f, ":", sys.exc_info()[1] visited[fullpath] = None if OPEN or SAVE: diff --git a/PIL/ImageCms.py b/PIL/ImageCms.py index d5f7b975c..9e9debc67 100644 --- a/PIL/ImageCms.py +++ b/PIL/ImageCms.py @@ -307,7 +307,7 @@ def profileToProfile(im, inputProfile, outputProfile, renderingIntent=INTENT_PER imOut = None else: imOut = transform.apply(im) - except (IOError, TypeError, ValueError), v: + except (IOError, TypeError, ValueError) as v: raise PyCMSError(v) return imOut @@ -334,7 +334,7 @@ def getOpenProfile(profileFilename): try: return ImageCmsProfile(profileFilename) - except (IOError, TypeError, ValueError), v: + except (IOError, TypeError, ValueError) as v: raise PyCMSError(v) ## @@ -408,7 +408,7 @@ def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent if not isinstance(outputProfile, ImageCmsProfile): outputProfile = ImageCmsProfile(outputProfile) return ImageCmsTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags) - except (IOError, TypeError, ValueError), v: + except (IOError, TypeError, ValueError) as v: raise PyCMSError(v) ## @@ -501,7 +501,7 @@ def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMo if not isinstance(proofProfile, ImageCmsProfile): proofProfile = ImageCmsProfile(proofProfile) return ImageCmsTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent, proofProfile, proofRenderingIntent, flags) - except (IOError, TypeError, ValueError), v: + except (IOError, TypeError, ValueError) as v: raise PyCMSError(v) buildTransformFromOpenProfiles = buildTransform @@ -557,7 +557,7 @@ def applyTransform(im, transform, inPlace=0): imOut = None else: imOut = transform.apply(im) - except (TypeError, ValueError), v: + except (TypeError, ValueError) as v: raise PyCMSError(v) return imOut @@ -602,7 +602,7 @@ def createProfile(colorSpace, colorTemp=-1): try: return core.createProfile(colorSpace, colorTemp) - except (TypeError, ValueError), v: + except (TypeError, ValueError) as v: raise PyCMSError(v) ## @@ -633,7 +633,7 @@ def getProfileName(profile): if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) return profile.profile.product_name + "\n" - except (AttributeError, IOError, TypeError, ValueError), v: + except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) ## @@ -665,7 +665,7 @@ def getProfileInfo(profile): profile = ImageCmsProfile(profile) # add an extra newline to preserve pyCMS compatibility return profile.product_info + "\n" - except (AttributeError, IOError, TypeError, ValueError), v: + except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) ## @@ -703,7 +703,7 @@ def getDefaultIntent(profile): if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) return profile.profile.rendering_intent - except (AttributeError, IOError, TypeError, ValueError), v: + except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) ## @@ -752,7 +752,7 @@ def isIntentSupported(profile, intent, direction): return 1 else: return -1 - except (AttributeError, IOError, TypeError, ValueError), v: + except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) ## diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index e093f6f56..7800d2a4b 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -89,25 +89,25 @@ class ImageFile(Image.Image): try: self._open() - except IndexError, v: # end of data + except IndexError as v: # end of data if Image.DEBUG > 1: traceback.print_exc() - raise SyntaxError, v - except TypeError, v: # end of data (ord) + raise SyntaxError(v) + except TypeError as v: # end of data (ord) if Image.DEBUG > 1: traceback.print_exc() - raise SyntaxError, v - except KeyError, v: # unsupported mode + raise SyntaxError(v) + except KeyError as v: # unsupported mode if Image.DEBUG > 1: traceback.print_exc() - raise SyntaxError, v - except EOFError, v: # got header but not the first frame + raise SyntaxError(v) + except EOFError as v: # got header but not the first frame if Image.DEBUG > 1: traceback.print_exc() - raise SyntaxError, v + raise SyntaxError(v) if not self.mode or self.size[0] <= 0: - raise SyntaxError, "not identified by this driver" + raise SyntaxError("not identified by this driver") def draft(self, mode, size): "Set draft mode" diff --git a/PIL/ImageMath.py b/PIL/ImageMath.py index 56c42a45a..4dada8258 100644 --- a/PIL/ImageMath.py +++ b/PIL/ImageMath.py @@ -38,7 +38,7 @@ class _Operand: elif im1.im.mode in ("I", "F"): return im1.im else: - raise ValueError, "unsupported mode: %s" % im1.im.mode + raise ValueError("unsupported mode: %s" % im1.im.mode) else: # argument was a constant if _isconstant(im1) and self.im.mode in ("1", "L", "I"): @@ -55,7 +55,7 @@ class _Operand: try: op = getattr(_imagingmath, op+"_"+im1.mode) except AttributeError: - raise TypeError, "bad operand type for '%s'" % op + raise TypeError("bad operand type for '%s'" % op) _imagingmath.unop(op, out.im.id, im1.im.id) else: # binary operation @@ -65,7 +65,7 @@ class _Operand: if im1.mode != "F": im1 = im1.convert("F") if im2.mode != "F": im2 = im2.convert("F") if im1.mode != im2.mode: - raise ValueError, "mode mismatch" + raise ValueError("mode mismatch") if im1.size != im2.size: # crop both arguments to a common size size = (min(im1.size[0], im2.size[0]), @@ -79,7 +79,7 @@ class _Operand: try: op = getattr(_imagingmath, op+"_"+im1.mode) except AttributeError: - raise TypeError, "bad operand type for '%s'" % op + raise TypeError("bad operand type for '%s'" % op) _imagingmath.binop(op, out.im.id, im1.im.id, im2.im.id) return _Operand(out) diff --git a/PIL/ImageOps.py b/PIL/ImageOps.py index b51d78e25..9ff0c4527 100644 --- a/PIL/ImageOps.py +++ b/PIL/ImageOps.py @@ -56,7 +56,7 @@ def _lut(image, lut): lut = lut + lut + lut return image.point(lut) else: - raise IOError, "not supported for this image mode" + raise IOError("not supported for this image mode") # # actions diff --git a/PIL/ImagePalette.py b/PIL/ImagePalette.py index 6efee2998..a13d23e2c 100644 --- a/PIL/ImagePalette.py +++ b/PIL/ImagePalette.py @@ -32,7 +32,7 @@ class ImagePalette: self.colors = {} self.dirty = None if len(self.mode)*256 != len(self.palette): - raise ValueError, "wrong palette size" + raise ValueError("wrong palette size") def getdata(self): # experimental: get palette contents in format suitable @@ -176,7 +176,7 @@ def load(filename): pass if not lut: - raise IOError, "cannot load palette" + raise IOError("cannot load palette") return lut # data, rawmode diff --git a/PIL/ImageStat.py b/PIL/ImageStat.py index 9ebdab030..0cfcc8d81 100644 --- a/PIL/ImageStat.py +++ b/PIL/ImageStat.py @@ -53,13 +53,13 @@ class Stat: except AttributeError: self.h = image_or_list # assume it to be a histogram list if type(self.h) != type([]): - raise TypeError, "first argument must be image or list" + raise TypeError("first argument must be image or list") self.bands = range(len(self.h) / 256) def __getattr__(self, id): "Calculate missing attribute" if id[:4] == "_get": - raise AttributeError, id + raise AttributeError(id) # calculate missing attribute v = getattr(self, "_get" + id)() setattr(self, id, v) diff --git a/PIL/ImageTk.py b/PIL/ImageTk.py index 8618139a5..b3adf1920 100644 --- a/PIL/ImageTk.py +++ b/PIL/ImageTk.py @@ -175,7 +175,7 @@ class PhotoImage: try: tk.call("PyImagingPhoto", self.__photo, block.id) - except Tkinter.TclError, v: + except Tkinter.TclError as v: # activate Tkinter hook try: import _imagingtk @@ -289,7 +289,7 @@ def _show(image, title): bg="black", bd=0) if not Tkinter._default_root: - raise IOError, "tkinter not initialized" + raise IOError("tkinter not initialized") top = Tkinter.Toplevel() if title: top.title(title) diff --git a/PIL/ImtImagePlugin.py b/PIL/ImtImagePlugin.py index bf5611b8a..af955deea 100644 --- a/PIL/ImtImagePlugin.py +++ b/PIL/ImtImagePlugin.py @@ -40,7 +40,7 @@ class ImtImageFile(ImageFile.ImageFile): # 100 bytes, this is (probably) not a text header. if not "\n" in self.fp.read(100): - raise SyntaxError, "not an IM file" + raise SyntaxError("not an IM file") self.fp.seek(0) xsize = ysize = 0 diff --git a/PIL/IptcImagePlugin.py b/PIL/IptcImagePlugin.py index acea5d18b..fc07d534d 100644 --- a/PIL/IptcImagePlugin.py +++ b/PIL/IptcImagePlugin.py @@ -70,12 +70,12 @@ class IptcImageFile(ImageFile.ImageFile): # syntax if ord(s[0]) != 0x1C or tag[0] < 1 or tag[0] > 9: - raise SyntaxError, "invalid IPTC/NAA file" + raise SyntaxError("invalid IPTC/NAA file") # field size size = ord(s[3]) if size > 132: - raise IOError, "illegal field length in IPTC/NAA file" + raise IOError("illegal field length in IPTC/NAA file") elif size == 128: size = 0 elif size > 128: @@ -150,7 +150,7 @@ class IptcImageFile(ImageFile.ImageFile): try: compression = COMPRESSION[self.getint((3,120))] except KeyError: - raise IOError, "Unknown IPTC image compression" + raise IOError("Unknown IPTC image compression") # tile if tag == (8,10): diff --git a/PIL/MicImagePlugin.py b/PIL/MicImagePlugin.py index b1a3bba79..f13c28202 100644 --- a/PIL/MicImagePlugin.py +++ b/PIL/MicImagePlugin.py @@ -47,7 +47,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): try: self.ole = OleFileIO(self.fp) except IOError: - raise SyntaxError, "not an MIC file; invalid OLE file" + raise SyntaxError("not an MIC file; invalid OLE file") # find ACI subfiles with Image members (maybe not the # best way to identify MIC files, but what the... ;-) @@ -60,7 +60,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): # if we didn't find any images, this is probably not # an MIC file. if not self.images: - raise SyntaxError, "not an MIC file; no image entries" + raise SyntaxError("not an MIC file; no image entries") self.__fp = self.fp self.frame = 0 @@ -75,7 +75,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): try: filename = self.images[frame] except IndexError: - raise EOFError, "no such frame" + raise EOFError("no such frame") self.fp = self.ole.openstream(filename) diff --git a/PIL/MpegImagePlugin.py b/PIL/MpegImagePlugin.py index c02edee82..043a5b07b 100644 --- a/PIL/MpegImagePlugin.py +++ b/PIL/MpegImagePlugin.py @@ -65,7 +65,7 @@ class MpegImageFile(ImageFile.ImageFile): s = BitStream(self.fp) if s.read(32) != 0x1B3: - raise SyntaxError, "not an MPEG file" + raise SyntaxError("not an MPEG file") self.mode = "RGB" self.size = s.read(12), s.read(12) diff --git a/PIL/MspImagePlugin.py b/PIL/MspImagePlugin.py index 9dac36b47..8de9f2506 100644 --- a/PIL/MspImagePlugin.py +++ b/PIL/MspImagePlugin.py @@ -45,14 +45,14 @@ class MspImageFile(ImageFile.ImageFile): # Header s = self.fp.read(32) if s[:4] not in ["DanM", "LinS"]: - raise SyntaxError, "not an MSP file" + raise SyntaxError("not an MSP file") # Header checksum sum = 0 for i in range(0, 32, 2): sum = sum ^ i16(s[i:i+2]) if sum != 0: - raise SyntaxError, "bad MSP checksum" + raise SyntaxError("bad MSP checksum") self.mode = "1" self.size = i16(s[4:]), i16(s[6:]) @@ -71,7 +71,7 @@ def o16(i): def _save(im, fp, filename): if im.mode != "1": - raise IOError, "cannot write mode %s as MSP" % im.mode + raise IOError("cannot write mode %s as MSP" % im.mode) # create MSP header header = [0] * 16 diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index 1792c9558..979072866 100644 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -273,7 +273,7 @@ class OleFileIO: header = self.fp.read(512) if len(header) != 512 or header[:8] != MAGIC: - raise IOError, "not an OLE2 structured storage file" + raise IOError("not an OLE2 structured storage file") # file clsid (probably never used, so we don't store it) clsid = self._clsid(header[8:24]) @@ -385,7 +385,7 @@ class OleFileIO: if kid.name == name: break else: - raise IOError, "file not found" + raise IOError("file not found") node = kid return node.sid @@ -423,7 +423,7 @@ class OleFileIO: slot = self._find(filename) name, type, sect, size, sids, clsid = self.sidlist[slot] if type != 2: - raise IOError, "this file is not a stream" + raise IOError("this file is not a stream") return self._open(sect, size) ## @@ -524,5 +524,5 @@ if __name__ == "__main__": props.sort() for k, v in props: print " ", k, v - except IOError, v: + except IOError as v: print "***", "cannot read", file, "-", v diff --git a/PIL/PaletteFile.py b/PIL/PaletteFile.py index 99098e8e9..bd842c3dc 100644 --- a/PIL/PaletteFile.py +++ b/PIL/PaletteFile.py @@ -33,7 +33,7 @@ class PaletteFile: if s[0] == "#": continue if len(s) > 100: - raise SyntaxError, "bad palette file" + raise SyntaxError("bad palette file") v = map(int, s.split()) try: diff --git a/PIL/PalmImagePlugin.py b/PIL/PalmImagePlugin.py index 785023130..59e53038b 100644 --- a/PIL/PalmImagePlugin.py +++ b/PIL/PalmImagePlugin.py @@ -158,7 +158,7 @@ def _save(im, fp, filename, check=0): else: - raise IOError, "cannot write mode %s as Palm" % im.mode + raise IOError("cannot write mode %s as Palm" % im.mode) if check: return check diff --git a/PIL/PcdImagePlugin.py b/PIL/PcdImagePlugin.py index 07bd27e9a..56d8f1dc0 100644 --- a/PIL/PcdImagePlugin.py +++ b/PIL/PcdImagePlugin.py @@ -37,7 +37,7 @@ class PcdImageFile(ImageFile.ImageFile): s = self.fp.read(2048) if s[:4] != "PCD_": - raise SyntaxError, "not a PCD file" + raise SyntaxError("not a PCD file") orientation = ord(s[1538]) & 3 if orientation == 1: diff --git a/PIL/PcfFontFile.py b/PIL/PcfFontFile.py index 92e96eb53..0a97a0734 100644 --- a/PIL/PcfFontFile.py +++ b/PIL/PcfFontFile.py @@ -66,7 +66,7 @@ class PcfFontFile(FontFile.FontFile): magic = l32(fp.read(4)) if magic != PCF_MAGIC: - raise SyntaxError, "not a PCF file" + raise SyntaxError("not a PCF file") FontFile.FontFile.__init__(self) @@ -196,7 +196,7 @@ class PcfFontFile(FontFile.FontFile): nbitmaps = i32(fp.read(4)) if nbitmaps != len(metrics): - raise IOError, "Wrong number of bitmaps" + raise IOError("Wrong number of bitmaps") offsets = [] for i in range(nbitmaps): diff --git a/PIL/PcxImagePlugin.py b/PIL/PcxImagePlugin.py index 15012d51c..a61f248b9 100644 --- a/PIL/PcxImagePlugin.py +++ b/PIL/PcxImagePlugin.py @@ -48,12 +48,12 @@ class PcxImageFile(ImageFile.ImageFile): # header s = self.fp.read(128) if not _accept(s): - raise SyntaxError, "not a PCX file" + raise SyntaxError("not a PCX file") # image bbox = i16(s,4), i16(s,6), i16(s,8)+1, i16(s,10)+1 if bbox[2] <= bbox[0] or bbox[3] <= bbox[1]: - raise SyntaxError, "bad PCX image size" + raise SyntaxError("bad PCX image size") # format version = ord(s[1]) @@ -91,7 +91,7 @@ class PcxImageFile(ImageFile.ImageFile): rawmode = "RGB;L" else: - raise IOError, "unknown PCX mode" + raise IOError("unknown PCX mode") self.mode = mode self.size = bbox[2]-bbox[0], bbox[3]-bbox[1] @@ -119,7 +119,7 @@ def _save(im, fp, filename, check=0): try: version, bits, planes, rawmode = SAVE[im.mode] except KeyError: - raise ValueError, "Cannot save %s images as PCX" % im.mode + raise ValueError("Cannot save %s images as PCX" % im.mode) if check: return check diff --git a/PIL/PixarImagePlugin.py b/PIL/PixarImagePlugin.py index d4c597b61..a428977ab 100644 --- a/PIL/PixarImagePlugin.py +++ b/PIL/PixarImagePlugin.py @@ -45,7 +45,7 @@ class PixarImageFile(ImageFile.ImageFile): # assuming a 4-byte magic label (FIXME: add "_accept" hook) s = self.fp.read(4) if s != "\200\350\000\000": - raise SyntaxError, "not a PIXAR file" + raise SyntaxError("not a PIXAR file") # read rest of header s = s + self.fp.read(508) diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index 4e58c2bf7..e915ed433 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -96,7 +96,7 @@ class ChunkStream: len = i32(s) if not is_cid(cid): - raise SyntaxError, "broken PNG file (chunk %s)" % repr(cid) + raise SyntaxError("broken PNG file (chunk %s)" % repr(cid)) return cid, pos, len @@ -120,8 +120,8 @@ class ChunkStream: crc1 = Image.core.crc32(data, Image.core.crc32(cid)) crc2 = i16(self.fp.read(2)), i16(self.fp.read(2)) if crc1 != crc2: - raise SyntaxError, "broken PNG file"\ - "(bad header checksum in %s)" % cid + raise SyntaxError("broken PNG file"\ + "(bad header checksum in %s)" % cid) def crc_skip(self, cid, data): "Read checksum. Used if the C module is not present" @@ -215,7 +215,7 @@ class PngStream(ChunkStream): if ord(s[12]): self.im_info["interlace"] = 1 if ord(s[11]): - raise SyntaxError, "unknown filter category" + raise SyntaxError("unknown filter category") return s def chunk_IDAT(self, pos, len): @@ -313,7 +313,7 @@ class PngImageFile(ImageFile.ImageFile): def _open(self): if self.fp.read(8) != _MAGIC: - raise SyntaxError, "not a PNG file" + raise SyntaxError("not a PNG file") # # Parse headers up to the first IDAT chunk @@ -503,7 +503,7 @@ def _save(im, fp, filename, chunk=putchunk, check=0): try: rawmode, mode = _OUTMODES[mode] except KeyError: - raise IOError, "cannot write mode %s as PNG" % mode + raise IOError("cannot write mode %s as PNG" % mode) if check: return check diff --git a/PIL/PpmImagePlugin.py b/PIL/PpmImagePlugin.py index e86146c10..a64dc7ae2 100644 --- a/PIL/PpmImagePlugin.py +++ b/PIL/PpmImagePlugin.py @@ -61,7 +61,7 @@ class PpmImageFile(ImageFile.ImageFile): # check magic s = self.fp.read(1) if s != "P": - raise SyntaxError, "not a PPM file" + raise SyntaxError("not a PPM file") mode = MODES[self._token(s)] if mode == "1": @@ -111,7 +111,7 @@ def _save(im, fp, filename): elif im.mode == "RGBA": rawmode, head = "RGB", "P6" else: - raise IOError, "cannot write mode %s as PPM" % im.mode + raise IOError("cannot write mode %s as PPM" % im.mode) fp.write(head + "\n%d %d\n" % im.size) if head != "P4": fp.write("255\n") diff --git a/PIL/PsdImagePlugin.py b/PIL/PsdImagePlugin.py index 4390f5f9c..021aacf80 100644 --- a/PIL/PsdImagePlugin.py +++ b/PIL/PsdImagePlugin.py @@ -65,7 +65,7 @@ class PsdImageFile(ImageFile.ImageFile): s = read(26) if s[:4] != "8BPS" or i16(s[4:]) != 1: - raise SyntaxError, "not a PSD file" + raise SyntaxError("not a PSD file") psd_bits = i16(s[22:]) psd_channels = i16(s[12:]) @@ -74,7 +74,7 @@ class PsdImageFile(ImageFile.ImageFile): mode, channels = MODES[(psd_mode, psd_bits)] if channels > psd_channels: - raise IOError, "not enough channels" + raise IOError("not enough channels") self.mode = mode self.size = i32(s[18:]), i32(s[14:]) @@ -146,7 +146,7 @@ class PsdImageFile(ImageFile.ImageFile): self.fp = self._fp return name, bbox except IndexError: - raise EOFError, "no such layer" + raise EOFError("no such layer") def tell(self): # return layer number (0=image, 1..max=layers) diff --git a/PIL/SpiderImagePlugin.py b/PIL/SpiderImagePlugin.py index e73d55918..5db357da4 100644 --- a/PIL/SpiderImagePlugin.py +++ b/PIL/SpiderImagePlugin.py @@ -101,14 +101,14 @@ class SpiderImageFile(ImageFile.ImageFile): t = struct.unpack('<27f',f) # little-endian hdrlen = isSpiderHeader(t) if hdrlen == 0: - raise SyntaxError, "not a valid Spider file" + raise SyntaxError("not a valid Spider file") except struct.error: - raise SyntaxError, "not a valid Spider file" + raise SyntaxError("not a valid Spider file") h = (99,) + t # add 1 value : spider header index starts at 1 iform = int(h[5]) if iform != 1: - raise SyntaxError, "not a Spider 2D image" + raise SyntaxError("not a Spider 2D image") self.size = int(h[12]), int(h[2]) # size in pixels (width, height) self.istack = int(h[24]) @@ -131,7 +131,7 @@ class SpiderImageFile(ImageFile.ImageFile): offset = hdrlen + self.stkoffset self.istack = 2 # So Image knows it's still a stack else: - raise SyntaxError, "inconsistent stack header values" + raise SyntaxError("inconsistent stack header values") if self.bigendian: self.rawmode = "F;32BF" @@ -154,7 +154,7 @@ class SpiderImageFile(ImageFile.ImageFile): if self.istack == 0: return if frame >= self.nimages: - raise EOFError, "attempt to seek past end of file" + raise EOFError("attempt to seek past end of file") self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes) self.fp = self.__fp self.fp.seek(self.stkoffset) @@ -239,13 +239,13 @@ def _save(im, fp, filename): hdr = makeSpiderHeader(im) if len(hdr) < 256: - raise IOError, "Error creating Spider header" + raise IOError("Error creating Spider header") # write the SPIDER header try: fp = open(filename, 'wb') except: - raise IOError, "Unable to open %s for writing" % filename + raise IOError("Unable to open %s for writing" % filename) fp.writelines(hdr) rawmode = "F;32NF" #32-bit native floating point diff --git a/PIL/SunImagePlugin.py b/PIL/SunImagePlugin.py index d189562a4..37ad4855b 100644 --- a/PIL/SunImagePlugin.py +++ b/PIL/SunImagePlugin.py @@ -46,7 +46,7 @@ class SunImageFile(ImageFile.ImageFile): # HEAD s = self.fp.read(32) if i32(s) != 0x59a66a95: - raise SyntaxError, "not an SUN raster file" + raise SyntaxError("not an SUN raster file") offset = 32 @@ -60,7 +60,7 @@ class SunImageFile(ImageFile.ImageFile): elif depth == 24: self.mode, rawmode = "RGB", "BGR" else: - raise SyntaxError, "unsupported mode" + raise SyntaxError("unsupported mode") compression = i32(s[20:24]) diff --git a/PIL/TarIO.py b/PIL/TarIO.py index 332b30716..7d625402f 100644 --- a/PIL/TarIO.py +++ b/PIL/TarIO.py @@ -36,12 +36,12 @@ class TarIO(ContainerIO.ContainerIO): s = fh.read(512) if len(s) != 512: - raise IOError, "unexpected end of tar file" + raise IOError("unexpected end of tar file") name = s[:100] i = name.find(chr(0)) if i == 0: - raise IOError, "cannot find subfile" + raise IOError("cannot find subfile") if i > 0: name = name[:i] diff --git a/PIL/TgaImagePlugin.py b/PIL/TgaImagePlugin.py index 3375991ca..ce0f8b465 100644 --- a/PIL/TgaImagePlugin.py +++ b/PIL/TgaImagePlugin.py @@ -75,7 +75,7 @@ class TgaImageFile(ImageFile.ImageFile): if id != 0 or colormaptype not in (0, 1) or\ self.size[0] <= 0 or self.size[1] <= 0 or\ depth not in (1, 8, 16, 24, 32): - raise SyntaxError, "not a TGA file" + raise SyntaxError("not a TGA file") # image mode if imagetype in (3, 11): @@ -89,7 +89,7 @@ class TgaImageFile(ImageFile.ImageFile): if depth == 32: self.mode = "RGBA" else: - raise SyntaxError, "unknown TGA mode" + raise SyntaxError("unknown TGA mode") # orientation orientation = flags & 0x30 @@ -98,7 +98,7 @@ class TgaImageFile(ImageFile.ImageFile): elif not orientation: orientation = -1 else: - raise SyntaxError, "unknown TGA orientation" + raise SyntaxError("unknown TGA orientation") self.info["orientation"] = orientation diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index a7f78627d..eea4151af 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -265,7 +265,7 @@ class ImageFileDirectory: # work around broken (?) matrox library # (from Ted Wright, via Bob Klimek) raise KeyError # use default - raise ValueError, "not a scalar" + raise ValueError("not a scalar") return value[0] except KeyError: if default is None: @@ -379,7 +379,7 @@ class ImageFileDirectory: data = ifd[8:8+size] if len(data) != size: - raise IOError, "not enough data" + raise IOError("not enough data") self.tagdata[tag] = typ, data self.tagtype[tag] = typ @@ -513,7 +513,7 @@ class TiffImageFile(ImageFile.ImageFile): ifh = self.fp.read(8) if ifh[:4] not in PREFIXES: - raise SyntaxError, "not a TIFF file" + raise SyntaxError("not a TIFF file") # image file directory (tag dictionary) self.tag = self.ifd = ImageFileDirectory(ifh[:2]) @@ -547,7 +547,7 @@ class TiffImageFile(ImageFile.ImageFile): self.__next = self.__first while self.__frame < frame: if not self.__next: - raise EOFError, "no more images in TIFF file" + raise EOFError("no more images in TIFF file") self.fp.seek(self.__next) self.tag.load(self.fp) self.__next = self.tag.next @@ -589,7 +589,7 @@ class TiffImageFile(ImageFile.ImageFile): "Setup this image object based on current tags" if self.tag.has_key(0xBC01): - raise IOError, "Windows Media Photo files not yet supported" + raise IOError("Windows Media Photo files not yet supported") getscalar = self.tag.getscalar @@ -633,7 +633,7 @@ class TiffImageFile(ImageFile.ImageFile): except KeyError: if Image.DEBUG: print "- unsupported format" - raise SyntaxError, "unknown pixel mode" + raise SyntaxError("unknown pixel mode") if Image.DEBUG: print "- raw mode:", rawmode @@ -751,7 +751,7 @@ def _save(im, fp, filename): try: rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode] except KeyError: - raise IOError, "cannot write mode %s as TIFF" % im.mode + raise IOError("cannot write mode %s as TIFF" % im.mode) ifd = ImageFileDirectory(prefix) diff --git a/PIL/XVThumbImagePlugin.py b/PIL/XVThumbImagePlugin.py index 91e30e9b8..2c6242ed8 100644 --- a/PIL/XVThumbImagePlugin.py +++ b/PIL/XVThumbImagePlugin.py @@ -41,7 +41,7 @@ class XVThumbImageFile(ImageFile.ImageFile): # check magic s = self.fp.read(6) if s != "P7 332": - raise SyntaxError, "not an XV thumbnail file" + raise SyntaxError("not an XV thumbnail file") # Skip to beginning of next line self.fp.readline() @@ -50,7 +50,7 @@ class XVThumbImageFile(ImageFile.ImageFile): while 1: s = self.fp.readline() if not s: - raise SyntaxError, "Unexpected EOF reading XV thumbnail file" + raise SyntaxError("Unexpected EOF reading XV thumbnail file") if s[0] != '#': break diff --git a/PIL/XbmImagePlugin.py b/PIL/XbmImagePlugin.py index ef7e05eba..ef1a42610 100644 --- a/PIL/XbmImagePlugin.py +++ b/PIL/XbmImagePlugin.py @@ -69,7 +69,7 @@ class XbmImageFile(ImageFile.ImageFile): def _save(im, fp, filename): if im.mode != "1": - raise IOError, "cannot write mode %s as XBM" % im.mode + raise IOError("cannot write mode %s as XBM" % im.mode) fp.write("#define im_width %d\n" % im.size[0]) fp.write("#define im_height %d\n" % im.size[1]) diff --git a/PIL/XpmImagePlugin.py b/PIL/XpmImagePlugin.py index 36cb2ada6..10d089e1d 100644 --- a/PIL/XpmImagePlugin.py +++ b/PIL/XpmImagePlugin.py @@ -39,13 +39,13 @@ class XpmImageFile(ImageFile.ImageFile): def _open(self): if not _accept(self.fp.read(9)): - raise SyntaxError, "not an XPM file" + raise SyntaxError("not an XPM file") # skip forward to next string while 1: s = self.fp.readline() if not s: - raise SyntaxError, "broken XPM file" + raise SyntaxError("broken XPM file") m = xpm_head.match(s) if m: break @@ -56,7 +56,7 @@ class XpmImageFile(ImageFile.ImageFile): bpp = int(m.group(4)) if pal > 256 or bpp != 1: - raise ValueError, "cannot read this XPM file" + raise ValueError("cannot read this XPM file") # # load palette description @@ -90,13 +90,13 @@ class XpmImageFile(ImageFile.ImageFile): chr(rgb & 255) else: # unknown colour - raise ValueError, "cannot read this XPM file" + raise ValueError("cannot read this XPM file") break else: # missing colour key - raise ValueError, "cannot read this XPM file" + raise ValueError("cannot read this XPM file") self.mode = "P" self.palette = ImagePalette.raw("RGB", "".join(palette)) diff --git a/Sane/sane.py b/Sane/sane.py index bc212e470..81c581115 100644 --- a/Sane/sane.py +++ b/Sane/sane.py @@ -105,7 +105,7 @@ class _SaneIterator: def next(self): try: self.device.start() - except error, v: + except error as v: if v == 'Document feeder out of documents': raise StopIteration else: @@ -169,11 +169,11 @@ class SaneDev: self.__dict__[key]=value ; return opt=optdict[key] if opt.type==TYPE_GROUP: - raise AttributeError, "Groups can't be set: "+key + raise AttributeError("Groups can't be set: "+key) if not _sane.OPTION_IS_ACTIVE(opt.cap): - raise AttributeError, 'Inactive option: '+key + raise AttributeError('Inactive option: '+key) if not _sane.OPTION_IS_SETTABLE(opt.cap): - raise AttributeError, "Option can't be set by software: "+key + raise AttributeError("Option can't be set by software: "+key) if type(value) == int and opt.type == TYPE_FIXED: # avoid annoying errors of backend if int is given instead float: value = float(value) @@ -190,14 +190,14 @@ class SaneDev: if key=='area': return (self.tl_x, self.tl_y),(self.br_x, self.br_y) if not optdict.has_key(key): - raise AttributeError, 'No such attribute: '+key + raise AttributeError('No such attribute: '+key) opt=optdict[key] if opt.type==TYPE_BUTTON: - raise AttributeError, "Buttons don't have values: "+key + raise AttributeError("Buttons don't have values: "+key) if opt.type==TYPE_GROUP: - raise AttributeError, "Groups don't have values: "+key + raise AttributeError("Groups don't have values: "+key) if not _sane.OPTION_IS_ACTIVE(opt.cap): - raise AttributeError, 'Inactive option: '+key + raise AttributeError('Inactive option: '+key) value = dev.get_option(opt.index) return value diff --git a/Scripts/pilconvert.py b/Scripts/pilconvert.py index 1c688f7c9..c7c3f66a1 100644 --- a/Scripts/pilconvert.py +++ b/Scripts/pilconvert.py @@ -41,7 +41,7 @@ if len(sys.argv) == 1: try: opt, argv = getopt.getopt(sys.argv[1:], "c:dfgopq:r") -except getopt.error, v: +except getopt.error as v: print v sys.exit(1) @@ -93,4 +93,4 @@ try: apply(im.save, (argv[1],), options) except: print "cannot convert image", - print "(%s:%s)" % (sys.exc_type, sys.exc_value) + print "(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1]) diff --git a/Scripts/pilfile.py b/Scripts/pilfile.py index 695725796..c4b4a6a62 100644 --- a/Scripts/pilfile.py +++ b/Scripts/pilfile.py @@ -34,7 +34,7 @@ if len(sys.argv) == 1: try: opt, args = getopt.getopt(sys.argv[1:], "fqivD") -except getopt.error, v: +except getopt.error as v: print v sys.exit(1) @@ -83,8 +83,8 @@ for file in globfix(args): except: if not quiet: print "failed to verify image", - print "(%s:%s)" % (sys.exc_type, sys.exc_value) - except IOError, v: + print "(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1]) + except IOError as v: if not quiet: print file, "failed:", v except: diff --git a/Scripts/pilprint.py b/Scripts/pilprint.py index a98b39f7d..f5279dd85 100644 --- a/Scripts/pilprint.py +++ b/Scripts/pilprint.py @@ -39,7 +39,7 @@ if len(sys.argv) == 1: try: opt, argv = getopt.getopt(sys.argv[1:], "cdpP:") -except getopt.error, v: +except getopt.error as v: print v sys.exit(1) @@ -90,4 +90,4 @@ for file in argv: except: print "cannot print image", - print "(%s:%s)" % (sys.exc_type, sys.exc_value) + print "(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1]) diff --git a/selftest.py b/selftest.py index 22bbe433b..3102b6f0d 100644 --- a/selftest.py +++ b/selftest.py @@ -12,7 +12,7 @@ from PIL import ImageMath try: Image.core.ping -except ImportError, v: +except ImportError as v: print "***", v sys.exit() except AttributeError: From 009eee05774922961ad06308db8b0ce193b4191a Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Thu, 11 Oct 2012 07:57:21 -0500 Subject: [PATCH 03/61] py3k: Get setup.py to run under python3 First real fix: open the temp file in text mode. --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c9b69573a..5608b0dd0 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,4 @@ +from __future__ import print_function import glob import os import platform @@ -433,7 +434,7 @@ class pil_build_ext(build_ext): tmpfile) try: if ret >> 8 == 0: - fp = open(tmpfile, 'rb') + fp = open(tmpfile, 'r') multiarch_path_component = fp.readline().strip() _add_directory(self.compiler.library_dirs, '/usr/lib/' + multiarch_path_component) From 9519013466f2b0828da4c05d0d48dd86c4a803d4 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Sat, 13 Oct 2012 11:53:07 -0500 Subject: [PATCH 04/61] py3k: Modernize type declarations This updates several Python type definitions and uses to bring us closer to Python 3 compatibility. This includes: * Replacing staticforward and statichere with static. These were a hack for old compilers and are not supported/needed anymore. * Using Py_TYPE() instead of ob_type; ob_type is hidden in Py3. * Replacing getattr with getters/setters. getattr is sort-of supported in Py3, but Py_FindMethod is not. So we just use the newer methods/getsetters mechanisms and start using PyType_Ready everywhere. * Use PyVarObject_HEAD_INIT for types, since types are PyVarObject. * Use PyMODINIT_FUNC for module initialization functions. There are some tab/space issues in this commit. I'm set for spaces; the source is a little schizo. --- Sane/_sane.c | 53 ++++++++------ _imaging.c | 186 ++++++++++++++++++++++++++++++++----------------- _imagingcms.c | 176 +++++++++++++++++++++++++++++++--------------- _imagingft.c | 112 ++++++++++++++++++----------- _imagingmath.c | 2 +- decode.c | 45 +++++++----- display.c | 66 ++++++++++++------ encode.c | 45 +++++++----- map.c | 45 +++++++----- outline.c | 44 ++++++++---- path.c | 56 +++++++++------ 11 files changed, 546 insertions(+), 284 deletions(-) diff --git a/Sane/_sane.c b/Sane/_sane.c index 21e542fa5..a4f4377ac 100644 --- a/Sane/_sane.c +++ b/Sane/_sane.c @@ -51,14 +51,18 @@ PySane_Error(SANE_Status st) return NULL; } -staticforward PyTypeObject SaneDev_Type; +static PyTypeObject SaneDev_Type; -#define SaneDevObject_Check(v) ((v)->ob_type == &SaneDev_Type) +#define SaneDevObject_Check(v) (Py_TYPE(v) == &SaneDev_Type) static SaneDevObject * newSaneDevObject(void) { SaneDevObject *self; + + if (PyType_Ready(&SaneDev_Type) < 0) + return NULL; + self = PyObject_NEW(SaneDevObject, &SaneDev_Type); if (self == NULL) return NULL; @@ -1095,29 +1099,38 @@ static PyMethodDef SaneDev_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject * -SaneDev_getattr(SaneDevObject *self, char *name) -{ - return Py_FindMethod(SaneDev_methods, (PyObject *)self, name); -} - -staticforward PyTypeObject SaneDev_Type = { - PyObject_HEAD_INIT(&PyType_Type) - 0, /*ob_size*/ +static PyTypeObject SaneDev_Type = { + PyVarObject_HEAD_INIT(NULL, 0) "SaneDev", /*tp_name*/ sizeof(SaneDevObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ (destructor)SaneDev_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)SaneDev_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + SaneDev_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ }; /* --------------------------------------------------------------------- */ @@ -1248,7 +1261,7 @@ insint(PyObject *d, char *name, int value) Py_DECREF(v); } -void +PyMODINIT_FUNC init_sane(void) { PyObject *m, *d; diff --git a/_imaging.c b/_imaging.c index 9afa08812..dfe0cf10d 100644 --- a/_imaging.c +++ b/_imaging.c @@ -126,7 +126,7 @@ typedef struct { ImagingAccess access; } ImagingObject; -staticforward PyTypeObject Imaging_Type; +static PyTypeObject Imaging_Type; #ifdef WITH_IMAGEDRAW @@ -148,7 +148,7 @@ typedef struct { Glyph glyphs[256]; } ImagingFontObject; -staticforward PyTypeObject ImagingFont_Type; +static PyTypeObject ImagingFont_Type; typedef struct { PyObject_HEAD @@ -157,7 +157,7 @@ typedef struct { int blend; } ImagingDrawObject; -staticforward PyTypeObject ImagingDraw_Type; +static PyTypeObject ImagingDraw_Type; #endif @@ -167,7 +167,7 @@ typedef struct { int readonly; } PixelAccessObject; -staticforward PyTypeObject PixelAccess_Type; +static PyTypeObject PixelAccess_Type; PyObject* PyImagingNew(Imaging imOut) @@ -207,7 +207,7 @@ _dealloc(ImagingObject* imagep) PyObject_Del(imagep); } -#define PyImaging_Check(op) ((op)->ob_type == &Imaging_Type) +#define PyImaging_Check(op) (Py_TYPE(op) == &Imaging_Type) Imaging PyImaging_AsImaging(PyObject *op) { @@ -2232,12 +2232,6 @@ static struct PyMethodDef _font_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -_font_getattr(ImagingFontObject* self, char* name) -{ - return Py_FindMethod(_font_methods, (PyObject*) self, name); -} - /* -------------------------------------------------------------------- */ static PyObject* @@ -2670,12 +2664,6 @@ static struct PyMethodDef _draw_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -_draw_getattr(ImagingDrawObject* self, char* name) -{ - return Py_FindMethod(_draw_methods, (PyObject*) self, name); -} - #endif @@ -2978,29 +2966,44 @@ static struct PyMethodDef methods[] = { /* attributes */ -static PyObject* -_getattr(ImagingObject* self, char* name) +static PyObject* +_getattr_mode(ImagingObject* self, void* closure) { - PyObject* res; - - res = Py_FindMethod(methods, (PyObject*) self, name); - if (res) - return res; - PyErr_Clear(); - if (strcmp(name, "mode") == 0) - return PyString_FromString(self->image->mode); - if (strcmp(name, "size") == 0) - return Py_BuildValue("ii", self->image->xsize, self->image->ysize); - if (strcmp(name, "bands") == 0) - return PyInt_FromLong(self->image->bands); - if (strcmp(name, "id") == 0) - return PyInt_FromLong((long) self->image); - if (strcmp(name, "ptr") == 0) - return PyCObject_FromVoidPtrAndDesc(self->image, IMAGING_MAGIC, NULL); - PyErr_SetString(PyExc_AttributeError, name); - return NULL; + return PyString_FromString(self->image->mode); } +static PyObject* +_getattr_size(ImagingObject* self, void* closure) +{ + return Py_BuildValue("ii", self->image->xsize, self->image->ysize); +} + +static PyObject* +_getattr_bands(ImagingObject* self, void* closure) +{ + return PyInt_FromLong(self->image->bands); +} + +static PyObject* +_getattr_id(ImagingObject* self, void* closure) +{ + return PyInt_FromLong((long) self->image); +} + +static PyObject* +_getattr_ptr(ImagingObject* self, void* closure) +{ + return PyCObject_FromVoidPtrAndDesc(self->image, IMAGING_MAGIC, NULL); +} + +static struct PyGetSetDef getsetters[] = { + { "mode", (getter) _getattr_mode }, + { "size", (getter) _getattr_size }, + { "bands", (getter) _getattr_bands }, + { "id", (getter) _getattr_id }, + { "ptr", (getter) _getattr_ptr }, + { NULL } +}; /* basic sequence semantics */ @@ -3040,49 +3043,108 @@ static PySequenceMethods image_as_sequence = { /* type description */ -statichere PyTypeObject Imaging_Type = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ +static PyTypeObject Imaging_Type = { + PyVarObject_HEAD_INIT(NULL, 0) "ImagingCore", /*tp_name*/ sizeof(ImagingObject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ 0, /*tp_as_number */ &image_as_sequence, /*tp_as_sequence */ 0, /*tp_as_mapping */ - 0 /*tp_hash*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + methods, /*tp_methods*/ + 0, /*tp_members*/ + getsetters, /*tp_getset*/ }; #ifdef WITH_IMAGEDRAW -statichere PyTypeObject ImagingFont_Type = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ +static PyTypeObject ImagingFont_Type = { + PyVarObject_HEAD_INIT(NULL, 0) "ImagingFont", /*tp_name*/ sizeof(ImagingFontObject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)_font_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)_font_getattr, /*tp_getattr*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + _font_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ }; -statichere PyTypeObject ImagingDraw_Type = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ +static PyTypeObject ImagingDraw_Type = { + PyVarObject_HEAD_INIT(NULL, 0) "ImagingDraw", /*tp_name*/ sizeof(ImagingDrawObject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)_draw_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)_draw_getattr, /*tp_getattr*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + _draw_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ }; #endif @@ -3095,9 +3157,9 @@ static PyMappingMethods pixel_access_as_mapping = { /* type description */ -statichere PyTypeObject PixelAccess_Type = { - PyObject_HEAD_INIT(NULL) - 0, "PixelAccess", sizeof(PixelAccessObject), 0, +static PyTypeObject PixelAccess_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "PixelAccess", sizeof(PixelAccessObject), 0, /* methods */ (destructor)pixel_access_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ @@ -3261,19 +3323,19 @@ static PyMethodDef functions[] = { {NULL, NULL} /* sentinel */ }; -DL_EXPORT(void) +PyMODINIT_FUNC init_imaging(void) { PyObject* m; PyObject* d; - /* Patch object type */ - Imaging_Type.ob_type = &PyType_Type; + /* Ready object types */ + PyType_Ready(&Imaging_Type); #ifdef WITH_IMAGEDRAW - ImagingFont_Type.ob_type = &PyType_Type; - ImagingDraw_Type.ob_type = &PyType_Type; + PyType_Ready(&ImagingFont_Type); + PyType_Ready(&ImagingDraw_Type); #endif - PixelAccess_Type.ob_type = &PyType_Type; + PyType_Ready(&PixelAccess_Type); ImagingAccessInit(); diff --git a/_imagingcms.c b/_imagingcms.c index a53351f42..aa09945e5 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -83,9 +83,9 @@ typedef struct { cmsHPROFILE profile; } CmsProfileObject; -staticforward PyTypeObject CmsProfile_Type; +static PyTypeObject CmsProfile_Type; -#define CmsProfile_Check(op) ((op)->ob_type == &CmsProfile_Type) +#define CmsProfile_Check(op) (Py_TYPE(op) == &CmsProfile_Type) static PyObject* cms_profile_new(cmsHPROFILE profile) @@ -158,9 +158,9 @@ typedef struct { cmsHTRANSFORM transform; } CmsTransformObject; -staticforward PyTypeObject CmsTransform_Type; +static PyTypeObject CmsTransform_Type; -#define CmsTransform_Check(op) ((op)->ob_type == &CmsTransform_Type) +#define CmsTransform_Check(op) (Py_TYPE(op) == &CmsTransform_Type) static PyObject* cms_transform_new(cmsHTRANSFORM transform, char* mode_in, char* mode_out) @@ -515,40 +515,83 @@ static struct PyMethodDef cms_profile_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -cms_profile_getattr(CmsProfileObject* self, char* name) +static PyObject* +cms_profile_getattr_product_name(CmsProfileObject* self, void* closure) { - if (!strcmp(name, "product_name")) - return PyString_FromString(cmsTakeProductName(self->profile)); - if (!strcmp(name, "product_desc")) - return PyString_FromString(cmsTakeProductDesc(self->profile)); - if (!strcmp(name, "product_info")) - return PyString_FromString(cmsTakeProductInfo(self->profile)); - if (!strcmp(name, "rendering_intent")) - return PyInt_FromLong(cmsTakeRenderingIntent(self->profile)); - if (!strcmp(name, "pcs")) - return PyString_FromString(findICmode(cmsGetPCS(self->profile))); - if (!strcmp(name, "color_space")) - return PyString_FromString(findICmode(cmsGetColorSpace(self->profile))); - /* FIXME: add more properties (creation_datetime etc) */ - - return Py_FindMethod(cms_profile_methods, (PyObject*) self, name); + return PyString_FromString(cmsTakeProductName(self->profile)); } -statichere PyTypeObject CmsProfile_Type = { - PyObject_HEAD_INIT(NULL) - 0, "CmsProfile", sizeof(CmsProfileObject), 0, +static PyObject* +cms_profile_getattr_product_desc(CmsProfileObject* self, void* closure) +{ + return PyString_FromString(cmsTakeProductDesc(self->profile)); +} + +static PyObject* +cms_profile_getattr_product_info(CmsProfileObject* self, void* closure) +{ + return PyString_FromString(cmsTakeProductInfo(self->profile)); +} + +static PyObject* +cms_profile_getattr_rendering_intent(CmsProfileObject* self, void* closure) +{ + return PyInt_FromLong(cmsTakeRenderingIntent(self->profile)); +} + +static PyObject* +cms_profile_getattr_pcs(CmsProfileObject* self, void* closure) +{ + return PyString_FromString(findICmode(cmsGetPCS(self->profile))); +} + +static PyObject* +cms_profile_getattr_color_space(CmsProfileObject* self, void* closure) +{ + return PyString_FromString(findICmode(cmsGetColorSpace(self->profile))); +} + +/* FIXME: add more properties (creation_datetime etc) */ +static struct PyGetSetDef cms_profile_getsetters[] = { + { "product_name", (getter) cms_profile_getattr_product_name }, + { "product_desc", (getter) cms_profile_getattr_product_desc }, + { "product_info", (getter) cms_profile_getattr_product_info }, + { "rendering_intent", (getter) cms_profile_getattr_rendering_intent }, + { "pcs", (getter) cms_profile_getattr_pcs }, + { "color_space", (getter) cms_profile_getattr_color_space }, + { NULL } +}; + +static PyTypeObject CmsProfile_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "CmsProfile", sizeof(CmsProfileObject), 0, /* methods */ (destructor) cms_profile_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc) cms_profile_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number */ - 0, /*tp_as_sequence */ - 0, /*tp_as_mapping */ - 0 /*tp_hash*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + cms_profile_methods, /*tp_methods*/ + 0, /*tp_members*/ + cms_profile_getsetters, /*tp_getset*/ }; static struct PyMethodDef cms_transform_methods[] = { @@ -556,43 +599,66 @@ static struct PyMethodDef cms_transform_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -cms_transform_getattr(CmsTransformObject* self, char* name) +static PyObject* +cms_transform_getattr_inputMode(CmsTransformObject* self, void* closure) { - if (!strcmp(name, "inputMode")) - return PyString_FromString(self->mode_in); - if (!strcmp(name, "outputMode")) - return PyString_FromString(self->mode_out); - - return Py_FindMethod(cms_transform_methods, (PyObject*) self, name); + return PyString_FromString(self->mode_in); } -statichere PyTypeObject CmsTransform_Type = { - PyObject_HEAD_INIT(NULL) - 0, "CmsTransform", sizeof(CmsTransformObject), 0, +static PyObject* +cms_transform_getattr_outputMode(CmsTransformObject* self, void* closure) +{ + return PyString_FromString(self->mode_out); +} + +static struct PyGetSetDef cms_transform_getsetters[] = { + { "inputMode", (getter) cms_transform_getattr_inputMode }, + { "outputMode", (getter) cms_transform_getattr_outputMode }, + { NULL } +}; + +static PyTypeObject CmsTransform_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "CmsTransform", sizeof(CmsTransformObject), 0, /* methods */ (destructor) cms_transform_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc) cms_transform_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number */ - 0, /*tp_as_sequence */ - 0, /*tp_as_mapping */ - 0 /*tp_hash*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + cms_transform_methods, /*tp_methods*/ + 0, /*tp_members*/ + cms_transform_getsetters, /*tp_getset*/ }; -DL_EXPORT(void) +PyMODINIT_FUNC init_imagingcms(void) { PyObject *m; PyObject *d; PyObject *v; - /* Patch up object types */ - CmsProfile_Type.ob_type = &PyType_Type; - CmsTransform_Type.ob_type = &PyType_Type; + /* Ready object types */ + PyType_Ready(&CmsProfile_Type); + PyType_Ready(&CmsTransform_Type); m = Py_InitModule("_imagingcms", pyCMSdll_methods); d = PyModule_GetDict(m); diff --git a/_imagingft.c b/_imagingft.c index 935808718..634d8b914 100644 --- a/_imagingft.c +++ b/_imagingft.c @@ -82,7 +82,7 @@ typedef struct { FT_Face face; } FontObject; -staticforward PyTypeObject Font_Type; +static PyTypeObject Font_Type; /* round a 26.6 pixel coordinate to the nearest larger integer */ #define PIXEL(x) ((((x)+63) & -64)>>6) @@ -420,49 +420,79 @@ static PyMethodDef font_methods[] = { {NULL, NULL} }; -static PyObject* -font_getattr(FontObject* self, char* name) +static PyObject* +font_getattr_family(FontObject* self, void* closure) { - PyObject* res; - - res = Py_FindMethod(font_methods, (PyObject*) self, name); - - if (res) - return res; - - PyErr_Clear(); - - /* attributes */ - if (!strcmp(name, "family")) { - if (self->face->family_name) - return PyString_FromString(self->face->family_name); - Py_RETURN_NONE; - } - if (!strcmp(name, "style")) { - if (self->face->style_name) - return PyString_FromString(self->face->style_name); - Py_RETURN_NONE; - } - if (!strcmp(name, "ascent")) - return PyInt_FromLong(PIXEL(self->face->size->metrics.ascender)); - if (!strcmp(name, "descent")) - return PyInt_FromLong(-PIXEL(self->face->size->metrics.descender)); - - if (!strcmp(name, "glyphs")) - /* number of glyphs provided by this font */ - return PyInt_FromLong(self->face->num_glyphs); - - PyErr_SetString(PyExc_AttributeError, name); - return NULL; + if (self->face->family_name) + return PyString_FromString(self->face->family_name); + Py_RETURN_NONE; } -statichere PyTypeObject Font_Type = { - PyObject_HEAD_INIT(NULL) - 0, "Font", sizeof(FontObject), 0, +static PyObject* +font_getattr_style(FontObject* self, void* closure) +{ + if (self->face->style_name) + return PyString_FromString(self->face->style_name); + Py_RETURN_NONE; +} + +static PyObject* +font_getattr_ascent(FontObject* self, void* closure) +{ + return PyInt_FromLong(PIXEL(self->face->size->metrics.ascender)); +} + +static PyObject* +font_getattr_descent(FontObject* self, void* closure) +{ + return PyInt_FromLong(-PIXEL(self->face->size->metrics.descender)); +} + +static PyObject* +font_getattr_glyphs(FontObject* self, void* closure) +{ + return PyInt_FromLong(self->face->num_glyphs); +} + +static struct PyGetSetDef font_getsetters[] = { + { "family", (getter) font_getattr_family }, + { "style", (getter) font_getattr_style }, + { "ascent", (getter) font_getattr_ascent }, + { "descent", (getter) font_getattr_descent }, + { "glyphs", (getter) font_getattr_glyphs }, + { NULL } +}; + +static PyTypeObject Font_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "Font", sizeof(FontObject), 0, /* methods */ (destructor)font_dealloc, /* tp_dealloc */ 0, /* tp_print */ - (getattrfunc)font_getattr, /* tp_getattr */ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + font_methods, /*tp_methods*/ + 0, /*tp_members*/ + font_getsetters, /*tp_getset*/ }; static PyMethodDef _functions[] = { @@ -470,7 +500,7 @@ static PyMethodDef _functions[] = { {NULL, NULL} }; -DL_EXPORT(void) +PyMODINIT_FUNC init_imagingft(void) { PyObject* m; @@ -478,8 +508,8 @@ init_imagingft(void) PyObject* v; int major, minor, patch; - /* Patch object type */ - Font_Type.ob_type = &PyType_Type; + /* Ready object type */ + PyType_Ready(&Font_Type); m = Py_InitModule("_imagingft", _functions); d = PyModule_GetDict(m); diff --git a/_imagingmath.c b/_imagingmath.c index 928986bb3..138684973 100644 --- a/_imagingmath.c +++ b/_imagingmath.c @@ -227,7 +227,7 @@ install(PyObject *d, char* name, void* value) Py_XDECREF(v); } -DL_EXPORT(void) +PyMODINIT_FUNC init_imagingmath(void) { PyObject* m; diff --git a/decode.c b/decode.c index 6ea8d9c3f..6f5fe278f 100644 --- a/decode.c +++ b/decode.c @@ -57,7 +57,7 @@ typedef struct { PyObject* lock; } ImagingDecoderObject; -staticforward PyTypeObject ImagingDecoderType; +static PyTypeObject ImagingDecoderType; static ImagingDecoderObject* PyImaging_DecoderNew(int contextsize) @@ -65,7 +65,8 @@ PyImaging_DecoderNew(int contextsize) ImagingDecoderObject *decoder; void *context; - ImagingDecoderType.ob_type = &PyType_Type; + if(PyType_Ready(&ImagingDecoderType) < 0) + return NULL; decoder = PyObject_New(ImagingDecoderObject, &ImagingDecoderType); if (decoder == NULL) @@ -185,26 +186,38 @@ static struct PyMethodDef methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -_getattr(ImagingDecoderObject* self, char* name) -{ - return Py_FindMethod(methods, (PyObject*) self, name); -} - -statichere PyTypeObject ImagingDecoderType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ +static PyTypeObject ImagingDecoderType = { + PyVarObject_HEAD_INIT(NULL, 0) "ImagingDecoder", /*tp_name*/ sizeof(ImagingDecoderObject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_hash*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ }; /* -------------------------------------------------------------------- */ diff --git a/display.c b/display.c index 1a08fadd5..b334fbc95 100644 --- a/display.c +++ b/display.c @@ -44,13 +44,16 @@ typedef struct { ImagingDIB dib; } ImagingDisplayObject; -staticforward PyTypeObject ImagingDisplayType; +static PyTypeObject ImagingDisplayType; static ImagingDisplayObject* _new(const char* mode, int xsize, int ysize) { ImagingDisplayObject *display; + if (PyType_Ready(&ImagingDisplayType) < 0) + return NULL; + display = PyObject_New(ImagingDisplayObject, &ImagingDisplayType); if (display == NULL) return NULL; @@ -217,37 +220,56 @@ static struct PyMethodDef methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -_getattr(ImagingDisplayObject* self, char* name) +static PyObject* +_getattr_mode(ImagingDisplayObject* self, void* closure) { - PyObject* res; - - res = Py_FindMethod(methods, (PyObject*) self, name); - if (res) - return res; - PyErr_Clear(); - if (!strcmp(name, "mode")) return Py_BuildValue("s", self->dib->mode); - if (!strcmp(name, "size")) - return Py_BuildValue("ii", self->dib->xsize, self->dib->ysize); - PyErr_SetString(PyExc_AttributeError, name); - return NULL; } -statichere PyTypeObject ImagingDisplayType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ +static PyObject* +_getattr_size(ImagingDisplayObject* self, void* closure) +{ + return Py_BuildValue("ii", self->dib->xsize, self->dib->ysize); +} + +static struct PyGetSetDef getsetters[] = { + { "mode", (getter) _getattr_mode }, + { "size", (getter) _getattr_size }, + { NULL } +}; + +static PyTypeObject ImagingDisplayType = { + PyVarObject_HEAD_INIT(NULL, 0) "ImagingDisplay", /*tp_name*/ sizeof(ImagingDisplayObject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)_delete, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_hash*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + methods, /*tp_methods*/ + 0, /*tp_members*/ + getsetters, /*tp_getset*/ }; PyObject* diff --git a/encode.c b/encode.c index 2ae13ad72..ed780128b 100644 --- a/encode.c +++ b/encode.c @@ -49,7 +49,7 @@ typedef struct { PyObject* lock; } ImagingEncoderObject; -staticforward PyTypeObject ImagingEncoderType; +static PyTypeObject ImagingEncoderType; static ImagingEncoderObject* PyImaging_EncoderNew(int contextsize) @@ -57,7 +57,8 @@ PyImaging_EncoderNew(int contextsize) ImagingEncoderObject *encoder; void *context; - ImagingEncoderType.ob_type = &PyType_Type; + if(!PyType_Ready(&ImagingEncoderType) < 0) + return NULL; encoder = PyObject_New(ImagingEncoderObject, &ImagingEncoderType); if (encoder == NULL) @@ -241,26 +242,38 @@ static struct PyMethodDef methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -_getattr(ImagingEncoderObject* self, char* name) -{ - return Py_FindMethod(methods, (PyObject*) self, name); -} - -statichere PyTypeObject ImagingEncoderType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ +static PyTypeObject ImagingEncoderType = { + PyVarObject_HEAD_INIT(NULL, 0) "ImagingEncoder", /*tp_name*/ sizeof(ImagingEncoderObject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_hash*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ }; /* -------------------------------------------------------------------- */ diff --git a/map.c b/map.c index beba8a242..493dae2f7 100644 --- a/map.c +++ b/map.c @@ -57,14 +57,15 @@ typedef struct { #endif } ImagingMapperObject; -staticforward PyTypeObject ImagingMapperType; +static PyTypeObject ImagingMapperType; ImagingMapperObject* PyImaging_MapperNew(const char* filename, int readonly) { ImagingMapperObject *mapper; - ImagingMapperType.ob_type = &PyType_Type; + if (PyType_Ready(&ImagingMapperType) < 0) + return NULL; mapper = PyObject_New(ImagingMapperObject, &ImagingMapperType); if (mapper == NULL) @@ -260,26 +261,38 @@ static struct PyMethodDef methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -mapping_getattr(ImagingMapperObject* self, char* name) -{ - return Py_FindMethod(methods, (PyObject*) self, name); -} - -statichere PyTypeObject ImagingMapperType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ +static PyTypeObject ImagingMapperType = { + PyVarObject_HEAD_INIT(NULL, 0) "ImagingMapper", /*tp_name*/ sizeof(ImagingMapperObject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)mapping_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)mapping_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_hash*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ }; PyObject* diff --git a/outline.c b/outline.c index 6bb2ebb54..46db7c938 100644 --- a/outline.c +++ b/outline.c @@ -35,15 +35,18 @@ typedef struct { ImagingOutline outline; } OutlineObject; -staticforward PyTypeObject OutlineType; +static PyTypeObject OutlineType; -#define PyOutline_Check(op) ((op)->ob_type == &OutlineType) +#define PyOutline_Check(op) (Py_TYPE(op) == &OutlineType) static OutlineObject* _outline_new(void) { OutlineObject *self; + if (PyType_Ready(&OutlineType) < 0) + return NULL; + self = PyObject_New(OutlineObject, &OutlineType); if (self == NULL) return NULL; @@ -159,21 +162,36 @@ static struct PyMethodDef _outline_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -_outline_getattr(OutlineObject* self, char* name) -{ - return Py_FindMethod(_outline_methods, (PyObject*) self, name); -} - -statichere PyTypeObject OutlineType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ +static PyTypeObject OutlineType = { + PyVarObject_HEAD_INIT(NULL, 0) "Outline", /*tp_name*/ sizeof(OutlineObject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)_outline_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)_outline_getattr, /*tp_getattr*/ - 0 /*tp_setattr*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + _outline_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ }; diff --git a/path.c b/path.c index f083b971d..e9cb74a76 100644 --- a/path.c +++ b/path.c @@ -59,7 +59,7 @@ typedef struct { int index; /* temporary use, e.g. in decimate */ } PyPathObject; -staticforward PyTypeObject PyPathType; +static PyTypeObject PyPathType; static double* alloc_array(int count) @@ -89,6 +89,9 @@ path_new(Py_ssize_t count, double* xy, int duplicate) xy = p; } + if (PyType_Ready(&PyPathType) < 0) + return NULL; + path = PyObject_New(PyPathObject, &PyPathType); if (path == NULL) return NULL; @@ -110,7 +113,7 @@ path_dealloc(PyPathObject* path) /* Helpers */ /* -------------------------------------------------------------------- */ -#define PyPath_Check(op) ((op)->ob_type == &PyPathType) +#define PyPath_Check(op) (Py_TYPE(op) == &PyPathType) int PyPath_Flatten(PyObject* data, double **pxy) @@ -539,24 +542,17 @@ static struct PyMethodDef methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -path_getattr(PyPathObject* self, char* name) +static PyObject* +path_getattr_id(PyPathObject* self, void* closure) { - PyObject* res; - - res = Py_FindMethod(methods, (PyObject*) self, name); - if (res) - return res; - - PyErr_Clear(); - - if (strcmp(name, "id") == 0) return Py_BuildValue("l", (long) self->xy); - - PyErr_SetString(PyExc_AttributeError, name); - return NULL; } +static struct PyGetSetDef getsetters[] = { + { "id", (getter) path_getattr_id }, + { NULL } +}; + static PySequenceMethods path_as_sequence = { (lenfunc)path_len, /*sq_length*/ (binaryfunc)0, /*sq_concat*/ @@ -567,21 +563,37 @@ static PySequenceMethods path_as_sequence = { (ssizessizeobjargproc)0, /*sq_ass_slice*/ }; -statichere PyTypeObject PyPathType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ +static PyTypeObject PyPathType = { + PyVarObject_HEAD_INIT(NULL, 0) "Path", /*tp_name*/ sizeof(PyPathObject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)path_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)path_getattr, /*tp_getattr*/ + 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number */ &path_as_sequence, /*tp_as_sequence */ - 0, /*tp_as_mapping */ - 0, /*tp_hash*/ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + methods, /*tp_methods*/ + 0, /*tp_members*/ + getsetters, /*tp_getset*/ }; + From 804095ecb351651ef65ae9aea8e1ce6a23368fb8 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Sat, 20 Oct 2012 16:13:12 -0500 Subject: [PATCH 05/61] py3k: Use new buffer protocol Other ports have taken advantage of the fact that Python 3 has wrappers for the old buffer protocol, but there's a significant disadvantage: you can't let the buffered object know when you're done with it. Since Python 2.6 supports the new protocol, we just go ahead and move to it. --- _imaging.c | 64 ++++++++++++++++++++---------------------------------- map.c | 20 ++++++++++------- path.c | 26 ++++++++++++---------- 3 files changed, 50 insertions(+), 60 deletions(-) diff --git a/_imaging.c b/_imaging.c index dfe0cf10d..f599e124e 100644 --- a/_imaging.c +++ b/_imaging.c @@ -103,19 +103,6 @@ #define L16(p, i) ((((int)p[(i)+1]) << 8) + p[(i)]) #define S16(v) ((v) < 32768 ? (v) : ((v) - 65536)) -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - -#if PY_VERSION_HEX < 0x02050000 -#define Py_ssize_t int -#define ssizeargfunc intargfunc -#define ssizessizeargfunc intintargfunc -#define ssizeobjargproc intobjargproc -#define ssizessizeobjargproc intintobjargproc -#endif - /* -------------------------------------------------------------------- */ /* OBJECT ADMINISTRATION */ /* -------------------------------------------------------------------- */ @@ -243,43 +230,38 @@ void ImagingSectionLeave(ImagingSectionCookie* cookie) /* -------------------------------------------------------------------- */ /* Python compatibility API */ -#if PY_VERSION_HEX < 0x02020000 - -int PyImaging_CheckBuffer(PyObject *buffer) -{ - PyBufferProcs *procs = buffer->ob_type->tp_as_buffer; - if (procs && procs->bf_getreadbuffer && procs->bf_getsegcount && - procs->bf_getsegcount(buffer, NULL) == 1) - return 1; - return 0; -} - -int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr) -{ - PyBufferProcs *procs = buffer->ob_type->tp_as_buffer; - return procs->bf_getreadbuffer(buffer, 0, ptr); -} - -#else - int PyImaging_CheckBuffer(PyObject* buffer) { - return PyObject_CheckReadBuffer(buffer); +#if PY_VERSION_HEX >= 0x03000000 + return PyObject_CheckBuffer(buffer); +#else + return PyObject_CheckBuffer(buffer) || PyObject_CheckReadBuffer(buffer); +#endif } -int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr) +int PyImaging_GetBuffer(PyObject* buffer, Py_buffer *view) { /* must call check_buffer first! */ -#if PY_VERSION_HEX < 0x02050000 - int n = 0; +#if PY_VERSION_HEX >= 0x03000000 + return PyObject_GetBuffer(buffer, view, PyBUF_SIMPLE); #else - Py_ssize_t n = 0; -#endif - PyObject_AsReadBuffer(buffer, ptr, &n); - return (int) n; -} + /* Use new buffer protocol if available + (mmap doesn't support this in 2.7, go figure) */ + if (PyObject_CheckBuffer(buffer)) { + return PyObject_GetBuffer(buffer, view, PyBUF_SIMPLE); + } + /* Pretend we support the new protocol; PyBuffer_Release happily ignores + calling bf_releasebuffer on objects that don't support it */ + *view = (Py_buffer) {0}; + view->readonly = 1; + + Py_INCREF(buffer); + view->obj = buffer; + + return PyObject_AsReadBuffer(buffer, (void *) &view->buf, &view->len); #endif +} /* -------------------------------------------------------------------- */ /* EXCEPTION REROUTING */ diff --git a/map.c b/map.c index 493dae2f7..1041d6121 100644 --- a/map.c +++ b/map.c @@ -41,7 +41,7 @@ /* compatibility wrappers (defined in _imaging.c) */ extern int PyImaging_CheckBuffer(PyObject* buffer); -extern int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr); +extern int PyImaging_GetBuffer(PyObject* buffer, Py_buffer *view); /* -------------------------------------------------------------------- */ /* Standard mapper */ @@ -311,6 +311,7 @@ PyImaging_Mapper(PyObject* self, PyObject* args) typedef struct ImagingBufferInstance { struct ImagingMemoryInstance im; PyObject* target; + Py_buffer view; } ImagingBufferInstance; static void @@ -318,6 +319,7 @@ mapping_destroy_buffer(Imaging im) { ImagingBufferInstance* buffer = (ImagingBufferInstance*) im; + PyBuffer_Release(&buffer->view); Py_XDECREF(buffer->target); } @@ -326,10 +328,9 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args) { int y, size; Imaging im; - char* ptr; - int bytes; PyObject* target; + Py_buffer view; char* mode; char* codec; PyObject* bbox; @@ -359,12 +360,14 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args) size = ysize * stride; /* check buffer size */ - bytes = PyImaging_ReadBuffer(target, (const void**) &ptr); - if (bytes < 0) { + if (PyImaging_GetBuffer(target, &view) < 0) + return NULL; + + if (view.len < 0) { PyErr_SetString(PyExc_ValueError, "buffer has negative size"); return NULL; } - if (offset + size > bytes) { + if (offset + size > view.len) { PyErr_SetString(PyExc_ValueError, "buffer is not large enough"); return NULL; } @@ -378,15 +381,16 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args) /* setup file pointers */ if (ystep > 0) for (y = 0; y < ysize; y++) - im->image[y] = ptr + offset + y * stride; + im->image[y] = view.buf + offset + y * stride; else for (y = 0; y < ysize; y++) - im->image[ysize-y-1] = ptr + offset + y * stride; + im->image[ysize-y-1] = view.buf + offset + y * stride; im->destroy = mapping_destroy_buffer; Py_INCREF(target); ((ImagingBufferInstance*) im)->target = target; + ((ImagingBufferInstance*) im)->view = view; if (!ImagingNewEpilogue(im)) return NULL; diff --git a/path.c b/path.c index e9cb74a76..b29f9b4d9 100644 --- a/path.c +++ b/path.c @@ -46,7 +46,7 @@ /* compatibility wrappers (defined in _imaging.c) */ extern int PyImaging_CheckBuffer(PyObject* buffer); -extern int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr); +extern int PyImaging_GetBuffer(PyObject* buffer, Py_buffer *view); /* -------------------------------------------------------------------- */ /* Class */ @@ -134,16 +134,20 @@ PyPath_Flatten(PyObject* data, double **pxy) if (PyImaging_CheckBuffer(data)) { /* Assume the buffer contains floats */ - float* ptr; - int n = PyImaging_ReadBuffer(data, (const void**) &ptr); - n /= 2 * sizeof(float); - xy = alloc_array(n); - if (!xy) - return -1; - for (i = 0; i < n+n; i++) - xy[i] = ptr[i]; - *pxy = xy; - return n; + Py_buffer buffer; + if (PyImaging_GetBuffer(data, &buffer) == 0) { + int n = buffer.len / (2 * sizeof(float)); + float *ptr = (float*) buffer.buf; + xy = alloc_array(n); + if (!xy) + return -1; + for (i = 0; i < n+n; i++) + xy[i] = ptr[i]; + *pxy = xy; + PyBuffer_Release(&buffer); + return n; + } + PyErr_Clear(); } if (!PySequence_Check(data)) { From d28a2fee7629258fd0d590a1254c2aabb92dd455 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Sat, 13 Oct 2012 13:43:54 -0500 Subject: [PATCH 06/61] py3k: Remove HAVE_UNICODE from _imagingft I'm pretty sure this preserves the intent of the code. HAVE_UNICODE is now assumed, and PyString is only used if we're not in Py3k. Since this is the only file that uses PyUnicode, I'm going to go ahead and #define PyUnicode and PyBytes back to PyString for 2.6, and explicitly change out every call so I have to check them all. --- _imagingft.c | 37 +++++++++++++------------------------ 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/_imagingft.c b/_imagingft.c index 634d8b914..36b7ec5e4 100644 --- a/_imagingft.c +++ b/_imagingft.c @@ -40,13 +40,6 @@ #define PyObject_Del PyMem_DEL #endif -#if PY_VERSION_HEX >= 0x01060000 -#if PY_VERSION_HEX < 0x02020000 || defined(Py_USING_UNICODE) -/* defining this enables unicode support (default under 1.6a1 and later) */ -#define HAVE_UNICODE -#endif -#endif - #if !defined(Py_RETURN_NONE) #define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None #endif @@ -118,16 +111,10 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw) "filename", "size", "index", "encoding", NULL }; -#if defined(HAVE_UNICODE) && PY_VERSION_HEX >= 0x02020000 if (!PyArg_ParseTupleAndKeywords(args, kw, "eti|is", kwlist, Py_FileSystemDefaultEncoding, &filename, &size, &index, &encoding)) return NULL; -#else - if (!PyArg_ParseTupleAndKeywords(args, kw, "si|is", kwlist, - &filename, &size, &index, &encoding)) - return NULL; -#endif if (!library) { PyErr_SetString( @@ -164,7 +151,6 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw) static int font_getchar(PyObject* string, int index, FT_ULong* char_out) { -#if defined(HAVE_UNICODE) if (PyUnicode_Check(string)) { Py_UNICODE* p = PyUnicode_AS_UNICODE(string); int size = PyUnicode_GET_SIZE(string); @@ -173,7 +159,8 @@ font_getchar(PyObject* string, int index, FT_ULong* char_out) *char_out = p[index]; return 1; } -#endif + +#if PY_VERSION_HEX < 0x03000000 if (PyString_Check(string)) { unsigned char* p = (unsigned char*) PyString_AS_STRING(string); int size = PyString_GET_SIZE(string); @@ -182,6 +169,8 @@ font_getchar(PyObject* string, int index, FT_ULong* char_out) *char_out = (unsigned char) p[index]; return 1; } +#endif + return 0; } @@ -201,10 +190,10 @@ font_getsize(FontObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "O:getsize", &string)) return NULL; -#if defined(HAVE_UNICODE) - if (!PyUnicode_Check(string) && !PyString_Check(string)) { +#if PY_VERSION_HEX >= 0x03000000 + if (!PyUnicode_Check(string)) { #else - if (!PyString_Check(string)) { + if (!PyUnicode_Check(string) && !PyString_Check(string)) { #endif PyErr_SetString(PyExc_TypeError, "expected string"); return NULL; @@ -267,10 +256,10 @@ font_getabc(FontObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "O:getabc", &string)) return NULL; -#if defined(HAVE_UNICODE) - if (!PyUnicode_Check(string) && !PyString_Check(string)) { +#if PY_VERSION_HEX >= 0x03000000 + if (!PyUnicode_Check(string)) { #else - if (!PyString_Check(string)) { + if (!PyUnicode_Check(string) && !PyString_Check(string)) { #endif PyErr_SetString(PyExc_TypeError, "expected string"); return NULL; @@ -315,10 +304,10 @@ font_render(FontObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "Ol|i:render", &string, &id, &mask)) return NULL; -#if defined(HAVE_UNICODE) - if (!PyUnicode_Check(string) && !PyString_Check(string)) { +#if PY_VERSION_HEX >= 0x03000000 + if (!PyUnicode_Check(string)) { #else - if (!PyString_Check(string)) { + if (!PyUnicode_Check(string) && !PyString_Check(string)) { #endif PyErr_SetString(PyExc_TypeError, "expected string"); return NULL; From 89e82c588882970ba3cab12b1ca17cf1e813eb3f Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Sat, 13 Oct 2012 22:24:48 -0500 Subject: [PATCH 07/61] py3k: Fix strict aliasing slip-up in _imaging Python 3 enables C's strict aliasing rules for the first time, which means you need to be careful about the ways you reference pointers. Here, we're using a char[4] as an INT32, so we cast between them using a union. --- _imaging.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/_imaging.c b/_imaging.c index f599e124e..355f0f86a 100644 --- a/_imaging.c +++ b/_imaging.c @@ -1308,14 +1308,18 @@ _putdata(ImagingObject* self, PyObject* args) break; default: for (i = x = y = 0; i < n; i++) { - char ink[4]; + union { + char ink[4]; + INT32 inkint; + } u; + PyObject *op = PySequence_GetItem(data, i); - if (!op || !getink(op, image, ink)) { + if (!op || !getink(op, image, u.ink)) { Py_DECREF(op); return NULL; } /* FIXME: what about scale and offset? */ - image->image32[y][x] = *((INT32*) ink); + image->image32[y][x] = u.inkint; Py_XDECREF(op); if (++x >= (int) image->xsize) x = 0, y++; From af5228896a92529d37bad4c5b609047e7b426da0 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Sat, 13 Oct 2012 23:47:30 -0500 Subject: [PATCH 08/61] py3k: Add module initialization and unicode/bytes int/long thunks This commit: * Adds Python 3 module initialization functions. I split out the main init of each module into a static setup_module function. * Adds a py3.h which unifies int/long in Python 3 and unicode/bytes in Python 2. _imagingft.c unfortunately looks a little kludgy after this because it was already using PyUnicode functions, and I had to mix and match there manually. With this commit, the modules all build successfully under Python 3. What this commit does NOT do is patch all of the uses of PyArg_ParseTuple and Py_BuildValue, which all need to be checked for proper use of bytes and unicode codes. It also does not let selftest.py run yet, because there are probably hundreds of issues to fix in the Python code itself. --- PIL/Image.py | 6 ++-- _imaging.c | 81 ++++++++++++++++++++++++++++++++++++-------------- _imagingcms.c | 66 +++++++++++++++++++++++++++------------- _imagingft.c | 68 ++++++++++++++++++++++++++++++++---------- _imagingmath.c | 43 ++++++++++++++++++++++----- _imagingtk.c | 18 ++++++++++- encode.c | 6 ++-- map.c | 6 ++-- path.c | 2 ++ py3.h | 48 ++++++++++++++++++++++++++++++ 10 files changed, 269 insertions(+), 75 deletions(-) create mode 100644 py3.h diff --git a/PIL/Image.py b/PIL/Image.py index 1123c9750..7f2ea381d 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -907,12 +907,12 @@ class Image: return self.im.getextrema() ## - # Returns a PyCObject that points to the internal image memory. + # Returns a capsule that points to the internal image memory. # - # @return A PyCObject object. + # @return A capsule object. def getim(self): - "Get PyCObject pointer to internal image memory" + "Get capsule pointer to internal image memory" self.load() return self.im.ptr diff --git a/_imaging.c b/_imaging.c index 355f0f86a..610710bd1 100644 --- a/_imaging.c +++ b/_imaging.c @@ -76,6 +76,7 @@ #include "Imaging.h" +#include "py3.h" /* Configuration stuff. Feel free to undef things you don't need. */ #define WITH_IMAGECHOPS /* ImageChops support */ @@ -896,11 +897,11 @@ _getpalette(ImagingObject* self, PyObject* args) return NULL; } - palette = PyString_FromStringAndSize(NULL, palettesize * bits / 8); + palette = PyBytes_FromStringAndSize(NULL, palettesize * bits / 8); if (!palette) return NULL; - pack((UINT8*) PyString_AsString(palette), + pack((UINT8*) PyBytes_AsString(palette), self->image->palette->palette, palettesize); return palette; @@ -1221,9 +1222,9 @@ _putdata(ImagingObject* self, PyObject* args) } if (image->image8) { - if (PyString_Check(data)) { + if (PyBytes_Check(data)) { unsigned char* p; - p = (unsigned char*) PyString_AS_STRING((PyStringObject*) data); + p = (unsigned char*) PyBytes_AS_STRING(data); if (scale == 1.0 && offset == 0.0) /* Plain string data */ for (i = y = 0; i < n; i += image->xsize, y++) { @@ -2817,11 +2818,10 @@ _getcodecstatus(PyObject* self, PyObject* args) case IMAGING_CODEC_MEMORY: msg = "out of memory"; break; default: - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } - return PyString_FromString(msg); + return PyUnicode_FromString(msg); } /* -------------------------------------------------------------------- */ @@ -2955,7 +2955,7 @@ static struct PyMethodDef methods[] = { static PyObject* _getattr_mode(ImagingObject* self, void* closure) { - return PyString_FromString(self->image->mode); + return PyUnicode_FromString(self->image->mode); } static PyObject* @@ -2979,7 +2979,11 @@ _getattr_id(ImagingObject* self, void* closure) static PyObject* _getattr_ptr(ImagingObject* self, void* closure) { +#if PY_VERSION_HEX >= 0x03020000 + return PyCapsule_New(self->image, IMAGING_MAGIC, NULL); +#else return PyCObject_FromVoidPtrAndDesc(self->image, IMAGING_MAGIC, NULL); +#endif } static struct PyGetSetDef getsetters[] = { @@ -3309,36 +3313,69 @@ static PyMethodDef functions[] = { {NULL, NULL} /* sentinel */ }; -PyMODINIT_FUNC -init_imaging(void) -{ - PyObject* m; - PyObject* d; +static int +setup_module(PyObject* m) { + PyObject* d = PyModule_GetDict(m); /* Ready object types */ - PyType_Ready(&Imaging_Type); + if (PyType_Ready(&Imaging_Type) < 0) + return -1; + #ifdef WITH_IMAGEDRAW - PyType_Ready(&ImagingFont_Type); - PyType_Ready(&ImagingDraw_Type); + if (PyType_Ready(&ImagingFont_Type) < 0) + return -1; + + if (PyType_Ready(&ImagingDraw_Type) < 0) + return -1; #endif - PyType_Ready(&PixelAccess_Type); + if (PyType_Ready(&PixelAccess_Type) < 0) + return -1; ImagingAccessInit(); - m = Py_InitModule("_imaging", functions); - d = PyModule_GetDict(m); - #ifdef HAVE_LIBJPEG { extern const char* ImagingJpegVersion(void); - PyDict_SetItemString(d, "jpeglib_version", PyString_FromString(ImagingJpegVersion())); + PyDict_SetItemString(d, "jpeglib_version", PyUnicode_FromString(ImagingJpegVersion())); } #endif #ifdef HAVE_LIBZ { extern const char* ImagingZipVersion(void); - PyDict_SetItemString(d, "zlib_version", PyString_FromString(ImagingZipVersion())); + PyDict_SetItemString(d, "zlib_version", PyUnicode_FromString(ImagingZipVersion())); } #endif + + return 0; } + +#if PY_VERSION_HEX >= 0x03000000 +PyMODINIT_FUNC +PyInit__imaging(void) { + PyObject* m; + + static PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "_imaging", /* m_name */ + NULL, /* m_doc */ + -1, /* m_size */ + functions, /* m_methods */ + }; + + m = PyModule_Create(&module_def); + + if (setup_module(m) < 0) + return NULL; + + return m; +} +#else +PyMODINIT_FUNC +init_imaging(void) +{ + PyObject* m = Py_InitModule("_imaging", functions); + setup_module(m); +} +#endif + diff --git a/_imagingcms.c b/_imagingcms.c index aa09945e5..693de5c3b 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -26,6 +26,7 @@ http://www.cazabon.com\n\ #include "Python.h" #include "lcms.h" #include "Imaging.h" +#include "py3.h" #if PY_VERSION_HEX < 0x01060000 #define PyObject_New PyObject_NEW @@ -518,19 +519,19 @@ static struct PyMethodDef cms_profile_methods[] = { static PyObject* cms_profile_getattr_product_name(CmsProfileObject* self, void* closure) { - return PyString_FromString(cmsTakeProductName(self->profile)); + return PyUnicode_FromString(cmsTakeProductName(self->profile)); } static PyObject* cms_profile_getattr_product_desc(CmsProfileObject* self, void* closure) { - return PyString_FromString(cmsTakeProductDesc(self->profile)); + return PyUnicode_FromString(cmsTakeProductDesc(self->profile)); } static PyObject* cms_profile_getattr_product_info(CmsProfileObject* self, void* closure) { - return PyString_FromString(cmsTakeProductInfo(self->profile)); + return PyUnicode_FromString(cmsTakeProductInfo(self->profile)); } static PyObject* @@ -542,13 +543,13 @@ cms_profile_getattr_rendering_intent(CmsProfileObject* self, void* closure) static PyObject* cms_profile_getattr_pcs(CmsProfileObject* self, void* closure) { - return PyString_FromString(findICmode(cmsGetPCS(self->profile))); + return PyUnicode_FromString(findICmode(cmsGetPCS(self->profile))); } static PyObject* cms_profile_getattr_color_space(CmsProfileObject* self, void* closure) { - return PyString_FromString(findICmode(cmsGetColorSpace(self->profile))); + return PyUnicode_FromString(findICmode(cmsGetColorSpace(self->profile))); } /* FIXME: add more properties (creation_datetime etc) */ @@ -602,13 +603,13 @@ static struct PyMethodDef cms_transform_methods[] = { static PyObject* cms_transform_getattr_inputMode(CmsTransformObject* self, void* closure) { - return PyString_FromString(self->mode_in); + return PyUnicode_FromString(self->mode_in); } static PyObject* cms_transform_getattr_outputMode(CmsTransformObject* self, void* closure) { - return PyString_FromString(self->mode_out); + return PyUnicode_FromString(self->mode_out); } static struct PyGetSetDef cms_transform_getsetters[] = { @@ -649,28 +650,51 @@ static PyTypeObject CmsTransform_Type = { cms_transform_getsetters, /*tp_getset*/ }; -PyMODINIT_FUNC -init_imagingcms(void) -{ - PyObject *m; +static int +setup_module(PyObject* m) { PyObject *d; PyObject *v; + d = PyModule_GetDict(m); + /* Ready object types */ PyType_Ready(&CmsProfile_Type); PyType_Ready(&CmsTransform_Type); - m = Py_InitModule("_imagingcms", pyCMSdll_methods); d = PyModule_GetDict(m); -#if PY_VERSION_HEX >= 0x02020000 - v = PyString_FromFormat("%d.%d", LCMS_VERSION / 100, LCMS_VERSION % 100); -#else - { - char buffer[100]; - sprintf(buffer, "%d.%d", LCMS_VERSION / 100, LCMS_VERSION % 100); - v = PyString_FromString(buffer); - } -#endif + v = PyUnicode_FromFormat("%d.%d", LCMS_VERSION / 100, LCMS_VERSION % 100); PyDict_SetItemString(d, "littlecms_version", v); + + return 0; } + +#if PY_VERSION_HEX >= 0x03000000 +PyMODINIT_FUNC +PyInit__imagingcms(void) { + PyObject* m; + + static PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "_imagingcms", /* m_name */ + NULL, /* m_doc */ + -1, /* m_size */ + pyCMSdll_methods, /* m_methods */ + }; + + m = PyModule_Create(&module_def); + + if (setup_module(m) < 0) + return NULL; + + return m; +} +#else +PyMODINIT_FUNC +init_imagingcms(void) +{ + PyObject *m = Py_InitModule("_imagingcms", pyCMSdll_methods); + setup_module(m); +} +#endif + diff --git a/_imagingft.c b/_imagingft.c index 36b7ec5e4..864d74033 100644 --- a/_imagingft.c +++ b/_imagingft.c @@ -44,6 +44,9 @@ #define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None #endif +#define KEEP_PY_UNICODE +#include "py3.h" + #if !defined(FT_LOAD_TARGET_MONO) #define FT_LOAD_TARGET_MONO FT_LOAD_MONOCHROME #endif @@ -412,16 +415,26 @@ static PyMethodDef font_methods[] = { static PyObject* font_getattr_family(FontObject* self, void* closure) { +#if PY_VERSION_HEX >= 0x03000000 + if (self->face->family_name) + return PyUnicode_FromString(self->face->family_name); +#else if (self->face->family_name) return PyString_FromString(self->face->family_name); +#endif Py_RETURN_NONE; } static PyObject* font_getattr_style(FontObject* self, void* closure) { +#if PY_VERSION_HEX >= 0x03000000 + if (self->face->style_name) + return PyUnicode_FromString(self->face->style_name); +#else if (self->face->style_name) return PyString_FromString(self->face->style_name); +#endif Py_RETURN_NONE; } @@ -489,33 +502,58 @@ static PyMethodDef _functions[] = { {NULL, NULL} }; -PyMODINIT_FUNC -init_imagingft(void) -{ - PyObject* m; +static int +setup_module(PyObject* m) { PyObject* d; PyObject* v; int major, minor, patch; + d = PyModule_GetDict(m); + /* Ready object type */ PyType_Ready(&Font_Type); - m = Py_InitModule("_imagingft", _functions); - d = PyModule_GetDict(m); - if (FT_Init_FreeType(&library)) - return; /* leave it uninitalized */ + return 0; /* leave it uninitalized */ FT_Library_Version(library, &major, &minor, &patch); -#if PY_VERSION_HEX >= 0x02020000 - v = PyString_FromFormat("%d.%d.%d", major, minor, patch); +#if PY_VERSION_HEX >= 0x03000000 + v = PyUnicode_FromFormat("%d.%d.%d", major, minor, patch); #else - { - char buffer[100]; - sprintf(buffer, "%d.%d.%d", major, minor, patch); - v = PyString_FromString(buffer); - } + v = PyString_FromFormat("%d.%d.%d", major, minor, patch); #endif PyDict_SetItemString(d, "freetype2_version", v); + + return 0; } + +#if PY_VERSION_HEX >= 0x03000000 +PyMODINIT_FUNC +PyInit__imagingft(void) { + PyObject* m; + + static PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "_imagingft", /* m_name */ + NULL, /* m_doc */ + -1, /* m_size */ + _functions, /* m_methods */ + }; + + m = PyModule_Create(&module_def); + + if (setup_module(m) < 0) + return NULL; + + return m; +} +#else +PyMODINIT_FUNC +init_imagingft(void) +{ + PyObject* m = Py_InitModule("_imagingft", _functions); + setup_module(m); +} +#endif + diff --git a/_imagingmath.c b/_imagingmath.c index 138684973..c21dac1de 100644 --- a/_imagingmath.c +++ b/_imagingmath.c @@ -16,6 +16,7 @@ #include "Python.h" #include "Imaging.h" +#include "py3.h" #include "math.h" #include "float.h" @@ -227,14 +228,9 @@ install(PyObject *d, char* name, void* value) Py_XDECREF(v); } -PyMODINIT_FUNC -init_imagingmath(void) -{ - PyObject* m; - PyObject* d; - - m = Py_InitModule("_imagingmath", _functions); - d = PyModule_GetDict(m); +static int +setup_module(PyObject* m) { + PyObject* d = PyModule_GetDict(m); install(d, "abs_I", abs_I); install(d, "neg_I", neg_I); @@ -281,4 +277,35 @@ init_imagingmath(void) install(d, "gt_F", gt_F); install(d, "ge_F", ge_F); + return 0; } + +#if PY_VERSION_HEX >= 0x03000000 +PyMODINIT_FUNC +PyInit__imagingmath(void) { + PyObject* m; + + static PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "_imagingmath", /* m_name */ + NULL, /* m_doc */ + -1, /* m_size */ + _functions, /* m_methods */ + }; + + m = PyModule_Create(&module_def); + + if (setup_module(m) < 0) + return NULL; + + return m; +} +#else +PyMODINIT_FUNC +init_imagingmath(void) +{ + PyObject* m = Py_InitModule("_imagingmath", _functions); + setup_module(m); +} +#endif + diff --git a/_imagingtk.c b/_imagingtk.c index 6165a2acb..b29cfdca8 100644 --- a/_imagingtk.c +++ b/_imagingtk.c @@ -63,8 +63,24 @@ static PyMethodDef functions[] = { {NULL, NULL} /* sentinel */ }; -DL_EXPORT(void) +#if PY_VERSION_HEX >= 0x03000000 +PyMODINIT_FUNC +PyInit__imagingtk(void) { + static PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "_imagingtk", /* m_name */ + NULL, /* m_doc */ + -1, /* m_size */ + functions, /* m_methods */ + }; + + return PyModule_Create(&module_def); +} +#else +PyMODINIT_FUNC init_imagingtk(void) { Py_InitModule("_imagingtk", functions); } +#endif + diff --git a/encode.c b/encode.c index ed780128b..939fc6b93 100644 --- a/encode.c +++ b/encode.c @@ -111,15 +111,15 @@ _encode(ImagingEncoderObject* encoder, PyObject* args) if (!PyArg_ParseTuple(args, "|i", &bufsize)) return NULL; - buf = PyString_FromStringAndSize(NULL, bufsize); + buf = PyBytes_FromStringAndSize(NULL, bufsize); if (!buf) return NULL; status = encoder->encode(encoder->im, &encoder->state, - (UINT8*) PyString_AsString(buf), bufsize); + (UINT8*) PyBytes_AsString(buf), bufsize); /* adjust string length to avoid slicing in encoder */ - if (_PyString_Resize(&buf, (status > 0) ? status : 0) < 0) + if (_PyBytes_Resize(&buf, (status > 0) ? status : 0) < 0) return NULL; result = Py_BuildValue("iiO", status, encoder->state.errcode, buf); diff --git a/map.c b/map.c index 1041d6121..cd3a8e43d 100644 --- a/map.c +++ b/map.c @@ -39,6 +39,8 @@ #include "windows.h" #endif +#include "py3.h" + /* compatibility wrappers (defined in _imaging.c) */ extern int PyImaging_CheckBuffer(PyObject* buffer); extern int PyImaging_GetBuffer(PyObject* buffer, Py_buffer *view); @@ -148,12 +150,12 @@ mapping_read(ImagingMapperObject* mapper, PyObject* args) if (size < 0) size = 0; - buf = PyString_FromStringAndSize(NULL, size); + buf = PyBytes_FromStringAndSize(NULL, size); if (!buf) return NULL; if (size > 0) { - memcpy(PyString_AsString(buf), mapper->base + mapper->offset, size); + memcpy(PyBytes_AsString(buf), mapper->base + mapper->offset, size); mapper->offset += size; } diff --git a/path.c b/path.c index b29f9b4d9..af0f4951d 100644 --- a/path.c +++ b/path.c @@ -44,6 +44,8 @@ #define ssizessizeobjargproc intintobjargproc #endif +#include "py3.h" + /* compatibility wrappers (defined in _imaging.c) */ extern int PyImaging_CheckBuffer(PyObject* buffer); extern int PyImaging_GetBuffer(PyObject* buffer, Py_buffer *view); diff --git a/py3.h b/py3.h new file mode 100644 index 000000000..a0054bb46 --- /dev/null +++ b/py3.h @@ -0,0 +1,48 @@ +/* + Python3 definition file to consistently map the code to Python 2.6 or + Python 3. + + PyInt and PyLong were merged into PyLong in Python 3, so all PyInt functions + are mapped to PyLong. + + PyString, on the other hand, was split into PyBytes and PyUnicode. We map + both back onto PyString, so use PyBytes or PyUnicode where appropriate. The + only exception to this is _imagingft.c, where PyUnicode is left alone. +*/ + +#if PY_VERSION_HEX >= 0x03000000 +/* Map PyInt -> PyLong */ +#define PyInt_AsLong PyLong_AsLong +#define PyInt_Check PyLong_Check +#define PyInt_FromLong PyLong_FromLong +#define PyInt_AS_LONG PyLong_AS_LONG + +#else + +#if !defined(KEEP_PY_UNICODE) +/* Map PyUnicode -> PyString */ +#undef PyUnicode_AsString +#undef PyUnicode_AS_STRING +#undef PyUnicode_Check +#undef PyUnicode_FromStringAndSize +#undef PyUnicode_FromString +#undef PyUnicode_FromFormat + +#define PyUnicode_AsString PyString_AsString +#define PyUnicode_AS_STRING PyString_AS_STRING +#define PyUnicode_Check PyString_Check +#define PyUnicode_FromStringAndSize PyString_FromStringAndSize +#define PyUnicode_FromString PyString_FromString +#define PyUnicode_FromFormat PyString_FromFormat +#endif + +/* Map PyBytes -> PyString */ +#define PyBytes_AsString PyString_AsString +#define PyBytes_AS_STRING PyString_AS_STRING +#define PyBytes_Check PyString_Check +#define PyBytes_FromStringAndSize PyString_FromStringAndSize +#define PyBytes_FromString PyString_FromString +#define _PyBytes_Resize _PyString_Resize + +#endif /* PY_VERSION_HEX < 0x03000000 */ + From 4459715b6e6fdd1754d77cba2107507ba0fed57f Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Sat, 13 Oct 2012 23:48:01 -0500 Subject: [PATCH 09/61] py3k: Fix strict aliasing warnings under Python 3 --- libImaging/Quant.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libImaging/Quant.c b/libImaging/Quant.c index d25ba05dd..f7d9bf1ac 100644 --- a/libImaging/Quant.c +++ b/libImaging/Quant.c @@ -151,7 +151,7 @@ rehash_collide(HashTable h, void *newkey, void *newval) { - *valp=(void *)((*(int *)valp)+(*(int *)&newval)); + *valp = (void *)(((int) *valp) + ((int) newval)); } /* %% */ @@ -244,7 +244,7 @@ hash_to_list(HashTable h, const void *key, const void *val, void *u) Pixel *pixel=(Pixel *)&key; int i; Pixel q; - int count=*(int *)&val; + int count=(int) val; PIXEL_SCALE(pixel,&q,d->scale); From 9631d42b60ec3f62c571bff6094424e1c98ba51c Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Sun, 14 Oct 2012 11:38:06 -0500 Subject: [PATCH 10/61] py3k: Use "y#" code in PyArg_ParseTuple where we expect byte data This commit also renames some functions from "fromstring" and the like to "frombytes". I'll probably need to come back later and update any references to "string," here or in the docs. I also noticed that encode allocates some data for certain codecs, but never frees them. That would be a good bug to fix. I fixed the one where it outright stole a pointer from Python. --- _imaging.c | 12 +++++++----- _imagingcms.c | 9 +++++++++ decode.c | 3 ++- display.c | 25 +++++++++++++++++++++---- encode.c | 24 +++++++++++++++++++----- py3.h | 5 ++++- 6 files changed, 62 insertions(+), 16 deletions(-) diff --git a/_imaging.c b/_imaging.c index 610710bd1..6276bc678 100644 --- a/_imaging.c +++ b/_imaging.c @@ -1366,7 +1366,7 @@ _putpalette(ImagingObject* self, PyObject* args) char* rawmode; UINT8* palette; int palettesize; - if (!PyArg_ParseTuple(args, "ss#", &rawmode, &palette, &palettesize)) + if (!PyArg_ParseTuple(args, "s"PY_ARG_BYTES_LENGTH, &rawmode, &palette, &palettesize)) return NULL; if (strcmp(self->image->mode, "L") != 0 && strcmp(self->image->mode, "P")) { @@ -1861,8 +1861,9 @@ _getprojection(ImagingObject* self, PyObject* args) ImagingGetProjection(self->image, (unsigned char *)xprofile, (unsigned char *)yprofile); - result = Py_BuildValue("s#s#", xprofile, self->image->xsize, - yprofile, self->image->ysize); + result = Py_BuildValue(PY_ARG_BYTES_LENGTH PY_ARG_BYTES_LENGTH, + xprofile, self->image->xsize, + yprofile, self->image->ysize); free(xprofile); free(yprofile); @@ -2089,7 +2090,7 @@ _font_new(PyObject* self_, PyObject* args) ImagingObject* imagep; unsigned char* glyphdata; int glyphdata_length; - if (!PyArg_ParseTuple(args, "O!s#", + if (!PyArg_ParseTuple(args, "O!"PY_ARG_BYTES_LENGTH, &Imaging_Type, &imagep, &glyphdata, &glyphdata_length)) return NULL; @@ -2787,7 +2788,8 @@ _crc32(PyObject* self, PyObject* args) hi = lo = 0; - if (!PyArg_ParseTuple(args, "s#|(ii)", &buffer, &bytes, &hi, &lo)) + if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH"|(ii)", + &buffer, &bytes, &hi, &lo)) return NULL; crc = ((UINT32) (hi & 0xFFFF) << 16) + (lo & 0xFFFF); diff --git a/_imagingcms.c b/_imagingcms.c index 693de5c3b..910dc6eeb 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -129,8 +129,13 @@ cms_profile_fromstring(PyObject* self, PyObject* args) char* pProfile; int nProfile; +#if PY_VERSION_HEX >= 0x03000000 + if (!PyArg_ParseTuple(args, "y#:profile_frombytes", &pProfile, &nProfile)) + return NULL; +#else if (!PyArg_ParseTuple(args, "s#:profile_fromstring", &pProfile, &nProfile)) return NULL; +#endif cmsErrorAction(LCMS_ERROR_IGNORE); @@ -496,7 +501,11 @@ cms_get_display_profile_win32(PyObject* self, PyObject* args) static PyMethodDef pyCMSdll_methods[] = { {"profile_open", cms_profile_open, 1}, +#if PY_VERSION_HEX >= 0x03000000 + {"profile_frombytes", cms_profile_fromstring, 1}, +#else {"profile_fromstring", cms_profile_fromstring, 1}, +#endif /* profile and transform functions */ {"buildTransform", buildTransform, 1}, diff --git a/decode.c b/decode.c index 6f5fe278f..0b2be8dfd 100644 --- a/decode.c +++ b/decode.c @@ -37,6 +37,7 @@ #endif #include "Imaging.h" +#include "py3.h" #include "Gif.h" #include "Lzw.h" @@ -111,7 +112,7 @@ _decode(ImagingDecoderObject* decoder, PyObject* args) UINT8* buffer; int bufsize, status; - if (!PyArg_ParseTuple(args, "s#", &buffer, &bufsize)) + if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH, &buffer, &bufsize)) return NULL; status = decoder->decode(decoder->im, &decoder->state, buffer, bufsize); diff --git a/display.c b/display.c index b334fbc95..9571b1cbe 100644 --- a/display.c +++ b/display.c @@ -31,6 +31,7 @@ #endif #include "Imaging.h" +#include "py3.h" /* -------------------------------------------------------------------- */ /* Windows DIB support */ @@ -183,8 +184,14 @@ _fromstring(ImagingDisplayObject* display, PyObject* args) { char* ptr; int bytes; + +#if PY_VERSION_HEX >= 0x03000000 + if (!PyArg_ParseTuple(args, "y#:frombytes", &ptr, &bytes)) + return NULL; +#else if (!PyArg_ParseTuple(args, "s#:fromstring", &ptr, &bytes)) - return NULL; + return NULL; +#endif if (display->dib->ysize * display->dib->linesize != bytes) { PyErr_SetString(PyExc_ValueError, "wrong size"); @@ -200,10 +207,15 @@ _fromstring(ImagingDisplayObject* display, PyObject* args) static PyObject* _tostring(ImagingDisplayObject* display, PyObject* args) { +#if PY_VERSION_HEX >= 0x03000000 + if (!PyArg_ParseTuple(args, ":tobytes")) + return NULL; +#else if (!PyArg_ParseTuple(args, ":tostring")) - return NULL; + return NULL; +#endif - return PyString_FromStringAndSize( + return PyBytes_FromStringAndSize( display->dib->bits, display->dib->ysize * display->dib->linesize ); } @@ -215,8 +227,13 @@ static struct PyMethodDef methods[] = { {"query_palette", (PyCFunction)_query_palette, 1}, {"getdc", (PyCFunction)_getdc, 1}, {"releasedc", (PyCFunction)_releasedc, 1}, +#if PY_VERSION_HEX >= 0x03000000 + {"frombytes", (PyCFunction)_fromstring, 1}, + {"tobytes", (PyCFunction)_tostring, 1}, +#else {"fromstring", (PyCFunction)_fromstring, 1}, {"tostring", (PyCFunction)_tostring, 1}, +#endif {NULL, NULL} /* sentinel */ }; @@ -771,7 +788,7 @@ PyImaging_DrawWmf(PyObject* self, PyObject* args) int datasize; int width, height; int x0, y0, x1, y1; - if (!PyArg_ParseTuple(args, "s#(ii)(iiii):_load", &data, &datasize, + if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH"(ii)(iiii):_load", &data, &datasize, &width, &height, &x0, &x1, &y0, &y1)) return NULL; diff --git a/encode.c b/encode.c index 939fc6b93..670bd7dc9 100644 --- a/encode.c +++ b/encode.c @@ -30,6 +30,7 @@ #endif #include "Imaging.h" +#include "py3.h" #include "Gif.h" #ifdef HAVE_UNISTD_H @@ -451,9 +452,21 @@ PyImaging_ZipEncoderNew(PyObject* self, PyObject* args) int optimize = 0; char* dictionary = NULL; int dictionary_size = 0; - if (!PyArg_ParseTuple(args, "ss|is#", &mode, &rawmode, &optimize, - &dictionary, &dictionary_size)) - return NULL; + if (!PyArg_ParseTuple(args, "ss|i"PY_ARG_BYTES_LENGTH, &mode, &rawmode, + &optimize, &dictionary, &dictionary_size)) + return NULL; + + /* Copy to avoid referencing Python's memory, but there's no mechanism to + free this memory later, so this function (and several others here) + leaks. */ + if (dictionary && dictionary_size > 0) { + char* p = malloc(dictionary_size); + if (!p) + return PyErr_NoMemory(); + memcpy(p, dictionary, dictionary_size); + dictionary = p; + } else + dictionary = NULL; encoder = PyImaging_EncoderNew(sizeof(ZIPSTATE)); if (encoder == NULL) @@ -513,8 +526,9 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args) int xdpi = 0, ydpi = 0; int subsampling = -1; /* -1=default, 0=none, 1=medium, 2=high */ char* extra = NULL; int extra_size; - if (!PyArg_ParseTuple(args, "ss|iiiiiiiis#", &mode, &rawmode, &quality, - &progressive, &smooth, &optimize, &streamtype, + if (!PyArg_ParseTuple(args, "ss|iiiiiiii"PY_ARG_BYTES_LENGTH, + &mode, &rawmode, &quality, + &progressive, &smooth, &optimize, &streamtype, &xdpi, &ydpi, &subsampling, &extra, &extra_size)) return NULL; diff --git a/py3.h b/py3.h index a0054bb46..e0658b0bb 100644 --- a/py3.h +++ b/py3.h @@ -11,13 +11,16 @@ */ #if PY_VERSION_HEX >= 0x03000000 +#define PY_ARG_BYTES_LENGTH "y#" + /* Map PyInt -> PyLong */ #define PyInt_AsLong PyLong_AsLong #define PyInt_Check PyLong_Check #define PyInt_FromLong PyLong_FromLong #define PyInt_AS_LONG PyLong_AS_LONG -#else +#else /* PY_VERSION_HEX < 0x03000000 */ +#define PY_ARG_BYTES_LENGTH "s#" #if !defined(KEEP_PY_UNICODE) /* Map PyUnicode -> PyString */ From ad784eb8080b653ed9a28b4d03195d379b2d0538 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Mon, 15 Oct 2012 15:26:38 -0500 Subject: [PATCH 11/61] py3k: Import Christoph Gohlke's test suite This is Christoph Gohlke's test suite from his personal PIL package found at http://www.lfd.uci.edu/~gohlke/pythonlibs/. This is just to bring it in as a separate commit. Future commits will align it with Pillow. --- Tests/README.txt | 4 + Tests/bench_get.py | 20 +++ Tests/cms_test.py | 222 +++++++++++++++++++++++++++++ Tests/crash_ttf_memory_error.py | 14 ++ Tests/fonts/helvO18.pcf | Bin 0 -> 78412 bytes Tests/icc/CMY.icm | Bin 0 -> 101936 bytes Tests/icc/YCC709.icm | Bin 0 -> 101940 bytes Tests/icc/sRGB.icm | Bin 0 -> 3144 bytes Tests/images/broken.png | 5 + Tests/images/caption_6_33_22.png | Bin 0 -> 15378 bytes Tests/images/pil123p.png | Bin 0 -> 12901 bytes Tests/images/pil123rgba.png | Bin 0 -> 28353 bytes Tests/images/pil136.tiff | Bin 0 -> 9654 bytes Tests/images/pil168.tif | Bin 0 -> 5757 bytes Tests/images/pil184.pcx | Bin 0 -> 5364 bytes Tests/images/pil_sample_cmyk.jpg | Bin 0 -> 29364 bytes Tests/images/pil_sample_rgb.jpg | Bin 0 -> 29342 bytes Tests/images/rgb.jpg | Bin 0 -> 20230 bytes Tests/import_all.py | 13 ++ Tests/make_hash.py | 57 ++++++++ Tests/run.py | 93 ++++++++++++ Tests/show_icc.py | 28 ++++ Tests/show_mcidas.py | 26 ++++ Tests/test_000_sanity.py | 21 +++ Tests/test_001_archive.py | 23 +++ Tests/test_contents.py | 31 ++++ Tests/test_file_bmp.py | 27 ++++ Tests/test_file_gif.py | 28 ++++ Tests/test_file_jpeg.py | 179 +++++++++++++++++++++++ Tests/test_file_msp.py | 15 ++ Tests/test_file_pcx.py | 39 +++++ Tests/test_file_png.py | 170 ++++++++++++++++++++++ Tests/test_file_ppm.py | 14 ++ Tests/test_file_tiff.py | 57 ++++++++ Tests/test_file_xbm.py | 34 +++++ Tests/test_font_bdf.py | 13 ++ Tests/test_font_pcf.py | 26 ++++ Tests/test_image.py | 39 +++++ Tests/test_image_array.py | 33 +++++ Tests/test_image_convert.py | 26 ++++ Tests/test_image_copy.py | 12 ++ Tests/test_image_crop.py | 52 +++++++ Tests/test_image_draft.py | 25 ++++ Tests/test_image_filter.py | 82 +++++++++++ Tests/test_image_fromstring.py | 11 ++ Tests/test_image_getbands.py | 15 ++ Tests/test_image_getbbox.py | 36 +++++ Tests/test_image_getcolors.py | 64 +++++++++ Tests/test_image_getdata.py | 28 ++++ Tests/test_image_getextrema.py | 17 +++ Tests/test_image_getim.py | 13 ++ Tests/test_image_getpalette.py | 19 +++ Tests/test_image_getpixel.py | 57 ++++++++ Tests/test_image_getprojection.py | 30 ++++ Tests/test_image_histogram.py | 19 +++ Tests/test_image_load.py | 11 ++ Tests/test_image_mode.py | 27 ++++ Tests/test_image_offset.py | 16 +++ Tests/test_image_paste.py | 5 + Tests/test_image_point.py | 19 +++ Tests/test_image_putalpha.py | 43 ++++++ Tests/test_image_putdata.py | 40 ++++++ Tests/test_image_putpalette.py | 28 ++++ Tests/test_image_putpixel.py | 43 ++++++ Tests/test_image_quantize.py | 15 ++ Tests/test_image_resize.py | 12 ++ Tests/test_image_rotate.py | 15 ++ Tests/test_image_save.py | 5 + Tests/test_image_seek.py | 5 + Tests/test_image_show.py | 5 + Tests/test_image_split.py | 42 ++++++ Tests/test_image_tell.py | 5 + Tests/test_image_thumbnail.py | 36 +++++ Tests/test_image_tobitmap.py | 15 ++ Tests/test_image_tostring.py | 8 ++ Tests/test_image_transform.py | 5 + Tests/test_image_transpose.py | 34 +++++ Tests/test_image_verify.py | 5 + Tests/test_imagechops.py | 56 ++++++++ Tests/test_imagecms.py | 82 +++++++++++ Tests/test_imagecolor.py | 31 ++++ Tests/test_imagedraw.py | 28 ++++ Tests/test_imageenhance.py | 19 +++ Tests/test_imagefile.py | 61 ++++++++ Tests/test_imagefileio.py | 24 ++++ Tests/test_imagefilter.py | 31 ++++ Tests/test_imagefont.py | 12 ++ Tests/test_imagegl.py | 9 ++ Tests/test_imagegrab.py | 13 ++ Tests/test_imagemath.py | 62 ++++++++ Tests/test_imagemode.py | 23 +++ Tests/test_imageops.py | 70 +++++++++ Tests/test_imageops_usm.py | 55 +++++++ Tests/test_imagepalette.py | 44 ++++++ Tests/test_imagepath.py | 49 +++++++ Tests/test_imageqt.py | 9 ++ Tests/test_imagesequence.py | 22 +++ Tests/test_imageshow.py | 6 + Tests/test_imagestat.py | 52 +++++++ Tests/test_imagetk.py | 9 ++ Tests/test_imagetransform.py | 18 +++ Tests/test_imagewin.py | 6 + Tests/test_lib_image.py | 30 ++++ Tests/test_lib_pack.py | 110 ++++++++++++++ Tests/test_mode_i16.py | 103 ++++++++++++++ Tests/test_numpy.py | 54 +++++++ Tests/tester.py | 229 ++++++++++++++++++++++++++++++ Tests/threaded_save.py | 56 ++++++++ Tests/versions.py | 23 +++ 109 files changed, 3572 insertions(+) create mode 100644 Tests/README.txt create mode 100644 Tests/bench_get.py create mode 100644 Tests/cms_test.py create mode 100644 Tests/crash_ttf_memory_error.py create mode 100644 Tests/fonts/helvO18.pcf create mode 100644 Tests/icc/CMY.icm create mode 100644 Tests/icc/YCC709.icm create mode 100644 Tests/icc/sRGB.icm create mode 100644 Tests/images/broken.png create mode 100644 Tests/images/caption_6_33_22.png create mode 100644 Tests/images/pil123p.png create mode 100644 Tests/images/pil123rgba.png create mode 100644 Tests/images/pil136.tiff create mode 100644 Tests/images/pil168.tif create mode 100644 Tests/images/pil184.pcx create mode 100644 Tests/images/pil_sample_cmyk.jpg create mode 100644 Tests/images/pil_sample_rgb.jpg create mode 100644 Tests/images/rgb.jpg create mode 100644 Tests/import_all.py create mode 100644 Tests/make_hash.py create mode 100644 Tests/run.py create mode 100644 Tests/show_icc.py create mode 100644 Tests/show_mcidas.py create mode 100644 Tests/test_000_sanity.py create mode 100644 Tests/test_001_archive.py create mode 100644 Tests/test_contents.py create mode 100644 Tests/test_file_bmp.py create mode 100644 Tests/test_file_gif.py create mode 100644 Tests/test_file_jpeg.py create mode 100644 Tests/test_file_msp.py create mode 100644 Tests/test_file_pcx.py create mode 100644 Tests/test_file_png.py create mode 100644 Tests/test_file_ppm.py create mode 100644 Tests/test_file_tiff.py create mode 100644 Tests/test_file_xbm.py create mode 100644 Tests/test_font_bdf.py create mode 100644 Tests/test_font_pcf.py create mode 100644 Tests/test_image.py create mode 100644 Tests/test_image_array.py create mode 100644 Tests/test_image_convert.py create mode 100644 Tests/test_image_copy.py create mode 100644 Tests/test_image_crop.py create mode 100644 Tests/test_image_draft.py create mode 100644 Tests/test_image_filter.py create mode 100644 Tests/test_image_fromstring.py create mode 100644 Tests/test_image_getbands.py create mode 100644 Tests/test_image_getbbox.py create mode 100644 Tests/test_image_getcolors.py create mode 100644 Tests/test_image_getdata.py create mode 100644 Tests/test_image_getextrema.py create mode 100644 Tests/test_image_getim.py create mode 100644 Tests/test_image_getpalette.py create mode 100644 Tests/test_image_getpixel.py create mode 100644 Tests/test_image_getprojection.py create mode 100644 Tests/test_image_histogram.py create mode 100644 Tests/test_image_load.py create mode 100644 Tests/test_image_mode.py create mode 100644 Tests/test_image_offset.py create mode 100644 Tests/test_image_paste.py create mode 100644 Tests/test_image_point.py create mode 100644 Tests/test_image_putalpha.py create mode 100644 Tests/test_image_putdata.py create mode 100644 Tests/test_image_putpalette.py create mode 100644 Tests/test_image_putpixel.py create mode 100644 Tests/test_image_quantize.py create mode 100644 Tests/test_image_resize.py create mode 100644 Tests/test_image_rotate.py create mode 100644 Tests/test_image_save.py create mode 100644 Tests/test_image_seek.py create mode 100644 Tests/test_image_show.py create mode 100644 Tests/test_image_split.py create mode 100644 Tests/test_image_tell.py create mode 100644 Tests/test_image_thumbnail.py create mode 100644 Tests/test_image_tobitmap.py create mode 100644 Tests/test_image_tostring.py create mode 100644 Tests/test_image_transform.py create mode 100644 Tests/test_image_transpose.py create mode 100644 Tests/test_image_verify.py create mode 100644 Tests/test_imagechops.py create mode 100644 Tests/test_imagecms.py create mode 100644 Tests/test_imagecolor.py create mode 100644 Tests/test_imagedraw.py create mode 100644 Tests/test_imageenhance.py create mode 100644 Tests/test_imagefile.py create mode 100644 Tests/test_imagefileio.py create mode 100644 Tests/test_imagefilter.py create mode 100644 Tests/test_imagefont.py create mode 100644 Tests/test_imagegl.py create mode 100644 Tests/test_imagegrab.py create mode 100644 Tests/test_imagemath.py create mode 100644 Tests/test_imagemode.py create mode 100644 Tests/test_imageops.py create mode 100644 Tests/test_imageops_usm.py create mode 100644 Tests/test_imagepalette.py create mode 100644 Tests/test_imagepath.py create mode 100644 Tests/test_imageqt.py create mode 100644 Tests/test_imagesequence.py create mode 100644 Tests/test_imageshow.py create mode 100644 Tests/test_imagestat.py create mode 100644 Tests/test_imagetk.py create mode 100644 Tests/test_imagetransform.py create mode 100644 Tests/test_imagewin.py create mode 100644 Tests/test_lib_image.py create mode 100644 Tests/test_lib_pack.py create mode 100644 Tests/test_mode_i16.py create mode 100644 Tests/test_numpy.py create mode 100644 Tests/tester.py create mode 100644 Tests/threaded_save.py create mode 100644 Tests/versions.py diff --git a/Tests/README.txt b/Tests/README.txt new file mode 100644 index 000000000..7622836b8 --- /dev/null +++ b/Tests/README.txt @@ -0,0 +1,4 @@ +Minimalistic PIL test framework. + +Test scripts are named "test_xxx" and are supposed to output "ok". +That's it. diff --git a/Tests/bench_get.py b/Tests/bench_get.py new file mode 100644 index 000000000..eca491600 --- /dev/null +++ b/Tests/bench_get.py @@ -0,0 +1,20 @@ +import sys +sys.path.insert(0, ".") + +import tester +import timeit + +def bench(mode): + im = tester.lena(mode) + get = im.im.getpixel + xy = 50, 50 # position shouldn't really matter + t0 = timeit.default_timer() + for i in range(1000000): + get(xy) + print(mode, timeit.default_timer() - t0, "us") + +bench("L") +bench("I") +bench("I;16") +bench("F") +bench("RGB") diff --git a/Tests/cms_test.py b/Tests/cms_test.py new file mode 100644 index 000000000..ba1c96527 --- /dev/null +++ b/Tests/cms_test.py @@ -0,0 +1,222 @@ +# PyCMSTests.py +# Examples of how to use pyCMS, as well as tests to verify it works properly +# By Kevin Cazabon (kevin@cazabon.com) + +# Imports +import os +from PIL import Image +from PIL import ImageCms + +# import PyCMSError separately so we can catch it +PyCMSError = ImageCms.PyCMSError + +####################################################################### +# Configuration: +####################################################################### +# set this to the image you want to test with +IMAGE = "c:\\temp\\test.tif" + +# set this to where you want to save the output images +OUTPUTDIR = "c:\\temp\\" + +# set these to two different ICC profiles, one for input, one for output +# set the corresponding mode to the proper PIL mode for that profile +INPUT_PROFILE = "c:\\temp\\profiles\\sRGB.icm" +INMODE = "RGB" + +OUTPUT_PROFILE = "c:\\temp\\profiles\\genericRGB.icm" +OUTMODE = "RGB" + +PROOF_PROFILE = "c:\\temp\\profiles\\monitor.icm" + +# set to True to show() images, False to save them into OUTPUT_DIRECTORY +SHOW = False + +# Tests you can enable/disable +TEST_error_catching = True +TEST_profileToProfile = True +TEST_profileToProfile_inPlace = True +TEST_buildTransform = True +TEST_buildTransformFromOpenProfiles = True +TEST_buildProofTransform = True +TEST_getProfileInfo = True +TEST_misc = False + +####################################################################### +# helper functions +####################################################################### +def outputImage(im, funcName = None): + # save or display the image, depending on value of SHOW_IMAGES + if SHOW == True: + im.show() + else: + im.save(os.path.join(OUTPUTDIR, "%s.tif" %funcName)) + + +####################################################################### +# The tests themselves +####################################################################### + +if TEST_error_catching == True: + im = Image.open(IMAGE) + try: + #neither of these proifles exists (unless you make them), so we should + # get an error + imOut = ImageCms.profileToProfile(im, "missingProfile.icm", "cmyk.icm") + + except PyCMSError as reason: + print("We caught a PyCMSError: %s\n\n" %reason) + + print("error catching test completed successfully (if you see the message \ + above that we caught the error).") + +if TEST_profileToProfile == True: + # open the image file using the standard PIL function Image.open() + im = Image.open(IMAGE) + + # send the image, input/output profiles, and rendering intent to + # ImageCms.profileToProfile() + imOut = ImageCms.profileToProfile(im, INPUT_PROFILE, OUTPUT_PROFILE, \ + outputMode = OUTMODE) + + # now that the image is converted, save or display it + outputImage(imOut, "profileToProfile") + + print("profileToProfile test completed successfully.") + +if TEST_profileToProfile_inPlace == True: + # we'll do the same test as profileToProfile, but modify im in place + # instead of getting a new image returned to us + im = Image.open(IMAGE) + + # send the image to ImageCms.profileToProfile(), specifying inPlace = True + result = ImageCms.profileToProfile(im, INPUT_PROFILE, OUTPUT_PROFILE, \ + outputMode = OUTMODE, inPlace = True) + + # now that the image is converted, save or display it + if result == None: + # this is the normal result when modifying in-place + outputImage(im, "profileToProfile_inPlace") + else: + # something failed... + print("profileToProfile in-place failed: %s" %result) + + print("profileToProfile in-place test completed successfully.") + +if TEST_buildTransform == True: + # make a transform using the input and output profile path strings + transform = ImageCms.buildTransform(INPUT_PROFILE, OUTPUT_PROFILE, INMODE, \ + OUTMODE) + + # now, use the trnsform to convert a couple images + im = Image.open(IMAGE) + + # transform im normally + im2 = ImageCms.applyTransform(im, transform) + outputImage(im2, "buildTransform") + + # then transform it again using the same transform, this time in-place. + result = ImageCms.applyTransform(im, transform, inPlace = True) + outputImage(im, "buildTransform_inPlace") + + print("buildTransform test completed successfully.") + + # and, to clean up a bit, delete the transform + # this should call the C destructor for the transform structure. + # Python should also do this automatically when it goes out of scope. + del(transform) + +if TEST_buildTransformFromOpenProfiles == True: + # we'll actually test a couple profile open/creation functions here too + + # first, get a handle to an input profile, in this case we'll create + # an sRGB profile on the fly: + inputProfile = ImageCms.createProfile("sRGB") + + # then, get a handle to the output profile + outputProfile = ImageCms.getOpenProfile(OUTPUT_PROFILE) + + # make a transform from these + transform = ImageCms.buildTransformFromOpenProfiles(inputProfile, \ + outputProfile, INMODE, OUTMODE) + + # now, use the trnsform to convert a couple images + im = Image.open(IMAGE) + + # transform im normally + im2 = ImageCms.applyTransform(im, transform) + outputImage(im2, "buildTransformFromOpenProfiles") + + # then do it again using the same transform, this time in-place. + result = ImageCms.applyTransform(im, transform, inPlace = True) + outputImage(im, "buildTransformFromOpenProfiles_inPlace") + + print("buildTransformFromOpenProfiles test completed successfully.") + + # and, to clean up a bit, delete the transform + # this should call the C destructor for the each item. + # Python should also do this automatically when it goes out of scope. + del(inputProfile) + del(outputProfile) + del(transform) + +if TEST_buildProofTransform == True: + # make a transform using the input and output and proof profile path + # strings + # images converted with this transform will simulate the appearance + # of the output device while actually being displayed/proofed on the + # proof device. This usually means a monitor, but can also mean + # other proof-printers like dye-sub, etc. + transform = ImageCms.buildProofTransform(INPUT_PROFILE, OUTPUT_PROFILE, \ + PROOF_PROFILE, INMODE, OUTMODE) + + # now, use the trnsform to convert a couple images + im = Image.open(IMAGE) + + # transform im normally + im2 = ImageCms.applyTransform(im, transform) + outputImage(im2, "buildProofTransform") + + # then transform it again using the same transform, this time in-place. + result = ImageCms.applyTransform(im, transform, inPlace = True) + outputImage(im, "buildProofTransform_inPlace") + + print("buildProofTransform test completed successfully.") + + # and, to clean up a bit, delete the transform + # this should call the C destructor for the transform structure. + # Python should also do this automatically when it goes out of scope. + del(transform) + +if TEST_getProfileInfo == True: + # get a profile handle + profile = ImageCms.getOpenProfile(INPUT_PROFILE) + + # lets print some info about our input profile: + print("Profile name (retrieved from profile string path name): %s" %ImageCms.getProfileName(INPUT_PROFILE)) + + # or, you could do the same thing using a profile handle as the arg + print("Profile name (retrieved from profile handle): %s" %ImageCms.getProfileName(profile)) + + # now lets get the embedded "info" tag contents + # once again, you can use a path to a profile, or a profile handle + print("Profile info (retrieved from profile handle): %s" %ImageCms.getProfileInfo(profile)) + + # and what's the default intent of this profile? + print("The default intent is (this will be an integer): %d" %(ImageCms.getDefaultIntent(profile))) + + # Hmmmm... but does this profile support INTENT_ABSOLUTE_COLORIMETRIC? + print("Does it support INTENT_ABSOLUTE_COLORIMETRIC?: (1 is yes, -1 is no): %s" \ + %ImageCms.isIntentSupported(profile, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, \ + ImageCms.DIRECTION_INPUT)) + + print("getProfileInfo test completed successfully.") + +if TEST_misc == True: + # test the versions, about, and copyright functions + print("Versions: %s" %str(ImageCms.versions())) + print("About:\n\n%s" %ImageCms.about()) + print("Copyright:\n\n%s" %ImageCms.copyright()) + + print("misc test completed successfully.") + diff --git a/Tests/crash_ttf_memory_error.py b/Tests/crash_ttf_memory_error.py new file mode 100644 index 000000000..881fc03a6 --- /dev/null +++ b/Tests/crash_ttf_memory_error.py @@ -0,0 +1,14 @@ +from PIL import Image, ImageFont, ImageDraw + +font = "../pil-archive/memory-error-2.ttf" + +s = "Test Text" +f = ImageFont.truetype(font, 64, index=0, encoding="unicode") +w, h = f.getsize(s) +i = Image.new("RGB", (500, h), "white") +d = ImageDraw.Draw(i) + +# this line causes a MemoryError +d.text((0,0), s, font=f, fill=0) + +i.show() diff --git a/Tests/fonts/helvO18.pcf b/Tests/fonts/helvO18.pcf new file mode 100644 index 0000000000000000000000000000000000000000..f5e68ae9c5a749d4c35c48f5a1038aa919162634 GIT binary patch literal 78412 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 tvJO2|hF& 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 literal 0 HcmV?d00001 diff --git a/Tests/icc/YCC709.icm b/Tests/icc/YCC709.icm new file mode 100644 index 0000000000000000000000000000000000000000..d9f5a5a5df83ba666057c836aaad7221972e8225 GIT binary patch literal 101940 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

Q~&++qG<@nP9v3{YJ?sW%q;NPM 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 L{PK7vZD!`*_k{EN literal 0 HcmV?d00001 diff --git a/Tests/images/broken.png b/Tests/images/broken.png new file mode 100644 index 000000000..7def38564 --- /dev/null +++ b/Tests/images/broken.png @@ -0,0 +1,5 @@ +‰PNG + +/Å}Pô¨áÌ’ï>ƒ’0jÈ‚&ë✉\Þ@äÖô­ T´LU +¥í÷âxÃÅ/Ð +Z`ײwëÙ;†9 âcˆ€ \ No newline at end of file diff --git a/Tests/images/caption_6_33_22.png b/Tests/images/caption_6_33_22.png new file mode 100644 index 0000000000000000000000000000000000000000..2aa0a50bf26ae78aafa6e85816c31c91e6040d96 GIT binary patch literal 15378 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 C3g^H8 literal 0 HcmV?d00001 diff --git a/Tests/images/pil123p.png b/Tests/images/pil123p.png new file mode 100644 index 0000000000000000000000000000000000000000..03960d4939938541ca3f8efee4bf13ffdecbb2f5 GIT binary patch literal 12901 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

BdL{r~fT zT;LAi384016ZMxc5W*w4@wZmT-n^^^&Ia-ib<(foe^CO$%n=pkI=1E*{q^i8^`zQS znupO4qJXu(F$Z)Za{`v$qZU?C0WIzA@Ao>+f2c>FD3B4@qVO`%I3Tiov9;^IfcCx? z(Ro)H&B16Gqj^|b5ejNaFsQ(2pyrSe9&JQ7uFqlfzon>s(p~6xcK|KoSxX17N#R3a z*It9~eJV!*{&`tg{VqFkzfoF;q9VfIO?eg`0jP99=>| zlaj>+n11Y}0v%{Vn-Mvr-WS%r6r-(F>3P=h!p>=2HfAp2&)Z02sICUZ{(*U?hrwY> zhn!>>R%%%C7ng0ROo0BkP<~MKly3+1g74&Nah}mjXV_X=Wb5!#1DbCvt*WrKrqI%| zsHA||mMuR%x!h8O3xV@QxD%-Fn@|3@VUR9d(@oc>DcYJWuD+~>Oe(-{uKN_tDwSuz z##dZ=(Pw-l>wuQ(`uwTeH+3&_mDhnaKpOyc69RhNg|?3Vm8IYMMNn%;1{7F0a+RA2!x@A?@5MR*+ejR=qAVETfC-{H|^ z&l&8)WdpF~lWf_2XQdwnG6>H%Zd8CCV2;A;NpZ&pMZmkohxHqu#d8h2#p?H<3|NlP zwDoiMR)6IT@H)_&z+C{GBH-d#$U!M2K#`BQ&nXoErTc8}EZ~HFxA~PEaXDi8tuL?C z;({V3OgIrpv-5sg;L;oirBN$a6-?DgeUJZAAw-;M1 zjv87|N$D`wu6>`z&1<^4G<^}CC+Z8gGI(aw0ki|%e&3M=8A7z7M9>iYEM{2rP8nmy zw=p?id2WZ&omYssF7bF4SF>cZX^tzk$VN)Ht=zhO0gwO^Mx+f`>#DBt!5+{A4+~@j z(Uv=e$PEFUq(gdR8AGn$$>Q&a_`A6dz1>1F<~94g)Np2%##9(aa*F?nnOV%57udC09tIM1p~2OFB6ncn8?Zp=SI8fxPOD zu$B&SsXhfBI6KSei8fv5Wtn=u#p9D*dO9KLDzQ#D87L7iBpWAS6q*%!5LTjOQSwWK zGj52oBb~<}BdmOH=Ns$aOP%Adsgth{s?!67VZ##(k^^c}07l&q)r#XCHa?wU()~7F z>!9`BER7Y0d+#XYzB^mE`(LBnJSs*s&nA%-qCFYn7Kjbw#+=WZ<|SC2F2Q1<`E!@j zJYh$-i*1AekW+4md3X9gQ}!NFo1>9Bf?fyK2+GqKIIkxxM^;MzWZhy`k6BJ_IzHQ*2PIT zLZV3c^f4iNj8b=cHDA2@1%X;tb5IMx`7fL6z4EwBymN^&{Sbu{9Nkp6B_jur-Oo$ydV|FdamUn0M-Fes@j9t-G* zvu(ZWnL)dwxFJJL^6Cd7yMITqGzUwgeu{!uJ(vYth-c;f#q#^AfC}~(HtwrQfXjXu z)K~6H|2U(3G4YzPFZvZd#rF5pc2ymMTl}3N?8M zl_<#qu~v7fGXN#gNdvbb0nI>;RMdINqqIG9{KVS)|` zM^rNLv${H)IQqEbC@3uF{A;FBGW2M?QiP&F0tdf(5;&Z74|_*B!c0reY=D+GmD6rK zigO!6D2_TLjKtu*aWQe!>=w5a+!bW6T`tIictsUsiHKY zFf3XCC)_%ibB6wvvR;=LyMiln%Lb9?=_S?^C#xC4mP1W-gts^MJ_x9SEl?sN)vDTt z7tjuvUmud>Vr%1QquEutntU&$wS~pn_`MvB_#bcjS_GrH7|p}hV$0USxnZr0gf!P! zikD%W1RRg&_xD@x?vukh;xogduP?E%$^)c!Wq50@@Y<^m(agR>0b7vt|?fj;8>1juI_9QW`J8a^U)_}%k4u8brG$8KWsE%$ zbOz5LdhvqtR`qh}7Qg?19Ne$DUp6D~r2!LyqM?gzG?l@PL-1IOW9pO~tTFy8>VrWc zteypj{AX#uISQiQx8*{-LgYb}WB^XRHnuixpn++iU8TR^I|X#>=te2h$$-R($+7g?LU-Kl(#j zZhs^&dsnD$)8qifK-L~ExT!QIV{p4%aKNq-9vBOEiv zD%i0pMPY@~n;Rfe0^_DD1!Lgn>y*g!N@R%QW(-o0%ZlHY^3$m?jzTF{>ANrLfpHVR zJ$m!y_YQCSp8~W2qTO)J=@uQ^g{nfA#do@F)-?60Lf-m$JK3y7UT%Q;Q=0giF=SL3 zY{dz=F+_zabD`s zYB-Y~4(mT$S;UXdYNtKsQa{3>Z36u8@(6cLOmNGqE|Z2`!rRX$_~Y!4DEOq4O)gXk zpi1D>6IuE38VbvWNU0KUgPwMws6?d>{Jr1FlbPK_czA^K9>Df z9c+NS&J1^+vXjxbz>rbG`j;&>J>oF8DURcSwaMZKPp+YLc{Bgb&Y1_vRb6NNckg?z z*XijVNuzxiLXy#fuqHX%45DrJZb!I-ig5=?=u*l`RF*pAr^ z1S1FvMjAG=2_hB=Nh5@o(K0iw>6z*2>FvFDPyV?5W=5FBmXJwRxK&-R=k<2(_s%`% z-0ysEN{E>4r-*k-_LU*CYNmn}*FvLz4G>T=%rFij~7Lv1S zG#xG!l>4A78r#g)#sa+XE%9Qoj50Z z`ueVFL0K|jDv)l_%Y6)|zWsf0&gn5Rg@E)CaatGDsr?U}{>|BJ@88ND#~QRN%s<~@ z*^vvl`^wkAQ4k8+RhXFIOosegp%u$_3}Qy|J>A&)uY^r;6P2P_*(}l}!dqekdr#xz z`|*elfWJkqYo6~hZBD?t8x@D018+TEAePFq>!~VpzwOX@k7ir4liUo4MQ2Q4!(&B4 zQIvXxEQl@B;8AHof(0MO7*C4Q18q(RtpbF4O=Q{@kPc@qqRB zXeRuXX3pmnTb|UU+KVi?I>qk$gg^gfE9-w6+&i$t@J9PyF1%+nJ(b|zLCn4TuylU` zAvPSFgT^J>gq0%fZ5&@R!qY}*R~FSOjY5KVNqr806LYTXX*bmR4Hy1A$!{)7v-!o+ zy=&XdhDoV#e#?mtg|~`qStILvcAEA1SIuiOvA(cpqp1Zo_9Z>Npkf5m@R_92^J~a! z4Ffl0OROl2)Ee^{5c|Bs`w;UQAY7_xUZA;VV>4^-sFE*Lx*D&`>IcHRpSZGqSLYg- zcS_S=0D)5Ut^HB(!?~xrpF~j|2*dA3tP((7B$|8{TUL#%nx$c64_4_k_-@GUXW-l4 zt+lBVe0@gW(nqiLzxT}_<;F2w-}M6>nT+u1MJ5IWCV-$1~Fz~K#}+Oe9Q4IDrdXymrjV1wm$Y%J;!gR9g~%b~wvq*6i? zYKS2y0u_WL;12}+8I}?r1B_9S6-y7t4#dFs+VXW`!$Y27HlU2XjDZ3VMGd?08El-d zVSTFJm~0liX)j>o+};B*@SWw3gY_F}{IJ5#+-Eq5dRQTei}l0GJHA6TzT+@#6S5y0 zu)8^c4f?y)W4dABdo6v{n26%NIcSaD(YXvy?x4&6kH^R3tXu16-D z#!KbMju}TPo?_yR*_6xul#4YAeRWEGJq-5e=`D0ntdt1EG&wEZ-z&4 z`?X)k4>e7xIMb&r!cAv*=Gm1P%#FQ%Z6%hdNHKh`NStBqTcmP`_I@F@UakUV1tqbY zfqv|909)FApwjSz#vuhUjV=U8ivsRSYW^coY*HTAs2Vo~id(MK%*+_11x~m%$KK6@ z1U0|dXTsBg7KLg7ax+qiOT*AObrD_heU^c> zrBts#w}|W!OEvUifj;08`;dVcS8BNywL*Z)M#EK~7m}xWgn>&?3fT9G;j_O2$8(su zU>Y+gPGj9mTX}nL$B?=WYzx}Dml|;{dOdQy;dL2pQYN>gC>puDv%kEG;r>Vj=pa@L z+k@q5DjbBa=8%DyR*o1E9-j@1FLsG9i=lD>S|{);B}@S0KP^0PuI9+2T}rRiIptB< z*(HPw(-RH~&(WllDQft;nqtl2+l9{Cfs(Rd49fnR+Zy|CjYn?gqBt0NX(UQIC}Gke z12Kh`H8X`JKXGs-X^c3iv?kb7r@lAD^g9H*Lk71BmptL{TzA-22&HX=UD2dMpw<9v zKrDD8>Bi_0G>*Ou_cbFxBcXF;#3KUwpp{*Jlwbhh((lLR$AAC+?U|HPg^f!T6Rvh~ zW(Wi!I+r5s9we-5Ot(;J7dAbscyPDkk**+Oyd&^NPD%l;*ks}Y8EM2lHT>Sl%c2+< zW#S0P2)Hu#LiAym@kqkwfRoD~4B}y*aMm1gjz0eR9Vy=BzGWEUJ!`(;w2VJfp+#FAwVXzs>0d7c3bwoDlD7@-#9JMfd1kI z@jy+0OD=#L9+uvxzEL0D_?(9oAmx;z3561&loI_`vCYAM!Jx+)oOv2&v`5$zAc5d_ z8l1_Rupo%52+Kn8zu@%yHJkgQ`<^ZP+mC+*mKffFr6Kzp;Ee=pBtRd@_&rzthu_9z zN4H^AQTUfj!q!tSj_1!i3+`-_{!}2@>2n-M@$- z>wKKF!Tk*A6o>-RE)s^Ql)_X15By#O%`v`pX2{A;=t6MOXa&mA6oT5-ST1)bwmUdd zcy&-k@`p8sKs^8clvI0!N{3?W??N(_kg?Mh+g^uorsjLg6}LSk%s($+<+qY_KV7kM zo>YuV)u-+o5@}6P*7#vSVQq%;!vkmoIs>#=&r>M{qYQeqi_XOp4fr)%-vWfKS~N8=@vCRn)6d4Rn9t6INc76Wl3Srg}4jb<_ zeCc09+DdSY2j?wr#jE#ISBA9LLNcxhL+Gq4o_J}1e>%IwiMU+0EXA^$lem-OtovDv zAKtW`d6_ut`>IrY$b|r6>lZYFVJEP&q4d-!1_R{eW$d2e_4N8elcz91g6I3s3poxi-mP;2rS<-|C0 za+Zm1n*PBezkRNUJFoYt&Qv5%5KfyVoVRQV$4+~NN*yv&JZfD5nZp&c7N_xALh{=k zI#(B2*b7;Nk|^>@NTC>~6eWaYXoFd?;e90(MeN#5G#S#(nwSgKx{%I6CZ#A3LN$a` z8m3N#H}@!}j8cSyhRMq`JGVM$CnP%sTAQFa;1G2A?AqwyH7WD}RQh20=QO{*tNh_* z=kwcA()321xyO05Jz{w2hLG~0pdH18&pD)gLuMS*0z+Oa+!^rYGYn?TLT);0J;#4X zaI|p2ml7nBi@Ere_2jpdNoIt7dkq<}x{EPDK`9afV-@5@*o@+{r+pC<4D#~2He3hN z386ZmaK;E@6PiLHq;rpA>?qjN1t6qSii8OLRZTnwPC}S8Mbq9M(mLHGRW;PA5DOrY z5ZXI6tvRo4b5Hq1Kuyi1f}MTkCTw-#F#NAKqKy9sW$qym&;70|TUHCnIht9=z=R_l zg0w;_O&OUiV0K+-W(^NO*piu^w49~43a(PnLbjaPtf z1$$BSD21=IN@kV1YuXG=U3u)@;cxZ`>+@!eUeM7K-mtB^_cn$zyTUR+P1tT}2HVUG zuoXZaTV4$?+*SQx5I=dTE4!Z59Cd1pXIGS&dMup$l^CPuXL$A(19X>t#MgMuf)ODI zVcRc-DaR>#3J#ZCqWSUiI;ljA&wQnsd8ec)c~t^W2>k#Z1T?#f@jAwvYhcw+`{~;i z5OWmc$7ssskUiZ(OV%Ng4cWa@@Ea~72!q47+6~=q2UL~9MhF*u-q?^8aP;%!BN>sx$sOx4Yjm`=XIX){bRK<3+YC+kmhf z3(Izlp)gBqTo4>K1Bq<{q(Y?P8Bak3X z?^CxG#0B%?>6ARG%0U`r=)>->r$GG9?v?t^`5Wmv+mP!P7G9cg)$0~>^V%JBb_!>H zOiA{_*dC$#t%i#i&F9D8+{4B%Ce$j>f1cs0PY&>vxBQy!iwwbBp)I!bukPZUfw|my z{couZ$0RYd%Q!HJP{ZEc1YWi!ET=b`4Zmzx6Q=-X zgz8>#n}TA>;-~;)stl`gP~Cg8eRT6SG21F0c3zwUamD()u6w=kOb_h)Y(mQ-p?9`r zXp5n;*E0KhVbAReXS`9E-4A0s;9vyH&Tr>?9~q}}jlf>Rn$NcLKi7^tRV(Y}B?Gx@ zT2@>9q7dy@mS1X^J-d(FuikTa^F?$A-Ze>NX~j^)OI0?`D-WPJF|4KRfXo0K!^MkR zam2DA8bdGh#2hYh(Ft@3bf!QGP*r8bI*%J^0`Q0f_$d&VU7XkVUuFr`&fyc64bj>s zM0KHkVNA!L30GX|(-pLH*C!2+e{JaQ?t;8&`2{9-@drJA`hCmnS9xr`C!uygIOhX! z|L5({@NbhWwM9>htXujPGe=tui)Q)k*rz=4@3ky@L*C;wlbF?-UY2P}wmQ{~(uy}U zOuPX%Y)dJ4&!#|TOqQ7-WWvV)M}ThyTGKf(L7CL)UU>8%E?(-DdfyUszwg|HfB3K^ zD#40>f-A0Uqkp7}kTQRZ1-?>}C#PFQ|PKexSY_sj{t z86dm3Pu{U`mB|+i(6h3WJ3n2z1IJ*jAdI8#MpX%bmRDWd`msmvn*3LU8g5t}Y{9!7 z>v(xyI+)$Zxa#LP_70ra9RRlBD4E-F`|27m-kX~1Ge>RH@`=B`w=S#Rn$vAhRPrA_ zv(g1D1O}enl6-3WRxX)4du$EUvnCO4-XB5TO19&GKB12$|2$`f*U~dm1LrZGx@2h` z;eaYbxCOxf#hsH+i_n3K+d^QpqRAy~fp?!o5T{<~W3UJbN^e1y57%@PZm#Pm<*>q3 z2y@twq{)s?$+WG=hH{n5a7@foaP2vkrN6h*=y({5P)8`C>{8f)1E&t;Uh`V-H_vYI zo*CX1-iBk=b&4x$4y&>qWhZW4v$$2XLxgtR+!ZQ}7ewyMS<`6{D7t36001BWNklz16^SA{#y~9G>LupB` zzbbB6Z`H=IE~VFbLUN3UDi2uP(3-#zQ3V%EB{XUiRvja@EuH(bxhUIzW`*;tzEM+{ z6zC9_&es{AmDNH@cDNc#g1hK!vJS}R zz=l-Og$+98IT@p%8K7*zP@tkp8xFK=H;~LY|M?b$Y=@D_Cr5B3-2xD+d)UJ?hVlyr z$j8!NC=HZ_1X+8cY$eY2u+ntEp+f|N13X8gSw(YgtpptkrhJAA(>m^n1 zj?_*xE3EnRj@DCVKDd`fo#mvRw3BwyPTEO3X(#QZowS+u2bW+R$`-eGl|)T<)h4vJ zwBQNUV+g|v#xulGM3^Mh!jK#ug_it?n&{($<=Qb}H5`e(Y(QMzzkGv@#@DrWbkUYC zvVZI#rw*LUvSp`3t-{W&o7lB)A7M43S{@~e>%@da)d*{qn1lo+2Il1oBU{5t&xyMB z!*-225q8d5XO-fS$>cF#5B(hr=PzOV=B<=RC)oSkcIt_+@bt5}@X{-3 z>+L}c1=@OMA+|;o#zb+HDq~VgN)i`y$j8?#TRgIJc=XZZeGh*yPi)8AP$(4EX+5N~ zwSd>+GdY$JwE7ftdFIYvPTwhuS=7IP6|2r5oE+t64?n}$$N`3SZloH)@cy0D>k$za zL8nqZ?#5h4lB?fv@khRX&xSAZ`+UY@W+xQHc1%f;?m!Ih9!S_w&NDHQqgo5`J&*Rb z4mxLb(caoi&U7;86^jrP5Jxek(ip=-dl(-(NO@#GmC7U&Q5B0N#6FT$2t5Qa_`avr zIMhXc{xw_UdNvC|u3WI9-vJDA)grI9K|91OjeEQZo-2d%685-P+t%n2%OP9{& zfnRLk=Rg1PSefK^+2ko8m9r|QQL9##7dG(b^2&63c>I$`c2FVfp3k5 zOn&cAhyf zoN4=-RRUAmK6T!hed$&~L~;T?B8qAz8p^WZFF!?9J4VCwy3R20wQi^dLeMVwiQ%Gc zmR%c^V8w-8{Hl#4<7L*)1G7KjnX<4Yf-sk(t*e82Xz&tC6xXSi>!=8cO+YM$n8ocL z#nymQ-P;}Jbjy~(@)JzgutegHDk5XR0ae|nt|pUt<54d(#|^|bj3zj3-s7t*3$8ZM z>0|ox)HYXm=YI;DpYy5a;0>?8kS8||vTO5mjFl%+HquNPq@)FJMbf{tDK>QV0(VYt z!Kb&gZSq$`r5l;9afwq&+gLiBOSobFl0ZCe!#NvUpPQr1U1P|d>pOxQHCmSVtp1g7 z^{Ml?@`LRh{LTihD;q}2mP}RZfNV(oGJ=!q+G|`WV+79#Mj|p&p0qC{46> z8}F+lHhCpYVU4C*L5|_Va@;_)!1+bxtrr=Jf7Fh@C_>doqnKp0&M5;vfBlJVTz*A6 zT2pRYZCSrru_1QzrnA6opC6ebxYU7(mYW4epNwJjGrV7)^FbiM88OKbuRaUV) z(rV)&?YM0F@~{D9$q&}UoHr@K0ARsJ5q3h+Bxb-A`QJxlKL4$dRp+r#dy^WCcwHfTGBo z1nN%BWO-GO(zPLVx&7}Eb(_Q(+SZANyboMn~_rYHT zhdr+yQ@Zyiq4g3$OoX%<&>|{I&|5$j8jys}`NF_FrQHbK;=e6@`%HB}29TZcdZrl2 z+M18k5Q3M|pQEH(JaryeQ#U1mdl(r#CZot4wY_Lh{J`z`FD#iSeEEjz^J(a&yeKc) zo$s)a6Li=|@)p{_wB(3KES|5h&`_wmH@$Y2a`#^7A-JYQlnv`<3UD?gn^)XSBwlM0 zjnC}&(Cn4*jMtjKH160XAl`FJpg+1J8QJ}R_V+Tvm%qKJTleVmeI<7`UrmAdmrFfz z=LjMOT4Hoekia44<!&EmINk`CpD<%YZWIzdS%xwnlG-bsUDHbJ9@O@+pyFm>p3 z#eMUNJM-tvh3bf;XDIe==?Q5E=w> zKfRE6)?y{zrpQ?kv;sNBjwgsF#5RDu#g-L11=}KY_A5O(VKj8Rksc{tThi)RaKrQf zZfiE&1Y`zUv+{n5GyXh)H6P9M?lDo_L(`l%-s&@|mcO}0yPHqAa6RibKH}3d3wAv~ zCaK5(%;R)%od_Lykq;H}La+>Soe5F^tyn|^&HG3K5?V|_(TWoETZ*&bU47X}T>3 zzyECElKv1cN!aWf$7h_yJkWvAD~3LSE>(R)HD?K*62jjYv~Ixz=`z?!iz&t=HFc`{ zM93`>a`TiwUL^(7i1)r*2=7n87ji2FI~HSngNEt7g@Vjhq(|`P8ctcJ%obsvy8Uv3G9Jg^$oHKz z8UbfK>=Vj_eIc6yv0UR{wtY4D!kK>>vhjz?tN+9d9(+uGb6}^Fg?;0Fm3iO)-Q40Z{nIJ_)?oEGAI_Zt;ibxo@dtdD8ESG)Hvfbz4?u5M?mG(%mqC97X;4F{oHj9)E$;Je) zUr2@&nGd=T#7|kmo`;_|eCe+edQ@19Q}&=1@Tod)oSRaJ;xykT20 z(LJZsqweae>RaEszu&#Td%qVi2KQFO%ik~fw_4O1tS2Cv;7LFR6f;~w2FhIhVd1{J ze5$Irt+J}5g^f*S!;Y9RY5kOzkfkGXGcP(fA z={$B%lW4C6M=+-skg|&@7#8;QbJg!m@zCx7FIIADm_!)^cB8G5G=n9csG;8?DFNAc z;z@&NuYdKx6^~vwIY03Fo`DBGJ^idRbv}`_mH6$QJ(?q8NOFWx3=QSsMawOJr(E|v z;g3EhT)QJiPJ{QIpXc<*;N&4^a^&(wdK`y(81d5s9v}J3CjNI~UO8m=z&lnj{QDMv zUoXG)+A+?_IUKamoC#^#RyR}Cc3sCL%Kld6wzSrf7l^MS8I>o2-IsmjMgO|Xcf_Dd{}JmT=J{}VDY zV|l?lT)y$i7(rS4m&yf^@V?);h+liWvwU+VgO3UJZObF-UXXa*OW4&JEJi$iriT=%)!Gor}$LC(7%raH+QQOYCG z3?1@L3|7bug4Dr!u=XUSQ4ps0z_G^*eZ$}nyUgx^%88btve4&RCU3LUVi(Q1c>6*K z^PI3D;Ff!%_w3yLvvcFLmH7VqhjpO2fbsHdIChACdta5iZ)uQQr8LHc7hfQZjVtx@ zl>CXpW#_;vH=oC)C*MszXSw*phU3mQ9J6u-FIsgmL<9WcTmG4S_te<1(ItP2N3T^L zBI)2TK=SymK$sIHT6+?^Cw8)El-?DILJJkpAekQ34+=dca0g&+)G}0X@KkYzh3brD z0LTv+@=otUP_IVy1hc??mOm%}^PH>#tT$J81`jW7%+~;1yz=am$Gt z`?zE0KK|FQe1&?|$BMB3=73|xuvU^P#|Z@yp`pq?EDW!wHhqBRpx}6lS5rzOP;|i$ z4D|!hcZ^cq3+0l~^n|f_CB#x`3JX=m4hXB8%JgWA_(EI|^1XtsLOuu8NpM5MzPe`s z>8li{n^iG%3Nq8`+3rBb6H0rEGxLq;$_w6U_~&1YIrSogs>!BDR@s83`x2J^2HWJ@(RS!|a z;|0I#aO$O&;!2m;RVv2d9A{Z;1Sc}=m{h*=rkEA4G5p|b%1hr+`62e~yNT(0cHuZl>^>hWs1RGIj6n5ZKtW2ZA3BMBau2hQLSJ5~ zPYctFN?$2v&pyG3a@Qll7lApfPKYppVNnF9$I+`BHQ@y>7jD0e9g!b}Yi!}z@^Imi z-LngCnICI-B$h3tq;?@M?2szg>J*{{#?Aoi$`tesX3g{(a%*wT@ z`JL&|{`tsY?(N%u#QLIfPTq8O+`MUf{f~8lc@a)lZHLj67WbnF{VgSyTJJ9+REScHHi9=j|T!B*6jeS|ks%cS7wa z%I|%yhwpvT}dNm7_O7?l~n%6(uz3I>QGd2<+MKITy*9c z!`}Drn*4|=O;tR!trSGKx0rl$u~8C7K;oigRebuZgW{P=@I`q9@daP~V~ z?)f(*U$>0BLfP~S!p8H4$&14^4^9$BisJ~)JxYENPI)1$>FK8)ui&R&pCoi6#EA&! zEb2Q%o`SWg>rjX-KMc9AP;<}3C7(zdx^vWfZ?Zc6G zB#TxFVUV_VP)QnU*%Q|1F4uWwbRqhrCUg=}YyWHsa$2-rmxQ5dN|pVZun5eMOd=jv ziS2>qyi13!9Ghw|b+_`XAMfMd8$%oe&B(IjKYaX+OJvtq2_@=s07#LUvKJ@Lj}pwy+dBf)aLc;#n|96z_X!jQ z@~yC?lB`NpfoX(;wAw(bGS5?vRhR|2UDkf*h8)czk5l0wYuWWoxk?()hYsc{WNy_7Ap_?* z6m!ak-xO@r%T4dB@`67R%6*Dy3N;tjoL{1+G{6m)AHdrSJsX5Ae^TbU->*_x@6vOI zg*jMtk>RX!PT-zvrrG`71zfcd8S0_p#9%B`>!5U4-VF(RM@ryu5M|gD*l)dV);h2C z!>38unbHieG;o_PT?|PwDwmDmB80%ADsu|c8Dh1x=VH{gJ>guj{Wz~Xt$1Z~!qT(R z0Si4YtZ)odW0tY+S_);Myg?~13cI#NI0GIRzHx*d*EX<`vj3+czjxhw?)uC;jZx^` zlyn8&@q;-E#|FId&yMBhe_Wutpsf)a$~{H=Fvd~K0798$y&;8KOwuZ@sS#n)M=$*- zY$+_|ncYjW_A=2@NWwsuA>a^6)~Q9Pw?0pmKSi>ZQ=`LS@uY~>4pHK(FXqbXY9|@+ zeQ4~2iAg2jC%FBJoFPnI3&&mrl_JdCsmy7ewHK8c+v<~FX=qfcoc-pLxb;g@UuAFZ zltQ06P8pgFVdAkWzxe)M_KtGb^#1UNzSRfjz3>6!N)TDrxFj$!t%N!kVX^&dguY@S z@SbG?vELHzi{fYihF@D`n5dTh&lbm27eFKgMi%n0>-h--X-*2Fi-sv8JXA{ z-Z1@`eIPe0XY`$tM2Gg?7V%qGx@+$H2|;tp&H{v1XNxW)O-nhQeC-;na;#E=H8rDUPE=+hgAHiDOr_ z4y)q_;;(G~QuLL@A1cK)%GdusAyobC zY%vbM29u833nYWbK}|aL&T5CNJ4M~o4vTec;;YS-r}o0&=3yTAUITwlS@)}k7heMF zHV#mFU>$GYeD|Gx#A5)94SnGL>*HYjr>xohMu$bQ*rqajzff3FV{E&Ocr`nk{@~eZ zd*8x#_4`+H%%l4wE_hq%j7P2y?{9r>1GU?8$XVS~Bc_#B9HaF1i0_BYkP_G>Xq+W^ zEDuMtmMS$%xSL&rPPeDM@YP15DZx$mz`3V{I4)F9P+oS0%Y`SeB7UsN4R`GI{g@q@ z@2amny6=Gf%9r2c-?{U%irXvnmW6Q6^5zee+h}b4xUd%Ly${?Nf8pTu_Q3A#KI=E< z-#};L?2A#)ZFAIVD-w{5+zy7ax|_++oYJZapwnXMaMC^p2?7^dTiRmjXKE+XR^r?Z z@!Xhah?_O6!ucP}acciczWMoa9{rDicf9S`LRNvJuvCeu>sF-W zfgDcSHoy593o-kH5IQz0M(|p`=@c+IJ)>W|?IYecN%+Yd2#W}_*3o};=(O*Z z*8NIS;$dZ3(iXyLmTZ|$*6fo>70l))W^L)ymL(^R4jT4)O0!nv+D{%l3BZRRJ^01Q zd-z9n22OIfEORj)iA|aTrbcg7uh=gvdP@Il*^;@)h=gl#Muhze`@ER%Xrk1VjY;r<%!<;tI&CoJj|XJtRu(rIQ5uWCw_6ES+SD678sE+t*>o9o0(qL{qi+^P@Kk zXT7SDw)2K`z9#KS?_OZ^u_^n<1*^Df$QMjZ>cDpZRo7(yJ-{xMy=aD1SBMsE^pZ4Z z&nIp1B@{3$)HP3sH>^bBl~h>DR`;CBk`p?f^6)=5Q---Ek&?T%hhM6!5;E_Ht;y>G z!U2-*6gz-NtK(YJnliN~{tQXi`=s@=wv2x=gP9~z6?7iIbV3>c;dLLL{Sv8B7Mdb7MB0z?0TMLs9^cDD z3>6?@9o|pkR)v}%CEq$-gXB>j1NNvghBAjHfm07&k`x3b53^gcyPaO>O`efduX$+& z8F+e+^fMyMolHo6=m2mW2^H!FlDL3FYhEQn)N*a7R2e0qH%;eoITC~nk#N4Q1x65z zz?8yHw9U8~)9C|HK`4+g=GTiNzfm&sVcVylTbR@PNmx9GNFLqfX;02MO2Tryi{!>M zcn+!mmdn{rALX=3VA{GeT@6cp(`51@?Vx2gu9q#&PNsQDD8G{aO@~J@n~2#&+eyuV zr}|kh)_-D-mB0xkicaT+=`ji{9i?-0g00VqJRTit>J_BnrqrRIBVkK72tA@8ovi%y zyQwWzk{?=p?B~f09&U`Vu7Lm>){nqzpNt5Jt*$U5e|L(_7 z=>!p`NK8V34x&AhT9s}-+viN4s6aBkqv@la4RmH5`D~K;a*lj9O(RuEm8b5vW@8iS z4bBEv)9CgSHlleZ@l*4&Z4KW%>IYFjuh!BeRM!# zBcYQNpALX&0r+!<$S1NLSv!-RfNSRSY;DMLZd5j4klMs-E6gX0n5?Up?dH?ns`Dv@ z_MZuPG6p#ND8kdbxYUcwl8`^uQrYwv*$x(mcvd{a?1WMk+m9s-8~} zC{Y|>EvP0LmO&IFBIHDH9Yb#+$C|+rc26DL)*!xSqEUO!yLbF&k)uF-0y(CCytrKj|hDg4LuMwQa53C5ay$vTT5U zC&$SvM&|03@|AZ#_VCXQF#k&f@ug>-zjb1AY)iGeNbLK(_JuFwmw)wj>}rP09@vGW z1@6A*ekLX-s4h$q+kkv7k8xaji+z~9%fQHq^siXM$iNELu0NJ2Y;x9xo0+dxnVy_u zVrCY9p~1xdN0^&FKx46qS6?6un}mUf*Qnup4ZB#eH+4H{y5LcHfTis&rocerqFjl3v;9^|}8gat7=Ynx_cwvMQ zp-{+!LaE0E6?#f#jN?!$l_>#+E_u-n97knf5F-&Q*9807*naRGdn#N69-8 zeLVxjiw?8?7;2Tti5hVfF+Dqu-)IswYFG(qHmZ1jowyldZA=skVH~!alSNAw&X*#o zlQ)sJ6c7Xl;iH$mo_BrgnqCggtm{79e_C+<+ySxduvM%%$7-jGQzx(65Ub^H&wJ+Jz%M^O@`1Ke+ zjOZy=DD@3fpFPNYV*$S&u`oYMquwO+7YY0jFZ582ab1VS(7yu&5yZ(E;?UmgtA4fh zGnPvb$!6SSI{?IGN3N>H`ev4v?1Be7eFis?=GnGskk+aIVAb@D;(jV zO$zIi(m3I?kzsyi^#bqx#{kn`!Pk}CKfQ^seWjm?ho%{?FHk=)MlNSqT&yFpB6-Lc zN)+=2`ukTy-&!1NDfO&@V3G0ho$TIqH_gyT)Z)j{9S!sfI*p&YHz%{1%$DAdZXjm2 zEV#0~0NpJqmg@nMErUN|XS2dJEy0Z9N$kEw8(PvIoy1N zpM$aS2dFoHLLA2nZr(mR6X+k1M)dCRB>OIX?5YfET~4KsY~u6awmF9+!Vx*uFic(F{rG(vyFU=t4v7-}~?l5|PHiH;Gy4vg#$P`RZHG=L`Ry=N~@veWoX?1b$SVv+-rE!mi?(!}7)c$k~*OEaki) z5#$tXBp^!DCOuv}Rl=6%8v@pXF-m9zlM`?axtzgqEsj$lz%X5H9>^Pq*eU44Q}fqz zct_)qWMFN9I#U{ySlfgnIw#rYnj!{O(ay25$9zs}1hVqHw1^dPG*U&;w$=*3AM^;n zvq5qCg#1cD1F)hP7veg^1&6D4C?9)FdCiF7$(Pdqcnjo17OpZi9Qhj-NpVvA3jz^P(+Pl8v*3eDv=kUNKRcJlx?6e{?f6YS}nPr(bF|sI1sFfKqCO6g~zpjzghXz`-S-FJUCcI?>KJh&7BO zOBiabYJ2mJ0=m1_{1BaaTb6BV$yP2pzg}?NEjeRe6o$d=GtKy|96olKwV2&3eE%st z=;sv>dqlQcl`W9;gMCTK@aa=5t1mJXUs=GZM6ePZ*I;9dNS>&XxHCA4E;`sb%XA>T z?7a~)2b6P5@P+^8P+4QCJ!CNjrS3ZX=C!bEPlv`aIcEYrgMbKm*B~N9*omSe1Zqd8 zyzpKUXCW;y%r>x0-_M3qLd|qcW@#h0?of?o8Bns+dC3B=d+yim>?~(NkBB_4fLJkR z7R#V3@aZ-1>T{v*mmzmv0SYk`TqF+~Mi4_>k5S)X8x~x!iwccC!bzWaK49UXa`S`2 z?fZmV$CbqpA_VJz7sZp3&r>Sp7@MhWJIp0@mt%&~37@2$1ASRa>(hx5KS!-dk)czrLiFBf-)VzL=21AJH1eM5T+;eq2YJO_71n6o3$uHLe%Xj7hjfFcXPb$y8jU`*YEU6 zOG((8Q{@wD4Zr+SrT2vnW)0X`a8?;8gyJu=D%fPy4xrkZ9;S^T+n%UAF^HENN)K7mV`=QL4a6?X zo3+f{4oWtl-Q8vLh{&@B#N##JMqIqt#kTWw`9TJ?fgHd z@~i;y7v5OVbr&lea)zz96(}F$l91CpJ;`A{_2IQ}`R@s)0@9a6%5*M7W5EnNP%u!+ zAx?yb26ZCT&0z)u;)NKjqK?6og!~F+b+4g0WqD%(Zm9~}tK=niRt+F;5^OMs{pK>4 zvYh3LmwD}eZ^`G{HOywL+j4WcB>>dr!MWW2>FaVGt&gZaII9)i6EiE&!Mvclw6#OK`Q~W+QlC9ksTnln7$Fg6oucOR6f{* zdMJb_f|DP>yg8T42UB6=n;dGbvP0q$54h{I?%^}L6xaU09^T1V&H{FO#gFF!ckjR4 z^~7EK9uYYNi2uL6Gmn$&s_y*v+_%)))m^=4-(|^?Y0GFis!= zlCWexNtie@WCGzs0yD{cCLusb!Vn+{OE6%(4mLIzTiBLuS+-@ZEveN@^;X-f_wJcL zUcIi@)h!#af{ml|`8-v1sotym?)|-U&wgHVq4Jv7Ipvv_`-x-YeB#b;v?{y4w6Ils zH|=;uVd8b~v*o>`4(q4j?DOICcfs)8jrT0_^|XI=F~2LMCib!oLg}rB;NA#tJ&+Sb z1)XT2Wr4s&`XY2ZK$bc%Wf3nzs}_*}e2Atr24O##9&n6ck3n(0a!sVXttR~C;kdgE z#AX|9wftWEHwhX{()t1&5GC?f;>%6!K%U49^P_<4M9NM#b&C_-08ZXN zue1P&wJknVA@oj1tX!kK`yHnIt&1GJ`D5XGW{>Hj9P%YE&FU+D$MW=Xq4;#jx>rEc z8h&_h>yD`-3mP$|D_bSO0O^Pn=y(LeJm%nr9q~+=!MOD^eX&tl3IdZ6YMan-fe>aR z!pR7y>|z6p>-j{rCa5BP20XA+ppk+v$O^?h3Rm_j_e>k^pN^w=;=*xrc0|97`<9&V zH;)9hzan;zCC$y#eRCFTa^7wKJCO|dY+y^jKGXXjg`VlU&kHT6lGxW{ZXDX8vn&GvhnnOm4QNv>p{+|4VU?@!Ow-n|`mgA$94r zFzwOl5KsUL2t^U{BIJ;`TT~ADGRWy~-0HC9S|xnQA|=7?bI_JWvI1E_tRQ^`aRl2G zY~5m9#px5YX)$>)9@t6HSs*L8gWxOy$r`XqZxNQ);jVF^VUq#Jw;8XJpTymbxfy@Sz>@DAWd`5q?X<@ZRQ8908I0(Z5blR(MDon%V}5sQvR zxOqdNAGWW9?faGcCS!yoTl8EpK~+=)Pt7Zbe0TF`(EJ#kIK+gjLx%${rXajvxX=rU z7S)>Xy~p+k?++%f{;#GyGz3!{Ece_EE0$TVdW*Sg_DJ-ek~EN63gJ&{%$( zRAe(A;ClTqs2Q6!pLvQry)LRb!v84HPuyX5nZA4RF_ht${27 zttsvrL1!(JRU`w>8lYDw4+(4Uvs9oC4tjI$;6hqa*r}$phXMt4?#*FIx?TA$a0J!L7jZ_2#{e8T0%H`YJA(&aVz?fTlc{Z?k(%U|hp=C+VEOO-P=!KO1U+b@7E zXTzFpmb1$S6Gh1U&e4r7>pFP&pDPmx!?Laxi6xj#`)oomQM>f~`Qf z7j#CEGH3`<6O04aw72pIOx~cLAg)5*AU=pExGVC=62k+3XV~?ag%ErU4TR=A>q%k|CPA|4sO)s5*Pkhu>Q9pvOiX{jbNfKiC+k4avg8~&r*Dgt zPrn@c`XcrmSLA@h$bpDM4?^D>IO9Br+=W>_^Ba?|d)cepuf6rx;p=;q;56aY>jcm5 z=R>a>W%4kz0#I#t;ByoWL0k~m;1v|(8cfp=HCluLG((4`4cX)f|L=1?y04BT4d%rz>tgVrf+sY7W(kQacvWm=vO>#d8E}4YepD zQl(FX8!yQ7pFf>Pqe+5?BlM7hDyB>v#P2ELAyC)gZ5O1}zRMoL%?RW?Y~&GEdpWvO z`SwRE+`qSuS63RuIRMjbi*p?~j$smQBP!Nn44uftc4wU}4+a13Ipu;0_}ZSV<{ZIBN&`zyJR-FO zO<}gCjEq>SlMsky+h#a2VOZAhFm7C$2ZR}~MYRz!Acc*F|C8|WEdgQGa^}T`;0$oCF?gP(Zy)@^%N@4&_fU2{ zrW&)DR)mTWMoOeUGl4L?&vI;^GImUPe0Ru^$Au=brt7&8<8{l-!H|_0h8$&3ly$yA z9l`H&F*%R%$&kIX5u^18-zXa~anl=tsdlKd#nPv-fu^a7s)EFw-6D-Ui|0`)2rfF; zXJ&lPEvxMbjIUvZky(vy0mA$e*}Qr4(CXebUNmy75|!)OR_Ue7w;cTTLDpYAL@g4G z3)z-SLtA(5yRGt-$+4DJj-Q+&OmBQkPB&i!%~4BluTXQr@syEA4ZBCR^4Q(cjpIjJ zFQ{@Q3xK%&jEuINh){)Q)v|R9yy~49`nNcU!qJ%)Y7GUW6x}TQ?u~ftyAf}BM}!3M z>3>otA5tFQZ4oOh|J4ZhGzaS|OZOZw}z@Qo;ECqy66~_p@ zk)>`7p~l;CNrh43_lDT^QUq!!lL*HVrieMG0bB#VCuDMv%|X7$5UJu9Amb|$9%4dz zhk9vf6Vsbz`2K0aStakon)4uAig%ou&{EV0F@|C@0Vt{UvKj7$l$M+~knxI@D1(TJ=Hji%yGxu_~zuD0BJpWJcJ?e*sr z5p^m%?tj3r`zFi&L*Qni@HPk6hNvjptZ?zGg{{4VeDC4q{Pll&l*mG4ELfq{ zRB{F3j8`e^UhA-7v%|7{FF0j-vUxUU1}J%D9BmN=$2l@H!oxqv@|`aoVEVQyRKbgc zJ}hGbQyOawS%o2ieuRpEsNy4(HSQbKi?+9BZM#J}(bzL6BM7q&xH<4MO4bv^6B>b~ zX8>9gV9SDA7n-3`E(r}B`n-sSQ7WT?21+I;^c93c9uAK{dC-y@6vihE<3}vZmx2uh z&jZ(2it7#j40uaCqNxD!fC0hlhj7@iZK>t4eK27h3d0eZvNGKgo_b6;>vZAC-4HdE zOk}XGB?t{Gmq(O`e4ZQ+?%1^}xaJu!@m#z&KNc+j;tdb>sAGH@w@k9^5(k-d*?gwU zp(*9>J`lqFLM|grR01Yz%FsF?za6Lwm3^^SBdS`izRa+17Do0frW}!dgFa^>-lg=P1F{byN4V%rk4>8{Cej{?UY@7+w)oHucMvo} zG*Big4Qi8?41xzN^$q1nDMd;TLanXwPKd#(G6=Xv5LNQT#=eXy6BwM;TbRmB)138r z!*9NwPyF!*@x-Dn@H60fafYB%7u+I9za<(s_<5zMFy3;=n>ZCAGaXSJFnHE79tq76 zB^1aS!G=Pi2-CB|@)ed=%W!;5(NJ+*Pz2vomM$|$PN_9LrY9`@JrRKeQ5}kXP^%kO zcre;j3L?yU!s;SShKBu5L<}tv4j-`;GX_s>#p(4PoT`LZWe{>XLCSjPV^21(V}6o< zf{X5;iS&Co$-rCy#7lo6r!7C^;;W%KWEooHbJL$h^wx!7NZ9w)5J!bpXelj$rDq$4 z)(S%#l&qsnR}6Ma2~naNv_yIEE&&&^mm1t$Y(}|eYsmHsJ@SKVdG9YAMxSW0`f??+ z!qQU^1_qU%f3=Ir7H|uLj7|^nCpSFB$o&(9ElaUvC=3{C`!c-+<$!f#l12Kmm%!9j9*#EGNWl4>#MivMyj`!S!lFw_T1-X;FLH3Q;hlSq z1uyGb?>RsJz%u8Z;F~0;#f{ABJw(52*o_rww?rkOg(nlJs@8W;Z$ug`>a7UZfa5}=od_QaNF;ckg2Wyi*Hc<*a2(6*n8R6XH}lxk z5iZ|&A%_MYWpex|VO40=AgDlZA7mUQYzRvV!f>EW3`Z z8tQn>>U21IY^M1+=Gz%(M5xhj$5`^^&qX;+1A07TC=WQSI?XUri8#JDwkbubsKP*> zFg^_hN68O)L{*2vfN)^A`UlfZ{i{WQlJ@l{eT$PLZG=yZ?}>{t9ch&K$kVRNlcMo2 z0OG2RMz8)Q!`fFH{ERZ*g1i34Ft*ELk2?&Mm5o2|vFu`p($Wldr$%H$dL_b$XAp%{Ia37sh2%ZO|tWUC_@9nn?5M06H#=1 zZvJwaJO5%7w@0BNs0fM>)ht0hqR;Q)HE(+x_k88^EZw$@hj#8ln{jhv6ueAa`(vhN zkx1J6ijwyP;|qlXRGXIOtWa7iRE`SyLJ`|+an7sHW!L@p;<}1$L0E%)0lWZ4MwI>> z1immitmm?3MM$mT5K8#9M~3aK#L4nCl)A=NrKZLm*Sn*B(m+fHoFty0tO!{;{_-TU zJfve6omH4=8xLmNpCy1iDPUXx#MK)defdWX2Or8(t=2j1{3U$l9mgp3!N%7L+pcuU zlru~`3A^s9GqX>r9fkV1#W9c_5d0pXC`hD4?VMj*6Y3R9mgq^eC$J(`XR;mVA{4#BSOC9lFb=Y0rrxprfpg0Jne(?Rcs>1B7C6hH&rYyE;a9rpq zDw0vk1BShOEM+%d%CT8!MM_=_brc6BQae(EJG&O8J18}-O(X4y8zB;iYjPq+z(r#B zpn{W8Jv*0`^^TgsT+YqZsu4@`%KGi4`ZvE@`H;r?RTCvpO{J&mf)1GixFYkoVcI{Q z4%&z_65|J@3QViUHqrKL;wegl@i}eN3xIgde{=M#m-X@Yzc`845Xu(_m%X>lJ)fH9 z;1>e&WmtNpkn@C3zxr_l~iDR}&iwrlhz<&h%e9t8$taL zO#IN&3>*Cx&6Y#$c$VUt9?B~XD|TeaE)kmAq~2=Kv=P-tiy({(C52%` zSOdp(K)`QWLPu~+Y|e3gAqV6=kF|b=#*EA8JrOtGG{don8aS%-6ohh*uzy4;I7+rp zm^h}iBJjMpZcZ2~wJ4U^j00iRnY=89CYC04X9Q0!vLI~2`#w7OgIdjE@TJyJadiwG{9{&DuzWa}Y&Ol#TIOBJU^qW3DbNvA_E|j(y z2Db@OQ3(zRYc>geuXkAL8kU!foL=bVj-wUs{$>R>Li15&@*b!?tu&6uxt9Gy!Y}`8 zA7NvfP+-p!F7;uM{8AqJ-^ZAGB0wT=GeUmAp>&2|vKGmJpI0I^WQvBC8xl=HI0G6e zPSGF^I7P)P3XTQi3#}PT*zP&qn}?O#e0H3-ioJ)ceC?y79DgLlQy5rnIC{v=&8kBu z_cA@xS>hFok>fBOM{%>rSJR($A18{K?oOe2n^IA9D`g@S^6r$D?+)@%jvm6 zzW(JY9{>9(*8NI>+}S=>Ct2Iy;?_U29Q~qFUM*bm)&i_6^X(fB;g<~kS183HhfU}7 zuxZ08M)$)bcaQVEz;ARXah)`mirij7ISi}+V zEq<@!_y$*$!k}SrNrCVD-v) z24FftKAn@?oqxQTte41p9^jR-&iUnxx$^W?gUwqWJ^HtEzZY}dBtJSW9<)^FwSKRs zUeg5vIsl!YmYR-2f6f~80wCVJ|1|yW|1v@4?pg9@D8abm=HjijcO48~Z8&p7A8x+D zmp*Wm>K=>lS=PN7vMa%#{G9N>9m0b* zhJ;l`BPgHgvF#Fbk0S(*Fg<0Ns&&fRDa3MPNhz&29NMF3 zBc5B+Ue_LkOdAE3SOu=O=YLJHz!S5xC*q1}4wPu5>7trRLCtgoce*-6GR#EtO)DHZ z;*gJ6-VM=U}qR&^FuDs4@cImy%@LU)gDDEFszoafxssAwE|iDztld(^D17g4D;)ps46T{iZf88D)e<}x znhgP~7>&(0#t~*i#f>7|Hrxa|ug;C%Ufr<$cQR?Q3anI#!@_(Fg-l`RvF7xX?XRTW zap|b-puOl-;%zRGxzYi(-Bn%FKy0f_b0EwSyVBbQM9D}3MwtVD0{XhfPNb^FLr*}M zPP|WVx811-i=`$!XIcQnv))|Lb-5OMehAfB%i!yTDDM*7V?iy(TKe;l+ak28hVr1# z;|~Tjp0ETh!Fcg}mCLZ9~P5yA;@>rh?;<@JVD z>&wi}WVq?~4+quZsA3TuQ5**%M}})nO~ArX-v7CJJ@Qe=pyb-08>D@7T1@!ul?~gf zYHS3Iw{L)w>v246CS0*w7E8*)C81)IsUy)w+fwX2K%^JE+ZLGSiaFxaidh6-+6qZq zA+qgjHs^pC2N|WS$99)9K9LvYq`UhT0P*xovU=%xA$F^JZ1u~@I5UoH8dxr87S1fB=er5s^B}Cg>^oeUF{#0lI5I0?$)eHL~UUP%c@?38FVV0YIZwjk|pM~D@g{ZF7 z4?);~Fi=)pZOFV#(WY|lHpAd*Wl6zfEONN(UmU8pIy`-QB`^ZZHyMA+YlUU&pyUci z4jA_A4#_MRiYp!V-(#_=*s7u}Ltme=>=$8KZ!g2UTYTdK(`jxqN4>sW)w4p%g^kAd zmlFGe5yDC3v`r3YUGDMlBg(h_sQN%{niB%D-*etnZ|S@rNLO7?xM3%|uk#%jLj#Ks zXvWz&+iX2SS~Kl%!zq%evN+~qoEVrh%;!ue0)uoNX%}4@n4JRiu`AwbpRC08#%9|P zKBu6txH0_SoECD#YkkeGhx0_)@fyJo2l(XIt32`fS!@W#b;$Gz&3c66T9&`p!Osb2 zoo89II!mwP;X59u`K$QwyT|zMmk#f2MmlfdGqA~b-9Qq5a%=zyb$-RLv~u+P0&)9mea)Q> z;iO?lKIDwEV4`;!|MM-!u(dc?z|AUy*BSa>rd)KkOKH$&Q?5i_dl_#u_`O%}Z;n15 z{WA-zH!Mn?u&LmgGrgSntF}V_wL-n9WD#b5Ak;@Jya$JS-*%~_=jBxCc!hV>3(Q%-QtpVt0)k@GEJdtA9L3T1f=#*Unr=-4i?*T_NU zK2sEzctr&z@0gMCD7u}FqLM^!&kzG|oEV5>?Z2n#cr1QWu9W7K=^_T*oeh$EPQ-?^ zMto8O8h#X7kj2+HY%8T9c_pi0d|23U8uSMN_dNl@sKxb_CBJC6^lie`=a<-89^m*v zmv4UR7`5gEGf|zFykeDm$KO|WF+a2@p#?W*VZ0IP?pjkHId)hd+4mj$NaYS48oQUG zG+}6iF!eN)fMz3N$J)HJb5CoiK4JH>P@{`QxYQ9^j!us2LnFKNht0h<@QP-&DMikj z?V0UUPlb(Ulj9_24xuGNGf;MgqIY}7T#BX#ofvYGRMVJWCfc+BE>7IcQ7M+P1htUy zt}&)H=*8@Y&$S-SV)n|kt@1?mdY-pBI17N-Z1Mfp0li|$8SeUU&>_==r5oU$+al}? zZ2v8fx4&a4_V5z^@-1UCU;M-Iy_E-RL-{R+ktc;$%4P0K-vQ$*(+8AX0j6s$N_{=Wy}RnWQrECZ3_^*0xeXEO0yTlU2z3z} zjRwO1|K5!FS6~nr0tT}V*KNDhd3pXcVfzlligSnf$aTXbvs2u` zLNN>G+oVo3F=U0aFSy8gska0w6=kp>46Ijjr4s-2*Hg!4C-qiRK$MP)Dlrfz5h;7?G>QoHI7)%AEE%ui{sz z+~kU$2`qO>notOK?j3EbW4uu-F}fqbb4;-s=_AZnERwnC=29mLK7COG@fizsHjgVo zOStO~TFf+c8L=r~F$??LR`b*W``L+42d~XFXO?eSTks!`FO{Ci&Xy!|#m;LfXL{P8 zYXNC0`+l%9`qE{t^BKoenlpw-GPFj57BL5jO%n<#Sc!FqL{Umi6-gnaW^c^Q-p z(yr2=?U+27+cqr%qG%o7mG|u@!oXY*v(^}XF=`Z_2-B7vBjVaRKEiCEEXl~#$D4XP zoq9m&B2;r_hI!^bc`sTpo{%E~Y(CT6_{go6qkCIP2zlDQG%vW4RFrQ$VqfKvaJda! z@7Uqs5xSrFc-IO98Y1x)B|=4n2|6Z?F*@5-1(>u|J+FU*SzgG(lfzzh`0?fk>Da(x zvx9V^O%x)Lxni1$BP3l?E?rhG=8!^F($|#vzSfrIs}`-en9Je`kv6JgWz?d0Mz97# zt2km%lvbqG0QRM(H_a8hIb(PKlOM&5^D|A)XjerTK>P$N#}s{j-2>nAc;i;fR>p?4x>1SXO1bdF<1dF1;B>4i9e(De|=_H{rb&pQ_I>~ROftZXbZR<<8>0-T17E?Tuxq{Av zl|0;u@|PUl$x6?wiRIstew?lXX-&69yf`&F7JDULhzuQ2rp+jc04^FU7KsnkZBJ|1 z_I)nq=uaz0FyBDi9WxgygzY@8TzhQK=0EK=C8g_ncXyZX_S^`I18}ieK1&R^bdpjs zo;hwWag(6#ToNaBjm6AiKhyN%wzuduyQFg-LSn$V?e8VrFdf;EaJ9MZPh&tPveR7u zIu>^|rC%4g@>1ioC@qA1YCP#;>x+32($`7k(&QBuiZPn{d@&nncQ9$PljPg?o~{8p z*ELiZ`g#}hPQB>pCjnwIc|ua*x5Evqbe{Bd1aiVL(og@KBR(~btY3;tdtnp$GYh^irI^vUi8}73yc<(J4Ab cKhyO80TC}blbXB4Qvd(}07*qoM6N<$f@RGag#Z8m literal 0 HcmV?d00001 diff --git a/Tests/images/pil136.tiff b/Tests/images/pil136.tiff new file mode 100644 index 0000000000000000000000000000000000000000..07d339e7c51774b7754365832003f868a494847d GIT binary patch literal 9654 zcmeI2ZA?>F7{{NY6ooRWQlv+rDVDjH7FemM;v{h$e-sYd4*Qd|# zJm;KyPkJLF$dgFc{iy-SsZYa+=42#pia>FuA4KoP;L6=5^c_t=)!q=q?@K{lNi>SeB9Ngj z#g+4E2+K%7ZBsTHb3##8pMp#GK0{Hs3KP+d$%YI@`XQcmYfC6fVKo=JFd+& z`B}LAQv<3l*W%{&BJ}^lVQBCm2ERLw-yRe}92tk+zBCjx9>AkN8qhOz1h;=^#P5&l zP8n`m3Wedmj_GT_8sUkX zkQ0&&&ys-6Sw7p*<$Ex6rx>MITkzwZDs-LMhg)|maQfRL7(ADap@+?g&yPoLWg>Eu z7xLQRvuk1*51WXyqIr0> z7P;$>zlki`KJl>Ga?{8i<4H))+P2Is&%-9-tVsn!G;*IXc;EW3*?HLPI2-bYA(~De zdi_-e2E)sDIo^$mMu^W+1o$q(rd`PhS>lV8tJYw}+V@#nj_9-uEcPF1w<>5Qf`Wq) zpqdXK|45{!#$rjpa`-ZZ$8JSrL>NLt{Gsyk$A+{##0M`$R8kUrVzRI+Aw=639X0{Y z7=-Y1?FZF$y{^qN%eD?E}})(bj|h_AHc~?ZfB2U*Y_hmr>u^gx%@;ajy3y z&i3~Vw>#a^hBMt=IG9m{uHI8ncXr@(PcQoV)W~h@z^Sft=xeV<|NXn@XgH3pzOQku z`w+HPoIpu}kG3B=Y*T0!_XaJ8p|T=3ZJWb+dl}n5Jd_ZLrI0y0!=CLW5~&n&r4kCc z3?i8VGCL_$9&*SO^5J%xI%la2_NA$@+_-qyzEmcLSnkUD zDR%yZMA%Q zQEhnG962$C!59h2Mav%v(rTJ<^1>IWX|6_KN;O)Vo6&MOSF7EaaemQtI&4C6;$~4K zjFD#kzNE2#9=kkjbJ`>n4P!Km);*68FRL#Pn+rFaHp5tHotCHn#?(F?EcFs7?-IU z`R|9FO=}yFahOA8dK@yU>v6oT0w3mA;aFEUnp+#NGj<)q1O1SkSBP!fV~}21h4j*D z98_WH-tWm6^If>D=FJ$cwR+Gd9yiWb?!qky!jB{R-l#!)8G?q1NPH zYpNK7#-=%F8S~@yHIYZaRzReA+eAPtmIA?OdI3vY45!%}HLfj!DWm1Au|PPQwqR*7 zY2LVDr8G?-87*feg4zF+EB_QYk>yAm*G|ElC+Tj1^HVn6pR&3B-*VPZ^FGhW$NF(G K(R@1^{r(%}tu+n+ literal 0 HcmV?d00001 diff --git a/Tests/images/pil168.tif b/Tests/images/pil168.tif new file mode 100644 index 0000000000000000000000000000000000000000..621f03a2fc9e621a28130d630aa6a68ade641cd4 GIT binary patch literal 5757 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 literal 0 HcmV?d00001 diff --git a/Tests/images/pil_sample_rgb.jpg b/Tests/images/pil_sample_rgb.jpg new file mode 100644 index 0000000000000000000000000000000000000000..956a86bae7286bda2069fb017746daac588965ad GIT binary patch literal 29342 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 n8K;|HN#M}fz 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 literal 0 HcmV?d00001 diff --git a/Tests/import_all.py b/Tests/import_all.py new file mode 100644 index 000000000..118bf69a7 --- /dev/null +++ b/Tests/import_all.py @@ -0,0 +1,13 @@ +import sys +sys.path.insert(0, ".") + +import glob, os +import traceback + +for file in glob.glob("PIL/*.py"): + module = os.path.basename(file)[:-3] + try: + exec("from PIL import " + module) + except (ImportError, SyntaxError): + print("===", "failed to import", module) + traceback.print_exc() diff --git a/Tests/make_hash.py b/Tests/make_hash.py new file mode 100644 index 000000000..7e1a1b2e1 --- /dev/null +++ b/Tests/make_hash.py @@ -0,0 +1,57 @@ +# brute-force search for access descriptor hash table + +import random + +modes = [ + "1", + "L", "LA", + "I", "I;16", "I;16L", "I;16B", "I;32L", "I;32B", + "F", + "P", "PA", + "RGB", "RGBA", "RGBa", "RGBX", + "CMYK", + "YCbCr", + ] + +def hash(s, i): + # djb2 hash: multiply by 33 and xor character + for c in s: + i = (((i<<5) + i) ^ ord(c)) & 0xffffffff + return i + +def check(size, i0): + h = [None] * size + for m in modes: + i = hash(m, i0) + i = i % size + if h[i]: + return 0 + h[i] = m + return h + +min_start = 0 + +# 1) find the smallest table size with no collisions +for min_size in range(len(modes), 16384): + if check(min_size, 0): + print(len(modes), "modes fit in", min_size, "slots") + break + +# 2) see if we can do better with a different initial value +for i0 in range(65556): + for size in range(1, min_size): + if check(size, i0): + if size < min_size: + print(len(modes), "modes fit in", size, "slots with start", i0) + min_size = size + min_start = i0 + +print() + +# print check(min_size, min_start) + +print("#define ACCESS_TABLE_SIZE", min_size) +print("#define ACCESS_TABLE_HASH", min_start) + +# for m in modes: +# print m, "=>", hash(m, min_start) % min_size diff --git a/Tests/run.py b/Tests/run.py new file mode 100644 index 000000000..27a7afa9f --- /dev/null +++ b/Tests/run.py @@ -0,0 +1,93 @@ +# minimal test runner + +import glob, os, sys + +try: + root = os.path.dirname(__file__) +except NameError: + root = os.path.dirname(sys.argv[0]) + +if not os.path.isfile("PIL/Image.py"): + print("***", "please run this script from the PIL development directory as") + print("***", "$ python Tests/run.py") + sys.exit(1) + +print("-"*68) + +python_options = [] +tester_options = [] + +if "--installed" not in sys.argv: + python_options.append("-S") + os.environ["PYTHONPATH"] = "." + +if "--coverage" in sys.argv: + tester_options.append("--coverage") + +if "--log" in sys.argv: + tester_options.append("--log") + +files = glob.glob(os.path.join(root, "test_*.py")) +files.sort() + +success = failure = 0 +include = [x for x in sys.argv[1:] if x[:2] != "--"] +skipped = [] + +python_options = " ".join(python_options) +tester_options = " ".join(tester_options) + +for file in files: + test, ext = os.path.splitext(os.path.basename(file)) + if include and test not in include: + continue + print("running", test, "...") + # 2>&1 works on unix and on modern windowses. we might care about + # very old Python versions, but not ancient microsoft products :-) + out = os.popen("%s %s -u %s %s 2>&1" % ( + sys.executable, python_options, file, tester_options + )) + result = out.read().strip() + if result == "ok": + result = None + elif result == "skip": + print("---", "skipped") # FIXME: driver should include a reason + skipped.append(test) + continue + elif not result: + result = "(no output)" + status = out.close() + if status or result: + if status: + print("=== error", status) + if result: + if result[-3:] == "\nok": + # if there's an ok at the end, it's not really ok + result = result[:-3] + print(result) + failure = failure + 1 + else: + success = success + 1 + +print("-"*68) + +tempfiles = glob.glob(os.path.join(root, "temp_*")) +if tempfiles: + print("===", "remaining temporary files") + for file in tempfiles: + print(file) + print("-"*68) + +def tests(n): + if n == 1: + return "1 test" + else: + return "%d tests" % n + +if skipped: + print("---", tests(len(skipped)), "skipped.") + print(skipped) +if failure: + print("***", tests(failure), "of", (success + failure), "failed.") +else: + print(tests(success), "passed.") diff --git a/Tests/show_icc.py b/Tests/show_icc.py new file mode 100644 index 000000000..e062747e7 --- /dev/null +++ b/Tests/show_icc.py @@ -0,0 +1,28 @@ +import sys +sys.path.insert(0, ".") + +from PIL import Image +from PIL import ImageCms + +try: + filename = sys.argv[1] +except IndexError: + filename = "../pil-archive/cmyk.jpg" + +i = Image.open(filename) + +print(i.format) +print(i.mode) +print(i.size) +print(i.tile) + +p = ImageCms.getMemoryProfile(i.info["icc_profile"]) + +print(repr(p.product_name)) +print(repr(p.product_info)) + +o = ImageCms.createProfile("sRGB") +t = ImageCms.buildTransformFromOpenProfiles(p, o, i.mode, "RGB") +i = ImageCms.applyTransform(i, t) + +i.show() diff --git a/Tests/show_mcidas.py b/Tests/show_mcidas.py new file mode 100644 index 000000000..db193b82a --- /dev/null +++ b/Tests/show_mcidas.py @@ -0,0 +1,26 @@ +import sys +sys.path.insert(0, ".") + +from PIL import Image +from PIL import ImageMath + +try: + filename = sys.argv[1] +except IndexError: + filename = "../pil-archive/goes12.2005.140.190925.BAND_01.mcidas" + # filename = "../pil-archive/goes12.2005.140.190925.BAND_01.im" + +im = Image.open(filename) + +print(im.format) +print(im.mode) +print(im.size) +print(im.tile) + +lo, hi = im.getextrema() + +print("map", lo, hi, "->", end=' ') +im = ImageMath.eval("convert(im*255/hi, 'L')", im=im, hi=hi) +print(im.getextrema()) + +im.show() diff --git a/Tests/test_000_sanity.py b/Tests/test_000_sanity.py new file mode 100644 index 000000000..cba99e4fe --- /dev/null +++ b/Tests/test_000_sanity.py @@ -0,0 +1,21 @@ +import PIL +import PIL.Image + +# Make sure we have the binary extension +im = PIL.Image.core.new("L", (100, 100)) + +assert PIL.Image.VERSION[:3] == '1.1' + +# Create an image and do stuff with it. +im = PIL.Image.new("1", (100, 100)) +assert (im.mode, im.size) == ('1', (100, 100)) +assert len(im.tobytes()) == 1300 + +# Create images in all remaining major modes. +im = PIL.Image.new("L", (100, 100)) +im = PIL.Image.new("P", (100, 100)) +im = PIL.Image.new("RGB", (100, 100)) +im = PIL.Image.new("I", (100, 100)) +im = PIL.Image.new("F", (100, 100)) + +print("ok") diff --git a/Tests/test_001_archive.py b/Tests/test_001_archive.py new file mode 100644 index 000000000..a914a6c4c --- /dev/null +++ b/Tests/test_001_archive.py @@ -0,0 +1,23 @@ +import PIL +import PIL.Image + +import glob, os + +for file in glob.glob("../pil-archive/*"): + f, e = os.path.splitext(file) + if e in [".txt", ".ttf", ".otf", ".zip"]: + continue + try: + im = PIL.Image.open(file) + im.load() + except IOError as v: + print("-", "failed to open", file, "-", v) + else: + print("+", file, im.mode, im.size, im.format) + if e == ".exif": + try: + info = im._getexif() + except KeyError as v: + print("-", "failed to get exif info from", file, "-", v) + +print("ok") diff --git a/Tests/test_contents.py b/Tests/test_contents.py new file mode 100644 index 000000000..0455ad89c --- /dev/null +++ b/Tests/test_contents.py @@ -0,0 +1,31 @@ +from tester import * + +import os, glob + +contents = {} +for file in open("CONTENTS"): + file = file.strip() + if file: + contents[os.path.normpath(file)] = None + +patterns = [ + "PIL/*.py", + "*.c", + "libImaging/*.c", + "libImaging/*.h", + ] + +def test_files(): + + for pattern in patterns: + for file in glob.glob(pattern): + file = os.path.normpath("Imaging/" + file) + assert_true(file in contents, "%r not in CONTENTS" % file) + +def test_contents(): + + for file in contents: + root, file = file.split(os.sep, 1) + if file == "MANIFEST": + continue # generated by distribution scripts + assert_true(os.path.isfile(file), "%r not found" % file) diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py new file mode 100644 index 000000000..e0584641c --- /dev/null +++ b/Tests/test_file_bmp.py @@ -0,0 +1,27 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + file = tempfile("temp.bmp") + + lena().save(file) + + im = Image.open(file) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "BMP") + + lena("1").save(file) + im = Image.open(file) + + lena("L").save(file) + im = Image.open(file) + + lena("P").save(file) + im = Image.open(file) + + lena("RGB").save(file) + im = Image.open(file) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py new file mode 100644 index 000000000..336aaba63 --- /dev/null +++ b/Tests/test_file_gif.py @@ -0,0 +1,28 @@ +from tester import * + +from PIL import Image + +codecs = dir(Image.core) + +if "gif_encoder" not in codecs or "gif_decoder" not in codecs: + skip("gif support not available") # can this happen? + +# sample gif stream +file = "Images/lena.gif" +data = open(file, "rb").read() + +def test_sanity(): + im = Image.open(file) + im.load() + assert_equal(im.mode, "P") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "GIF") + +def test_optimize(): + def test(optimize): + im = Image.new("L", (1, 1), 0) + file = BytesIO() + im.save(file, "GIF", optimize=optimize) + return len(file.getvalue()) + assert_equal(test(0), 800) + assert_equal(test(1), 32) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py new file mode 100644 index 000000000..126c67237 --- /dev/null +++ b/Tests/test_file_jpeg.py @@ -0,0 +1,179 @@ +from tester import * + +from PIL import Image +from PIL import ImageFile + +codecs = dir(Image.core) + +if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs: + skip("jpeg support not available") + +# sample jpeg stream +file = "Images/lena.jpg" +data = open(file, "rb").read() + +def roundtrip(im, **options): + out = BytesIO() + im.save(out, "JPEG", **options) + bytes = out.tell() + out.seek(0) + im = Image.open(out) + im.bytes = bytes # for testing only + return im + +# -------------------------------------------------------------------- + +def test_sanity(): + + # internal version number + assert_match(Image.core.jpeglib_version, "\d+\.\d+$") + + im = Image.open(file) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "JPEG") + +# -------------------------------------------------------------------- + +def test_app(): + # Test APP/COM reader (@PIL135) + im = Image.open(file) + assert_equal(im.applist[0], + ("APP0", b"JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00")) + assert_equal(im.applist[1], ("COM", b"Python Imaging Library")) + assert_equal(len(im.applist), 2) + +def test_cmyk(): + # Test CMYK handling. Thanks to Tim and Charlie for test data, + # Michael for getting me to look one more time. + file = "Tests/images/pil_sample_cmyk.jpg" + im = Image.open(file) + # the source image has red pixels in the upper left corner. + c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))] + assert_true(c == 0.0 and m > 0.8 and y > 0.8 and k == 0.0) + # the opposite corner is black + c, m, y, k = [x / 255.0 for x in im.getpixel((im.size[0]-1, im.size[1]-1))] + assert_true(k > 0.9) + # roundtrip, and check again + im = roundtrip(im) + c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))] + assert_true(c == 0.0 and m > 0.8 and y > 0.8 and k == 0.0) + c, m, y, k = [x / 255.0 for x in im.getpixel((im.size[0]-1, im.size[1]-1))] + assert_true(k > 0.9) + +def test_dpi(): + def test(xdpi, ydpi=None): + im = Image.open(file) + im = roundtrip(im, dpi=(xdpi, ydpi or xdpi)) + return im.info.get("dpi") + assert_equal(test(72), (72, 72)) + assert_equal(test(300), (300, 300)) + assert_equal(test(100, 200), (100, 200)) + assert_equal(test(0), None) # square pixels + +def test_icc(): + # Test ICC support + im1 = Image.open("Tests/images/rgb.jpg") + icc_profile = im1.info["icc_profile"] + assert_equal(len(icc_profile), 3144) + # Roundtrip via physical file. + file = tempfile("temp.jpg") + im1.save(file, icc_profile=icc_profile) + im2 = Image.open(file) + assert_equal(im2.info.get("icc_profile"), icc_profile) + # Roundtrip via memory buffer. + im1 = roundtrip(lena()) + im2 = roundtrip(lena(), icc_profile=icc_profile) + assert_image_equal(im1, im2) + assert_false(im1.info.get("icc_profile")) + assert_true(im2.info.get("icc_profile")) + +def test_icc_big(): + # Make sure that the "extra" support handles large blocks + def test(n): + # The ICC APP marker can store 65519 bytes per marker, so + # using a 4-byte test code should allow us to detect out of + # order issues. + icc_profile = (b"Test"*int(n/4+1))[:n] + assert len(icc_profile) == n # sanity + im1 = roundtrip(lena(), icc_profile=icc_profile) + assert_equal(im1.info.get("icc_profile"), icc_profile or None) + test(0); test(1) + test(3); test(4); test(5) + test(65533-14) # full JPEG marker block + test(65533-14+1) # full block plus one byte + test(ImageFile.MAXBLOCK) # full buffer block + test(ImageFile.MAXBLOCK+1) # full buffer block plus one byte + test(ImageFile.MAXBLOCK*4+3) # large block + +def test_optimize(): + im1 = roundtrip(lena()) + im2 = roundtrip(lena(), optimize=1) + assert_image_equal(im1, im2) + assert_true(im1.bytes >= im2.bytes) + +def test_progressive(): + im1 = roundtrip(lena()) + im2 = roundtrip(lena(), progressive=1) + im3 = roundtrip(lena(), progression=1) # compatibility + assert_image_equal(im1, im2) + assert_image_equal(im1, im3) + assert_false(im1.info.get("progressive")) + assert_false(im1.info.get("progression")) + assert_true(im2.info.get("progressive")) + assert_true(im2.info.get("progression")) + assert_true(im3.info.get("progressive")) + assert_true(im3.info.get("progression")) + +def test_quality(): + im1 = roundtrip(lena()) + im2 = roundtrip(lena(), quality=50) + assert_image(im1, im2.mode, im2.size) + assert_true(im1.bytes >= im2.bytes) + +def test_smooth(): + im1 = roundtrip(lena()) + im2 = roundtrip(lena(), smooth=100) + assert_image(im1, im2.mode, im2.size) + +def test_subsampling(): + def getsampling(im): + layer = im.layer + return layer[0][1:3] + layer[1][1:3] + layer[2][1:3] + # experimental API + im = roundtrip(lena(), subsampling=-1) # default + assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1)) + im = roundtrip(lena(), subsampling=0) # 4:4:4 + assert_equal(getsampling(im), (1, 1, 1, 1, 1, 1)) + im = roundtrip(lena(), subsampling=1) # 4:2:2 + assert_equal(getsampling(im), (2, 1, 1, 1, 1, 1)) + im = roundtrip(lena(), subsampling=2) # 4:1:1 + assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1)) + im = roundtrip(lena(), subsampling=3) # default (undefined) + assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1)) + + im = roundtrip(lena(), subsampling="4:4:4") + assert_equal(getsampling(im), (1, 1, 1, 1, 1, 1)) + im = roundtrip(lena(), subsampling="4:2:2") + assert_equal(getsampling(im), (2, 1, 1, 1, 1, 1)) + im = roundtrip(lena(), subsampling="4:1:1") + assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1)) + + assert_exception(TypeError, lambda: roundtrip(lena(), subsampling="1:1:1")) + +def test_truncated_jpeg(): + def test(junk): + if junk: + # replace "junk" bytes at the end with junk + file = BytesIO(data[:-junk] + bytes(junk*[0])) + else: + file = BytesIO(data) + im = Image.open(file) + im.load() + assert_no_exception(lambda: test(0)) + assert_exception(IOError, lambda: test(1)) + assert_no_exception(lambda: test(2)) + assert_no_exception(lambda: test(4)) + assert_no_exception(lambda: test(8)) + assert_exception(IOError, lambda: test(10)) diff --git a/Tests/test_file_msp.py b/Tests/test_file_msp.py new file mode 100644 index 000000000..7ed200e43 --- /dev/null +++ b/Tests/test_file_msp.py @@ -0,0 +1,15 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + file = tempfile("temp.msp") + + lena("1").save(file) + + im = Image.open(file) + im.load() + assert_equal(im.mode, "1") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "MSP") diff --git a/Tests/test_file_pcx.py b/Tests/test_file_pcx.py new file mode 100644 index 000000000..73d358229 --- /dev/null +++ b/Tests/test_file_pcx.py @@ -0,0 +1,39 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + file = tempfile("temp.pcx") + + lena("1").save(file) + + im = Image.open(file) + im.load() + assert_equal(im.mode, "1") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "PCX") + + lena("1").save(file) + im = Image.open(file) + + lena("L").save(file) + im = Image.open(file) + + lena("P").save(file) + im = Image.open(file) + + lena("RGB").save(file) + im = Image.open(file) + +def test_pil184(): + # Check reading of files where xmin/xmax is not zero. + + file = "Tests/images/pil184.pcx" + im = Image.open(file) + + assert_equal(im.size, (447, 144)) + assert_equal(im.tile[0][1], (0, 0, 447, 144)) + + # Make sure all pixels are either 0 or 255. + assert_equal(im.histogram()[0] + im.histogram()[255], 447*144) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py new file mode 100644 index 000000000..329598c9d --- /dev/null +++ b/Tests/test_file_png.py @@ -0,0 +1,170 @@ +from tester import * + +from PIL import Image +from PIL import PngImagePlugin + +codecs = dir(Image.core) + +if "zip_encoder" not in codecs or "zip_decoder" not in codecs: + skip("zip/deflate support not available") + +# sample png stream + +file = "Images/lena.png" +data = open(file, "rb").read() + +# stuff to create inline PNG images + +MAGIC = PngImagePlugin._MAGIC + +def chunk(cid, *data): + file = BytesIO() + PngImagePlugin.putchunk(*(file, cid) + data) + return file.getvalue() + +o32 = PngImagePlugin.o32 + +IHDR = chunk(b"IHDR", o32(1), o32(1), bytes((8,2)), bytes((0,0,0))) +IDAT = chunk(b"IDAT") +IEND = chunk(b"IEND") + +HEAD = MAGIC + IHDR +TAIL = IDAT + IEND + +def load(data): + return Image.open(BytesIO(data)) + +def roundtrip(im, **options): + out = BytesIO() + im.save(out, "PNG", **options) + out.seek(0) + return Image.open(out) + +# -------------------------------------------------------------------- + +def test_sanity(): + + # internal version number + assert_match(Image.core.zlib_version, "\d+\.\d+\.\d+(\.\d+)?$") + + file = tempfile("temp.png") + + lena("RGB").save(file) + + im = Image.open(file) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "PNG") + + lena("1").save(file) + im = Image.open(file) + + lena("L").save(file) + im = Image.open(file) + + lena("P").save(file) + im = Image.open(file) + + lena("RGB").save(file) + im = Image.open(file) + + lena("I").save(file) + im = Image.open(file) + +# -------------------------------------------------------------------- + +def test_broken(): + # Check reading of totally broken files. In this case, the test + # file was checked into Subversion as a text file. + + file = "Tests/images/broken.png" + assert_exception(IOError, lambda: Image.open(file)) + +def test_bad_text(): + # Make sure PIL can read malformed tEXt chunks (@PIL152) + + im = load(HEAD + chunk(b'tEXt') + TAIL) + assert_equal(im.info, {}) + + im = load(HEAD + chunk(b'tEXt', b'spam') + TAIL) + assert_equal(im.info, {b'spam': b''}) + + im = load(HEAD + chunk(b'tEXt', b'spam\0') + TAIL) + assert_equal(im.info, {b'spam': b''}) + + im = load(HEAD + chunk(b'tEXt', b'spam\0egg') + TAIL) + assert_equal(im.info, {b'spam': b'egg'}) + + im = load(HEAD + chunk(b'tEXt', b'spam\0egg\0') + TAIL) + assert_equal(im.info, {b'spam': b'egg\x00'}) + +def test_interlace(): + + file = "Tests/images/pil123p.png" + im = Image.open(file) + + assert_image(im, "P", (162, 150)) + assert_true(im.info.get("interlace")) + + assert_no_exception(lambda: im.load()) + + file = "Tests/images/pil123rgba.png" + im = Image.open(file) + + assert_image(im, "RGBA", (162, 150)) + assert_true(im.info.get("interlace")) + + assert_no_exception(lambda: im.load()) + +def test_load_verify(): + # Check open/load/verify exception (@PIL150) + + im = Image.open("Images/lena.png") + assert_no_exception(lambda: im.verify()) + + im = Image.open("Images/lena.png") + im.load() + assert_exception(RuntimeError, lambda: im.verify()) + +def test_roundtrip_dpi(): + # Check dpi roundtripping + + im = Image.open(file) + + im = roundtrip(im, dpi=(100, 100)) + assert_equal(im.info["dpi"], (100, 100)) + +def test_roundtrip_text(): + # Check text roundtripping + + im = Image.open(file) + + info = PngImagePlugin.PngInfo() + info.add_text(b"TXT", b"VALUE") + info.add_text(b"ZIP", b"VALUE", 1) + + im = roundtrip(im, pnginfo=info) + assert_equal(im.info, {b'TXT': b'VALUE', b'ZIP': b'VALUE'}) + assert_equal(im.text, {b'TXT': b'VALUE', b'ZIP': b'VALUE'}) + +def test_scary(): + # Check reading of evil PNG file. For information, see: + # http://scary.beasts.org/security/CESA-2004-001.txt + + import base64 + file = "Tests/images/pngtest_bad.png.base64" + data = base64.decodebytes(open(file, 'rb').read()) + file = BytesIO(data) + assert_exception(IOError, lambda: Image.open(file)) + +def test_trns_rgb(): + # Check writing and reading of tRNS chunks for RGB images. + # Independent file sample provided by Sebastian Spaeth. + + file = "Tests/images/caption_6_33_22.png" + im = Image.open(file) + assert_equal(im.info["transparency"], (248, 248, 248)) + + im = roundtrip(im, transparency=(0, 1, 2)) + assert_equal(im.info["transparency"], (0, 1, 2)) diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py new file mode 100644 index 000000000..fccb94905 --- /dev/null +++ b/Tests/test_file_ppm.py @@ -0,0 +1,14 @@ +from tester import * + +from PIL import Image + +# sample ppm stream +file = "Images/lena.ppm" +data = open(file, "rb").read() + +def test_sanity(): + im = Image.open(file) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "PPM") diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py new file mode 100644 index 000000000..843e13d28 --- /dev/null +++ b/Tests/test_file_tiff.py @@ -0,0 +1,57 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + file = tempfile("temp.tif") + + lena("RGB").save(file) + + im = Image.open(file) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "TIFF") + + lena("1").save(file) + im = Image.open(file) + + lena("L").save(file) + im = Image.open(file) + + lena("P").save(file) + im = Image.open(file) + + lena("RGB").save(file) + im = Image.open(file) + + lena("I").save(file) + im = Image.open(file) + +def test_mac_tiff(): + # Read RGBa images from Mac OS X [@PIL136] + + file = "Tests/images/pil136.tiff" + im = Image.open(file) + + assert_equal(im.mode, "RGBA") + assert_equal(im.size, (55, 43)) + assert_equal(im.tile, [('raw', (0, 0, 55, 43), 8, ('RGBa', 0, 1))]) + assert_no_exception(lambda: im.load()) + +def test_gimp_tiff(): + # Read TIFF JPEG images from GIMP [@PIL168] + + file = "Tests/images/pil168.tif" + im = Image.open(file) + + assert_equal(im.mode, "RGB") + assert_equal(im.size, (256, 256)) + assert_equal(im.tile, [ + ('jpeg', (0, 0, 256, 64), 8, ('RGB', '')), + ('jpeg', (0, 64, 256, 128), 1215, ('RGB', '')), + ('jpeg', (0, 128, 256, 192), 2550, ('RGB', '')), + ('jpeg', (0, 192, 256, 256), 3890, ('RGB', '')), + ]) + assert_no_exception(lambda: im.load()) diff --git a/Tests/test_file_xbm.py b/Tests/test_file_xbm.py new file mode 100644 index 000000000..f27a3a349 --- /dev/null +++ b/Tests/test_file_xbm.py @@ -0,0 +1,34 @@ +from tester import * + +from PIL import Image + +PIL151 = b""" +#define basic_width 32 +#define basic_height 32 +static char basic_bits[] = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, +0x80, 0xff, 0xff, 0x01, 0x40, 0x00, 0x00, 0x02, +0x20, 0x00, 0x00, 0x04, 0x20, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x08, +0x10, 0x00, 0x00, 0x08, +0x10, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x08, +0x10, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x08, +0x20, 0x00, 0x00, 0x04, +0x20, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00, 0x02, +0x80, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, +}; +""" + +def test_pil151(): + + im = Image.open(BytesIO(PIL151)) + + assert_no_exception(lambda: im.load()) + assert_equal(im.mode, '1') + assert_equal(im.size, (32, 32)) diff --git a/Tests/test_font_bdf.py b/Tests/test_font_bdf.py new file mode 100644 index 000000000..366bb4468 --- /dev/null +++ b/Tests/test_font_bdf.py @@ -0,0 +1,13 @@ +from tester import * + +from PIL import Image, FontFile, BdfFontFile + +filename = "Images/courB08.bdf" + +def test_sanity(): + + file = open(filename, "rb") + font = BdfFontFile.BdfFontFile(file) + + assert_true(isinstance(font, FontFile.FontFile)) + assert_equal(len([_f for _f in font.glyph if _f]), 190) diff --git a/Tests/test_font_pcf.py b/Tests/test_font_pcf.py new file mode 100644 index 000000000..958657eaa --- /dev/null +++ b/Tests/test_font_pcf.py @@ -0,0 +1,26 @@ +from tester import * + +from PIL import Image, FontFile, PcfFontFile +from PIL import ImageFont, ImageDraw + +fontname = "Tests/fonts/helvO18.pcf" +tempname = tempfile("temp.pil", "temp.pbm") + +message = "hello, world" + +def test_sanity(): + + file = open(fontname, "rb") + font = PcfFontFile.PcfFontFile(file) + assert_true(isinstance(font, FontFile.FontFile)) + assert_equal(len([_f for _f in font.glyph if _f]), 192) + + font.save(tempname) + +def test_draw(): + + font = ImageFont.load(tempname) + image = Image.new("L", font.getsize(message), "white") + draw = ImageDraw.Draw(image) + draw.text((0, 0), message, font=font) + # assert_signature(image, "7216c60f988dea43a46bb68321e3c1b03ec62aee") diff --git a/Tests/test_image.py b/Tests/test_image.py new file mode 100644 index 000000000..cb1de08d5 --- /dev/null +++ b/Tests/test_image.py @@ -0,0 +1,39 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + im = Image.new("L", (100, 100)) + assert_equal(repr(im)[:45], " 2**32: + assert_equal(put(sys.maxsize), (255, 255, 255, 255)) + else: + assert_equal(put(sys.maxsize), (255, 255, 255, 127)) diff --git a/Tests/test_image_putpalette.py b/Tests/test_image_putpalette.py new file mode 100644 index 000000000..b7ebb8853 --- /dev/null +++ b/Tests/test_image_putpalette.py @@ -0,0 +1,28 @@ +from tester import * + +from PIL import Image +from PIL import ImagePalette + +def test_putpalette(): + def palette(mode): + im = lena(mode).copy() + im.putpalette(list(range(256))*3) + p = im.getpalette() + if p: + return im.mode, p[:10] + return im.mode + assert_exception(ValueError, lambda: palette("1")) + assert_equal(palette("L"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) + assert_equal(palette("P"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) + assert_exception(ValueError, lambda: palette("I")) + assert_exception(ValueError, lambda: palette("F")) + assert_exception(ValueError, lambda: palette("RGB")) + assert_exception(ValueError, lambda: palette("RGBA")) + assert_exception(ValueError, lambda: palette("YCbCr")) + +def test_imagepalette(): + im = lena("P") + assert_no_exception(lambda: im.putpalette(ImagePalette.negative())) + assert_no_exception(lambda: im.putpalette(ImagePalette.random())) + assert_no_exception(lambda: im.putpalette(ImagePalette.sepia())) + assert_no_exception(lambda: im.putpalette(ImagePalette.wedge())) diff --git a/Tests/test_image_putpixel.py b/Tests/test_image_putpixel.py new file mode 100644 index 000000000..555a92f74 --- /dev/null +++ b/Tests/test_image_putpixel.py @@ -0,0 +1,43 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + im1 = lena() + im2 = Image.new(im1.mode, im1.size, 0) + + for y in range(im1.size[1]): + for x in range(im1.size[0]): + pos = x, y + im2.putpixel(pos, im1.getpixel(pos)) + + assert_image_equal(im1, im2) + + im2 = Image.new(im1.mode, im1.size, 0) + im2.readonly = 1 + + for y in range(im1.size[1]): + for x in range(im1.size[0]): + pos = x, y + im2.putpixel(pos, im1.getpixel(pos)) + + assert_false(im2.readonly) + assert_image_equal(im1, im2) + + im2 = Image.new(im1.mode, im1.size, 0) + + pix1 = im1.load() + pix2 = im2.load() + + for y in range(im1.size[1]): + for x in range(im1.size[0]): + pix2[x, y] = pix1[x, y] + + assert_image_equal(im1, im2) + + + + +# see test_image_getpixel for more tests + diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py new file mode 100644 index 000000000..5dcbab7fe --- /dev/null +++ b/Tests/test_image_quantize.py @@ -0,0 +1,15 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + im = lena() + + im = im.quantize() + assert_image(im, "P", im.size) + + im = lena() + im = im.quantize(palette=lena("P")) + assert_image(im, "P", im.size) + diff --git a/Tests/test_image_resize.py b/Tests/test_image_resize.py new file mode 100644 index 000000000..4e228a396 --- /dev/null +++ b/Tests/test_image_resize.py @@ -0,0 +1,12 @@ +from tester import * + +from PIL import Image + +def test_resize(): + def resize(mode, size): + out = lena(mode).resize(size) + assert_equal(out.mode, mode) + assert_equal(out.size, size) + for mode in "1", "P", "L", "RGB", "I", "F": + yield_test(resize, mode, (100, 100)) + yield_test(resize, mode, (200, 200)) diff --git a/Tests/test_image_rotate.py b/Tests/test_image_rotate.py new file mode 100644 index 000000000..5e4782c87 --- /dev/null +++ b/Tests/test_image_rotate.py @@ -0,0 +1,15 @@ +from tester import * + +from PIL import Image + +def test_rotate(): + def rotate(mode): + im = lena(mode) + out = im.rotate(45) + assert_equal(out.mode, mode) + assert_equal(out.size, im.size) # default rotate clips output + out = im.rotate(45, expand=1) + assert_equal(out.mode, mode) + assert_true(out.size != im.size) + for mode in "1", "P", "L", "RGB", "I", "F": + yield_test(rotate, mode) diff --git a/Tests/test_image_save.py b/Tests/test_image_save.py new file mode 100644 index 000000000..7d4b6d9b3 --- /dev/null +++ b/Tests/test_image_save.py @@ -0,0 +1,5 @@ +from tester import * + +from PIL import Image + +success() diff --git a/Tests/test_image_seek.py b/Tests/test_image_seek.py new file mode 100644 index 000000000..7d4b6d9b3 --- /dev/null +++ b/Tests/test_image_seek.py @@ -0,0 +1,5 @@ +from tester import * + +from PIL import Image + +success() diff --git a/Tests/test_image_show.py b/Tests/test_image_show.py new file mode 100644 index 000000000..7d4b6d9b3 --- /dev/null +++ b/Tests/test_image_show.py @@ -0,0 +1,5 @@ +from tester import * + +from PIL import Image + +success() diff --git a/Tests/test_image_split.py b/Tests/test_image_split.py new file mode 100644 index 000000000..7fc70182b --- /dev/null +++ b/Tests/test_image_split.py @@ -0,0 +1,42 @@ +from tester import * + +from PIL import Image + +def test_split(): + def split(mode): + layers = lena(mode).split() + return [(i.mode, i.size[0], i.size[1]) for i in layers] + assert_equal(split("1"), [('1', 128, 128)]) + assert_equal(split("L"), [('L', 128, 128)]) + assert_equal(split("I"), [('I', 128, 128)]) + assert_equal(split("F"), [('F', 128, 128)]) + assert_equal(split("P"), [('P', 128, 128)]) + assert_equal(split("RGB"), [('L', 128, 128), ('L', 128, 128), ('L', 128, 128)]) + assert_equal(split("RGBA"), [('L', 128, 128), ('L', 128, 128), ('L', 128, 128), ('L', 128, 128)]) + assert_equal(split("CMYK"), [('L', 128, 128), ('L', 128, 128), ('L', 128, 128), ('L', 128, 128)]) + assert_equal(split("YCbCr"), [('L', 128, 128), ('L', 128, 128), ('L', 128, 128)]) + +def test_split_merge(): + def split_merge(mode): + return Image.merge(mode, lena(mode).split()) + assert_image_equal(lena("1"), split_merge("1")) + assert_image_equal(lena("L"), split_merge("L")) + assert_image_equal(lena("I"), split_merge("I")) + assert_image_equal(lena("F"), split_merge("F")) + assert_image_equal(lena("P"), split_merge("P")) + assert_image_equal(lena("RGB"), split_merge("RGB")) + assert_image_equal(lena("RGBA"), split_merge("RGBA")) + assert_image_equal(lena("CMYK"), split_merge("CMYK")) + assert_image_equal(lena("YCbCr"), split_merge("YCbCr")) + +def test_split_open(): + file = tempfile("temp.png") + def split_open(mode): + lena(mode).save(file) + im = Image.open(file) + return len(im.split()) + assert_equal(split_open("1"), 1) + assert_equal(split_open("L"), 1) + assert_equal(split_open("P"), 1) + assert_equal(split_open("RGB"), 3) + assert_equal(split_open("RGBA"), 4) diff --git a/Tests/test_image_tell.py b/Tests/test_image_tell.py new file mode 100644 index 000000000..7d4b6d9b3 --- /dev/null +++ b/Tests/test_image_tell.py @@ -0,0 +1,5 @@ +from tester import * + +from PIL import Image + +success() diff --git a/Tests/test_image_thumbnail.py b/Tests/test_image_thumbnail.py new file mode 100644 index 000000000..871dd1f54 --- /dev/null +++ b/Tests/test_image_thumbnail.py @@ -0,0 +1,36 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + im = lena() + im.thumbnail((100, 100)) + + assert_image(im, im.mode, (100, 100)) + +def test_aspect(): + + im = lena() + im.thumbnail((100, 100)) + assert_image(im, im.mode, (100, 100)) + + im = lena().resize((128, 256)) + im.thumbnail((100, 100)) + assert_image(im, im.mode, (50, 100)) + + im = lena().resize((128, 256)) + im.thumbnail((50, 100)) + assert_image(im, im.mode, (50, 100)) + + im = lena().resize((256, 128)) + im.thumbnail((100, 100)) + assert_image(im, im.mode, (100, 50)) + + im = lena().resize((256, 128)) + im.thumbnail((100, 50)) + assert_image(im, im.mode, (100, 50)) + + im = lena().resize((128, 128)) + im.thumbnail((100, 100)) + assert_image(im, im.mode, (100, 100)) diff --git a/Tests/test_image_tobitmap.py b/Tests/test_image_tobitmap.py new file mode 100644 index 000000000..f8186ae14 --- /dev/null +++ b/Tests/test_image_tobitmap.py @@ -0,0 +1,15 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + assert_exception(ValueError, lambda: lena().tobitmap()) + assert_no_exception(lambda: lena().convert("1").tobitmap()) + + im1 = lena().convert("1") + + bitmap = im1.tobitmap() + + assert_true(isinstance(bitmap, bytes)) + assert_image_equal(im1, fromstring(bitmap)) diff --git a/Tests/test_image_tostring.py b/Tests/test_image_tostring.py new file mode 100644 index 000000000..b992b63c6 --- /dev/null +++ b/Tests/test_image_tostring.py @@ -0,0 +1,8 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + data = lena().tobytes() + assert_true(isinstance(data, bytes)) diff --git a/Tests/test_image_transform.py b/Tests/test_image_transform.py new file mode 100644 index 000000000..7d4b6d9b3 --- /dev/null +++ b/Tests/test_image_transform.py @@ -0,0 +1,5 @@ +from tester import * + +from PIL import Image + +success() diff --git a/Tests/test_image_transpose.py b/Tests/test_image_transpose.py new file mode 100644 index 000000000..010478df4 --- /dev/null +++ b/Tests/test_image_transpose.py @@ -0,0 +1,34 @@ +from tester import * + +from PIL import Image + +FLIP_LEFT_RIGHT = Image.FLIP_LEFT_RIGHT +FLIP_TOP_BOTTOM = Image.FLIP_TOP_BOTTOM +ROTATE_90 = Image.ROTATE_90 +ROTATE_180 = Image.ROTATE_180 +ROTATE_270 = Image.ROTATE_270 + +def test_sanity(): + + im = lena() + + assert_no_exception(lambda: im.transpose(FLIP_LEFT_RIGHT)) + assert_no_exception(lambda: im.transpose(FLIP_TOP_BOTTOM)) + + assert_no_exception(lambda: im.transpose(ROTATE_90)) + assert_no_exception(lambda: im.transpose(ROTATE_180)) + assert_no_exception(lambda: im.transpose(ROTATE_270)) + +def test_roundtrip(): + + im = lena() + + def transpose(first, second): + return im.transpose(first).transpose(second) + + assert_image_equal(im, transpose(FLIP_LEFT_RIGHT, FLIP_LEFT_RIGHT)) + assert_image_equal(im, transpose(FLIP_TOP_BOTTOM, FLIP_TOP_BOTTOM)) + + assert_image_equal(im, transpose(ROTATE_90, ROTATE_270)) + assert_image_equal(im, transpose(ROTATE_180, ROTATE_180)) + diff --git a/Tests/test_image_verify.py b/Tests/test_image_verify.py new file mode 100644 index 000000000..7d4b6d9b3 --- /dev/null +++ b/Tests/test_image_verify.py @@ -0,0 +1,5 @@ +from tester import * + +from PIL import Image + +success() diff --git a/Tests/test_imagechops.py b/Tests/test_imagechops.py new file mode 100644 index 000000000..16eaaf55e --- /dev/null +++ b/Tests/test_imagechops.py @@ -0,0 +1,56 @@ +from tester import * + +from PIL import Image +from PIL import ImageChops + +def test_sanity(): + + im = lena("L") + + ImageChops.constant(im, 128) + ImageChops.duplicate(im) + ImageChops.invert(im) + ImageChops.lighter(im, im) + ImageChops.darker(im, im) + ImageChops.difference(im, im) + ImageChops.multiply(im, im) + ImageChops.screen(im, im) + + ImageChops.add(im, im) + ImageChops.add(im, im, 2.0) + ImageChops.add(im, im, 2.0, 128) + ImageChops.subtract(im, im) + ImageChops.subtract(im, im, 2.0) + ImageChops.subtract(im, im, 2.0, 128) + + ImageChops.add_modulo(im, im) + ImageChops.subtract_modulo(im, im) + + ImageChops.blend(im, im, 0.5) + ImageChops.composite(im, im, im) + + ImageChops.offset(im, 10) + ImageChops.offset(im, 10, 20) + +def test_logical(): + + def table(op, a, b): + out = [] + for x in (a, b): + imx = Image.new("1", (1, 1), x) + for y in (a, b): + imy = Image.new("1", (1, 1), y) + out.append(op(imx, imy).getpixel((0, 0))) + return tuple(out) + + assert_equal(table(ImageChops.logical_and, 0, 1), (0, 0, 0, 255)) + assert_equal(table(ImageChops.logical_or, 0, 1), (0, 255, 255, 255)) + assert_equal(table(ImageChops.logical_xor, 0, 1), (0, 255, 255, 0)) + + assert_equal(table(ImageChops.logical_and, 0, 128), (0, 0, 0, 255)) + assert_equal(table(ImageChops.logical_or, 0, 128), (0, 255, 255, 255)) + assert_equal(table(ImageChops.logical_xor, 0, 128), (0, 255, 255, 0)) + + assert_equal(table(ImageChops.logical_and, 0, 255), (0, 0, 0, 255)) + assert_equal(table(ImageChops.logical_or, 0, 255), (0, 255, 255, 255)) + assert_equal(table(ImageChops.logical_xor, 0, 255), (0, 255, 255, 0)) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py new file mode 100644 index 000000000..29e578192 --- /dev/null +++ b/Tests/test_imagecms.py @@ -0,0 +1,82 @@ +from tester import * + +from PIL import Image +try: + from PIL import ImageCms +except ImportError: + skip() + +SRGB = "Tests/icc/sRGB.icm" + +def test_sanity(): + + # basic smoke test. + # this mostly follows the cms_test outline. + + v = ImageCms.versions() # should return four strings + assert_equal(v[0], '0.1.0 pil') + assert_equal(list(map(type, v)), [str, str, str, str]) + + # internal version number + assert_match(ImageCms.core.littlecms_version, "\d+\.\d+$") + + i = ImageCms.profileToProfile(lena(), SRGB, SRGB) + assert_image(i, "RGB", (128, 128)) + + t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") + i = ImageCms.applyTransform(lena(), t) + assert_image(i, "RGB", (128, 128)) + + p = ImageCms.createProfile("sRGB") + o = ImageCms.getOpenProfile(SRGB) + t = ImageCms.buildTransformFromOpenProfiles(p, o, "RGB", "RGB") + i = ImageCms.applyTransform(lena(), t) + assert_image(i, "RGB", (128, 128)) + + t = ImageCms.buildProofTransform(SRGB, SRGB, SRGB, "RGB", "RGB") + assert_equal(t.inputMode, "RGB") + assert_equal(t.outputMode, "RGB") + i = ImageCms.applyTransform(lena(), t) + assert_image(i, "RGB", (128, 128)) + + # get profile information for file + assert_equal(ImageCms.getProfileName(SRGB).strip(), + 'IEC 61966-2.1 Default RGB colour space - sRGB') + assert_equal(ImageCms.getProfileInfo(SRGB).splitlines(), + ['sRGB IEC61966-2.1', '', + 'Copyright (c) 1998 Hewlett-Packard Company', '', + 'WhitePoint : D65 (daylight)', '', + 'Tests/icc/sRGB.icm']) + assert_equal(ImageCms.getDefaultIntent(SRGB), 0) + assert_equal(ImageCms.isIntentSupported( + SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, + ImageCms.DIRECTION_INPUT), 1) + + # same, using profile object + p = ImageCms.createProfile("sRGB") + assert_equal(ImageCms.getProfileName(p).strip(), + 'sRGB built-in - (lcms internal)') + assert_equal(ImageCms.getProfileInfo(p).splitlines(), + ['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', '']) + assert_equal(ImageCms.getDefaultIntent(p), 0) + assert_equal(ImageCms.isIntentSupported( + p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, + ImageCms.DIRECTION_INPUT), 1) + + # extensions + i = Image.open("Tests/images/rgb.jpg") + p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"])) + assert_equal(ImageCms.getProfileName(p).strip(), + 'IEC 61966-2.1 Default RGB colour space - sRGB') + + # the procedural pyCMS API uses PyCMSError for all sorts of errors + assert_exception(ImageCms.PyCMSError, lambda: ImageCms.profileToProfile(lena(), "foo", "bar")) + assert_exception(ImageCms.PyCMSError, lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB")) + assert_exception(ImageCms.PyCMSError, lambda: ImageCms.getProfileName(None)) + assert_exception(ImageCms.PyCMSError, lambda: ImageCms.isIntentSupported(SRGB, None, None)) + + # test PointTransform convenience API + im = lena().point(t) + + # try fetching the profile for the current display device + assert_no_exception(lambda: ImageCms.get_display_profile()) diff --git a/Tests/test_imagecolor.py b/Tests/test_imagecolor.py new file mode 100644 index 000000000..534005fe4 --- /dev/null +++ b/Tests/test_imagecolor.py @@ -0,0 +1,31 @@ +from tester import * + +from PIL import Image +from PIL import ImageColor + +# -------------------------------------------------------------------- +# sanity + +assert_equal((255, 0, 0), ImageColor.getrgb("#f00")) +assert_equal((255, 0, 0), ImageColor.getrgb("#ff0000")) +assert_equal((255, 0, 0), ImageColor.getrgb("rgb(255,0,0)")) +assert_equal((255, 0, 0), ImageColor.getrgb("rgb(100%,0%,0%)")) +assert_equal((255, 0, 0), ImageColor.getrgb("hsl(0, 100%, 50%)")) +assert_equal((255, 0, 0), ImageColor.getrgb("red")) + +# -------------------------------------------------------------------- +# look for rounding errors (based on code by Tim Hatch) + +for color in list(ImageColor.colormap.keys()): + expected = Image.new("RGB", (1, 1), color).convert("L").getpixel((0, 0)) + actual = Image.new("L", (1, 1), color).getpixel((0, 0)) + assert_equal(expected, actual) + +assert_equal((0, 0, 0), ImageColor.getcolor("black", "RGB")) +assert_equal((255, 255, 255), ImageColor.getcolor("white", "RGB")) + +assert_equal(0, ImageColor.getcolor("black", "L")) +assert_equal(255, ImageColor.getcolor("white", "L")) + +assert_equal(0, ImageColor.getcolor("black", "1")) +assert_equal(255, ImageColor.getcolor("white", "1")) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py new file mode 100644 index 000000000..c6fd20f52 --- /dev/null +++ b/Tests/test_imagedraw.py @@ -0,0 +1,28 @@ +from tester import * + +from PIL import Image +from PIL import ImageDraw + +def test_sanity(): + + im = lena("RGB").copy() + + draw = ImageDraw.ImageDraw(im) + draw = ImageDraw.Draw(im) + + draw.ellipse(list(range(4))) + draw.line(list(range(10))) + draw.polygon(list(range(100))) + draw.rectangle(list(range(4))) + + success() + +def test_deprecated(): + + im = lena().copy() + + draw = ImageDraw.Draw(im) + + assert_warning(DeprecationWarning, lambda: draw.setink(0)) + assert_warning(DeprecationWarning, lambda: draw.setfill(0)) + diff --git a/Tests/test_imageenhance.py b/Tests/test_imageenhance.py new file mode 100644 index 000000000..04f16bfa5 --- /dev/null +++ b/Tests/test_imageenhance.py @@ -0,0 +1,19 @@ +from tester import * + +from PIL import Image +from PIL import ImageEnhance + +def test_sanity(): + + # FIXME: assert_image + assert_no_exception(lambda: ImageEnhance.Color(lena()).enhance(0.5)) + assert_no_exception(lambda: ImageEnhance.Contrast(lena()).enhance(0.5)) + assert_no_exception(lambda: ImageEnhance.Brightness(lena()).enhance(0.5)) + assert_no_exception(lambda: ImageEnhance.Sharpness(lena()).enhance(0.5)) + +def test_crash(): + + # crashes on small images + im = Image.new("RGB", (1, 1)) + assert_no_exception(lambda: ImageEnhance.Sharpness(im).enhance(0.5)) + diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py new file mode 100644 index 000000000..e260a24dd --- /dev/null +++ b/Tests/test_imagefile.py @@ -0,0 +1,61 @@ +from tester import * + +from PIL import Image +from PIL import ImageFile + +# save original block sizes +MAXBLOCK = ImageFile.MAXBLOCK +SAFEBLOCK = ImageFile.SAFEBLOCK + +def test_parser(): + + def roundtrip(format): + + im = lena("L").resize((1000, 1000)) + if format in ("MSP", "XBM"): + im = im.convert("1") + + file = BytesIO() + + im.save(file, format) + + data = file.getvalue() + + parser = ImageFile.Parser() + parser.feed(data) + imOut = parser.close() + + return im, imOut + + assert_image_equal(*roundtrip("BMP")) + assert_image_equal(*roundtrip("GIF")) + assert_image_equal(*roundtrip("IM")) + assert_image_equal(*roundtrip("MSP")) + try: + # force multiple blocks in PNG driver + ImageFile.MAXBLOCK = 8192 + assert_image_equal(*roundtrip("PNG")) + finally: + ImageFile.MAXBLOCK = MAXBLOCK + assert_image_equal(*roundtrip("PPM")) + assert_image_equal(*roundtrip("TIFF")) + assert_image_equal(*roundtrip("XBM")) + + im1, im2 = roundtrip("JPEG") # lossy compression + assert_image(im1, im2.mode, im2.size) + + assert_exception(IOError, lambda: roundtrip("PCX")) + assert_exception(IOError, lambda: roundtrip("PDF")) + + +def test_safeblock(): + + im1 = lena() + + try: + ImageFile.SAFEBLOCK = 1 + im2 = fromstring(tostring(im1, "PNG")) + finally: + ImageFile.SAFEBLOCK = SAFEBLOCK + + assert_image_equal(im1, im2) diff --git a/Tests/test_imagefileio.py b/Tests/test_imagefileio.py new file mode 100644 index 000000000..259c6fe1c --- /dev/null +++ b/Tests/test_imagefileio.py @@ -0,0 +1,24 @@ +from tester import * + +from PIL import Image +from PIL import ImageFileIO + +def test_fileio(): + + class DumbFile: + def __init__(self, data): + self.data = data + def read(self, bytes=None): + assert_equal(bytes, None) + return self.data + def close(self): + pass + + im1 = lena() + + io = ImageFileIO.ImageFileIO(DumbFile(tostring(im1, "PPM"))) + + im2 = Image.open(io) + assert_image_equal(im1, im2) + + diff --git a/Tests/test_imagefilter.py b/Tests/test_imagefilter.py new file mode 100644 index 000000000..214f88024 --- /dev/null +++ b/Tests/test_imagefilter.py @@ -0,0 +1,31 @@ +from tester import * + +from PIL import Image +from PIL import ImageFilter + +def test_sanity(): + # see test_image_filter for more tests + + assert_no_exception(lambda: ImageFilter.MaxFilter) + assert_no_exception(lambda: ImageFilter.MedianFilter) + assert_no_exception(lambda: ImageFilter.MinFilter) + assert_no_exception(lambda: ImageFilter.ModeFilter) + assert_no_exception(lambda: ImageFilter.Kernel((3, 3), list(range(9)))) + assert_no_exception(lambda: ImageFilter.GaussianBlur) + assert_no_exception(lambda: ImageFilter.GaussianBlur(5)) + assert_no_exception(lambda: ImageFilter.UnsharpMask) + assert_no_exception(lambda: ImageFilter.UnsharpMask(10)) + + assert_no_exception(lambda: ImageFilter.BLUR) + assert_no_exception(lambda: ImageFilter.CONTOUR) + assert_no_exception(lambda: ImageFilter.DETAIL) + assert_no_exception(lambda: ImageFilter.EDGE_ENHANCE) + assert_no_exception(lambda: ImageFilter.EDGE_ENHANCE_MORE) + assert_no_exception(lambda: ImageFilter.EMBOSS) + assert_no_exception(lambda: ImageFilter.FIND_EDGES) + assert_no_exception(lambda: ImageFilter.SMOOTH) + assert_no_exception(lambda: ImageFilter.SMOOTH_MORE) + assert_no_exception(lambda: ImageFilter.SHARPEN) + + + diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py new file mode 100644 index 000000000..3c4e1f1b8 --- /dev/null +++ b/Tests/test_imagefont.py @@ -0,0 +1,12 @@ +from tester import * + +from PIL import Image +try: + from PIL import ImageFont + ImageFont.core.getfont # check if freetype is available +except ImportError: + skip() + +def test_sanity(): + + assert_match(ImageFont.core.freetype2_version, "\d+\.\d+\.\d+$") diff --git a/Tests/test_imagegl.py b/Tests/test_imagegl.py new file mode 100644 index 000000000..87dd6aa2d --- /dev/null +++ b/Tests/test_imagegl.py @@ -0,0 +1,9 @@ +from tester import * + +from PIL import Image +try: + from PIL import ImageGL +except ImportError as v: + skip(v) + +success() diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py new file mode 100644 index 000000000..67ff71960 --- /dev/null +++ b/Tests/test_imagegrab.py @@ -0,0 +1,13 @@ +from tester import * + +from PIL import Image +try: + from PIL import ImageGrab +except ImportError as v: + skip(v) + +def test_grab(): + im = ImageGrab.grab() + assert_image(im, im.mode, im.size) + + diff --git a/Tests/test_imagemath.py b/Tests/test_imagemath.py new file mode 100644 index 000000000..eaeb711ba --- /dev/null +++ b/Tests/test_imagemath.py @@ -0,0 +1,62 @@ +from tester import * + +from PIL import Image +from PIL import ImageMath + +def pixel(im): + if hasattr(im, "im"): + return "%s %r" % (im.mode, im.getpixel((0, 0))) + else: + if isinstance(im, type(0)): + return int(im) # hack to deal with booleans + print(im) + +A = Image.new("L", (1, 1), 1) +B = Image.new("L", (1, 1), 2) +F = Image.new("F", (1, 1), 3) +I = Image.new("I", (1, 1), 4) + +images = {"A": A, "B": B, "F": F, "I": I} + +def test_sanity(): + assert_equal(ImageMath.eval("1"), 1) + assert_equal(ImageMath.eval("1+A", A=2), 3) + assert_equal(pixel(ImageMath.eval("A+B", A=A, B=B)), "I 3") + assert_equal(pixel(ImageMath.eval("A+B", images)), "I 3") + assert_equal(pixel(ImageMath.eval("float(A)+B", images)), "F 3.0") + assert_equal(pixel(ImageMath.eval("int(float(A)+B)", images)), "I 3") + +def test_ops(): + + assert_equal(pixel(ImageMath.eval("-A", images)), "I -1") + assert_equal(pixel(ImageMath.eval("+B", images)), "L 2") + + assert_equal(pixel(ImageMath.eval("A+B", images)), "I 3") + assert_equal(pixel(ImageMath.eval("A-B", images)), "I -1") + assert_equal(pixel(ImageMath.eval("A*B", images)), "I 2") + assert_equal(pixel(ImageMath.eval("A/B", images)), "I 0") + assert_equal(pixel(ImageMath.eval("B**2", images)), "I 4") + assert_equal(pixel(ImageMath.eval("B**33", images)), "I 2147483647") + + assert_equal(pixel(ImageMath.eval("float(A)+B", images)), "F 3.0") + assert_equal(pixel(ImageMath.eval("float(A)-B", images)), "F -1.0") + assert_equal(pixel(ImageMath.eval("float(A)*B", images)), "F 2.0") + assert_equal(pixel(ImageMath.eval("float(A)/B", images)), "F 0.5") + assert_equal(pixel(ImageMath.eval("float(B)**2", images)), "F 4.0") + assert_equal(pixel(ImageMath.eval("float(B)**33", images)), "F 8589934592.0") + +def test_logical(): + assert_equal(pixel(ImageMath.eval("not A", images)), 0) + assert_equal(pixel(ImageMath.eval("A and B", images)), "L 2") + assert_equal(pixel(ImageMath.eval("A or B", images)), "L 1") + +def test_convert(): + assert_equal(pixel(ImageMath.eval("convert(A+B, 'L')", images)), "L 3") + assert_equal(pixel(ImageMath.eval("convert(A+B, '1')", images)), "1 0") + assert_equal(pixel(ImageMath.eval("convert(A+B, 'RGB')", images)), "RGB (3, 3, 3)") + +def test_compare(): + assert_equal(pixel(ImageMath.eval("min(A, B)", images)), "I 1") + assert_equal(pixel(ImageMath.eval("max(A, B)", images)), "I 2") + assert_equal(pixel(ImageMath.eval("A == 1", images)), "I 1") + assert_equal(pixel(ImageMath.eval("A == 2", images)), "I 0") diff --git a/Tests/test_imagemode.py b/Tests/test_imagemode.py new file mode 100644 index 000000000..54b04435f --- /dev/null +++ b/Tests/test_imagemode.py @@ -0,0 +1,23 @@ +from tester import * + +from PIL import Image +from PIL import ImageMode + +ImageMode.getmode("1") +ImageMode.getmode("L") +ImageMode.getmode("P") +ImageMode.getmode("RGB") +ImageMode.getmode("I") +ImageMode.getmode("F") + +m = ImageMode.getmode("1") +assert_equal(m.mode, "1") +assert_equal(m.bands, ("1",)) +assert_equal(m.basemode, "L") +assert_equal(m.basetype, "L") + +m = ImageMode.getmode("RGB") +assert_equal(m.mode, "RGB") +assert_equal(m.bands, ("R", "G", "B")) +assert_equal(m.basemode, "RGB") +assert_equal(m.basetype, "L") diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py new file mode 100644 index 000000000..362937e81 --- /dev/null +++ b/Tests/test_imageops.py @@ -0,0 +1,70 @@ +from tester import * + +from PIL import Image +from PIL import ImageOps + +class Deformer: + def getmesh(self, im): + x, y = im.size + return [((0, 0, x, y), (0, 0, x, 0, x, y, y, 0))] + +deformer = Deformer() + +def test_sanity(): + + ImageOps.autocontrast(lena("L")) + ImageOps.autocontrast(lena("RGB")) + + ImageOps.autocontrast(lena("L"), cutoff=10) + ImageOps.autocontrast(lena("L"), ignore=[0, 255]) + + ImageOps.colorize(lena("L"), (0, 0, 0), (255, 255, 255)) + ImageOps.colorize(lena("L"), "black", "white") + + ImageOps.crop(lena("L"), 1) + ImageOps.crop(lena("RGB"), 1) + + ImageOps.deform(lena("L"), deformer) + ImageOps.deform(lena("RGB"), deformer) + + ImageOps.equalize(lena("L")) + ImageOps.equalize(lena("RGB")) + + ImageOps.expand(lena("L"), 1) + ImageOps.expand(lena("RGB"), 1) + ImageOps.expand(lena("L"), 2, "blue") + ImageOps.expand(lena("RGB"), 2, "blue") + + ImageOps.fit(lena("L"), (128, 128)) + ImageOps.fit(lena("RGB"), (128, 128)) + + ImageOps.flip(lena("L")) + ImageOps.flip(lena("RGB")) + + ImageOps.grayscale(lena("L")) + ImageOps.grayscale(lena("RGB")) + + ImageOps.invert(lena("L")) + ImageOps.invert(lena("RGB")) + + ImageOps.mirror(lena("L")) + ImageOps.mirror(lena("RGB")) + + ImageOps.posterize(lena("L"), 4) + ImageOps.posterize(lena("RGB"), 4) + + ImageOps.solarize(lena("L")) + ImageOps.solarize(lena("RGB")) + + success() + +def test_pil163(): + # Division by zero in equalize if < 255 pixels in image (@PIL163) + + i = lena("RGB").resize((15, 16)) + + ImageOps.equalize(i.convert("L")) + ImageOps.equalize(i.convert("P")) + ImageOps.equalize(i.convert("RGB")) + + success() diff --git a/Tests/test_imageops_usm.py b/Tests/test_imageops_usm.py new file mode 100644 index 000000000..83a93b8e0 --- /dev/null +++ b/Tests/test_imageops_usm.py @@ -0,0 +1,55 @@ +from tester import * + +from PIL import Image +from PIL import ImageOps +from PIL import ImageFilter + +im = Image.open("Images/lena.ppm") + +def test_ops_api(): + + i = ImageOps.gaussian_blur(im, 2.0) + assert_equal(i.mode, "RGB") + assert_equal(i.size, (128, 128)) + # i.save("blur.bmp") + + i = ImageOps.usm(im, 2.0, 125, 8) + assert_equal(i.mode, "RGB") + assert_equal(i.size, (128, 128)) + # i.save("usm.bmp") + +def test_filter_api(): + + filter = ImageFilter.GaussianBlur(2.0) + i = im.filter(filter) + assert_equal(i.mode, "RGB") + assert_equal(i.size, (128, 128)) + + filter = ImageFilter.UnsharpMask(2.0, 125, 8) + i = im.filter(filter) + assert_equal(i.mode, "RGB") + assert_equal(i.size, (128, 128)) + +def test_usm(): + + usm = ImageOps.unsharp_mask + assert_exception(ValueError, lambda: usm(im.convert("1"))) + assert_no_exception(lambda: usm(im.convert("L"))) + assert_exception(ValueError, lambda: usm(im.convert("I"))) + assert_exception(ValueError, lambda: usm(im.convert("F"))) + assert_no_exception(lambda: usm(im.convert("RGB"))) + assert_no_exception(lambda: usm(im.convert("RGBA"))) + assert_no_exception(lambda: usm(im.convert("CMYK"))) + assert_exception(ValueError, lambda: usm(im.convert("YCbCr"))) + +def test_blur(): + + blur = ImageOps.gaussian_blur + assert_exception(ValueError, lambda: blur(im.convert("1"))) + assert_no_exception(lambda: blur(im.convert("L"))) + assert_exception(ValueError, lambda: blur(im.convert("I"))) + assert_exception(ValueError, lambda: blur(im.convert("F"))) + assert_no_exception(lambda: blur(im.convert("RGB"))) + assert_no_exception(lambda: blur(im.convert("RGBA"))) + assert_no_exception(lambda: blur(im.convert("CMYK"))) + assert_exception(ValueError, lambda: blur(im.convert("YCbCr"))) diff --git a/Tests/test_imagepalette.py b/Tests/test_imagepalette.py new file mode 100644 index 000000000..573c706b1 --- /dev/null +++ b/Tests/test_imagepalette.py @@ -0,0 +1,44 @@ +from tester import * + +from PIL import Image +from PIL import ImagePalette + +ImagePalette = ImagePalette.ImagePalette + +def test_sanity(): + + assert_no_exception(lambda: ImagePalette("RGB", list(range(256))*3)) + assert_exception(ValueError, lambda: ImagePalette("RGB", list(range(256))*2)) + +def test_getcolor(): + + palette = ImagePalette() + + map = {} + for i in range(256): + map[palette.getcolor((i, i, i))] = i + + assert_equal(len(map), 256) + assert_exception(ValueError, lambda: palette.getcolor((1, 2, 3))) + +def test_file(): + + palette = ImagePalette() + + file = tempfile("temp.lut") + + palette.save(file) + + from PIL.ImagePalette import load, raw + + p = load(file) + + # load returns raw palette information + assert_equal(len(p[0]), 768) + assert_equal(p[1], "RGB") + + p = raw(p[1], p[0]) + assert_true(isinstance(p, ImagePalette)) + + + diff --git a/Tests/test_imagepath.py b/Tests/test_imagepath.py new file mode 100644 index 000000000..beadff10e --- /dev/null +++ b/Tests/test_imagepath.py @@ -0,0 +1,49 @@ +from tester import * + +from PIL import Image +from PIL import ImagePath + +import array + +def test_path(): + + p = ImagePath.Path(list(range(10))) + + # sequence interface + assert_equal(len(p), 5) + assert_equal(p[0], (0.0, 1.0)) + assert_equal(p[-1], (8.0, 9.0)) + assert_equal(list(p[:1]), [(0.0, 1.0)]) + assert_equal(list(p), [(0.0, 1.0), (2.0, 3.0), (4.0, 5.0), (6.0, 7.0), (8.0, 9.0)]) + + # method sanity check + assert_equal(p.tolist(), [(0.0, 1.0), (2.0, 3.0), (4.0, 5.0), (6.0, 7.0), (8.0, 9.0)]) + assert_equal(p.tolist(1), [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]) + + assert_equal(p.getbbox(), (0.0, 1.0, 8.0, 9.0)) + + assert_equal(p.compact(5), 2) + assert_equal(list(p), [(0.0, 1.0), (4.0, 5.0), (8.0, 9.0)]) + + p.transform((1,0,1,0,1,1)) + assert_equal(list(p), [(1.0, 2.0), (5.0, 6.0), (9.0, 10.0)]) + + # alternative constructors + p = ImagePath.Path([0, 1]) + assert_equal(list(p), [(0.0, 1.0)]) + p = ImagePath.Path([0.0, 1.0]) + assert_equal(list(p), [(0.0, 1.0)]) + p = ImagePath.Path([0, 1]) + assert_equal(list(p), [(0.0, 1.0)]) + p = ImagePath.Path([(0, 1)]) + assert_equal(list(p), [(0.0, 1.0)]) + p = ImagePath.Path(p) + assert_equal(list(p), [(0.0, 1.0)]) + p = ImagePath.Path(p.tolist(0)) + assert_equal(list(p), [(0.0, 1.0)]) + p = ImagePath.Path(p.tolist(1)) + assert_equal(list(p), [(0.0, 1.0)]) + p = ImagePath.Path(array.array("f", [0, 1])) + assert_equal(list(p), [(0.0, 1.0)]) + p = ImagePath.Path(array.array("f", [0, 1]).tostring()) + assert_equal(list(p), [(0.0, 1.0)]) diff --git a/Tests/test_imageqt.py b/Tests/test_imageqt.py new file mode 100644 index 000000000..8d6ac9f3c --- /dev/null +++ b/Tests/test_imageqt.py @@ -0,0 +1,9 @@ +from tester import * + +from PIL import Image +try: + from PIL import ImageQt +except ImportError as v: + skip(v) + +success() diff --git a/Tests/test_imagesequence.py b/Tests/test_imagesequence.py new file mode 100644 index 000000000..0b244d88c --- /dev/null +++ b/Tests/test_imagesequence.py @@ -0,0 +1,22 @@ +from tester import * + +from PIL import Image +from PIL import ImageSequence + +def test_sanity(): + + file = tempfile("temp.im") + + im = lena("RGB") + im.save(file) + + seq = ImageSequence.Iterator(im) + + index = 0 + for frame in seq: + assert_image_equal(im, frame) + assert_equal(im.tell(), index) + index = index + 1 + + assert_equal(index, 1) + diff --git a/Tests/test_imageshow.py b/Tests/test_imageshow.py new file mode 100644 index 000000000..99ec005c8 --- /dev/null +++ b/Tests/test_imageshow.py @@ -0,0 +1,6 @@ +from tester import * + +from PIL import Image +from PIL import ImageShow + +success() diff --git a/Tests/test_imagestat.py b/Tests/test_imagestat.py new file mode 100644 index 000000000..02a461e22 --- /dev/null +++ b/Tests/test_imagestat.py @@ -0,0 +1,52 @@ +from tester import * + +from PIL import Image +from PIL import ImageStat + +def test_sanity(): + + im = lena() + + st = ImageStat.Stat(im) + st = ImageStat.Stat(im.histogram()) + st = ImageStat.Stat(im, Image.new("1", im.size, 1)) + + assert_no_exception(lambda: st.extrema) + assert_no_exception(lambda: st.sum) + assert_no_exception(lambda: st.mean) + assert_no_exception(lambda: st.median) + assert_no_exception(lambda: st.rms) + assert_no_exception(lambda: st.sum2) + assert_no_exception(lambda: st.var) + assert_no_exception(lambda: st.stddev) + assert_exception(AttributeError, lambda: st.spam) + + assert_exception(TypeError, lambda: ImageStat.Stat(1)) + +def test_lena(): + + im = lena() + + st = ImageStat.Stat(im) + + # verify a few values + assert_equal(st.extrema[0], (61, 255)) + assert_equal(st.median[0], 197) + assert_equal(st.sum[0], 2954416) + assert_equal(st.sum[1], 2027250) + assert_equal(st.sum[2], 1727331) + +def test_constant(): + + im = Image.new("L", (128, 128), 128) + + st = ImageStat.Stat(im) + + assert_equal(st.extrema[0], (128, 128)) + assert_equal(st.sum[0], 128**3) + assert_equal(st.sum2[0], 128**4) + assert_equal(st.mean[0], 128) + assert_equal(st.median[0], 128) + assert_equal(st.rms[0], 128) + assert_equal(st.var[0], 0) + assert_equal(st.stddev[0], 0) diff --git a/Tests/test_imagetk.py b/Tests/test_imagetk.py new file mode 100644 index 000000000..5c39c9283 --- /dev/null +++ b/Tests/test_imagetk.py @@ -0,0 +1,9 @@ +from tester import * + +from PIL import Image +try: + from PIL import ImageTk +except ImportError as v: + skip(v) + +success() diff --git a/Tests/test_imagetransform.py b/Tests/test_imagetransform.py new file mode 100644 index 000000000..884e6bb1c --- /dev/null +++ b/Tests/test_imagetransform.py @@ -0,0 +1,18 @@ +from tester import * + +from PIL import Image +from PIL import ImageTransform + +im = Image.new("L", (100, 100)) + +seq = tuple(range(10)) + +def test_sanity(): + transform = ImageTransform.AffineTransform(seq[:6]) + assert_no_exception(lambda: im.transform((100, 100), transform)) + transform = ImageTransform.ExtentTransform(seq[:4]) + assert_no_exception(lambda: im.transform((100, 100), transform)) + transform = ImageTransform.QuadTransform(seq[:8]) + assert_no_exception(lambda: im.transform((100, 100), transform)) + transform = ImageTransform.MeshTransform([(seq[:4], seq[:8])]) + assert_no_exception(lambda: im.transform((100, 100), transform)) diff --git a/Tests/test_imagewin.py b/Tests/test_imagewin.py new file mode 100644 index 000000000..268a75b6f --- /dev/null +++ b/Tests/test_imagewin.py @@ -0,0 +1,6 @@ +from tester import * + +from PIL import Image +from PIL import ImageWin + +success() diff --git a/Tests/test_lib_image.py b/Tests/test_lib_image.py new file mode 100644 index 000000000..93aa694d8 --- /dev/null +++ b/Tests/test_lib_image.py @@ -0,0 +1,30 @@ +from tester import * + +from PIL import Image + +def test_setmode(): + + im = Image.new("L", (1, 1), 255) + im.im.setmode("1") + assert_equal(im.im.getpixel((0, 0)), 255) + im.im.setmode("L") + assert_equal(im.im.getpixel((0, 0)), 255) + + im = Image.new("1", (1, 1), 1) + im.im.setmode("L") + assert_equal(im.im.getpixel((0, 0)), 255) + im.im.setmode("1") + assert_equal(im.im.getpixel((0, 0)), 255) + + im = Image.new("RGB", (1, 1), (1, 2, 3)) + im.im.setmode("RGB") + assert_equal(im.im.getpixel((0, 0)), (1, 2, 3)) + im.im.setmode("RGBA") + assert_equal(im.im.getpixel((0, 0)), (1, 2, 3, 255)) + im.im.setmode("RGBX") + assert_equal(im.im.getpixel((0, 0)), (1, 2, 3, 255)) + im.im.setmode("RGB") + assert_equal(im.im.getpixel((0, 0)), (1, 2, 3)) + + assert_exception(ValueError, lambda: im.im.setmode("L")) + assert_exception(ValueError, lambda: im.im.setmode("RGBABCDE")) diff --git a/Tests/test_lib_pack.py b/Tests/test_lib_pack.py new file mode 100644 index 000000000..cd26a54ea --- /dev/null +++ b/Tests/test_lib_pack.py @@ -0,0 +1,110 @@ +from tester import * + +from PIL import Image + +def pack(): + pass # not yet + +def test_pack(): + + def pack(mode, rawmode): + if len(mode) == 1: + im = Image.new(mode, (1, 1), 1) + else: + im = Image.new(mode, (1, 1), (1, 2, 3, 4)[:len(mode)]) + return list(im.tobytes("raw", rawmode)) + + assert_equal(pack("1", "1"), [128]) + assert_equal(pack("1", "1;I"), [0]) + assert_equal(pack("1", "1;R"), [1]) + assert_equal(pack("1", "1;IR"), [0]) + + assert_equal(pack("L", "L"), [1]) + + assert_equal(pack("I", "I"), [1, 0, 0, 0]) + + assert_equal(pack("F", "F"), [0, 0, 128, 63]) + + assert_equal(pack("LA", "LA"), [1, 2]) + + assert_equal(pack("RGB", "RGB"), [1, 2, 3]) + assert_equal(pack("RGB", "RGB;L"), [1, 2, 3]) + assert_equal(pack("RGB", "BGR"), [3, 2, 1]) + assert_equal(pack("RGB", "RGBX"), [1, 2, 3, 255]) # 255? + assert_equal(pack("RGB", "BGRX"), [3, 2, 1, 0]) + assert_equal(pack("RGB", "XRGB"), [0, 1, 2, 3]) + assert_equal(pack("RGB", "XBGR"), [0, 3, 2, 1]) + + assert_equal(pack("RGBX", "RGBX"), [1, 2, 3, 4]) # 4->255? + + assert_equal(pack("RGBA", "RGBA"), [1, 2, 3, 4]) + + assert_equal(pack("CMYK", "CMYK"), [1, 2, 3, 4]) + assert_equal(pack("YCbCr", "YCbCr"), [1, 2, 3]) + +def test_unpack(): + + def unpack(mode, rawmode, bytes_): + data = bytes(range(1,bytes_+1)) + im = Image.frombytes(mode, (1, 1), data, "raw", rawmode, 0, 1) + return im.getpixel((0, 0)) + + def unpack_1(mode, rawmode, value): + assert mode == "1" + im = Image.frombytes(mode, (8, 1), bytes([value]), "raw", rawmode, 0, 1) + return tuple(im.getdata()) + + X = 255 + + assert_equal(unpack_1("1", "1", 1), (0,0,0,0,0,0,0,X)) + assert_equal(unpack_1("1", "1;I", 1), (X,X,X,X,X,X,X,0)) + assert_equal(unpack_1("1", "1;R", 1), (X,0,0,0,0,0,0,0)) + assert_equal(unpack_1("1", "1;IR", 1), (0,X,X,X,X,X,X,X)) + + assert_equal(unpack_1("1", "1", 170), (X,0,X,0,X,0,X,0)) + assert_equal(unpack_1("1", "1;I", 170), (0,X,0,X,0,X,0,X)) + assert_equal(unpack_1("1", "1;R", 170), (0,X,0,X,0,X,0,X)) + assert_equal(unpack_1("1", "1;IR", 170), (X,0,X,0,X,0,X,0)) + + assert_equal(unpack("L", "L;2", 1), 0) + assert_equal(unpack("L", "L;4", 1), 0) + assert_equal(unpack("L", "L", 1), 1) + assert_equal(unpack("L", "L;I", 1), 254) + assert_equal(unpack("L", "L;R", 1), 128) + assert_equal(unpack("L", "L;16", 2), 2) # little endian + assert_equal(unpack("L", "L;16B", 2), 1) # big endian + + assert_equal(unpack("LA", "LA", 2), (1, 2)) + assert_equal(unpack("LA", "LA;L", 2), (1, 2)) + + assert_equal(unpack("RGB", "RGB", 3), (1, 2, 3)) + assert_equal(unpack("RGB", "RGB;L", 3), (1, 2, 3)) + assert_equal(unpack("RGB", "RGB;R", 3), (128, 64, 192)) + assert_equal(unpack("RGB", "RGB;16B", 6), (1, 3, 5)) # ? + assert_equal(unpack("RGB", "BGR", 3), (3, 2, 1)) + assert_equal(unpack("RGB", "BGR;15", 2), (0, 131, 8)) + assert_equal(unpack("RGB", "BGR;16", 2), (0, 64, 8)) + + assert_equal(unpack("RGB", "RGBX", 4), (1, 2, 3)) + assert_equal(unpack("RGB", "BGRX", 4), (3, 2, 1)) + assert_equal(unpack("RGB", "XRGB", 4), (2, 3, 4)) + assert_equal(unpack("RGB", "XBGR", 4), (4, 3, 2)) + + assert_equal(unpack("RGBA", "RGBA", 4), (1, 2, 3, 4)) + assert_equal(unpack("RGBA", "BGRA", 4), (3, 2, 1, 4)) + assert_equal(unpack("RGBA", "ARGB", 4), (2, 3, 4, 1)) + assert_equal(unpack("RGBA", "ABGR", 4), (4, 3, 2, 1)) + + assert_equal(unpack("RGBX", "RGBX", 4), (1, 2, 3, 4)) # 4->255? + assert_equal(unpack("RGBX", "BGRX", 4), (3, 2, 1, 255)) + assert_equal(unpack("RGBX", "XRGB", 4), (2, 3, 4, 255)) + assert_equal(unpack("RGBX", "XBGR", 4), (4, 3, 2, 255)) + + assert_equal(unpack("CMYK", "CMYK", 4), (1, 2, 3, 4)) + assert_equal(unpack("CMYK", "CMYK;I", 4), (254, 253, 252, 251)) + + assert_exception(ValueError, lambda: unpack("L", "L", 0)) + assert_exception(ValueError, lambda: unpack("RGB", "RGB", 2)) + assert_exception(ValueError, lambda: unpack("CMYK", "CMYK", 2)) + +run() diff --git a/Tests/test_mode_i16.py b/Tests/test_mode_i16.py new file mode 100644 index 000000000..91042e6f1 --- /dev/null +++ b/Tests/test_mode_i16.py @@ -0,0 +1,103 @@ +from tester import * + +from PIL import Image + +def verify(im1): + im2 = lena("I") + assert_equal(im1.size, im2.size) + pix1 = im1.load() + pix2 = im2.load() + for y in range(im1.size[1]): + for x in range(im1.size[0]): + xy = x, y + if pix1[xy] != pix2[xy]: + failure( + "got %r from mode %s at %s, expected %r" % + (pix1[xy], im1.mode, xy, pix2[xy]) + ) + return + success() + +def test_basic(): + # PIL 1.1 has limited support for 16-bit image data. Check that + # create/copy/transform and save works as expected. + + def basic(mode): + + imIn = lena("I").convert(mode) + verify(imIn) + + w, h = imIn.size + + imOut = imIn.copy() + verify(imOut) # copy + + imOut = imIn.transform((w, h), Image.EXTENT, (0, 0, w, h)) + verify(imOut) # transform + + filename = tempfile("temp.im") + imIn.save(filename) + + imOut = Image.open(filename) + + verify(imIn) + verify(imOut) + + imOut = imIn.crop((0, 0, w, h)) + verify(imOut) + + imOut = Image.new(mode, (w, h), None) + imOut.paste(imIn.crop((0, 0, w//2, h)), (0, 0)) + imOut.paste(imIn.crop((w//2, 0, w, h)), (w//2, 0)) + + verify(imIn) + verify(imOut) + + imIn = Image.new(mode, (1, 1), 1) + assert_equal(imIn.getpixel((0, 0)), 1) + + imIn.putpixel((0, 0), 2) + assert_equal(imIn.getpixel((0, 0)), 2) + + if mode == "L": + max = 255 + else: + max = 32767 + + imIn = Image.new(mode, (1, 1), 256) + assert_equal(imIn.getpixel((0, 0)), min(256, max)) + + imIn.putpixel((0, 0), 512) + assert_equal(imIn.getpixel((0, 0)), min(512, max)) + + basic("L") + + basic("I;16") + basic("I;16B") + basic("I;16L") + + basic("I") + + +def test_tostring(): + + def tostring(mode): + return Image.new(mode, (1, 1), 1).tobytes() + + assert_equal(tostring("L"), b"\x01") + assert_equal(tostring("I;16"), b"\x01\x00") + assert_equal(tostring("I;16B"), b"\x00\x01") + assert_equal(tostring("I"), b"\x01\x00\x00\x00") + + +def test_convert(): + + im = lena("I") + + verify(im.convert("I;16")) + verify(im.convert("I;16").convert("L")) + verify(im.convert("I;16").convert("I")) + + verify(im.convert("I;16B")) + verify(im.convert("I;16B").convert("L")) + verify(im.convert("I;16B").convert("I")) diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py new file mode 100644 index 000000000..ea154afb8 --- /dev/null +++ b/Tests/test_numpy.py @@ -0,0 +1,54 @@ +from tester import * + +from PIL import Image + +try: + import site + import numpy +except ImportError: + skip() + +def test_numpy_to_image(): + + def to_image(dtype, bands=1, bool=0): + if bands == 1: + if bool: + data = [0, 1] * 50 + else: + data = list(range(100)) + a = numpy.array(data, dtype=dtype) + a.shape = 10, 10 + i = Image.fromarray(a) + if list(i.getdata()) != data: + print("data mismatch for", dtype) + else: + data = list(range(100)) + a = numpy.array([[x]*bands for x in data], dtype=dtype) + a.shape = 10, 10, bands + i = Image.fromarray(a) + if list(i.split()[0].getdata()) != list(range(100)): + print("data mismatch for", dtype) + # print dtype, list(i.getdata()) + return i + + # assert_image(to_image(numpy.bool, bool=1), "1", (10, 10)) + # assert_image(to_image(numpy.bool8, bool=1), "1", (10, 10)) + + assert_exception(TypeError, lambda: to_image(numpy.uint)) + assert_image(to_image(numpy.uint8), "L", (10, 10)) + assert_exception(TypeError, lambda: to_image(numpy.uint16)) + assert_exception(TypeError, lambda: to_image(numpy.uint32)) + assert_exception(TypeError, lambda: to_image(numpy.uint64)) + + assert_image(to_image(numpy.int), "I", (10, 10)) + assert_image(to_image(numpy.int8), "I", (10, 10)) + assert_image(to_image(numpy.int16), "I;16", (10, 10)) + assert_image(to_image(numpy.int32), "I", (10, 10)) + assert_exception(TypeError, lambda: to_image(numpy.int64)) + + assert_image(to_image(numpy.float), "F", (10, 10)) + assert_image(to_image(numpy.float32), "F", (10, 10)) + assert_image(to_image(numpy.float64), "F", (10, 10)) + + assert_image(to_image(numpy.uint8, 3), "RGB", (10, 10)) + assert_image(to_image(numpy.uint8, 4), "RGBA", (10, 10)) diff --git a/Tests/tester.py b/Tests/tester.py new file mode 100644 index 000000000..3e9d9e689 --- /dev/null +++ b/Tests/tester.py @@ -0,0 +1,229 @@ +# some test helpers + +_target = None +_tempfiles = [] +_logfile = None + +def success(): + import sys + success.count += 1 + if _logfile: + print(sys.argv[0], success.count, failure.count, file=_logfile) + +def failure(msg=None, frame=None): + import sys, linecache + failure.count += 1 + if _target: + if frame is None: + frame = sys._getframe() + while frame.f_globals.get("__name__") != _target.__name__: + frame = frame.f_back + location = (frame.f_code.co_filename, frame.f_lineno) + prefix = "%s:%d: " % location + line = linecache.getline(*location) + print(prefix + line.strip() + " failed:") + if msg: + print("- " + msg) + if _logfile: + print(sys.argv[0], success.count, failure.count, file=_logfile) + +success.count = failure.count = 0 + +# predicates + +def assert_true(v, msg=None): + if v: + success() + else: + failure(msg or "got %r, expected true value" % v) + +def assert_false(v, msg=None): + if v: + failure(msg or "got %r, expected false value" % v) + else: + success() + +def assert_equal(a, b, msg=None): + if a == b: + success() + else: + failure(msg or "got %r, expected %r" % (a, b)) + +def assert_match(v, pattern, msg=None): + import re + if re.match(pattern, v): + success() + else: + failure(msg or "got %r, doesn't match pattern %r" % (v, pattern)) + +def assert_exception(exc_class, func): + import sys, traceback + try: + func() + except exc_class: + success() + except: + failure("expected %r exception, got %r" % ( + exc_class.__name__, sys.exc_info()[0].__name__)) + traceback.print_exc() + else: + failure("expected %r exception, got no exception" % exc_class.__name__) + +def assert_no_exception(func): + import sys, traceback + try: + func() + except: + failure("expected no exception, got %r" % sys.exc_info()[0].__name__) + traceback.print_exc() + else: + success() + +def assert_warning(warn_class, func): + # note: this assert calls func three times! + import warnings + def warn_error(message, category, **options): + raise category(message) + def warn_ignore(message, category, **options): + pass + warn = warnings.warn + result = None + try: + warnings.warn = warn_ignore + assert_no_exception(func) + result = func() + warnings.warn = warn_error + assert_exception(warn_class, func) + finally: + warnings.warn = warn # restore + return result + +# helpers + +from io import BytesIO + +def fromstring(data): + from PIL import Image + return Image.open(BytesIO(data)) + +def tostring(im, format, **options): + out = BytesIO() + im.save(out, format, **options) + return out.getvalue() + +def lena(mode="RGB", cache={}): + from PIL import Image + im = cache.get(mode) + if im is None: + if mode == "RGB": + im = Image.open("Images/lena.ppm") + elif mode == "F": + im = lena("L").convert(mode) + elif mode[:4] == "I;16": + im = lena("I").convert(mode) + else: + im = lena("RGB").convert(mode) + cache[mode] = im + return im + +def assert_image(im, mode, size, msg=None): + if mode is not None and im.mode != mode: + failure(msg or "got mode %r, expected %r" % (im.mode, mode)) + elif size is not None and im.size != size: + failure(msg or "got size %r, expected %r" % (im.size, size)) + else: + success() + +def assert_image_equal(a, b, msg=None): + if a.mode != b.mode: + failure(msg or "got mode %r, expected %r" % (a.mode, b.mode)) + elif a.size != b.size: + failure(msg or "got size %r, expected %r" % (a.size, b.size)) + elif a.tobytes() != b.tobytes(): + failure(msg or "got different content") + # generate better diff? + else: + success() + +def tempfile(template, *extra): + import os, sys + files = [] + for temp in (template,) + extra: + assert temp[:5] in ("temp.", "temp_") + root, name = os.path.split(sys.argv[0]) + name = temp[:4] + os.path.splitext(name)[0][4:] + name = name + "_%d" % len(_tempfiles) + temp[4:] + name = os.path.join(root, name) + files.append(name) + _tempfiles.extend(files) + return files[0] + +# test runner + +def run(): + global _target, _tests, run + import sys, traceback + _target = sys.modules["__main__"] + run = None # no need to run twice + tests = [] + for name, value in list(vars(_target).items()): + if name[:5] == "test_" and type(value) is type(success): + tests.append((value.__code__.co_firstlineno, name, value)) + tests.sort() # sort by line + for lineno, name, func in tests: + try: + _tests = [] + func() + for func, args in _tests: + func(*args) + except: + t, v, tb = sys.exc_info() + tb = tb.tb_next + if tb: + failure(frame=tb.tb_frame) + traceback.print_exception(t, v, tb) + else: + print("%s:%d: cannot call test function: %s" % ( + sys.argv[0], lineno, v)) + failure.count += 1 + +def yield_test(function, *args): + # collect delayed/generated tests + _tests.append((function, args)) + +def skip(msg=None): + import os + print("skip") + os._exit(0) # don't run exit handlers + +def _setup(): + global _logfile + def report(): + if run: + run() + if success.count and not failure.count: + print("ok") + # only clean out tempfiles if test passed + import os + for file in _tempfiles: + try: + os.remove(file) + except OSError: + pass # report? + if "--coverage" in sys.argv: + import coverage + coverage.stop() + # The coverage module messes up when used from inside an + # atexit handler. Do an explicit save to make sure that + # we actually flush the coverage cache. + coverage.the_coverage.save() + import atexit, sys + atexit.register(report) + if "--coverage" in sys.argv: + import coverage + coverage.start() + if "--log" in sys.argv: + _logfile = open("test.log", "a") + + +_setup() diff --git a/Tests/threaded_save.py b/Tests/threaded_save.py new file mode 100644 index 000000000..8162e713c --- /dev/null +++ b/Tests/threaded_save.py @@ -0,0 +1,56 @@ +from PIL import Image + +import sys, time +import io +import threading, queue + +try: + format = sys.argv[1] +except: + format = "PNG" + +im = Image.open("Images/lena.ppm") +im.load() + +queue = queue.Queue() + +result = [] + +class Worker(threading.Thread): + def run(self): + while 1: + im = queue.get() + if im is None: + queue.task_done() + sys.stdout.write("x") + break + f = io.BytesIO() + im.save(f, format, optimize=1) + data = f.getvalue() + result.append(len(data)) + im = Image.open(io.BytesIO(data)) + im.load() + sys.stdout.write(".") + queue.task_done() + +t0 = time.time() + +threads = 20 +jobs = 100 + +for i in range(threads): + w = Worker() + w.start() + +for i in range(jobs): + queue.put(im) + +for i in range(threads): + queue.put(None) + +queue.join() + +print() +print(time.time() - t0) +print(len(result), sum(result)) +print(result) diff --git a/Tests/versions.py b/Tests/versions.py new file mode 100644 index 000000000..a4e4a0bc2 --- /dev/null +++ b/Tests/versions.py @@ -0,0 +1,23 @@ +from PIL import Image + +def version(module, version): + v = getattr(module.core, version + "_version", None) + if v: + print(version, v) + +version(Image, "jpeglib") +version(Image, "zlib") + +try: + from PIL import ImageFont +except ImportError: + pass +else: + version(ImageFont, "freetype2") + +try: + from PIL import ImageCms +except ImportError: + pass +else: + version(ImageCms, "littlecms") From 197885164b22f82653af514e66c76f4b778c0b1b Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Mon, 15 Oct 2012 00:55:39 -0500 Subject: [PATCH 12/61] py3k: Backport Gohlke's tests to run on 2.6/2.7 Most of the differences are in tobytes/tostring naming and expected behavior of the bytes() constructor. The latter was usually easy to fix with the right bytes literal. This is a good preview of what will have to happen in the Python 3 code. --- PIL/ImageFile.py | 3 ++- Tests/run.py | 2 ++ Tests/test_000_sanity.py | 5 ++++- Tests/test_contents.py | 31 ------------------------------- Tests/test_file_jpeg.py | 2 +- Tests/test_file_png.py | 10 ++++++++-- Tests/test_image_fromstring.py | 7 ++++++- Tests/test_image_getim.py | 5 +++-- Tests/test_image_tostring.py | 3 +-- Tests/test_lib_pack.py | 25 +++++++++++++++++++++---- Tests/test_mode_i16.py | 5 ++++- Tests/tester.py | 10 +++++++++- 12 files changed, 61 insertions(+), 47 deletions(-) delete mode 100644 Tests/test_contents.py diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index 7800d2a4b..ef13e5b00 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -29,6 +29,7 @@ import Image import traceback, os +import io MAXBLOCK = 65536 @@ -475,7 +476,7 @@ def _save(im, fp, tile): try: fh = fp.fileno() fp.flush() - except AttributeError: + except (AttributeError, io.UnsupportedOperation): # compress to Python file-compatible object for e, b, o, a in tile: e = Image._getencoder(im.mode, e, a, im.encoderconfig) diff --git a/Tests/run.py b/Tests/run.py index 27a7afa9f..1ce31448c 100644 --- a/Tests/run.py +++ b/Tests/run.py @@ -1,3 +1,5 @@ +from __future__ import print_function + # minimal test runner import glob, os, sys diff --git a/Tests/test_000_sanity.py b/Tests/test_000_sanity.py index cba99e4fe..f9c34527c 100644 --- a/Tests/test_000_sanity.py +++ b/Tests/test_000_sanity.py @@ -1,3 +1,6 @@ +from __future__ import print_function +from tester import * + import PIL import PIL.Image @@ -9,7 +12,7 @@ assert PIL.Image.VERSION[:3] == '1.1' # Create an image and do stuff with it. im = PIL.Image.new("1", (100, 100)) assert (im.mode, im.size) == ('1', (100, 100)) -assert len(im.tobytes()) == 1300 +assert len(im.tobytes() if py3 else im.tostring()) == 1300 # Create images in all remaining major modes. im = PIL.Image.new("L", (100, 100)) diff --git a/Tests/test_contents.py b/Tests/test_contents.py deleted file mode 100644 index 0455ad89c..000000000 --- a/Tests/test_contents.py +++ /dev/null @@ -1,31 +0,0 @@ -from tester import * - -import os, glob - -contents = {} -for file in open("CONTENTS"): - file = file.strip() - if file: - contents[os.path.normpath(file)] = None - -patterns = [ - "PIL/*.py", - "*.c", - "libImaging/*.c", - "libImaging/*.h", - ] - -def test_files(): - - for pattern in patterns: - for file in glob.glob(pattern): - file = os.path.normpath("Imaging/" + file) - assert_true(file in contents, "%r not in CONTENTS" % file) - -def test_contents(): - - for file in contents: - root, file = file.split(os.sep, 1) - if file == "MANIFEST": - continue # generated by distribution scripts - assert_true(os.path.isfile(file), "%r not found" % file) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 126c67237..5246930c8 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -166,7 +166,7 @@ def test_truncated_jpeg(): def test(junk): if junk: # replace "junk" bytes at the end with junk - file = BytesIO(data[:-junk] + bytes(junk*[0])) + file = BytesIO(data[:-junk] + b'\0'*junk) else: file = BytesIO(data) im = Image.open(file) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 329598c9d..6fb67fc02 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -24,7 +24,7 @@ def chunk(cid, *data): o32 = PngImagePlugin.o32 -IHDR = chunk(b"IHDR", o32(1), o32(1), bytes((8,2)), bytes((0,0,0))) +IHDR = chunk(b"IHDR", o32(1), o32(1), b'\x08\x02', b'\0\0\0') IDAT = chunk(b"IDAT") IEND = chunk(b"IEND") @@ -154,7 +154,13 @@ def test_scary(): import base64 file = "Tests/images/pngtest_bad.png.base64" - data = base64.decodebytes(open(file, 'rb').read()) + data = None + + if py3: + data = base64.decodebytes(open(file, 'rb').read()) + else: + data = base64.decodestring(open(file, 'rb').read()) + file = BytesIO(data) assert_exception(IOError, lambda: Image.open(file)) diff --git a/Tests/test_image_fromstring.py b/Tests/test_image_fromstring.py index f529993cd..ddc53ebcc 100644 --- a/Tests/test_image_fromstring.py +++ b/Tests/test_image_fromstring.py @@ -5,7 +5,12 @@ from PIL import Image def test_sanity(): im1 = lena() - im2 = Image.frombytes(im1.mode, im1.size, im1.tobytes()) + im2 = None + + if py3: + im2 = Image.frombytes(im1.mode, im1.size, im1.tobytes()) + else: + im2 = Image.fromstring(im1.mode, im1.size, im1.tostring()) assert_image_equal(im1, im2) diff --git a/Tests/test_image_getim.py b/Tests/test_image_getim.py index 14ca427d0..8d2f12fc2 100644 --- a/Tests/test_image_getim.py +++ b/Tests/test_image_getim.py @@ -5,9 +5,10 @@ from PIL import Image def test_sanity(): im = lena() - type_repr = repr(type(im.getim())) - assert_true("PyCapsule" in type_repr) + + if py3: + assert_true("PyCapsule" in type_repr) assert_true(isinstance(im.im.id, int)) diff --git a/Tests/test_image_tostring.py b/Tests/test_image_tostring.py index b992b63c6..c4d89df93 100644 --- a/Tests/test_image_tostring.py +++ b/Tests/test_image_tostring.py @@ -3,6 +3,5 @@ from tester import * from PIL import Image def test_sanity(): - - data = lena().tobytes() + data = lena().tobytes() if py3 else lena().tostring() assert_true(isinstance(data, bytes)) diff --git a/Tests/test_lib_pack.py b/Tests/test_lib_pack.py index cd26a54ea..c295ac652 100644 --- a/Tests/test_lib_pack.py +++ b/Tests/test_lib_pack.py @@ -12,7 +12,11 @@ def test_pack(): im = Image.new(mode, (1, 1), 1) else: im = Image.new(mode, (1, 1), (1, 2, 3, 4)[:len(mode)]) - return list(im.tobytes("raw", rawmode)) + + if py3: + return list(im.tobytes("raw", rawmode)) + else: + return [ord(c) for c in im.tostring("raw", rawmode)] assert_equal(pack("1", "1"), [128]) assert_equal(pack("1", "1;I"), [0]) @@ -45,13 +49,26 @@ def test_pack(): def test_unpack(): def unpack(mode, rawmode, bytes_): - data = bytes(range(1,bytes_+1)) - im = Image.frombytes(mode, (1, 1), data, "raw", rawmode, 0, 1) + im = None + + if py3: + data = bytes(range(1,bytes_+1)) + im = Image.frombytes(mode, (1, 1), data, "raw", rawmode, 0, 1) + else: + data = ''.join(chr(i) for i in range(1,bytes_+1)) + im = Image.fromstring(mode, (1, 1), data, "raw", rawmode, 0, 1) + return im.getpixel((0, 0)) def unpack_1(mode, rawmode, value): assert mode == "1" - im = Image.frombytes(mode, (8, 1), bytes([value]), "raw", rawmode, 0, 1) + im = None + + if py3: + im = Image.frombytes(mode, (8, 1), bytes([value]), "raw", rawmode, 0, 1) + else: + im = Image.fromstring(mode, (8, 1), chr(value), "raw", rawmode, 0, 1) + return tuple(im.getdata()) X = 255 diff --git a/Tests/test_mode_i16.py b/Tests/test_mode_i16.py index 91042e6f1..060a68bca 100644 --- a/Tests/test_mode_i16.py +++ b/Tests/test_mode_i16.py @@ -82,7 +82,10 @@ def test_basic(): def test_tostring(): def tostring(mode): - return Image.new(mode, (1, 1), 1).tobytes() + if py3: + return Image.new(mode, (1, 1), 1).tobytes() + else: + return Image.new(mode, (1, 1), 1).tostring() assert_equal(tostring("L"), b"\x01") assert_equal(tostring("I;16"), b"\x01\x00") diff --git a/Tests/tester.py b/Tests/tester.py index 3e9d9e689..14e4a2d2d 100644 --- a/Tests/tester.py +++ b/Tests/tester.py @@ -1,3 +1,8 @@ +from __future__ import print_function + +import sys +py3 = (sys.version_info >= (3,0)) + # some test helpers _target = None @@ -139,9 +144,12 @@ def assert_image_equal(a, b, msg=None): failure(msg or "got mode %r, expected %r" % (a.mode, b.mode)) elif a.size != b.size: failure(msg or "got size %r, expected %r" % (a.size, b.size)) - elif a.tobytes() != b.tobytes(): + elif py3 and a.tobytes() != b.tobytes(): failure(msg or "got different content") # generate better diff? + elif not py3 and a.tostring() != b.tostring(): + failure(msg or "got different content") + # same complaint? else: success() From c8ce29c23903b5145ed0e1a23804e7e99faa5eb8 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Mon, 15 Oct 2012 15:03:09 -0500 Subject: [PATCH 13/61] FIX: Handle long values in _imaging getink This gets the putdata test case to run correctly under 2.6/2.7. It fixes an issue where the value 0xFFFFFFFF (which is long in old Python) isn't recognized and putdata tries to parse it as a tuple. The original fix comes from Christoph Gohlke. It was adapted to work in both 2.* and 3.*. --- _imaging.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/_imaging.c b/_imaging.c index 6276bc678..59b045abf 100644 --- a/_imaging.c +++ b/_imaging.c @@ -510,8 +510,17 @@ getink(PyObject* color, Imaging im, char* ink) ink[1] = ink[2] = ink[3] = 0; } else { a = 255; - if (PyInt_Check(color)) { - r = PyInt_AS_LONG(color); +#if PY_VERSION_HEX >= 0x03000000 + if (PyLong_Check(color)) { + r = (int) PyLong_AsLong(color); +#else + if (PyInt_Check(color) || PyLong_Check(color)) { + if (PyInt_Check(color)) + r = PyInt_AS_LONG(color); + else + r = (int) PyLong_AsLong(color); +#endif + /* compatibility: ABGR */ a = (UINT8) (r >> 24); b = (UINT8) (r >> 16); @@ -1205,8 +1214,9 @@ _putdata(ImagingObject* self, PyObject* args) PyObject* data; double scale = 1.0; double offset = 0.0; + if (!PyArg_ParseTuple(args, "O|dd", &data, &scale, &offset)) - return NULL; + return NULL; if (!PySequence_Check(data)) { PyErr_SetString(PyExc_TypeError, must_be_sequence); From 718dbcc8cad0fbdb97ea42e00e7e65971e04f4ac Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Mon, 15 Oct 2012 15:29:43 -0500 Subject: [PATCH 14/61] py3k: Provide the missing pngtest_bad.png.base64 This is from http://scary.beasts.org/security/CESA-2004-001.txt. This was missing from Gohlke's tests. With this file, I see 79 tests and no failures on my setup from 2.6/2.7. --- Tests/images/pngtest_bad.png.base64 | 151 ++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 Tests/images/pngtest_bad.png.base64 diff --git a/Tests/images/pngtest_bad.png.base64 b/Tests/images/pngtest_bad.png.base64 new file mode 100644 index 000000000..44e85d644 --- /dev/null +++ b/Tests/images/pngtest_bad.png.base64 @@ -0,0 +1,151 @@ +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 +Hqz9H24IVyP3c8UAAAAAAElFTkSuQmCC From b386ed14dd1ed800762d0c11e10d991061c7fbab Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Mon, 15 Oct 2012 15:47:53 -0500 Subject: [PATCH 15/61] py3k: Remove callable() function callable(c) is isinstance(x, collections.Callable) in py3k. --- PIL/Image.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PIL/Image.py b/PIL/Image.py index 7f2ea381d..6d62f5918 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -71,6 +71,7 @@ import os, sys # type stuff from types import IntType, StringType, TupleType +import collections try: UnicodeStringType = type(unicode("")) @@ -802,7 +803,7 @@ class Image: self.load() - if callable(filter): + if isinstance(filter, collections.Callable): filter = filter() if not hasattr(filter, "filter"): raise TypeError("filter argument should be ImageFilter.Filter instance or class") From 09f1081c957d195e3c55e81e03cef15b5a1d1f6c Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Mon, 15 Oct 2012 16:18:27 -0500 Subject: [PATCH 16/61] py3k: Fix up uses of dictionary views, ranges, and has_key() y.has_key(x) is gone (use x in y), and keys(), values(), items(), and range() all return views. Some iterables needed to be packed into lists, either because the code expected a list (such as "range(256) * 3") or because the original collection was being modified (automatic global declarations). The Tiff ImageFileDictionary is a special case and will be dealt with in another commit. --- PIL/BmpImagePlugin.py | 2 +- PIL/FpxImagePlugin.py | 2 +- PIL/IcnsImagePlugin.py | 2 +- PIL/ImImagePlugin.py | 8 ++++---- PIL/Image.py | 6 +++--- PIL/ImageMath.py | 4 ++-- PIL/ImagePalette.py | 6 +++--- PIL/ImageStat.py | 2 +- PIL/ImageTk.py | 8 ++++---- PIL/IptcImagePlugin.py | 4 ++-- PIL/JpegImagePlugin.py | 6 +++--- PIL/OleFileIO.py | 4 ++-- PIL/PSDraw.py | 2 +- PIL/PalmImagePlugin.py | 10 +++++----- PIL/PngImagePlugin.py | 10 +++++----- PIL/PsdImagePlugin.py | 2 +- PIL/TiffImagePlugin.py | 42 +++++++++++++++++++++--------------------- Sane/sane.py | 6 +++--- Scripts/pilconvert.py | 2 +- 19 files changed, 64 insertions(+), 64 deletions(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index c2c3f6d49..eb9a0bf60 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -131,7 +131,7 @@ class BmpImageFile(ImageFile.ImageFile): if colors == 2: indices = (0, 255) else: - indices = range(colors) + indices = list(range(colors)) for i in indices: rgb = read(lutsize)[:3] if rgb != chr(i)*3: diff --git a/PIL/FpxImagePlugin.py b/PIL/FpxImagePlugin.py index 3fd6e8bfa..e44c667ba 100644 --- a/PIL/FpxImagePlugin.py +++ b/PIL/FpxImagePlugin.py @@ -108,7 +108,7 @@ class FpxImageFile(ImageFile.ImageFile): self.jpeg = {} for i in range(256): id = 0x3000001|(i << 16) - if prop.has_key(id): + if id in prop: self.jpeg[i] = prop[id] # print len(self.jpeg), "tables loaded" diff --git a/PIL/IcnsImagePlugin.py b/PIL/IcnsImagePlugin.py index 9f427019b..883ac67dd 100644 --- a/PIL/IcnsImagePlugin.py +++ b/PIL/IcnsImagePlugin.py @@ -125,7 +125,7 @@ class IcnsFile: sizes = [] for size, fmts in self.SIZES.items(): for (fmt, reader) in fmts: - if self.dct.has_key(fmt): + if fmt in self.dct: sizes.append(size) break return sizes diff --git a/PIL/ImImagePlugin.py b/PIL/ImImagePlugin.py index a7272cc1a..d7f38cb68 100644 --- a/PIL/ImImagePlugin.py +++ b/PIL/ImImagePlugin.py @@ -162,20 +162,20 @@ class ImImageFile(ImageFile.ImageFile): v = tuple(map(number, v.split(","))) if len(v) == 1: v = v[0] - elif k == MODE and OPEN.has_key(v): + elif k == MODE and v in OPEN: v, self.rawmode = OPEN[v] # Add to dictionary. Note that COMMENT tags are # combined into a list of strings. if k == COMMENT: - if self.info.has_key(k): + if k in self.info: self.info[k].append(v) else: self.info[k] = [v] else: self.info[k] = v - if TAGS.has_key(k): + if k in TAGS: n = n + 1 else: @@ -195,7 +195,7 @@ class ImImageFile(ImageFile.ImageFile): if not s: raise SyntaxError("File truncated") - if self.info.has_key(LUT): + if LUT in self.info: # convert lookup table to palette or lut attribute palette = self.fp.read(768) greyscale = 1 # greyscale palette diff --git a/PIL/Image.py b/PIL/Image.py index 6d62f5918..9d12954bd 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -224,7 +224,7 @@ def _conv_type_shape(im): return shape+(extra,), typ -MODES = _MODEINFO.keys() +MODES = list(_MODEINFO.keys()) MODES.sort() # raw modes that may be memory mapped. NOTE: if you change this, you @@ -343,7 +343,7 @@ def init(): # only check directories (including current, if present in the path) for directory in filter(isDirectory, directories): fullpath = os.path.abspath(directory) - if visited.has_key(fullpath): + if fullpath in visited: continue for file in os.listdir(directory): if file[-14:] == "ImagePlugin.py": @@ -610,7 +610,7 @@ class Image: self.palette.dirty = 0 self.palette.mode = "RGB" self.palette.rawmode = None - if self.info.has_key("transparency"): + if "transparency" in self.info: self.im.putpalettealpha(self.info["transparency"], 0) self.palette.mode = "RGBA" if self.im: diff --git a/PIL/ImageMath.py b/PIL/ImageMath.py index 4dada8258..a9a129b19 100644 --- a/PIL/ImageMath.py +++ b/PIL/ImageMath.py @@ -175,7 +175,7 @@ def imagemath_convert(self, mode): return _Operand(self.im.convert(mode)) ops = {} -for k, v in globals().items(): +for k, v in list(globals().items()): if k[:10] == "imagemath_": ops[k[10:]] = v @@ -195,7 +195,7 @@ def eval(expression, _dict={}, **kw): args = ops.copy() args.update(_dict) args.update(kw) - for k, v in args.items(): + for k, v in list(args.items()): if hasattr(v, "im"): args[k] = _Operand(v) diff --git a/PIL/ImagePalette.py b/PIL/ImagePalette.py index a13d23e2c..0bc717316 100644 --- a/PIL/ImagePalette.py +++ b/PIL/ImagePalette.py @@ -28,7 +28,7 @@ class ImagePalette: def __init__(self, mode = "RGB", palette = None): self.mode = mode self.rawmode = None # if set, palette contains raw data - self.palette = palette or range(256)*len(self.mode) + self.palette = palette or list(range(256))*len(self.mode) self.colors = {} self.dirty = None if len(self.mode)*256 != len(self.palette): @@ -119,7 +119,7 @@ def new(mode, data): return Image.core.new_palette(mode, data) def negative(mode="RGB"): - palette = range(256) + palette = list(range(256)) palette.reverse() return ImagePalette(mode, palette * len(mode)) @@ -138,7 +138,7 @@ def sepia(white="#fff0c0"): return ImagePalette("RGB", r + g + b) def wedge(mode="RGB"): - return ImagePalette(mode, range(256) * len(mode)) + return ImagePalette(mode, list(range(256)) * len(mode)) def load(filename): diff --git a/PIL/ImageStat.py b/PIL/ImageStat.py index 0cfcc8d81..7ab258c1a 100644 --- a/PIL/ImageStat.py +++ b/PIL/ImageStat.py @@ -54,7 +54,7 @@ class Stat: self.h = image_or_list # assume it to be a histogram list if type(self.h) != type([]): raise TypeError("first argument must be image or list") - self.bands = range(len(self.h) / 256) + self.bands = list(range(len(self.h) // 256)) def __getattr__(self, id): "Calculate missing attribute" diff --git a/PIL/ImageTk.py b/PIL/ImageTk.py index b3adf1920..6b4311a6b 100644 --- a/PIL/ImageTk.py +++ b/PIL/ImageTk.py @@ -81,10 +81,10 @@ class PhotoImage: # Tk compatibility: file or data if image is None: - if kw.has_key("file"): + if "file" in kw: image = Image.open(kw["file"]) del kw["file"] - elif kw.has_key("data"): + elif "data" in kw: from StringIO import StringIO image = Image.open(StringIO(kw["data"])) del kw["data"] @@ -213,10 +213,10 @@ class BitmapImage: # Tk compatibility: file or data if image is None: - if kw.has_key("file"): + if "file" in kw: image = Image.open(kw["file"]) del kw["file"] - elif kw.has_key("data"): + elif "data" in kw: from StringIO import StringIO image = Image.open(StringIO(kw["data"])) del kw["data"] diff --git a/PIL/IptcImagePlugin.py b/PIL/IptcImagePlugin.py index fc07d534d..ceb9e5237 100644 --- a/PIL/IptcImagePlugin.py +++ b/PIL/IptcImagePlugin.py @@ -119,7 +119,7 @@ class IptcImageFile(ImageFile.ImageFile): tagdata = self.fp.read(size) else: tagdata = None - if tag in self.info.keys(): + if tag in list(self.info.keys()): if isinstance(self.info[tag], list): self.info[tag].append(tagdata) else: @@ -132,7 +132,7 @@ class IptcImageFile(ImageFile.ImageFile): # mode layers = ord(self.info[(3,60)][0]) component = ord(self.info[(3,60)][1]) - if self.info.has_key((3,65)): + if (3,65) in self.info: id = ord(self.info[(3,65)][0])-1 else: id = 0 diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 3c4d5bd4e..d7c7f151f 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -293,7 +293,7 @@ class JpegImageFile(ImageFile.ImageFile): i = i16(s) - if MARKER.has_key(i): + if i in MARKER: name, description, handler = MARKER[i] # print hex(i), name, description if handler is not None: @@ -458,9 +458,9 @@ def _save(im, fp, filename): # "progressive" is the official name, but older documentation # says "progression" # FIXME: issue a warning if the wrong form is used (post-1.1.7) - info.has_key("progressive") or info.has_key("progression"), + "progressive" in info or "progression" in info, info.get("smooth", 0), - info.has_key("optimize"), + "optimize" in info, info.get("streamtype", 0), dpi[0], dpi[1], subsampling, diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index 979072866..269f602a9 100644 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -65,7 +65,7 @@ VT_VECTOR=0x1000; # map property id to name (for debugging purposes) VT = {} -for k, v in vars().items(): +for k, v in list(vars().items()): if k[:3] == "VT_": VT[v] = k @@ -520,7 +520,7 @@ if __name__ == "__main__": if file[-1][0] == "\005": print file props = ole.getproperties(file) - props = props.items() + props = list(props.items()) props.sort() for k, v in props: print " ", k, v diff --git a/PIL/PSDraw.py b/PIL/PSDraw.py index bc423fc8c..29bfb235c 100644 --- a/PIL/PSDraw.py +++ b/PIL/PSDraw.py @@ -51,7 +51,7 @@ class PSDraw: self.fp.flush() def setfont(self, font, size): - if not self.isofont.has_key(font): + if font not in self.isofont: # reencode font self.fp.write("/PSDraw-%s ISOLatin1Encoding /%s E\n" %\ (font, font)) diff --git a/PIL/PalmImagePlugin.py b/PIL/PalmImagePlugin.py index 59e53038b..7d26ce6d6 100644 --- a/PIL/PalmImagePlugin.py +++ b/PIL/PalmImagePlugin.py @@ -80,7 +80,7 @@ _Palm8BitColormapValues = ( # so build a prototype image to be used for palette resampling def build_prototype_image(): image = Image.new("L", (1,len(_Palm8BitColormapValues),)) - image.putdata(range(len(_Palm8BitColormapValues))) + image.putdata(list(range(len(_Palm8BitColormapValues)))) palettedata = () for i in range(len(_Palm8BitColormapValues)): palettedata = palettedata + _Palm8BitColormapValues[i] @@ -127,7 +127,7 @@ def _save(im, fp, filename, check=0): bpp = 8 version = 1 - elif im.mode == "L" and im.encoderinfo.has_key("bpp") and im.encoderinfo["bpp"] in (1, 2, 4): + elif im.mode == "L" and "bpp" in im.encoderinfo and im.encoderinfo["bpp"] in (1, 2, 4): # this is 8-bit grayscale, so we shift it to get the high-order bits, and invert it because # Palm does greyscale from white (0) to black (1) @@ -138,7 +138,7 @@ def _save(im, fp, filename, check=0): rawmode = "P;" + str(bpp) version = 1 - elif im.mode == "L" and im.info.has_key("bpp") and im.info["bpp"] in (1, 2, 4): + elif im.mode == "L" and "bpp" in im.info and im.info["bpp"] in (1, 2, 4): # here we assume that even though the inherent mode is 8-bit grayscale, only # the lower bpp bits are significant. We invert them to match the Palm. @@ -177,7 +177,7 @@ def _save(im, fp, filename, check=0): compression_type = _COMPRESSION_TYPES["none"] flags = 0; - if im.mode == "P" and im.info.has_key("custom-colormap"): + if im.mode == "P" and "custom-colormap" in im.info: flags = flags & _FLAGS["custom-colormap"] colormapsize = 4 * 256 + 2; colormapmode = im.palette.mode @@ -185,7 +185,7 @@ def _save(im, fp, filename, check=0): else: colormapsize = 0 - if im.info.has_key("offset"): + if "offset" in im.info: offset = (rowbytes * rows + 16 + 3 + colormapsize) / 4; else: offset = 0 diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index e915ed433..c9f21f7dd 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -469,7 +469,7 @@ def _save(im, fp, filename, chunk=putchunk, check=0): # # attempt to minimize storage requirements for palette images - if im.encoderinfo.has_key("bits"): + if "bits" in im.encoderinfo: # number of bits specified by user n = 1 << im.encoderinfo["bits"] @@ -492,12 +492,12 @@ def _save(im, fp, filename, chunk=putchunk, check=0): mode = "%s;%d" % (mode, bits) # encoder options - if im.encoderinfo.has_key("dictionary"): + if "dictionary" in im.encoderinfo: dictionary = im.encoderinfo["dictionary"] else: dictionary = "" - im.encoderconfig = (im.encoderinfo.has_key("optimize"), dictionary) + im.encoderconfig = ("optimize" in im.encoderinfo, dictionary) # get the corresponding PNG mode try: @@ -523,7 +523,7 @@ def _save(im, fp, filename, chunk=putchunk, check=0): if im.mode == "P": chunk(fp, "PLTE", im.im.getpalette("RGB")) - if im.encoderinfo.has_key("transparency"): + if "transparency" in im.encoderinfo: if im.mode == "P": transparency = max(0, min(255, im.encoderinfo["transparency"])) chunk(fp, "tRNS", chr(255) * transparency + chr(0)) @@ -553,7 +553,7 @@ def _save(im, fp, filename, chunk=putchunk, check=0): chunk(fp, cid, data) # ICC profile writing support -- 2008-06-06 Florian Hoech - if im.info.has_key("icc_profile"): + if "icc_profile" in im.info: # ICC profile # according to PNG spec, the iCCP chunk contains: # Profile name 1-79 bytes (character string) diff --git a/PIL/PsdImagePlugin.py b/PIL/PsdImagePlugin.py index 021aacf80..5ef1d808d 100644 --- a/PIL/PsdImagePlugin.py +++ b/PIL/PsdImagePlugin.py @@ -174,7 +174,7 @@ def _layerinfo(file): # image info info = [] mode = [] - types = range(i16(read(2))) + types = list(range(i16(read(2)))) if len(types) > 4: continue diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index eea4151af..335fe2616 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -402,7 +402,7 @@ class ImageFileDirectory: fp.write(o16(len(self.tags))) # always write in ascending tag order - tags = self.tags.items() + tags = list(self.tags.items()) tags.sort() directory = [] @@ -417,7 +417,7 @@ class ImageFileDirectory: typ = None - if self.tagtype.has_key(tag): + if tag in self.tagtype: typ = self.tagtype[tag] if typ == 1: @@ -569,18 +569,18 @@ class TiffImageFile(ImageFile.ImageFile): args = (rawmode, 0, 1) elif compression == "jpeg": args = rawmode, "" - if self.tag.has_key(JPEGTABLES): + if JPEGTABLES in self.tag: # Hack to handle abbreviated JPEG headers self.tile_prefix = self.tag[JPEGTABLES] elif compression == "packbits": args = rawmode elif compression == "tiff_lzw": args = rawmode - if self.tag.has_key(317): + if 317 in self.tag: # Section 14: Differencing Predictor self.decoderconfig = (self.tag[PREDICTOR][0],) - if self.tag.has_key(ICCPROFILE): + if ICCPROFILE in self.tag: self.info['icc_profile'] = self.tag[ICCPROFILE] return args @@ -588,7 +588,7 @@ class TiffImageFile(ImageFile.ImageFile): def _setup(self): "Setup this image object based on current tags" - if self.tag.has_key(0xBC01): + if 0xBC01 in self.tag: raise IOError("Windows Media Photo files not yet supported") getscalar = self.tag.getscalar @@ -658,7 +658,7 @@ class TiffImageFile(ImageFile.ImageFile): # build tile descriptors x = y = l = 0 self.tile = [] - if self.tag.has_key(STRIPOFFSETS): + if STRIPOFFSETS in self.tag: # striped image h = getscalar(ROWSPERSTRIP, ysize) w = self.size[0] @@ -675,7 +675,7 @@ class TiffImageFile(ImageFile.ImageFile): x = y = 0 l = l + 1 a = None - elif self.tag.has_key(TILEOFFSETS): + elif TILEOFFSETS in self.tag: # tiled image w = getscalar(322) h = getscalar(323) @@ -769,28 +769,28 @@ def _save(im, fp, filename): if hasattr(im, 'tag'): # preserve tags from original TIFF image file for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION): - if im.tag.tagdata.has_key(key): + if key in im.tag.tagdata: ifd[key] = im.tag.tagdata.get(key) # preserve some more tags from original TIFF image file # -- 2008-06-06 Florian Hoech ifd.tagtype = im.tag.tagtype for key in (IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP): - if im.tag.has_key(key): + if key in im.tag: ifd[key] = im.tag[key] # preserve ICC profile (should also work when saving other formats # which support profiles as TIFF) -- 2008-06-06 Florian Hoech - if im.info.has_key("icc_profile"): + if "icc_profile" in im.info: ifd[ICCPROFILE] = im.info["icc_profile"] - if im.encoderinfo.has_key("description"): + if "description" in im.encoderinfo: ifd[IMAGEDESCRIPTION] = im.encoderinfo["description"] - if im.encoderinfo.has_key("resolution"): + if "resolution" in im.encoderinfo: ifd[X_RESOLUTION] = ifd[Y_RESOLUTION] \ = _cvt_res(im.encoderinfo["resolution"]) - if im.encoderinfo.has_key("x resolution"): + if "x resolution" in im.encoderinfo: ifd[X_RESOLUTION] = _cvt_res(im.encoderinfo["x resolution"]) - if im.encoderinfo.has_key("y resolution"): + if "y resolution" in im.encoderinfo: ifd[Y_RESOLUTION] = _cvt_res(im.encoderinfo["y resolution"]) - if im.encoderinfo.has_key("resolution unit"): + if "resolution unit" in im.encoderinfo: unit = im.encoderinfo["resolution unit"] if unit == "inch": ifd[RESOLUTION_UNIT] = 2 @@ -798,13 +798,13 @@ def _save(im, fp, filename): ifd[RESOLUTION_UNIT] = 3 else: ifd[RESOLUTION_UNIT] = 1 - if im.encoderinfo.has_key("software"): + if "software" in im.encoderinfo: ifd[SOFTWARE] = im.encoderinfo["software"] - if im.encoderinfo.has_key("date time"): + if "date time" in im.encoderinfo: ifd[DATE_TIME] = im.encoderinfo["date time"] - if im.encoderinfo.has_key("artist"): + if "artist" in im.encoderinfo: ifd[ARTIST] = im.encoderinfo["artist"] - if im.encoderinfo.has_key("copyright"): + if "copyright" in im.encoderinfo: ifd[COPYRIGHT] = im.encoderinfo["copyright"] dpi = im.encoderinfo.get("dpi") @@ -843,7 +843,7 @@ def _save(im, fp, filename): # -- helper for multi-page save -- - if im.encoderinfo.has_key("_debug_multipage"): + if "_debug_multipage" in im.encoderinfo: #just to access o32 and o16 (using correct byte order) im._debug_multipage = ifd diff --git a/Sane/sane.py b/Sane/sane.py index 81c581115..0a70073e5 100644 --- a/Sane/sane.py +++ b/Sane/sane.py @@ -165,7 +165,7 @@ class SaneDev: def __setattr__(self, key, value): dev=self.__dict__['dev'] optdict=self.__dict__['opt'] - if not optdict.has_key(key): + if key not in optdict: self.__dict__[key]=value ; return opt=optdict[key] if opt.type==TYPE_GROUP: @@ -186,10 +186,10 @@ class SaneDev: dev=self.__dict__['dev'] optdict=self.__dict__['opt'] if key=='optlist': - return self.opt.keys() + return list(self.opt.keys()) if key=='area': return (self.tl_x, self.tl_y),(self.br_x, self.br_y) - if not optdict.has_key(key): + if key not in optdict: raise AttributeError('No such attribute: '+key) opt=optdict[key] if opt.type==TYPE_BUTTON: diff --git a/Scripts/pilconvert.py b/Scripts/pilconvert.py index c7c3f66a1..e6adadc71 100644 --- a/Scripts/pilconvert.py +++ b/Scripts/pilconvert.py @@ -58,7 +58,7 @@ for o, a in opt: id.sort() print "Supported formats (* indicates output format):" for i in id: - if Image.SAVE.has_key(i): + if i in Image.SAVE: print i+"*", else: print i, From e514912378f6c2f65f633fde1f7c55c62af53807 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Mon, 15 Oct 2012 16:19:55 -0500 Subject: [PATCH 17/61] py3k: Rewrite dictionary support for Tiff ImageFileDictionary This commit brings in the collections.MutableMapping mixin to provide full dictionary support for ImageFileDictionary. --- PIL/TiffImagePlugin.py | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 335fe2616..cdc17d242 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -45,6 +45,8 @@ import Image, ImageFile import ImagePalette import array, sys +import collections +import itertools II = "II" # little-endian (intel-style) MM = "MM" # big-endian (motorola-style) @@ -204,7 +206,7 @@ def _accept(prefix): ## # Wrapper for TIFF IFDs. -class ImageFileDirectory: +class ImageFileDirectory(collections.MutableMapping): # represents a TIFF tag directory. to speed things up, # we don't decode tags unless they're asked for. @@ -227,16 +229,7 @@ class ImageFileDirectory: self.tagtype = {} # added 2008-06-05 by Florian Hoech self.next = None - # dictionary API (sort of) - - def keys(self): - return self.tagdata.keys() + self.tags.keys() - - def items(self): - items = self.tags.items() - for tag in self.tagdata.keys(): - items.append((tag, self[tag])) - return items + # dictionary API def __len__(self): return len(self.tagdata) + len(self.tags) @@ -251,12 +244,6 @@ class ImageFileDirectory: del self.tagdata[tag] return data - def get(self, tag, default=None): - try: - return self[tag] - except KeyError: - return default - def getscalar(self, tag, default=None): try: value = self[tag] @@ -272,14 +259,24 @@ class ImageFileDirectory: raise return default - def has_key(self, tag): - return self.tags.has_key(tag) or self.tagdata.has_key(tag) + def __contains__(self, tag): + return tag in self.tags or tag in self.tagdata + + if sys.version_info < (3,0): + def has_key(self, tag): + return tag in self def __setitem__(self, tag, value): - if type(value) is not type(()): + if not isinstance(value, tuple): value = (value,) self.tags[tag] = value + def __delitem__(self, tag): + self.tags.pop(tag, self.tagdata.pop(tag, None)) + + def __iter__(self): + return itertools.chain(self.tags.__iter__(), self.tagdata.__iter__()) + # load primitives load_dispatch = {} From fc035814bd76333dfc69e1529c6326ec7d01a5be Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Mon, 15 Oct 2012 20:58:46 -0500 Subject: [PATCH 18/61] py3k: map and filter to list comprehensions What's really going on is that map() and filter() return iterators in py3k. I've just gone ahead and turned them all into list comprehensions, because I find them much easier to read. --- PIL/BdfFontFile.py | 4 ++-- PIL/EpsImagePlugin.py | 2 +- PIL/FliImagePlugin.py | 4 ++-- PIL/GimpGradientFile.py | 2 +- PIL/GimpPaletteFile.py | 2 +- PIL/ImImagePlugin.py | 4 ++-- PIL/Image.py | 6 +++--- PIL/ImageOps.py | 2 +- PIL/ImagePalette.py | 2 +- PIL/OleFileIO.py | 4 ++-- PIL/PaletteFile.py | 4 ++-- PIL/TiffImagePlugin.py | 4 ++-- 12 files changed, 20 insertions(+), 20 deletions(-) diff --git a/PIL/BdfFontFile.py b/PIL/BdfFontFile.py index 8652ed0b6..eb656d3ac 100644 --- a/PIL/BdfFontFile.py +++ b/PIL/BdfFontFile.py @@ -69,8 +69,8 @@ def bdf_char(f): bitmap.append(s[:-1]) bitmap = "".join(bitmap) - [x, y, l, d] = map(int, props["BBX"].split()) - [dx, dy] = map(int, props["DWIDTH"].split()) + [x, y, l, d] = [int(s) for s in props["BBX"].split()] + [dx, dy] = [int(s) for s in props["DWIDTH"].split()] bbox = (dx, dy), (l, -d-y, x+l, -d), (0, 0, x, y) diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py index bef485258..450ba2422 100644 --- a/PIL/EpsImagePlugin.py +++ b/PIL/EpsImagePlugin.py @@ -183,7 +183,7 @@ class EpsImageFile(ImageFile.ImageFile): # Note: The DSC spec says that BoundingBox # fields should be integers, but some drivers # put floating point values there anyway. - box = map(int, map(float, v.split())) + box = [int(float(s)) for s in v.split()] self.size = box[2] - box[0], box[3] - box[1] self.tile = [("eps", (0,0) + self.size, offset, (length, box))] diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py index 9f85a0ae4..8f48e29b8 100644 --- a/PIL/FliImagePlugin.py +++ b/PIL/FliImagePlugin.py @@ -63,7 +63,7 @@ class FliImageFile(ImageFile.ImageFile): self.info["duration"] = duration # look for palette - palette = map(lambda a: (a,a,a), range(256)) + palette = [(a,a,a) for a in range(256)] s = self.fp.read(16) @@ -82,7 +82,7 @@ class FliImageFile(ImageFile.ImageFile): elif i16(s[4:6]) == 4: self._palette(palette, 0) - palette = map(lambda (r,g,b): chr(r)+chr(g)+chr(b), palette) + palette = [chr(r)+chr(g)+chr(b) for (r,g,b) in palette] self.palette = ImagePalette.raw("RGB", "".join(palette)) # set things up to decode first frame diff --git a/PIL/GimpGradientFile.py b/PIL/GimpGradientFile.py index 1e9baa28d..c2db7bc7c 100644 --- a/PIL/GimpGradientFile.py +++ b/PIL/GimpGradientFile.py @@ -105,7 +105,7 @@ class GimpGradientFile(GradientFile): for i in range(count): s = fp.readline().split() - w = map(float, s[:11]) + w = [float(x) for x in s[:11]] x0, x1 = w[0], w[2] xm = w[1] diff --git a/PIL/GimpPaletteFile.py b/PIL/GimpPaletteFile.py index a0887a31a..a904cf6dc 100644 --- a/PIL/GimpPaletteFile.py +++ b/PIL/GimpPaletteFile.py @@ -25,7 +25,7 @@ class GimpPaletteFile: def __init__(self, fp): - self.palette = map(lambda i: chr(i)*3, range(256)) + self.palette = [chr(i)*3 for i in range(256)] if fp.readline()[:12] != "GIMP Palette": raise SyntaxError("not a GIMP palette file") diff --git a/PIL/ImImagePlugin.py b/PIL/ImImagePlugin.py index d7f38cb68..bc4910234 100644 --- a/PIL/ImImagePlugin.py +++ b/PIL/ImImagePlugin.py @@ -209,7 +209,7 @@ class ImImageFile(ImageFile.ImageFile): if self.mode == "L" or self.mode == "LA": if greyscale: if not linear: - self.lut = map(ord, palette[:256]) + self.lut = [ord(c) for c in palette[:256]] else: if self.mode == "L": self.mode = self.rawmode = "P" @@ -218,7 +218,7 @@ class ImImageFile(ImageFile.ImageFile): self.palette = ImagePalette.raw("RGB;L", palette) elif self.mode == "RGB": if not greyscale or not linear: - self.lut = map(ord, palette) + self.lut = [ord(c) for c in palette] self.frame = 0 diff --git a/PIL/Image.py b/PIL/Image.py index 9d12954bd..64027cdec 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -930,7 +930,7 @@ class Image: self.load() try: - return map(ord, self.im.getpalette()) + return [ord(c) for c in self.im.getpalette()] except ValueError: return None # no palette @@ -959,7 +959,7 @@ class Image: self.load() x, y = self.im.getprojection() - return map(ord, x), map(ord, y) + return [ord(c) for c in x], [ord(c) for c in y] ## # Returns a histogram for the image. The histogram is returned as @@ -1129,7 +1129,7 @@ class Image: scale, offset = _getscaleoffset(lut) return self._new(self.im.point_transform(scale, offset)) # for other modes, convert the function to a table - lut = map(lut, range(256)) * self.im.bands + lut = [lut(i) for i in range(256)] * self.im.bands if self.mode == "F": # FIXME: _imaging returns a confusing error message for this case diff --git a/PIL/ImageOps.py b/PIL/ImageOps.py index 9ff0c4527..9825d9ced 100644 --- a/PIL/ImageOps.py +++ b/PIL/ImageOps.py @@ -209,7 +209,7 @@ def equalize(image, mask=None): h = image.histogram(mask) lut = [] for b in range(0, len(h), 256): - histo = filter(None, h[b:b+256]) + histo = [_f for _f in h[b:b+256] if _f] if len(histo) <= 1: lut.extend(range(256)) else: diff --git a/PIL/ImagePalette.py b/PIL/ImagePalette.py index 0bc717316..a57688b53 100644 --- a/PIL/ImagePalette.py +++ b/PIL/ImagePalette.py @@ -59,7 +59,7 @@ class ImagePalette: except KeyError: # allocate new color slot if Image.isStringType(self.palette): - self.palette = map(int, self.palette) + self.palette = [int(x) for x in self.palette] index = len(self.colors) if index >= 256: raise ValueError("cannot allocate more than 256 colors") diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index 269f602a9..f8c25181f 100644 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -307,7 +307,7 @@ class OleFileIO: if ix == -2 or ix == -1: # ix == 0xFFFFFFFEL or ix == 0xFFFFFFFFL: break s = self.getsect(ix) - fat = fat + map(lambda i, s=s: i32(s, i), range(0, len(s), 4)) + fat = fat + [i32(s, i) for i in range(0, len(s), 4)] self.fat = fat def loadminifat(self): @@ -316,7 +316,7 @@ class OleFileIO: s = self._open(self.minifatsect).read() - self.minifat = map(lambda i, s=s: i32(s, i), range(0, len(s), 4)) + self.minifat = [i32(s, i) for i in range(0, len(s), 4)] def getsect(self, sect): # Read given sector diff --git a/PIL/PaletteFile.py b/PIL/PaletteFile.py index bd842c3dc..7df149301 100644 --- a/PIL/PaletteFile.py +++ b/PIL/PaletteFile.py @@ -22,7 +22,7 @@ class PaletteFile: def __init__(self, fp): - self.palette = map(lambda i: (i, i, i), range(256)) + self.palette = [(i, i, i) for i in range(256)] while 1: @@ -35,7 +35,7 @@ class PaletteFile: if len(s) > 100: raise SyntaxError("bad palette file") - v = map(int, s.split()) + v = [int(x) for x in s.split()] try: [i, r, g, b] = v except ValueError: diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index cdc17d242..bd860454b 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -701,7 +701,7 @@ class TiffImageFile(ImageFile.ImageFile): # fixup palette descriptor if self.mode == "P": - palette = map(lambda a: chr(a / 256), self.tag[COLORMAP]) + palette = [chr(a / 256) for a in self.tag[COLORMAP]] self.palette = ImagePalette.raw("RGB;L", "".join(palette)) # # -------------------------------------------------------------------- @@ -823,7 +823,7 @@ def _save(im, fp, filename): if im.mode == "P": lut = im.im.getpalette("RGB", "RGB;L") - ifd[COLORMAP] = tuple(map(lambda v: ord(v) * 256, lut)) + ifd[COLORMAP] = tuple(ord(v) * 256 for v in lut) # data orientation stride = len(bits) * ((im.size[0]*bits[0]+7)/8) From 3a665a783532ec8337d7acac70b0d09e86e2a0e1 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Mon, 15 Oct 2012 20:59:31 -0500 Subject: [PATCH 19/61] py3k: Add true Unicode support to OleFileIO The day has arrived when Python provides an official way to handle Unicode strings. --- PIL/OleFileIO.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index f8c25181f..d093d71a7 100644 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -37,7 +37,7 @@ # import StringIO - +import sys def i16(c, o = 0): return ord(c[o])+(ord(c[o+1])<<8) @@ -327,9 +327,12 @@ class OleFileIO: def _unicode(self, s): # Map unicode string to Latin 1 - # FIXME: some day, Python will provide an official way to handle - # Unicode strings, but until then, this will have to do... - return filter(ord, s) + if sys.version_info >= (3,0): + # Provide actual Unicode string + return s.decode('utf-16') + else: + # Old version tried to produce a Latin-1 str + return s.decode('utf-16').encode('latin-1', 'replace') def loaddirectory(self, sect): # Load the directory. The directory is stored in a standard From eed042fae5c4fac102a39ef1bae30530505b5f7f Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Mon, 15 Oct 2012 21:05:26 -0500 Subject: [PATCH 20/61] py3k: __nonzero__ is now __bool__ --- PIL/ImageMath.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/PIL/ImageMath.py b/PIL/ImageMath.py index a9a129b19..28dd254be 100644 --- a/PIL/ImageMath.py +++ b/PIL/ImageMath.py @@ -17,6 +17,7 @@ import Image import _imagingmath +import sys VERBOSE = 0 @@ -84,9 +85,15 @@ class _Operand: return _Operand(out) # unary operators - def __nonzero__(self): - # an image is "true" if it contains at least one non-zero pixel - return self.im.getbbox() is not None + if sys.version_info >= (3,0): + def __bool__(self): + # an image is "true" if it contains at least one non-zero pixel + return self.im.getbbox() is not None + else: + def __nonzero__(self): + # an image is "true" if it contains at least one non-zero pixel + return self.im.getbbox() is not None + def __abs__(self): return self.apply("abs", self) def __pos__(self): From da1d715b8e437d01b4856828e4cee2a9da8bf461 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Mon, 15 Oct 2012 21:13:49 -0500 Subject: [PATCH 21/61] py3k: Use isinstance for numbers and sequences operator.isNumberType() and .isSequenceType() go away in py3k. --- PIL/Image.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 64027cdec..6dd89a753 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -72,6 +72,7 @@ import os, sys # type stuff from types import IntType, StringType, TupleType import collections +import numbers try: UnicodeStringType = type(unicode("")) @@ -104,8 +105,6 @@ def isImageType(t): def isDirectory(f): return isStringType(f) and os.path.isdir(f) -from operator import isNumberType, isSequenceType - # # Debug level @@ -414,15 +413,15 @@ def _getscaleoffset(expr): data = expr(_E(stub)).data try: (a, b, c) = data # simplified syntax - if (a is stub and b == "__mul__" and isNumberType(c)): + if (a is stub and b == "__mul__" and isinstance(c, numbers.Number)): return c, 0.0 - if (a is stub and b == "__add__" and isNumberType(c)): + if (a is stub and b == "__add__" and isinstance(c, numbers.Number)): return 1.0, c except TypeError: pass try: ((a, b, c), d, e) = data # full syntax - if (a is stub and b == "__mul__" and isNumberType(c) and - d == "__add__" and isNumberType(e)): + if (a is stub and b == "__mul__" and isinstance(c, numbers.Number) and + d == "__add__" and isinstance(e, numbers.Number)): return c, e except TypeError: pass raise ValueError("illegal expression") @@ -1122,7 +1121,7 @@ class Image: if isinstance(lut, ImagePointHandler): return lut.point(self) - if not isSequenceType(lut): + if not isinstance(lut, collections.Sequence): # if it isn't a list, it should be a function if self.mode in ("I", "I;16", "F"): # check if the function can be used with point_transform From aeab3f59113fac9681977c2b44851a655d183bf3 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Mon, 15 Oct 2012 21:15:25 -0500 Subject: [PATCH 22/61] py3k: Import reduce function reduce() is no longer a built-in function in py3k. --- PIL/ImageFilter.py | 2 ++ PIL/ImageOps.py | 1 + PIL/ImageStat.py | 1 + 3 files changed, 4 insertions(+) diff --git a/PIL/ImageFilter.py b/PIL/ImageFilter.py index 6d328cc1a..cfdad5fc5 100644 --- a/PIL/ImageFilter.py +++ b/PIL/ImageFilter.py @@ -15,6 +15,8 @@ # See the README file for information on usage and redistribution. # +from functools import reduce + class Filter: pass diff --git a/PIL/ImageOps.py b/PIL/ImageOps.py index 9825d9ced..de32726a1 100644 --- a/PIL/ImageOps.py +++ b/PIL/ImageOps.py @@ -19,6 +19,7 @@ import Image import operator +from functools import reduce ## # (New in 1.1.3) The ImageOps module contains a number of diff --git a/PIL/ImageStat.py b/PIL/ImageStat.py index 7ab258c1a..c87bd57cb 100644 --- a/PIL/ImageStat.py +++ b/PIL/ImageStat.py @@ -23,6 +23,7 @@ import Image import operator, math +from functools import reduce ## # The ImageStat module calculates global statistics for an From 5076c35cc50086cd1ca742b299771ec51bbebbdb Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Mon, 15 Oct 2012 21:27:35 -0500 Subject: [PATCH 23/61] py3k: print is a function Seriously, if you didn't know that, you've been in a freaking cave, man. --- PIL/ArgImagePlugin.py | 16 ++++++++------- PIL/Image.py | 6 ++++-- PIL/ImageCms.py | 10 +++++---- PIL/ImageFont.py | 12 ++++++----- PIL/ImageShow.py | 4 +++- PIL/IptcImagePlugin.py | 5 +++-- PIL/OleFileIO.py | 22 +++++++++++--------- PIL/PSDraw.py | 4 +++- PIL/PngImagePlugin.py | 10 +++++---- PIL/SpiderImagePlugin.py | 24 ++++++++++++---------- PIL/TiffImagePlugin.py | 44 +++++++++++++++++++++------------------- PIL/WalImageFile.py | 4 +++- Sane/demo_numarray.py | 8 +++++--- Sane/demo_pil.py | 8 +++++--- Scripts/explode.py | 26 +++++++++++++----------- Scripts/gifmaker.py | 6 ++++-- Scripts/pilconvert.py | 44 +++++++++++++++++++++------------------- Scripts/pildriver.py | 10 +++++---- Scripts/pilfile.py | 36 ++++++++++++++++---------------- Scripts/pilfont.py | 20 ++++++++++-------- Scripts/pilprint.py | 22 +++++++++++--------- Scripts/player.py | 6 ++++-- Scripts/viewer.py | 4 +++- 23 files changed, 198 insertions(+), 153 deletions(-) diff --git a/PIL/ArgImagePlugin.py b/PIL/ArgImagePlugin.py index 3507e0c1f..ea9914deb 100644 --- a/PIL/ArgImagePlugin.py +++ b/PIL/ArgImagePlugin.py @@ -18,6 +18,8 @@ # See the README file for information on usage and redistribution. # +from __future__ import print_function + __version__ = "0.4" import Image, ImageFile, ImagePalette @@ -70,8 +72,8 @@ class ArgStream(ChunkStream): raise SyntaxError("unknown ARG mode") if Image.DEBUG: - print "AHDR size", self.size - print "AHDR mode", self.mode, self.rawmode + print("AHDR size", self.size) + print("AHDR mode", self.mode, self.rawmode) return s @@ -98,7 +100,7 @@ class ArgStream(ChunkStream): self.repair = None if Image.DEBUG: - print "AFRM", self.id, self.count + print("AFRM", self.id, self.count) return s @@ -121,7 +123,7 @@ class ArgStream(ChunkStream): self.count = i16(s[2:4]) if Image.DEBUG: - print "ADEF", self.id, self.count + print("ADEF", self.id, self.count) return s @@ -141,7 +143,7 @@ class ArgStream(ChunkStream): "AEND -- end of animation" if Image.DEBUG: - print "AEND" + print("AEND") self.eof = 1 @@ -256,7 +258,7 @@ class ArgStream(ChunkStream): bbox = offs + (offs[0]+size[0], offs[1]+size[1]) if Image.DEBUG: - print "DHDR", diff, bbox + print("DHDR", diff, bbox) # FIXME: decode and apply image self.action = ("DHDR", diff, bbox) @@ -473,7 +475,7 @@ class ArgImageFile(ImageFile.ImageFile): except "glurk": # AttributeError if Image.DEBUG: - print cid, bytes, "(unknown)" + print(cid, bytes, "(unknown)") s = self.fp.read(bytes) self.arg.crc(cid, s) diff --git a/PIL/Image.py b/PIL/Image.py index 6dd89a753..f03c8fa37 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -24,6 +24,8 @@ # See the README file for information on usage and redistribution. # +from __future__ import print_function + VERSION = "1.1.7" try: @@ -355,8 +357,8 @@ def init(): del sys.path[0] except ImportError: if DEBUG: - print "Image: failed to import", - print f, ":", sys.exc_info()[1] + print("Image: failed to import", end=' ') + print(f, ":", sys.exc_info()[1]) visited[fullpath] = None if OPEN or SAVE: diff --git a/PIL/ImageCms.py b/PIL/ImageCms.py index 9e9debc67..ec1eeb7ed 100644 --- a/PIL/ImageCms.py +++ b/PIL/ImageCms.py @@ -15,6 +15,8 @@ # below for the original description. # +from __future__ import print_function + DESCRIPTION = """ pyCMS @@ -770,16 +772,16 @@ if __name__ == "__main__": # create a cheap manual from the __doc__ strings for the functions above import ImageCms - print __doc__ + print(__doc__) for f in dir(pyCMS): - print "="*80 - print "%s" %f + print("="*80) + print("%s" %f) try: exec ("doc = ImageCms.%s.__doc__" %(f)) if "pyCMS" in doc: # so we don't get the __doc__ string for imported modules - print doc + print(doc) except AttributeError: pass diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index 5531c8b47..a65881822 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -25,6 +25,8 @@ # See the README file for information on usage and redistribution. # +from __future__ import print_function + import Image import os, sys @@ -381,10 +383,10 @@ if __name__ == "__main__": # create font data chunk for embedding import base64, os, sys font = "../Images/courB08" - print " f._load_pilfont_data(" - print " # %s" % os.path.basename(font) - print " StringIO(base64.decodestring('''" + print(" f._load_pilfont_data(") + print(" # %s" % os.path.basename(font)) + print(" StringIO(base64.decodestring('''") base64.encode(open(font + ".pil", "rb"), sys.stdout) - print "''')), Image.open(StringIO(base64.decodestring('''" + print("''')), Image.open(StringIO(base64.decodestring('''") base64.encode(open(font + ".pbm", "rb"), sys.stdout) - print "'''))))" + print("'''))))") diff --git a/PIL/ImageShow.py b/PIL/ImageShow.py index 05270f440..609c6ee56 100644 --- a/PIL/ImageShow.py +++ b/PIL/ImageShow.py @@ -12,6 +12,8 @@ # See the README file for information on usage and redistribution. # +from __future__ import print_function + import Image import os, sys @@ -160,4 +162,4 @@ else: if __name__ == "__main__": # usage: python ImageShow.py imagefile [title] - print show(Image.open(sys.argv[1]), *sys.argv[2:]) + print(show(Image.open(sys.argv[1]), *sys.argv[2:])) diff --git a/PIL/IptcImagePlugin.py b/PIL/IptcImagePlugin.py index ceb9e5237..ba4b8dcc8 100644 --- a/PIL/IptcImagePlugin.py +++ b/PIL/IptcImagePlugin.py @@ -15,6 +15,7 @@ # See the README file for information on usage and redistribution. # +from __future__ import print_function __version__ = "0.3" @@ -44,8 +45,8 @@ def i(c): def dump(c): for i in c: - print "%02x" % ord(i), - print + print("%02x" % ord(i), end=' ') + print() ## # Image plugin for IPTC/NAA datastreams. To read IPTC/NAA fields diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index d093d71a7..5c1248f58 100644 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -36,6 +36,8 @@ # See the README file for information on usage and redistribution. # +from __future__ import print_function + import StringIO import sys @@ -208,12 +210,12 @@ class _OleDirectoryEntry: TYPES = ["(invalid)", "(storage)", "(stream)", "(lockbytes)", "(property)", "(root)"] - print " "*tab + repr(self.name), TYPES[self.type], + print(" "*tab + repr(self.name), TYPES[self.type], end=' ') if self.type in (2, 5): - print self.size, "bytes", - print + print(self.size, "bytes", end=' ') + print() if self.type in (1, 5) and self.clsid: - print " "*tab + "{%s}" % self.clsid + print(" "*tab + "{%s}" % self.clsid) for kid in self.kids: kid.dump(tab + 2) @@ -515,17 +517,17 @@ if __name__ == "__main__": for file in sys.argv[1:]: try: ole = OleFileIO(file) - print "-" * 68 - print file - print "-" * 68 + print("-" * 68) + print(file) + print("-" * 68) ole.dumpdirectory() for file in ole.listdir(): if file[-1][0] == "\005": - print file + print(file) props = ole.getproperties(file) props = list(props.items()) props.sort() for k, v in props: - print " ", k, v + print(" ", k, v) except IOError as v: - print "***", "cannot read", file, "-", v + print("***", "cannot read", file, "-", v) diff --git a/PIL/PSDraw.py b/PIL/PSDraw.py index 29bfb235c..ede863de7 100644 --- a/PIL/PSDraw.py +++ b/PIL/PSDraw.py @@ -15,6 +15,8 @@ # See the README file for information on usage and redistribution. # +from __future__ import print_function + import EpsImagePlugin ## @@ -60,7 +62,7 @@ class PSDraw: self.fp.write("/F0 %d /PSDraw-%s F\n" % (size, font)) def setink(self, ink): - print "*** NOT YET IMPLEMENTED ***" + print("*** NOT YET IMPLEMENTED ***") def line(self, xy0, xy1): xy = xy0 + xy1 diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index c9f21f7dd..ccdec2829 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -31,6 +31,8 @@ # See the README file for information on usage and redistribution. # +from __future__ import print_function + __version__ = "0.9" import re @@ -111,7 +113,7 @@ class ChunkStream: "Call the appropriate chunk handler" if Image.DEBUG: - print "STREAM", cid, pos, len + print("STREAM", cid, pos, len) return getattr(self, "chunk_" + cid)(pos, len) def crc(self, cid, data): @@ -191,8 +193,8 @@ class PngStream(ChunkStream): # Compressed profile n bytes (zlib with deflate compression) i = s.find(chr(0)) if Image.DEBUG: - print "iCCP profile name", s[:i] - print "Compression method", ord(s[i]) + print("iCCP profile name", s[:i]) + print("Compression method", ord(s[i])) comp_method = ord(s[i]) if comp_method != 0: raise SyntaxError("Unknown compression method %s in iCCP chunk" % comp_method) @@ -333,7 +335,7 @@ class PngImageFile(ImageFile.ImageFile): break except AttributeError: if Image.DEBUG: - print cid, pos, len, "(unknown)" + print(cid, pos, len, "(unknown)") s = ImageFile._safe_read(self.fp, len) self.png.crc(cid, s) diff --git a/PIL/SpiderImagePlugin.py b/PIL/SpiderImagePlugin.py index 5db357da4..ce8cdace6 100644 --- a/PIL/SpiderImagePlugin.py +++ b/PIL/SpiderImagePlugin.py @@ -33,6 +33,8 @@ # http://www.wadsworth.org/spider_doc/spider/docs/image_doc.html # +from __future__ import print_function + import Image, ImageFile import os, struct, sys @@ -186,13 +188,13 @@ def loadImageSeries(filelist=None): imglist = [] for img in filelist: if not os.path.exists(img): - print "unable to find %s" % img + print("unable to find %s" % img) continue try: im = Image.open(img).convert2byte() except: if not isSpiderImage(img): - print img + " is not a Spider image file" + print(img + " is not a Spider image file") continue im.info['filename'] = img imglist.append(im) @@ -267,12 +269,12 @@ Image.register_save("SPIDER", _save_spider) if __name__ == "__main__": if not sys.argv[1:]: - print "Syntax: python SpiderImagePlugin.py Spiderimage [outfile]" + print("Syntax: python SpiderImagePlugin.py Spiderimage [outfile]") sys.exit() filename = sys.argv[1] if not isSpiderImage(filename): - print "input image must be in Spider format" + print("input image must be in Spider format") sys.exit() outfile = "" @@ -280,15 +282,15 @@ if __name__ == "__main__": outfile = sys.argv[2] im = Image.open(filename) - print "image: " + str(im) - print "format: " + str(im.format) - print "size: " + str(im.size) - print "mode: " + str(im.mode) - print "max, min: ", - print im.getextrema() + print("image: " + str(im)) + print("format: " + str(im.format)) + print("size: " + str(im.size)) + print("mode: " + str(im.mode)) + print("max, min: ", end=' ') + print(im.getextrema()) if outfile != "": # perform some image operation im = im.transpose(Image.FLIP_LEFT_RIGHT) - print "saving a flipped version of %s as %s " % (os.path.basename(filename), outfile) + print("saving a flipped version of %s as %s " % (os.path.basename(filename), outfile)) im.save(outfile, "SPIDER") diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index bd860454b..b74bc2455 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -39,6 +39,8 @@ # See the README file for information on usage and redistribution. # +from __future__ import print_function + __version__ = "1.3.5" import Image, ImageFile @@ -352,14 +354,14 @@ class ImageFileDirectory(collections.MutableMapping): import TiffTags tagname = TiffTags.TAGS.get(tag, "unknown") typname = TiffTags.TYPES.get(typ, "unknown") - print "tag: %s (%d)" % (tagname, tag), - print "- type: %s (%d)" % (typname, typ), + print("tag: %s (%d)" % (tagname, tag), end=' ') + print("- type: %s (%d)" % (typname, typ), end=' ') try: dispatch = self.load_dispatch[typ] except KeyError: if Image.DEBUG: - print "- unsupported type", typ + print("- unsupported type", typ) continue # ignore unsupported type size, handler = dispatch @@ -383,9 +385,9 @@ class ImageFileDirectory(collections.MutableMapping): if Image.DEBUG: if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, ICCPROFILE, XMP): - print "- value: " % size + print("- value: " % size) else: - print "- value:", self[tag] + print("- value:", self[tag]) self.next = i32(fp.read(4)) @@ -449,13 +451,13 @@ class ImageFileDirectory(collections.MutableMapping): import TiffTags tagname = TiffTags.TAGS.get(tag, "unknown") typname = TiffTags.TYPES.get(typ, "unknown") - print "save: %s (%d)" % (tagname, tag), - print "- type: %s (%d)" % (typname, typ), + print("save: %s (%d)" % (tagname, tag), end=' ') + print("- type: %s (%d)" % (typname, typ), end=' ') if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, ICCPROFILE, XMP): size = len(data) - print "- value: " % size + print("- value: " % size) else: - print "- value:", value + print("- value:", value) # figure out if data fits into the directory if len(data) == 4: @@ -481,7 +483,7 @@ class ImageFileDirectory(collections.MutableMapping): # pass 2: write directory to file for tag, typ, count, value, data in directory: if Image.DEBUG > 1: - print tag, typ, count, repr(value), repr(data) + print(tag, typ, count, repr(value), repr(data)) fp.write(o16(tag) + o16(typ) + o32(count) + value) # -- overwrite here for multi-page -- @@ -601,11 +603,11 @@ class TiffImageFile(ImageFile.ImageFile): fillorder = getscalar(FILLORDER, 1) if Image.DEBUG: - print "*** Summary ***" - print "- compression:", self._compression - print "- photometric_interpretation:", photo - print "- planar_configuration:", self._planar_configuration - print "- fill_order:", fillorder + print("*** Summary ***") + print("- compression:", self._compression) + print("- photometric_interpretation:", photo) + print("- planar_configuration:", self._planar_configuration) + print("- fill_order:", fillorder) # size xsize = getscalar(IMAGEWIDTH) @@ -613,7 +615,7 @@ class TiffImageFile(ImageFile.ImageFile): self.size = xsize, ysize if Image.DEBUG: - print "- size:", self.size + print("- size:", self.size) format = getscalar(SAMPLEFORMAT, 1) @@ -624,17 +626,17 @@ class TiffImageFile(ImageFile.ImageFile): self.tag.get(EXTRASAMPLES, ()) ) if Image.DEBUG: - print "format key:", key + print("format key:", key) try: self.mode, rawmode = OPEN_INFO[key] except KeyError: if Image.DEBUG: - print "- unsupported format" + print("- unsupported format") raise SyntaxError("unknown pixel mode") if Image.DEBUG: - print "- raw mode:", rawmode - print "- pil mode:", self.mode + print("- raw mode:", rawmode) + print("- pil mode:", self.mode) self.info["compression"] = self._compression @@ -695,7 +697,7 @@ class TiffImageFile(ImageFile.ImageFile): a = None else: if Image.DEBUG: - print "- unsupported data organization" + print("- unsupported data organization") raise SyntaxError("unknown data organization") # fixup palette descriptor diff --git a/PIL/WalImageFile.py b/PIL/WalImageFile.py index fb8c38ab4..b05ea4a19 100644 --- a/PIL/WalImageFile.py +++ b/PIL/WalImageFile.py @@ -21,6 +21,8 @@ # http://www.flipcode.com/tutorials/tut_q2levels.shtml # and has been tested with a few sample files found using google. +from __future__ import print_function + import Image def i32(c, o=0): @@ -122,5 +124,5 @@ quake2palette = ( if __name__ == "__main__": im = open("../hacks/sample.wal") - print im.info, im.mode, im.size + print(im.info, im.mode, im.size) im.save("../out.png") diff --git a/Sane/demo_numarray.py b/Sane/demo_numarray.py index 0104af2d5..2eccb72a3 100644 --- a/Sane/demo_numarray.py +++ b/Sane/demo_numarray.py @@ -4,6 +4,8 @@ # Shows how to scan a 16 bit grayscale image into a numarray object # +from __future__ import print_function + # Get the path set up to find PIL modules if not installed yet: import sys ; sys.path.append('../PIL') @@ -23,8 +25,8 @@ def toImage(arr): im = Image.fromstring('L', arr.shape[::-1], arr.tostring()) return im -print 'SANE version:', sane.init() -print 'Available devices=', sane.get_devices() +print('SANE version:', sane.init()) +print('Available devices=', sane.get_devices()) s = sane.open(sane.get_devices()[0][0]) @@ -32,7 +34,7 @@ s = sane.open(sane.get_devices()[0][0]) s.mode = 'gray' s.br_x=320. ; s.br_y=240. -print 'Device parameters:', s.get_parameters() +print('Device parameters:', s.get_parameters()) s.depth=16 arr16 = s.arr_scan() diff --git a/Sane/demo_pil.py b/Sane/demo_pil.py index 016361f8a..490f33158 100644 --- a/Sane/demo_pil.py +++ b/Sane/demo_pil.py @@ -4,19 +4,21 @@ # Shows how to scan a color image into a PIL rgb-image # +from __future__ import print_function + # Get the path set up to find PIL modules if not installed yet: import sys ; sys.path.append('../PIL') import sane -print 'SANE version:', sane.init() -print 'Available devices=', sane.get_devices() +print('SANE version:', sane.init()) +print('Available devices=', sane.get_devices()) s = sane.open(sane.get_devices()[0][0]) s.mode = 'color' s.br_x=320. ; s.br_y=240. -print 'Device parameters:', s.get_parameters() +print('Device parameters:', s.get_parameters()) # Initiate the scan s.start() diff --git a/Scripts/explode.py b/Scripts/explode.py index 6081f751a..3ff96dd2c 100644 --- a/Scripts/explode.py +++ b/Scripts/explode.py @@ -5,6 +5,8 @@ # split an animation into a number of frame files # +from __future__ import print_function + from PIL import Image import os, sys @@ -53,17 +55,17 @@ if sys.argv[1:2] == ["-h"]: del sys.argv[1] if not sys.argv[2:]: - print - print "Syntax: python explode.py infile template [range]" - print - print "The template argument is used to construct the names of the" - print "individual frame files. The frames are numbered file001.ext," - print "file002.ext, etc. You can insert %d to control the placement" - print "and syntax of the frame number." - print - print "The optional range argument specifies which frames to extract." - print "You can give one or more ranges like 1-10, 5, -15 etc. If" - print "omitted, all frames are extracted." + print() + print("Syntax: python explode.py infile template [range]") + print() + print("The template argument is used to construct the names of the") + print("individual frame files. The frames are numbered file001.ext,") + print("file002.ext, etc. You can insert %d to control the placement") + print("and syntax of the frame number.") + print() + print("The optional range argument specifies which frames to extract.") + print("You can give one or more ranges like 1-10, 5, -15 etc. If") + print("omitted, all frames are extracted.") sys.exit(1) infile = sys.argv[1] @@ -91,7 +93,7 @@ while 1: if frames[ix]: im.save(outfile % ix) - print outfile % ix + print(outfile % ix) if html: html.write("
\n" % outfile % ix) diff --git a/Scripts/gifmaker.py b/Scripts/gifmaker.py index e3b6701cf..3180c13b4 100644 --- a/Scripts/gifmaker.py +++ b/Scripts/gifmaker.py @@ -39,6 +39,8 @@ # write data directly to a socket. Or something... # +from __future__ import print_function + from PIL import Image, ImageChops from PIL.GifImagePlugin import getheader, getdata @@ -127,8 +129,8 @@ if __name__ == "__main__": import sys if len(sys.argv) < 3: - print "GIFMAKER -- create GIF animations" - print "Usage: gifmaker infile outfile" + print("GIFMAKER -- create GIF animations") + print("Usage: gifmaker infile outfile") sys.exit(1) compress(sys.argv[1], sys.argv[2]) diff --git a/Scripts/pilconvert.py b/Scripts/pilconvert.py index e6adadc71..64900db8a 100644 --- a/Scripts/pilconvert.py +++ b/Scripts/pilconvert.py @@ -13,27 +13,29 @@ # 0.5 98-12-30 fl Fixed -f option (from Anthony Baxter) # +from __future__ import print_function + import site import getopt, string, sys from PIL import Image def usage(): - print "PIL Convert 0.5/1998-12-30 -- convert image files" - print "Usage: pilconvert [option] infile outfile" - print - print "Options:" - print - print " -c convert to format (default is given by extension)" - print - print " -g convert to greyscale" - print " -p convert to palette image (using standard palette)" - print " -r convert to rgb" - print - print " -o optimize output (trade speed for size)" - print " -q set compression quality (0-100, JPEG only)" - print - print " -f list supported file formats" + print("PIL Convert 0.5/1998-12-30 -- convert image files") + print("Usage: pilconvert [option] infile outfile") + print() + print("Options:") + print() + print(" -c convert to format (default is given by extension)") + print() + print(" -g convert to greyscale") + print(" -p convert to palette image (using standard palette)") + print(" -r convert to rgb") + print() + print(" -o optimize output (trade speed for size)") + print(" -q set compression quality (0-100, JPEG only)") + print() + print(" -f list supported file formats") sys.exit(1) if len(sys.argv) == 1: @@ -42,7 +44,7 @@ if len(sys.argv) == 1: try: opt, argv = getopt.getopt(sys.argv[1:], "c:dfgopq:r") except getopt.error as v: - print v + print(v) sys.exit(1) format = None @@ -56,12 +58,12 @@ for o, a in opt: Image.init() id = Image.ID[:] id.sort() - print "Supported formats (* indicates output format):" + print("Supported formats (* indicates output format):") for i in id: if i in Image.SAVE: - print i+"*", + print(i+"*", end=' ') else: - print i, + print(i, end=' ') sys.exit(1) elif o == "-c": @@ -92,5 +94,5 @@ try: else: apply(im.save, (argv[1],), options) except: - print "cannot convert image", - print "(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1]) + print("cannot convert image", end=' ') + print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1])) diff --git a/Scripts/pildriver.py b/Scripts/pildriver.py index 19c83dbc7..ed3e5b4fe 100644 --- a/Scripts/pildriver.py +++ b/Scripts/pildriver.py @@ -48,6 +48,8 @@ of its upper-left-hand corner and displays the cropped portion. # 3. Add support for composing and decomposing multiple-image files. # +from __future__ import print_function + from PIL import Image class PILDriver: @@ -481,7 +483,7 @@ class PILDriver: self.push(list[0]) list = list[1:] if self.verbose: - print "Stack: " + `self.stack` + print("Stack: " + `self.stack`) top = self.top() if type(top) != type(""): continue; @@ -507,15 +509,15 @@ if __name__ == '__main__': if len(sys.argv[1:]) > 0: driver.execute(sys.argv[1:]) else: - print "PILDriver says hello." + print("PILDriver says hello.") while 1: try: line = raw_input('pildriver> '); except EOFError: - print "\nPILDriver says goodbye." + print("\nPILDriver says goodbye.") break driver.execute(line.split()) - print driver.stack + print(driver.stack) # The following sets edit modes for GNU EMACS # Local Variables: diff --git a/Scripts/pilfile.py b/Scripts/pilfile.py index c4b4a6a62..4d33d23c0 100644 --- a/Scripts/pilfile.py +++ b/Scripts/pilfile.py @@ -17,25 +17,27 @@ # 0.4 2003-09-30 fl Expand wildcards on Windows; robustness tweaks # +from __future__ import print_function + import site import getopt, glob, sys from PIL import Image if len(sys.argv) == 1: - print "PIL File 0.4/2003-09-30 -- identify image files" - print "Usage: pilfile [option] files..." - print "Options:" - print " -f list supported file formats" - print " -i show associated info and tile data" - print " -v verify file headers" - print " -q quiet, don't warn for unidentified/missing/broken files" + print("PIL File 0.4/2003-09-30 -- identify image files") + print("Usage: pilfile [option] files...") + print("Options:") + print(" -f list supported file formats") + print(" -i show associated info and tile data") + print(" -v verify file headers") + print(" -q quiet, don't warn for unidentified/missing/broken files") sys.exit(1) try: opt, args = getopt.getopt(sys.argv[1:], "fqivD") except getopt.error as v: - print v + print(v) sys.exit(1) verbose = quiet = verify = 0 @@ -45,9 +47,9 @@ for o, a in opt: Image.init() id = Image.ID[:] id.sort() - print "Supported formats:" + print("Supported formats:") for i in id: - print i, + print(i, end=' ') sys.exit(1) elif o == "-i": verbose = 1 @@ -73,22 +75,22 @@ def globfix(files): for file in globfix(args): try: im = Image.open(file) - print "%s:" % file, im.format, "%dx%d" % im.size, im.mode, + print("%s:" % file, im.format, "%dx%d" % im.size, im.mode, end=' ') if verbose: - print im.info, im.tile, - print + print(im.info, im.tile, end=' ') + print() if verify: try: im.verify() except: if not quiet: - print "failed to verify image", - print "(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1]) + print("failed to verify image", end=' ') + print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1])) except IOError as v: if not quiet: - print file, "failed:", v + print(file, "failed:", v) except: import traceback if not quiet: - print file, "failed:", "unexpected error" + print(file, "failed:", "unexpected error") traceback.print_exc(file=sys.stdout) diff --git a/Scripts/pilfont.py b/Scripts/pilfont.py index df08d4c08..21cfa864d 100644 --- a/Scripts/pilfont.py +++ b/Scripts/pilfont.py @@ -9,6 +9,8 @@ # 2002-03-10 fl use "from PIL import" # +from __future__ import print_function + VERSION = "0.4" import site @@ -19,12 +21,12 @@ from PIL import BdfFontFile from PIL import PcfFontFile if len(sys.argv) <= 1: - print "PILFONT", VERSION, "-- PIL font compiler." - print - print "Usage: pilfont fontfiles..." - print - print "Convert given font files to the PIL raster font format." - print "This version of pilfont supports X BDF and PCF fonts." + print("PILFONT", VERSION, "-- PIL font compiler.") + print() + print("Usage: pilfont fontfiles...") + print() + print("Convert given font files to the PIL raster font format.") + print("This version of pilfont supports X BDF and PCF fonts.") sys.exit(1) files = [] @@ -33,7 +35,7 @@ for f in sys.argv[1:]: for f in files: - print f + "...", + print(f + "...", end=' ') try: @@ -48,7 +50,7 @@ for f in files: p.save(f) except (SyntaxError, IOError): - print "failed" + print("failed") else: - print "OK" + print("OK") diff --git a/Scripts/pilprint.py b/Scripts/pilprint.py index f5279dd85..9c30e6343 100644 --- a/Scripts/pilprint.py +++ b/Scripts/pilprint.py @@ -11,6 +11,8 @@ # 0.3 2003-05-06 fl Fixed a typo or two. # +from __future__ import print_function + VERSION = "pilprint 0.3/2003-05-05" from PIL import Image @@ -29,18 +31,18 @@ def description(file, image): import getopt, os, sys if len(sys.argv) == 1: - print "PIL Print 0.2a1/96-10-04 -- print image files" - print "Usage: pilprint files..." - print "Options:" - print " -c colour printer (default is monochrome)" - print " -p print via lpr (default is stdout)" - print " -P same as -p but use given printer" + print("PIL Print 0.2a1/96-10-04 -- print image files") + print("Usage: pilprint files...") + print("Options:") + print(" -c colour printer (default is monochrome)") + print(" -p print via lpr (default is stdout)") + print(" -P same as -p but use given printer") sys.exit(1) try: opt, argv = getopt.getopt(sys.argv[1:], "cdpP:") except getopt.error as v: - print v + print(v) sys.exit(1) printer = None # print to stdout @@ -50,7 +52,7 @@ for o, a in opt: if o == "-d": # debug: show available drivers Image.init() - print Image.ID + print(Image.ID) sys.exit(1) elif o == "-c": # colour printer @@ -89,5 +91,5 @@ for file in argv: ps.end_document() except: - print "cannot print image", - print "(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1]) + print("cannot print image", end=' ') + print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1])) diff --git a/Scripts/player.py b/Scripts/player.py index 9ca4500bf..b8239ebfc 100644 --- a/Scripts/player.py +++ b/Scripts/player.py @@ -3,6 +3,8 @@ # $Id$ # +from __future__ import print_function + from Tkinter import * from PIL import Image, ImageTk import sys @@ -98,7 +100,7 @@ class UI(Label): if __name__ == "__main__": if not sys.argv[1:]: - print "Syntax: python player.py imagefile(s)" + print("Syntax: python player.py imagefile(s)") sys.exit(1) filename = sys.argv[1] @@ -108,7 +110,7 @@ if __name__ == "__main__": if len(sys.argv) > 2: # list of images - print "loading..." + print("loading...") im = [] for filename in sys.argv[1:]: im.append(Image.open(filename)) diff --git a/Scripts/viewer.py b/Scripts/viewer.py index 6e4dc8b67..6cb42a553 100644 --- a/Scripts/viewer.py +++ b/Scripts/viewer.py @@ -3,6 +3,8 @@ # $Id$ # +from __future__ import print_function + from Tkinter import * from PIL import Image, ImageTk @@ -31,7 +33,7 @@ if __name__ == "__main__": import sys if not sys.argv[1:]: - print "Syntax: python viewer.py imagefile" + print("Syntax: python viewer.py imagefile") sys.exit(1) filename = sys.argv[1] From abd215e457e1230591a990d08fb4232d4ec2f92f Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Mon, 15 Oct 2012 21:32:28 -0500 Subject: [PATCH 24/61] py3k: Remove tuples in parameter lists Py3k no longer supports unpacking tuples in the parameters. --- PIL/IcnsImagePlugin.py | 11 +++++++---- PIL/Image.py | 3 ++- PIL/ImageDraw2.py | 3 ++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/PIL/IcnsImagePlugin.py b/PIL/IcnsImagePlugin.py index 883ac67dd..5f8ba063b 100644 --- a/PIL/IcnsImagePlugin.py +++ b/PIL/IcnsImagePlugin.py @@ -22,19 +22,21 @@ HEADERSIZE = 8 def nextheader(fobj): return struct.unpack('>4sI', fobj.read(HEADERSIZE)) -def read_32t(fobj, (start, length), (width, height)): +def read_32t(fobj, start_length, size): # The 128x128 icon seems to have an extra header for some reason. + (start, length) = start_length fobj.seek(start) sig = fobj.read(4) if sig != '\x00\x00\x00\x00': raise SyntaxError('Unknown signature, expecting 0x00000000') - return read_32(fobj, (start + 4, length - 4), (width, height)) + return read_32(fobj, (start + 4, length - 4), size) -def read_32(fobj, (start, length), size): +def read_32(fobj, start_length, size): """ Read a 32bit RGB icon resource. Seems to be either uncompressed or an RLE packbits-like scheme. """ + (start, length) = start_length fobj.seek(start) sizesq = size[0] * size[1] if length == sizesq * 3: @@ -73,8 +75,9 @@ def read_32(fobj, (start, length), size): im.im.putband(band.im, band_ix) return {"RGB": im} -def read_mk(fobj, (start, length), size): +def read_mk(fobj, start_length, size): # Alpha masks seem to be uncompressed + (start, length) = start_length fobj.seek(start) band = Image.frombuffer( "L", size, fobj.read(size[0]*size[1]), "raw", "L", 0, 1 diff --git a/PIL/Image.py b/PIL/Image.py index f03c8fa37..50d4e9e54 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1332,7 +1332,8 @@ class Image: math.cos(angle), math.sin(angle), 0.0, -math.sin(angle), math.cos(angle), 0.0 ] - def transform(x, y, (a, b, c, d, e, f)=matrix): + def transform(x, y, matrix=matrix): + (a, b, c, d, e, f) = matrix return a*x + b*y + c, d*x + e*y + f # calculate output size diff --git a/PIL/ImageDraw2.py b/PIL/ImageDraw2.py index dbf1a1f88..98cf0c3eb 100644 --- a/PIL/ImageDraw2.py +++ b/PIL/ImageDraw2.py @@ -68,7 +68,8 @@ class Draw: else: getattr(self.draw, op)(xy, fill=fill, outline=outline) - def settransform(self, (xoffset, yoffset)): + def settransform(self, offset): + (xoffset, yoffset) = offset self.transform = (1, 0, xoffset, 0, 1, yoffset) def arc(self, xy, start, end, *options): From 83ff0b3b316b76789d3fac20d246ede938c34f67 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Mon, 15 Oct 2012 21:49:13 -0500 Subject: [PATCH 25/61] py3k: Use relative imports In py3k, imports are absolute unless using the "from . import" syntax. This commit also solves a recursive import between Image, ImageColor, and ImagePalette by delay-importing ImagePalette in Image. I'm not too keen on this commit because the syntax is ugly. I might go back and prefer the prettier "from PIL import". --- PIL/ArgImagePlugin.py | 4 ++-- PIL/BdfFontFile.py | 4 ++-- PIL/BmpImagePlugin.py | 2 +- PIL/BufrStubImagePlugin.py | 2 +- PIL/CurImagePlugin.py | 2 +- PIL/DcxImagePlugin.py | 4 ++-- PIL/EpsImagePlugin.py | 2 +- PIL/FitsStubImagePlugin.py | 2 +- PIL/FliImagePlugin.py | 2 +- PIL/FontFile.py | 2 +- PIL/FpxImagePlugin.py | 4 ++-- PIL/GbrImagePlugin.py | 2 +- PIL/GdImageFile.py | 2 +- PIL/GifImagePlugin.py | 2 +- PIL/GribStubImagePlugin.py | 2 +- PIL/Hdf5StubImagePlugin.py | 2 +- PIL/IcnsImagePlugin.py | 2 +- PIL/IcoImagePlugin.py | 2 +- PIL/ImImagePlugin.py | 2 +- PIL/Image.py | 23 ++++++++++++----------- PIL/ImageChops.py | 2 +- PIL/ImageCms.py | 6 +++--- PIL/ImageColor.py | 2 +- PIL/ImageDraw.py | 6 +++--- PIL/ImageDraw2.py | 2 +- PIL/ImageEnhance.py | 2 +- PIL/ImageFile.py | 2 +- PIL/ImageFont.py | 2 +- PIL/ImageGrab.py | 5 +++-- PIL/ImageMath.py | 2 +- PIL/ImageMode.py | 2 +- PIL/ImageOps.py | 4 ++-- PIL/ImagePalette.py | 8 ++++---- PIL/ImagePath.py | 2 +- PIL/ImageQt.py | 2 +- PIL/ImageShow.py | 2 +- PIL/ImageStat.py | 2 +- PIL/ImageTk.py | 3 ++- PIL/ImageTransform.py | 2 +- PIL/ImageWin.py | 2 +- PIL/ImtImagePlugin.py | 2 +- PIL/IptcImagePlugin.py | 4 ++-- PIL/JpegImagePlugin.py | 5 +++-- PIL/McIdasImagePlugin.py | 2 +- PIL/MicImagePlugin.py | 4 ++-- PIL/MpegImagePlugin.py | 2 +- PIL/MspImagePlugin.py | 2 +- PIL/PSDraw.py | 2 +- PIL/PalmImagePlugin.py | 2 +- PIL/PcdImagePlugin.py | 2 +- PIL/PcfFontFile.py | 4 ++-- PIL/PcxImagePlugin.py | 2 +- PIL/PdfImagePlugin.py | 2 +- PIL/PixarImagePlugin.py | 2 +- PIL/PngImagePlugin.py | 3 ++- PIL/PpmImagePlugin.py | 2 +- PIL/PsdImagePlugin.py | 2 +- PIL/SgiImagePlugin.py | 2 +- PIL/SpiderImagePlugin.py | 4 ++-- PIL/SunImagePlugin.py | 2 +- PIL/TarIO.py | 2 +- PIL/TgaImagePlugin.py | 2 +- PIL/TiffImagePlugin.py | 8 ++++---- PIL/WalImageFile.py | 2 +- PIL/WmfImagePlugin.py | 2 +- PIL/XVThumbImagePlugin.py | 2 +- PIL/XbmImagePlugin.py | 2 +- PIL/XpmImagePlugin.py | 2 +- 68 files changed, 104 insertions(+), 99 deletions(-) diff --git a/PIL/ArgImagePlugin.py b/PIL/ArgImagePlugin.py index ea9914deb..c192dd955 100644 --- a/PIL/ArgImagePlugin.py +++ b/PIL/ArgImagePlugin.py @@ -22,9 +22,9 @@ from __future__ import print_function __version__ = "0.4" -import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette -from PngImagePlugin import i16, i32, ChunkStream, _MODES +from .PngImagePlugin import i16, i32, ChunkStream, _MODES MAGIC = "\212ARG\r\n\032\n" diff --git a/PIL/BdfFontFile.py b/PIL/BdfFontFile.py index eb656d3ac..5b38d5957 100644 --- a/PIL/BdfFontFile.py +++ b/PIL/BdfFontFile.py @@ -17,8 +17,8 @@ # See the README file for information on usage and redistribution. # -import Image -import FontFile +from . import Image +from . import FontFile # -------------------------------------------------------------------- diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index eb9a0bf60..ad59d5de9 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -27,7 +27,7 @@ __version__ = "0.7" -import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette # diff --git a/PIL/BufrStubImagePlugin.py b/PIL/BufrStubImagePlugin.py index 4b111b393..e17bada90 100644 --- a/PIL/BufrStubImagePlugin.py +++ b/PIL/BufrStubImagePlugin.py @@ -9,7 +9,7 @@ # See the README file for information on usage and redistribution. # -import Image, ImageFile +from . import Image, ImageFile _handler = None diff --git a/PIL/CurImagePlugin.py b/PIL/CurImagePlugin.py index 98b971c0a..c2c46e270 100644 --- a/PIL/CurImagePlugin.py +++ b/PIL/CurImagePlugin.py @@ -19,7 +19,7 @@ __version__ = "0.1" -import Image, BmpImagePlugin +from . import Image, BmpImagePlugin # diff --git a/PIL/DcxImagePlugin.py b/PIL/DcxImagePlugin.py index 60c022f8e..9a512c28c 100644 --- a/PIL/DcxImagePlugin.py +++ b/PIL/DcxImagePlugin.py @@ -23,9 +23,9 @@ __version__ = "0.2" -import Image +from . import Image -from PcxImagePlugin import PcxImageFile +from .PcxImagePlugin import PcxImageFile MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then? diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py index 450ba2422..9910f382a 100644 --- a/PIL/EpsImagePlugin.py +++ b/PIL/EpsImagePlugin.py @@ -21,7 +21,7 @@ __version__ = "0.5" import re -import Image, ImageFile +from . import Image, ImageFile # # -------------------------------------------------------------------- diff --git a/PIL/FitsStubImagePlugin.py b/PIL/FitsStubImagePlugin.py index ac8be80a9..f442caefc 100644 --- a/PIL/FitsStubImagePlugin.py +++ b/PIL/FitsStubImagePlugin.py @@ -9,7 +9,7 @@ # See the README file for information on usage and redistribution. # -import Image, ImageFile +from . import Image, ImageFile _handler = None diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py index 8f48e29b8..1404cb9e5 100644 --- a/PIL/FliImagePlugin.py +++ b/PIL/FliImagePlugin.py @@ -18,7 +18,7 @@ __version__ = "0.2" -import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette def i16(c): diff --git a/PIL/FontFile.py b/PIL/FontFile.py index d9d8eb38a..a7912fefc 100644 --- a/PIL/FontFile.py +++ b/PIL/FontFile.py @@ -15,7 +15,7 @@ # import os -import Image +from . import Image import marshal diff --git a/PIL/FpxImagePlugin.py b/PIL/FpxImagePlugin.py index e44c667ba..6612c3e1a 100644 --- a/PIL/FpxImagePlugin.py +++ b/PIL/FpxImagePlugin.py @@ -19,8 +19,8 @@ __version__ = "0.1" -import Image, ImageFile -from OleFileIO import * +from . import Image, ImageFile +from .OleFileIO import * # we map from colour field tuples to (mode, rawmode) descriptors diff --git a/PIL/GbrImagePlugin.py b/PIL/GbrImagePlugin.py index db48bf968..3efcb730c 100644 --- a/PIL/GbrImagePlugin.py +++ b/PIL/GbrImagePlugin.py @@ -13,7 +13,7 @@ # See the README file for information on usage and redistribution. # -import Image, ImageFile +from . import Image, ImageFile def i32(c): return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24L) diff --git a/PIL/GdImageFile.py b/PIL/GdImageFile.py index b5f1dd25d..e211d3528 100644 --- a/PIL/GdImageFile.py +++ b/PIL/GdImageFile.py @@ -25,7 +25,7 @@ __version__ = "0.1" -import ImageFile, ImagePalette +from . import ImageFile, ImagePalette def i16(c): return ord(c[1]) + (ord(c[0])<<8) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index fa3b66643..138627574 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -28,7 +28,7 @@ __version__ = "0.9" -import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette # -------------------------------------------------------------------- diff --git a/PIL/GribStubImagePlugin.py b/PIL/GribStubImagePlugin.py index e232f90db..7125751bd 100644 --- a/PIL/GribStubImagePlugin.py +++ b/PIL/GribStubImagePlugin.py @@ -9,7 +9,7 @@ # See the README file for information on usage and redistribution. # -import Image, ImageFile +from . import Image, ImageFile _handler = None diff --git a/PIL/Hdf5StubImagePlugin.py b/PIL/Hdf5StubImagePlugin.py index bc5e37433..d4b1f7932 100644 --- a/PIL/Hdf5StubImagePlugin.py +++ b/PIL/Hdf5StubImagePlugin.py @@ -9,7 +9,7 @@ # See the README file for information on usage and redistribution. # -import Image, ImageFile +from . import Image, ImageFile _handler = None diff --git a/PIL/IcnsImagePlugin.py b/PIL/IcnsImagePlugin.py index 5f8ba063b..ac45b99f7 100644 --- a/PIL/IcnsImagePlugin.py +++ b/PIL/IcnsImagePlugin.py @@ -14,7 +14,7 @@ # See the README file for information on usage and redistribution. # -import Image, ImageFile +from . import Image, ImageFile import struct HEADERSIZE = 8 diff --git a/PIL/IcoImagePlugin.py b/PIL/IcoImagePlugin.py index 65e2fff26..7549a2771 100644 --- a/PIL/IcoImagePlugin.py +++ b/PIL/IcoImagePlugin.py @@ -19,7 +19,7 @@ __version__ = "0.1" -import Image, BmpImagePlugin +from . import Image, BmpImagePlugin # diff --git a/PIL/ImImagePlugin.py b/PIL/ImImagePlugin.py index bc4910234..a105ecaef 100644 --- a/PIL/ImImagePlugin.py +++ b/PIL/ImImagePlugin.py @@ -29,7 +29,7 @@ __version__ = "0.7" import re -import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette # -------------------------------------------------------------------- diff --git a/PIL/Image.py b/PIL/Image.py index 50d4e9e54..33154480d 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -66,8 +66,7 @@ except ImportError as v: RuntimeWarning ) -import ImageMode -import ImagePalette +from . import ImageMode import os, sys @@ -295,23 +294,23 @@ def preinit(): return try: - import BmpImagePlugin + from . import BmpImagePlugin except ImportError: pass try: - import GifImagePlugin + from . import GifImagePlugin except ImportError: pass try: - import JpegImagePlugin + from . import JpegImagePlugin except ImportError: pass try: - import PpmImagePlugin + from . import PpmImagePlugin except ImportError: pass try: - import PngImagePlugin + from . import PngImagePlugin except ImportError: pass # try: @@ -464,6 +463,7 @@ class Image: new.size = im.size new.palette = self.palette if im.mode == "P": + from . import ImagePalette new.palette = ImagePalette.ImagePalette() try: new.info = self.info.copy() @@ -1014,7 +1014,7 @@ class Image: "'offset' is deprecated; use 'ImageChops.offset' instead", DeprecationWarning, stacklevel=2 ) - import ImageChops + from . import ImageChops return ImageChops.offset(self, xoffset, yoffset) ## @@ -1081,7 +1081,7 @@ class Image: box = box + (box[0]+size[0], box[1]+size[1]) if isStringType(im): - import ImageColor + from . import ImageColor im = ImageColor.getcolor(im, self.mode) elif isImageType(im): @@ -1227,6 +1227,7 @@ class Image: def putpalette(self, data, rawmode="RGB"): "Put palette data into an image." + from . import ImagePalette if self.mode not in ("L", "P"): raise ValueError("illegal image mode") @@ -1758,7 +1759,7 @@ def new(mode, size, color=0): if isStringType(color): # css3-style specifier - import ImageColor + from . import ImageColor color = ImageColor.getcolor(color, mode) return Image()._new(core.fill(mode, size, color)) @@ -2139,5 +2140,5 @@ def _show(image, **options): apply(_showxv, (image,), options) def _showxv(image, title=None, **options): - import ImageShow + from . import ImageShow apply(ImageShow.show, (image, title), options) diff --git a/PIL/ImageChops.py b/PIL/ImageChops.py index 82861fc7a..d37019575 100644 --- a/PIL/ImageChops.py +++ b/PIL/ImageChops.py @@ -15,7 +15,7 @@ # See the README file for information on usage and redistribution. # -import Image +from . import Image ## # The ImageChops module contains a number of arithmetical image diff --git a/PIL/ImageCms.py b/PIL/ImageCms.py index ec1eeb7ed..0f2dd0e4d 100644 --- a/PIL/ImageCms.py +++ b/PIL/ImageCms.py @@ -81,7 +81,7 @@ VERSION = "0.1.0 pil" # --------------------------------------------------------------------. -import Image +from . import Image import _imagingcms core = _imagingcms @@ -207,7 +207,7 @@ class ImageCmsTransform(Image.ImagePointHandler): def get_display_profile(handle=None): import sys if sys.platform == "win32": - import ImageWin + from . import ImageWin if isinstance(handle, ImageWin.HDC): profile = core.get_display_profile_win32(handle, 1) else: @@ -771,7 +771,7 @@ def versions(): if __name__ == "__main__": # create a cheap manual from the __doc__ strings for the functions above - import ImageCms + from . import ImageCms print(__doc__) for f in dir(pyCMS): diff --git a/PIL/ImageColor.py b/PIL/ImageColor.py index 8d3354a3c..247a00efc 100644 --- a/PIL/ImageColor.py +++ b/PIL/ImageColor.py @@ -17,7 +17,7 @@ # See the README file for information on usage and redistribution. # -import Image +from . import Image import re diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py index 5217a7366..d0e735abe 100644 --- a/PIL/ImageDraw.py +++ b/PIL/ImageDraw.py @@ -30,7 +30,7 @@ # See the README file for information on usage and redistribution. # -import Image, ImageColor +from . import Image, ImageColor try: import warnings @@ -127,7 +127,7 @@ class ImageDraw: def getfont(self): if not self.font: # FIXME: should add a font repository - import ImageFont + from . import ImageFont self.font = ImageFont.load_default() return self.font @@ -318,7 +318,7 @@ def getdraw(im=None, hints=None): except ImportError: pass if handler is None: - import ImageDraw2 + from . import ImageDraw2 handler = ImageDraw2 if im: im = handler.Draw(im) diff --git a/PIL/ImageDraw2.py b/PIL/ImageDraw2.py index 98cf0c3eb..b453e81d5 100644 --- a/PIL/ImageDraw2.py +++ b/PIL/ImageDraw2.py @@ -16,7 +16,7 @@ # See the README file for information on usage and redistribution. # -import Image, ImageColor, ImageDraw, ImageFont, ImagePath +from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath class Pen: def __init__(self, color, width=1, opacity=255): diff --git a/PIL/ImageEnhance.py b/PIL/ImageEnhance.py index 86a19e650..767c5ba05 100644 --- a/PIL/ImageEnhance.py +++ b/PIL/ImageEnhance.py @@ -18,7 +18,7 @@ # See the README file for information on usage and redistribution. # -import Image, ImageFilter, ImageStat +from . import Image, ImageFilter, ImageStat class _Enhance: diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index ef13e5b00..41782d5d6 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -27,7 +27,7 @@ # See the README file for information on usage and redistribution. # -import Image +from . import Image import traceback, os import io diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index a65881822..939887db2 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -27,7 +27,7 @@ from __future__ import print_function -import Image +from . import Image import os, sys class _imagingft_not_installed: diff --git a/PIL/ImageGrab.py b/PIL/ImageGrab.py index 9bcd804d3..f3de6fde4 100644 --- a/PIL/ImageGrab.py +++ b/PIL/ImageGrab.py @@ -15,7 +15,7 @@ # See the README file for information on usage and redistribution. # -import Image +from . import Image ## # (New in 1.1.3) The ImageGrab module can be used to copy @@ -66,6 +66,7 @@ def grabclipboard(): debug = 0 # temporary interface data = Image.core.grabclipboard(debug) if Image.isStringType(data): - import BmpImagePlugin, StringIO + from . import BmpImagePlugin + import StringIO return BmpImagePlugin.DibImageFile(StringIO.StringIO(data)) return data diff --git a/PIL/ImageMath.py b/PIL/ImageMath.py index 28dd254be..f43501400 100644 --- a/PIL/ImageMath.py +++ b/PIL/ImageMath.py @@ -15,7 +15,7 @@ # See the README file for information on usage and redistribution. # -import Image +from . import Image import _imagingmath import sys diff --git a/PIL/ImageMode.py b/PIL/ImageMode.py index 1d5df1c6d..58daa999e 100644 --- a/PIL/ImageMode.py +++ b/PIL/ImageMode.py @@ -36,7 +36,7 @@ class ModeDescriptor: def getmode(mode): if not _modes: # initialize mode cache - import Image + from . import Image # core modes for m, (basemode, basetype, bands) in Image._MODEINFO.items(): _modes[m] = ModeDescriptor(m, bands, basemode, basetype) diff --git a/PIL/ImageOps.py b/PIL/ImageOps.py index de32726a1..496631aa9 100644 --- a/PIL/ImageOps.py +++ b/PIL/ImageOps.py @@ -17,7 +17,7 @@ # See the README file for information on usage and redistribution. # -import Image +from . import Image import operator from functools import reduce @@ -44,7 +44,7 @@ def _border(border): def _color(color, mode): if Image.isStringType(color): - import ImageColor + from . import ImageColor color = ImageColor.getcolor(color, mode) return color diff --git a/PIL/ImagePalette.py b/PIL/ImagePalette.py index a57688b53..b5baf7cbf 100644 --- a/PIL/ImagePalette.py +++ b/PIL/ImagePalette.py @@ -17,7 +17,7 @@ # import array -import Image, ImageColor +from . import Image, ImageColor ## # Colour palette wrapper for palette mapped images. @@ -150,7 +150,7 @@ def load(filename): if not lut: try: - import GimpPaletteFile + from . import GimpPaletteFile fp.seek(0) p = GimpPaletteFile.GimpPaletteFile(fp) lut = p.getpalette() @@ -159,7 +159,7 @@ def load(filename): if not lut: try: - import GimpGradientFile + from . import GimpGradientFile fp.seek(0) p = GimpGradientFile.GimpGradientFile(fp) lut = p.getpalette() @@ -168,7 +168,7 @@ def load(filename): if not lut: try: - import PaletteFile + from . import PaletteFile fp.seek(0) p = PaletteFile.PaletteFile(fp) lut = p.getpalette() diff --git a/PIL/ImagePath.py b/PIL/ImagePath.py index 721fd9449..fe280ff29 100644 --- a/PIL/ImagePath.py +++ b/PIL/ImagePath.py @@ -14,7 +14,7 @@ # See the README file for information on usage and redistribution. # -import Image +from . import Image ## # Path wrapper. diff --git a/PIL/ImageQt.py b/PIL/ImageQt.py index 50ee07d6c..1388dbefa 100644 --- a/PIL/ImageQt.py +++ b/PIL/ImageQt.py @@ -15,7 +15,7 @@ # See the README file for information on usage and redistribution. # -import Image +from . import Image from PyQt4.QtGui import QImage, qRgb diff --git a/PIL/ImageShow.py b/PIL/ImageShow.py index 609c6ee56..a5275def4 100644 --- a/PIL/ImageShow.py +++ b/PIL/ImageShow.py @@ -14,7 +14,7 @@ from __future__ import print_function -import Image +from . import Image import os, sys _viewers = [] diff --git a/PIL/ImageStat.py b/PIL/ImageStat.py index c87bd57cb..e2e63cabc 100644 --- a/PIL/ImageStat.py +++ b/PIL/ImageStat.py @@ -21,7 +21,7 @@ # See the README file for information on usage and redistribution. # -import Image +from . import Image import operator, math from functools import reduce diff --git a/PIL/ImageTk.py b/PIL/ImageTk.py index 6b4311a6b..5563b679f 100644 --- a/PIL/ImageTk.py +++ b/PIL/ImageTk.py @@ -25,7 +25,8 @@ # See the README file for information on usage and redistribution. # -import Tkinter, Image +import Tkinter +from . import Image ## # The ImageTk module contains support to create and modify diff --git a/PIL/ImageTransform.py b/PIL/ImageTransform.py index cc323d3b9..12e3aaa57 100644 --- a/PIL/ImageTransform.py +++ b/PIL/ImageTransform.py @@ -13,7 +13,7 @@ # See the README file for information on usage and redistribution. # -import Image +from . import Image class Transform(Image.ImageTransformHandler): def __init__(self, data): diff --git a/PIL/ImageWin.py b/PIL/ImageWin.py index f98725f74..8e0cd9728 100644 --- a/PIL/ImageWin.py +++ b/PIL/ImageWin.py @@ -17,7 +17,7 @@ # See the README file for information on usage and redistribution. # -import Image +from . import Image ## # The ImageWin module contains support to create and display diff --git a/PIL/ImtImagePlugin.py b/PIL/ImtImagePlugin.py index af955deea..853344b24 100644 --- a/PIL/ImtImagePlugin.py +++ b/PIL/ImtImagePlugin.py @@ -19,7 +19,7 @@ __version__ = "0.2" import re -import Image, ImageFile +from . import Image, ImageFile # # -------------------------------------------------------------------- diff --git a/PIL/IptcImagePlugin.py b/PIL/IptcImagePlugin.py index ba4b8dcc8..c42df00ae 100644 --- a/PIL/IptcImagePlugin.py +++ b/PIL/IptcImagePlugin.py @@ -20,7 +20,7 @@ from __future__ import print_function __version__ = "0.3" -import Image, ImageFile +from . import Image, ImageFile import os, tempfile @@ -219,7 +219,7 @@ Image.register_extension("IPTC", ".iim") def getiptcinfo(im): - import TiffImagePlugin, JpegImagePlugin + from . import TiffImagePlugin, JpegImagePlugin import StringIO data = None diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index d7c7f151f..bf1ef4a26 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -35,7 +35,7 @@ __version__ = "0.6" import array, struct -import Image, ImageFile +from . import Image, ImageFile def i16(c,o=0): return ord(c[o+1]) + (ord(c[o])<<8) @@ -361,7 +361,8 @@ class JpegImageFile(ImageFile.ImageFile): # Extract EXIF information. This method is highly experimental, # and is likely to be replaced with something better in a future # version. - import TiffImagePlugin, StringIO + from . import TiffImagePlugin + import StringIO def fixup(value): if len(value) == 1: return value[0] diff --git a/PIL/McIdasImagePlugin.py b/PIL/McIdasImagePlugin.py index 62ee52cf8..3f77ea198 100644 --- a/PIL/McIdasImagePlugin.py +++ b/PIL/McIdasImagePlugin.py @@ -19,7 +19,7 @@ __version__ = "0.2" import struct -import Image, ImageFile +from . import Image, ImageFile def _accept(s): return s[:8] == "\x00\x00\x00\x00\x00\x00\x00\x04" diff --git a/PIL/MicImagePlugin.py b/PIL/MicImagePlugin.py index f13c28202..b8ee55e06 100644 --- a/PIL/MicImagePlugin.py +++ b/PIL/MicImagePlugin.py @@ -20,8 +20,8 @@ __version__ = "0.1" -import Image, TiffImagePlugin -from OleFileIO import * +from . import Image, TiffImagePlugin +from .OleFileIO import * # diff --git a/PIL/MpegImagePlugin.py b/PIL/MpegImagePlugin.py index 043a5b07b..310cd2363 100644 --- a/PIL/MpegImagePlugin.py +++ b/PIL/MpegImagePlugin.py @@ -15,7 +15,7 @@ __version__ = "0.1" -import Image, ImageFile +from . import Image, ImageFile # # Bitstream parser diff --git a/PIL/MspImagePlugin.py b/PIL/MspImagePlugin.py index 8de9f2506..0d9ee1686 100644 --- a/PIL/MspImagePlugin.py +++ b/PIL/MspImagePlugin.py @@ -19,7 +19,7 @@ __version__ = "0.1" -import Image, ImageFile +from . import Image, ImageFile # diff --git a/PIL/PSDraw.py b/PIL/PSDraw.py index ede863de7..2679b34ca 100644 --- a/PIL/PSDraw.py +++ b/PIL/PSDraw.py @@ -17,7 +17,7 @@ from __future__ import print_function -import EpsImagePlugin +from . import EpsImagePlugin ## # Simple Postscript graphics interface. diff --git a/PIL/PalmImagePlugin.py b/PIL/PalmImagePlugin.py index 7d26ce6d6..6c6376830 100644 --- a/PIL/PalmImagePlugin.py +++ b/PIL/PalmImagePlugin.py @@ -9,7 +9,7 @@ __version__ = "1.0" -import Image, ImageFile +from . import Image, ImageFile _Palm8BitColormapValues = ( ( 255, 255, 255 ), ( 255, 204, 255 ), ( 255, 153, 255 ), ( 255, 102, 255 ), diff --git a/PIL/PcdImagePlugin.py b/PIL/PcdImagePlugin.py index 56d8f1dc0..3c0ba9ed0 100644 --- a/PIL/PcdImagePlugin.py +++ b/PIL/PcdImagePlugin.py @@ -18,7 +18,7 @@ __version__ = "0.1" -import Image, ImageFile +from . import Image, ImageFile ## # Image plugin for PhotoCD images. This plugin only reads the 768x512 diff --git a/PIL/PcfFontFile.py b/PIL/PcfFontFile.py index 0a97a0734..0bc53aa5b 100644 --- a/PIL/PcfFontFile.py +++ b/PIL/PcfFontFile.py @@ -16,8 +16,8 @@ # See the README file for information on usage and redistribution. # -import Image -import FontFile +from . import Image +from . import FontFile # -------------------------------------------------------------------- # declarations diff --git a/PIL/PcxImagePlugin.py b/PIL/PcxImagePlugin.py index a61f248b9..597cc6d9e 100644 --- a/PIL/PcxImagePlugin.py +++ b/PIL/PcxImagePlugin.py @@ -27,7 +27,7 @@ __version__ = "0.6" -import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette def i16(c,o): return ord(c[o]) + (ord(c[o+1])<<8) diff --git a/PIL/PdfImagePlugin.py b/PIL/PdfImagePlugin.py index 4f957765c..54b40b0f3 100644 --- a/PIL/PdfImagePlugin.py +++ b/PIL/PdfImagePlugin.py @@ -22,7 +22,7 @@ __version__ = "0.4" -import Image, ImageFile +from . import Image, ImageFile import StringIO diff --git a/PIL/PixarImagePlugin.py b/PIL/PixarImagePlugin.py index a428977ab..60abd3c12 100644 --- a/PIL/PixarImagePlugin.py +++ b/PIL/PixarImagePlugin.py @@ -21,7 +21,7 @@ __version__ = "0.1" -import Image, ImageFile +from . import Image, ImageFile # # helpers diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index ccdec2829..be9ddcc30 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -37,7 +37,8 @@ __version__ = "0.9" import re -import Image, ImageFile, ImagePalette, zlib +from . import Image, ImageFile, ImagePalette +import zlib def i16(c): diff --git a/PIL/PpmImagePlugin.py b/PIL/PpmImagePlugin.py index a64dc7ae2..7e50f8c67 100644 --- a/PIL/PpmImagePlugin.py +++ b/PIL/PpmImagePlugin.py @@ -19,7 +19,7 @@ __version__ = "0.2" import string -import Image, ImageFile +from . import Image, ImageFile # # -------------------------------------------------------------------- diff --git a/PIL/PsdImagePlugin.py b/PIL/PsdImagePlugin.py index 5ef1d808d..0fc07d488 100644 --- a/PIL/PsdImagePlugin.py +++ b/PIL/PsdImagePlugin.py @@ -18,7 +18,7 @@ __version__ = "0.4" -import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette MODES = { # (photoshop mode, bits) -> (pil mode, required channels) diff --git a/PIL/SgiImagePlugin.py b/PIL/SgiImagePlugin.py index c65b91aca..94b51fc17 100644 --- a/PIL/SgiImagePlugin.py +++ b/PIL/SgiImagePlugin.py @@ -21,7 +21,7 @@ __version__ = "0.2" -import Image, ImageFile +from . import Image, ImageFile def i16(c): diff --git a/PIL/SpiderImagePlugin.py b/PIL/SpiderImagePlugin.py index ce8cdace6..2b3f4fc20 100644 --- a/PIL/SpiderImagePlugin.py +++ b/PIL/SpiderImagePlugin.py @@ -35,7 +35,7 @@ from __future__ import print_function -import Image, ImageFile +from . import Image, ImageFile import os, struct, sys def isInt(f): @@ -173,7 +173,7 @@ class SpiderImageFile(ImageFile.ImageFile): # returns a ImageTk.PhotoImage object, after rescaling to 0..255 def tkPhotoImage(self): - import ImageTk + from . import ImageTk return ImageTk.PhotoImage(self.convert2byte(), palette=256) # -------------------------------------------------------------------- diff --git a/PIL/SunImagePlugin.py b/PIL/SunImagePlugin.py index 37ad4855b..1e03a028e 100644 --- a/PIL/SunImagePlugin.py +++ b/PIL/SunImagePlugin.py @@ -20,7 +20,7 @@ __version__ = "0.3" -import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette def i16(c): diff --git a/PIL/TarIO.py b/PIL/TarIO.py index 7d625402f..f56987f6e 100644 --- a/PIL/TarIO.py +++ b/PIL/TarIO.py @@ -14,7 +14,7 @@ # See the README file for information on usage and redistribution. # -import ContainerIO +from . import ContainerIO ## # A file object that provides read access to a given member of a TAR diff --git a/PIL/TgaImagePlugin.py b/PIL/TgaImagePlugin.py index ce0f8b465..945e8d1e7 100644 --- a/PIL/TgaImagePlugin.py +++ b/PIL/TgaImagePlugin.py @@ -19,7 +19,7 @@ __version__ = "0.3" -import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette # diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index b74bc2455..960430f2f 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -43,8 +43,8 @@ from __future__ import print_function __version__ = "1.3.5" -import Image, ImageFile -import ImagePalette +from . import Image, ImageFile +from . import ImagePalette import array, sys import collections @@ -351,7 +351,7 @@ class ImageFileDirectory(collections.MutableMapping): tag, typ = i16(ifd), i16(ifd, 2) if Image.DEBUG: - import TiffTags + from . import TiffTags tagname = TiffTags.TAGS.get(tag, "unknown") typname = TiffTags.TYPES.get(typ, "unknown") print("tag: %s (%d)" % (tagname, tag), end=' ') @@ -448,7 +448,7 @@ class ImageFileDirectory(collections.MutableMapping): data = "".join(map(o32, value)) if Image.DEBUG: - import TiffTags + from . import TiffTags tagname = TiffTags.TAGS.get(tag, "unknown") typname = TiffTags.TYPES.get(typ, "unknown") print("save: %s (%d)" % (tagname, tag), end=' ') diff --git a/PIL/WalImageFile.py b/PIL/WalImageFile.py index b05ea4a19..16c2d90d5 100644 --- a/PIL/WalImageFile.py +++ b/PIL/WalImageFile.py @@ -23,7 +23,7 @@ from __future__ import print_function -import Image +from . import Image def i32(c, o=0): return ord(c[o])+(ord(c[o+1])<<8)+(ord(c[o+2])<<16)+(ord(c[o+3])<<24) diff --git a/PIL/WmfImagePlugin.py b/PIL/WmfImagePlugin.py index 5191f1e51..93124be23 100644 --- a/PIL/WmfImagePlugin.py +++ b/PIL/WmfImagePlugin.py @@ -17,7 +17,7 @@ __version__ = "0.2" -import Image, ImageFile +from . import Image, ImageFile _handler = None diff --git a/PIL/XVThumbImagePlugin.py b/PIL/XVThumbImagePlugin.py index 2c6242ed8..57de388f9 100644 --- a/PIL/XVThumbImagePlugin.py +++ b/PIL/XVThumbImagePlugin.py @@ -19,7 +19,7 @@ __version__ = "0.1" -import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette # standard color palette for thumbnails (RGB332) PALETTE = "" diff --git a/PIL/XbmImagePlugin.py b/PIL/XbmImagePlugin.py index ef1a42610..1c6bbcbc3 100644 --- a/PIL/XbmImagePlugin.py +++ b/PIL/XbmImagePlugin.py @@ -22,7 +22,7 @@ __version__ = "0.6" import re -import Image, ImageFile +from . import Image, ImageFile # XBM header xbm_head = re.compile( diff --git a/PIL/XpmImagePlugin.py b/PIL/XpmImagePlugin.py index 10d089e1d..88723529b 100644 --- a/PIL/XpmImagePlugin.py +++ b/PIL/XpmImagePlugin.py @@ -19,7 +19,7 @@ __version__ = "0.2" import re -import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette # XPM header xpm_head = re.compile("\"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)") From a45300765190cf8ac799fd9c3c701161b9c9905e Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Mon, 15 Oct 2012 21:52:31 -0500 Subject: [PATCH 26/61] py3k: sys.maxint renamed to sys.maxsize --- Scripts/explode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/explode.py b/Scripts/explode.py index 3ff96dd2c..d6c2cd935 100644 --- a/Scripts/explode.py +++ b/Scripts/explode.py @@ -36,7 +36,7 @@ class Interval: self.hilo.append((hi, lo)) if not self.hilo: - self.hilo = [(sys.maxint, 0)] + self.hilo = [(sys.maxsize, 0)] def __getitem__(self, index): From f6fa0941fde3b983d1ac4255dabd107a483afc31 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Mon, 15 Oct 2012 21:54:12 -0500 Subject: [PATCH 27/61] py3k: Convert backticks to repr() Backticks are no longer valid syntax for repr(). --- Sane/sane.py | 2 +- Scripts/pildriver.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sane/sane.py b/Sane/sane.py index 0a70073e5..387435b57 100644 --- a/Sane/sane.py +++ b/Sane/sane.py @@ -85,7 +85,7 @@ active: %s settable: %s\n""" % (self.py_name, curValue, self.index, self.title, self.desc, TYPE_STR[self.type], UNIT_STR[self.unit], - `self.constraint`, active, settable) + repr(self.constraint), active, settable) return s diff --git a/Scripts/pildriver.py b/Scripts/pildriver.py index ed3e5b4fe..980919288 100644 --- a/Scripts/pildriver.py +++ b/Scripts/pildriver.py @@ -483,7 +483,7 @@ class PILDriver: self.push(list[0]) list = list[1:] if self.verbose: - print("Stack: " + `self.stack`) + print("Stack: " + repr(self.stack)) top = self.top() if type(top) != type(""): continue; From dfb1b144d86d0882d10e7b31e1ff2d4779c6cb4c Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Mon, 15 Oct 2012 22:14:10 -0500 Subject: [PATCH 28/61] py3k: Remove Image types import For awhile now, str == type("") and so on. So we use the appropriate int, str, basestring, or tuple type where applicable. --- PIL/Image.py | 32 ++++++++++---------------------- PIL/ImagePalette.py | 2 +- docs/pythondoc-PIL.Image.rst | 1 - 3 files changed, 11 insertions(+), 24 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 33154480d..ddd9fb8cc 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -71,27 +71,15 @@ from . import ImageMode import os, sys # type stuff -from types import IntType, StringType, TupleType import collections import numbers -try: - UnicodeStringType = type(unicode("")) - ## - # (Internal) Checks if an object is a string. If the current - # Python version supports Unicode, this checks for both 8-bit - # and Unicode strings. +if sys.version_info >= (3,0): def isStringType(t): - return isinstance(t, StringType) or isinstance(t, UnicodeStringType) -except NameError: + return isinstance(t, str) +else: def isStringType(t): - return isinstance(t, StringType) - -## -# (Internal) Checks if an object is a tuple. - -def isTupleType(t): - return isinstance(t, TupleType) + return isinstance(t, basestring) ## # (Internal) Checks if an object is an image object. @@ -372,7 +360,7 @@ def _getdecoder(mode, decoder_name, args, extra=()): # tweak arguments if args is None: args = () - elif not isTupleType(args): + elif not isinstance(args, tuple): args = (args,) try: @@ -388,7 +376,7 @@ def _getencoder(mode, encoder_name, args, extra=()): # tweak arguments if args is None: args = () - elif not isTupleType(args): + elif not isinstance(args, tuple): args = (args,) try: @@ -523,7 +511,7 @@ class Image: "Return image as a binary string" # may pass tuple instead of argument list - if len(args) == 1 and isTupleType(args[0]): + if len(args) == 1 and isinstance(args[0], tuple): args = args[0] if encoder_name == "raw" and args == (): @@ -578,7 +566,7 @@ class Image: "Load data to image from binary string" # may pass tuple instead of argument list - if len(args) == 1 and isTupleType(args[0]): + if len(args) == 1 and isinstance(args[0], tuple): args = args[0] # default format @@ -1789,7 +1777,7 @@ def fromstring(mode, size, data, decoder_name="raw", *args): "Load image from string" # may pass tuple instead of argument list - if len(args) == 1 and isTupleType(args[0]): + if len(args) == 1 and isinstance(args[0], tuple): args = args[0] if decoder_name == "raw" and args == (): @@ -1836,7 +1824,7 @@ def frombuffer(mode, size, data, decoder_name="raw", *args): "Load image from string or buffer" # may pass tuple instead of argument list - if len(args) == 1 and isTupleType(args[0]): + if len(args) == 1 and isinstance(args[0], tuple): args = args[0] if decoder_name == "raw": diff --git a/PIL/ImagePalette.py b/PIL/ImagePalette.py index b5baf7cbf..1a00813b7 100644 --- a/PIL/ImagePalette.py +++ b/PIL/ImagePalette.py @@ -53,7 +53,7 @@ class ImagePalette: # experimental: given an rgb tuple, allocate palette entry if self.rawmode: raise ValueError("palette contains raw palette data") - if Image.isTupleType(color): + if isinstance(color, tuple): try: return self.colors[color] except KeyError: diff --git a/docs/pythondoc-PIL.Image.rst b/docs/pythondoc-PIL.Image.rst index 081711aaa..5ddb7bd51 100644 --- a/docs/pythondoc-PIL.Image.rst +++ b/docs/pythondoc-PIL.Image.rst @@ -116,7 +116,6 @@ The PIL.Image Module **isDirectory(f)** [`# <#PIL.Image.isDirectory-function>`_] **isImageType(t)** [`# <#PIL.Image.isImageType-function>`_] **isStringType(t)** [`# <#PIL.Image.isStringType-function>`_] -**isTupleType(t)** [`# <#PIL.Image.isTupleType-function>`_] **merge(mode, bands)** [`# <#PIL.Image.merge-function>`_] *mode* From fa348ee9fe4de303ab5e03ef50795496d403fb1d Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Tue, 16 Oct 2012 21:56:20 -0500 Subject: [PATCH 29/61] py3k: __builtin__ module is now builtins --- PIL/GdImageFile.py | 9 +++++++-- PIL/Image.py | 12 ++++++++---- PIL/ImageMath.py | 9 +++++++-- PIL/WalImageFile.py | 9 +++++++-- 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/PIL/GdImageFile.py b/PIL/GdImageFile.py index e211d3528..0aebe857c 100644 --- a/PIL/GdImageFile.py +++ b/PIL/GdImageFile.py @@ -27,6 +27,12 @@ __version__ = "0.1" from . import ImageFile, ImagePalette +try: + import builtins +except ImportError: + import __builtin__ + builtins = __builtin__ + def i16(c): return ord(c[1]) + (ord(c[0])<<8) @@ -73,9 +79,8 @@ def open(fp, mode = "r"): raise ValueError("bad mode") if type(fp) == type(""): - import __builtin__ filename = fp - fp = __builtin__.open(fp, "rb") + fp = builtins.open(fp, "rb") else: filename = "" diff --git a/PIL/Image.py b/PIL/Image.py index ddd9fb8cc..f2a7de141 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -66,6 +66,12 @@ except ImportError as v: RuntimeWarning ) +try: + import builtins +except ImportError: + import __builtin__ + builtins = __builtin__ + from . import ImageMode import os, sys @@ -1419,8 +1425,7 @@ class Image: save_handler = SAVE[format.upper()] # unknown format if isStringType(fp): - import __builtin__ - fp = __builtin__.open(fp, "wb") + fp = builtins.open(fp, "wb") close = 1 else: close = 0 @@ -1937,9 +1942,8 @@ def open(fp, mode="r"): raise ValueError("bad mode") if isStringType(fp): - import __builtin__ filename = fp - fp = __builtin__.open(fp, "rb") + fp = builtins.open(fp, "rb") else: filename = "" diff --git a/PIL/ImageMath.py b/PIL/ImageMath.py index f43501400..86f6bdeab 100644 --- a/PIL/ImageMath.py +++ b/PIL/ImageMath.py @@ -19,6 +19,12 @@ from . import Image import _imagingmath import sys +try: + import builtins +except ImportError: + import __builtin__ + builtins = __builtin__ + VERBOSE = 0 def _isconstant(v): @@ -206,8 +212,7 @@ def eval(expression, _dict={}, **kw): if hasattr(v, "im"): args[k] = _Operand(v) - import __builtin__ - out =__builtin__.eval(expression, args) + out = builtins.eval(expression, args) try: return out.im except AttributeError: diff --git a/PIL/WalImageFile.py b/PIL/WalImageFile.py index 16c2d90d5..d67d6d62d 100644 --- a/PIL/WalImageFile.py +++ b/PIL/WalImageFile.py @@ -25,6 +25,12 @@ from __future__ import print_function from . import Image +try: + import builtins +except ImportError: + import __builtin__ + builtins = __builtin__ + def i32(c, o=0): return ord(c[o])+(ord(c[o+1])<<8)+(ord(c[o+2])<<16)+(ord(c[o+3])<<24) @@ -44,8 +50,7 @@ def open(filename): if hasattr(filename, "read"): fp = filename else: - import __builtin__ - fp = __builtin__.open(filename, "rb") + fp = builtins.open(filename, "rb") # read header fields header = fp.read(32+24+32+12) From dda0e9a3edbca54db826ab9734a2de78c7aa6e11 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Tue, 16 Oct 2012 21:58:29 -0500 Subject: [PATCH 30/61] py3k: Tkinter module is now tkinter --- PIL/ImageTk.py | 28 +++++++++++++++++----------- Scripts/enhancer.py | 6 +++++- Scripts/painter.py | 6 +++++- Scripts/player.py | 6 +++++- Scripts/thresholder.py | 6 +++++- Scripts/viewer.py | 6 +++++- 6 files changed, 42 insertions(+), 16 deletions(-) diff --git a/PIL/ImageTk.py b/PIL/ImageTk.py index 5563b679f..d4602b3d1 100644 --- a/PIL/ImageTk.py +++ b/PIL/ImageTk.py @@ -25,7 +25,13 @@ # See the README file for information on usage and redistribution. # -import Tkinter +try: + import tkinter +except ImportError: + import Tkinter + tkinter = Tkinter + del Tkinter + from . import Image ## @@ -46,9 +52,9 @@ def _pilbitmap_check(): if _pilbitmap_ok is None: try: im = Image.new("1", (1,1)) - Tkinter.BitmapImage(data="PIL:%d" % im.im.id) + tkinter.BitmapImage(data="PIL:%d" % im.im.id) _pilbitmap_ok = 1 - except Tkinter.TclError: + except tkinter.TclError: _pilbitmap_ok = 0 return _pilbitmap_ok @@ -111,7 +117,7 @@ class PhotoImage: self.__mode = mode self.__size = size - self.__photo = apply(Tkinter.PhotoImage, (), kw) + self.__photo = apply(tkinter.PhotoImage, (), kw) self.tk = self.__photo.tk if image: self.paste(image) @@ -176,7 +182,7 @@ class PhotoImage: try: tk.call("PyImagingPhoto", self.__photo, block.id) - except Tkinter.TclError as v: + except tkinter.TclError as v: # activate Tkinter hook try: import _imagingtk @@ -185,7 +191,7 @@ class PhotoImage: except AttributeError: _imagingtk.tkinit(id(tk), 0) tk.call("PyImagingPhoto", self.__photo, block.id) - except (ImportError, AttributeError, Tkinter.TclError): + except (ImportError, AttributeError, tkinter.TclError): raise # configuration problem; cannot attach to Tkinter # -------------------------------------------------------------------- @@ -233,7 +239,7 @@ class BitmapImage: else: # slow but safe way kw["data"] = image.tobitmap() - self.__photo = apply(Tkinter.BitmapImage, (), kw) + self.__photo = apply(tkinter.BitmapImage, (), kw) def __del__(self): name = self.__photo.name @@ -280,18 +286,18 @@ def getimage(photo): def _show(image, title): - class UI(Tkinter.Label): + class UI(tkinter.Label): def __init__(self, master, im): if im.mode == "1": self.image = BitmapImage(im, foreground="white", master=master) else: self.image = PhotoImage(im, master=master) - Tkinter.Label.__init__(self, master, image=self.image, + tkinter.Label.__init__(self, master, image=self.image, bg="black", bd=0) - if not Tkinter._default_root: + if not tkinter._default_root: raise IOError("tkinter not initialized") - top = Tkinter.Toplevel() + top = tkinter.Toplevel() if title: top.title(title) UI(top, image).pack() diff --git a/Scripts/enhancer.py b/Scripts/enhancer.py index 957b51c8d..546172f81 100644 --- a/Scripts/enhancer.py +++ b/Scripts/enhancer.py @@ -6,7 +6,11 @@ # drag the slider to modify the image. # -from Tkinter import * +try: + from tkinter import * +except ImportError: + from Tkinter import * + from PIL import Image, ImageTk, ImageEnhance import sys diff --git a/Scripts/painter.py b/Scripts/painter.py index efe307386..d6821e476 100644 --- a/Scripts/painter.py +++ b/Scripts/painter.py @@ -8,7 +8,11 @@ # the image into a set of tiles. # -from Tkinter import * +try: + from tkinter import * +except ImportError: + from Tkinter import * + from PIL import Image, ImageTk import sys diff --git a/Scripts/player.py b/Scripts/player.py index b8239ebfc..016e8b1d3 100644 --- a/Scripts/player.py +++ b/Scripts/player.py @@ -5,7 +5,11 @@ from __future__ import print_function -from Tkinter import * +try: + from tkinter import * +except ImportError: + from Tkinter import * + from PIL import Image, ImageTk import sys diff --git a/Scripts/thresholder.py b/Scripts/thresholder.py index eb5109872..9a8610892 100644 --- a/Scripts/thresholder.py +++ b/Scripts/thresholder.py @@ -6,7 +6,11 @@ # as a dynamically updated overlay # -from Tkinter import * +try: + from tkinter import * +except ImportError: + from Tkinter import * + from PIL import Image, ImageTk import sys diff --git a/Scripts/viewer.py b/Scripts/viewer.py index 6cb42a553..8c26e9c5a 100644 --- a/Scripts/viewer.py +++ b/Scripts/viewer.py @@ -5,7 +5,11 @@ from __future__ import print_function -from Tkinter import * +try: + from tkinter import * +except ImportError: + from Tkinter import * + from PIL import Image, ImageTk # From 260c1fad14fd55130a3c356508b025f3e41484bf Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Tue, 16 Oct 2012 22:01:19 -0500 Subject: [PATCH 31/61] py3k: Convert StringIO.StringIO to io.BytesIO io.BytesIO is already in 2.6. Some of the more obvious bytes literals are marked in this commit. --- PIL/ImageFileIO.py | 6 +-- PIL/ImageFont.py | 10 ++--- PIL/ImageGrab.py | 4 +- PIL/ImageTk.py | 8 ++-- PIL/IptcImagePlugin.py | 4 +- PIL/JpegImagePlugin.py | 4 +- PIL/OleFileIO.py | 8 ++-- PIL/PdfImagePlugin.py | 100 ++++++++++++++++++++--------------------- 8 files changed, 72 insertions(+), 72 deletions(-) diff --git a/PIL/ImageFileIO.py b/PIL/ImageFileIO.py index c12a25738..e2f53e38a 100644 --- a/PIL/ImageFileIO.py +++ b/PIL/ImageFileIO.py @@ -12,7 +12,7 @@ # See the README file for information on usage and redistribution. # -from StringIO import StringIO +from io import BytesIO ## # The ImageFileIO module can be used to read an image from a @@ -23,7 +23,7 @@ from StringIO import StringIO # # @see ImageFile#Parser -class ImageFileIO(StringIO): +class ImageFileIO(BytesIO): ## # Adds buffering to a stream file object, in order to @@ -36,4 +36,4 @@ class ImageFileIO(StringIO): def __init__(self, fp): data = fp.read() - StringIO.__init__(self, data) + BytesIO.__init__(self, data) diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index 939887db2..0bb846fd3 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -255,12 +255,12 @@ def load_path(filename): def load_default(): "Load a default font." - from StringIO import StringIO + from io import BytesIO import base64 f = ImageFont() f._load_pilfont_data( # courB08 - StringIO(base64.decodestring(''' + BytesIO(base64.decodestring(b''' UElMZm9udAo7Ozs7OzsxMDsKREFUQQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA @@ -352,7 +352,7 @@ AJsAEQAGAAAAAP/6AAX//wCbAAoAoAAPAAYAAAAA//oABQABAKAACgClABEABgAA////+AAGAAAA pQAKAKwAEgAGAAD////4AAYAAACsAAoAswASAAYAAP////gABgAAALMACgC6ABIABgAA////+QAG AAAAugAKAMEAEQAGAAD////4AAYAAgDBAAoAyAAUAAYAAP////kABQACAMgACgDOABMABgAA//// +QAGAAIAzgAKANUAEw== -''')), Image.open(StringIO(base64.decodestring(''' +''')), Image.open(BytesIO(base64.decodestring(b''' iVBORw0KGgoAAAANSUhEUgAAAx4AAAAUAQAAAAArMtZoAAAEwElEQVR4nABlAJr/AHVE4czCI/4u Mc4b7vuds/xzjz5/3/7u/n9vMe7vnfH/9++vPn/xyf5zhxzjt8GHw8+2d83u8x27199/nxuQ6Od9 M43/5z2I+9n9ZtmDBwMQECDRQw/eQIQohJXxpBCNVE6QCCAAAAD//wBlAJr/AgALyj1t/wINwq0g @@ -385,8 +385,8 @@ if __name__ == "__main__": font = "../Images/courB08" print(" f._load_pilfont_data(") print(" # %s" % os.path.basename(font)) - print(" StringIO(base64.decodestring('''") + print(" BytesIO(base64.decodestring(b'''") base64.encode(open(font + ".pil", "rb"), sys.stdout) - print("''')), Image.open(StringIO(base64.decodestring('''") + print("''')), Image.open(BytesIO(base64.decodestring(b'''") base64.encode(open(font + ".pbm", "rb"), sys.stdout) print("'''))))") diff --git a/PIL/ImageGrab.py b/PIL/ImageGrab.py index f3de6fde4..f938efa6f 100644 --- a/PIL/ImageGrab.py +++ b/PIL/ImageGrab.py @@ -67,6 +67,6 @@ def grabclipboard(): data = Image.core.grabclipboard(debug) if Image.isStringType(data): from . import BmpImagePlugin - import StringIO - return BmpImagePlugin.DibImageFile(StringIO.StringIO(data)) + import io + return BmpImagePlugin.DibImageFile(io.BytesIO(data)) return data diff --git a/PIL/ImageTk.py b/PIL/ImageTk.py index d4602b3d1..8f3b4c3e5 100644 --- a/PIL/ImageTk.py +++ b/PIL/ImageTk.py @@ -92,8 +92,8 @@ class PhotoImage: image = Image.open(kw["file"]) del kw["file"] elif "data" in kw: - from StringIO import StringIO - image = Image.open(StringIO(kw["data"])) + from io import BytesIO + image = Image.open(BytesIO(kw["data"])) del kw["data"] if hasattr(image, "mode") and hasattr(image, "size"): @@ -224,8 +224,8 @@ class BitmapImage: image = Image.open(kw["file"]) del kw["file"] elif "data" in kw: - from StringIO import StringIO - image = Image.open(StringIO(kw["data"])) + from io import BytesIO + image = Image.open(BytesIO(kw["data"])) del kw["data"] self.__mode = image.mode diff --git a/PIL/IptcImagePlugin.py b/PIL/IptcImagePlugin.py index c42df00ae..51e060a66 100644 --- a/PIL/IptcImagePlugin.py +++ b/PIL/IptcImagePlugin.py @@ -220,7 +220,7 @@ Image.register_extension("IPTC", ".iim") def getiptcinfo(im): from . import TiffImagePlugin, JpegImagePlugin - import StringIO + import io data = None @@ -279,7 +279,7 @@ def getiptcinfo(im): # parse the IPTC information chunk im.info = {} - im.fp = StringIO.StringIO(data) + im.fp = io.BytesIO(data) try: im._open() diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index bf1ef4a26..2a71544d9 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -362,7 +362,7 @@ class JpegImageFile(ImageFile.ImageFile): # and is likely to be replaced with something better in a future # version. from . import TiffImagePlugin - import StringIO + import io def fixup(value): if len(value) == 1: return value[0] @@ -373,7 +373,7 @@ class JpegImageFile(ImageFile.ImageFile): data = self.info["exif"] except KeyError: return None - file = StringIO.StringIO(data[6:]) + file = io.BytesIO(data[6:]) head = file.read(8) exif = {} # process dictionary diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index 5c1248f58..f8399eac4 100644 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -38,7 +38,7 @@ from __future__ import print_function -import StringIO +import io import sys def i16(c, o = 0): @@ -81,7 +81,7 @@ WORD_CLSID = "00020900-0000-0000-C000-000000000046" # # -------------------------------------------------------------------- -class _OleStream(StringIO.StringIO): +class _OleStream(io.BytesIO): """OLE2 Stream @@ -107,11 +107,11 @@ class _OleStream(StringIO.StringIO): data.append(fp.read(sectorsize)) sect = fat[sect] - data = "".join(data) + data = b"".join(data) # print len(data), size - StringIO.StringIO.__init__(self, data[:size]) + io.BytesIO.__init__(self, data[:size]) # # -------------------------------------------------------------------- diff --git a/PIL/PdfImagePlugin.py b/PIL/PdfImagePlugin.py index 54b40b0f3..f54491871 100644 --- a/PIL/PdfImagePlugin.py +++ b/PIL/PdfImagePlugin.py @@ -23,7 +23,7 @@ __version__ = "0.4" from . import Image, ImageFile -import StringIO +import io # @@ -37,16 +37,16 @@ import StringIO # 5. page contents def _obj(fp, obj, **dict): - fp.write("%d 0 obj\n" % obj) + fp.write(b"%d 0 obj\n" % obj) if dict: - fp.write("<<\n") + fp.write(b"<<\n") for k, v in dict.items(): if v is not None: - fp.write("/%s %s\n" % (k, v)) - fp.write(">>\n") + fp.write(b"/%s %s\n" % (k, v)) + fp.write(b">>\n") def _endobj(fp): - fp.write("endobj\n") + fp.write(b"endobj\n") ## # (Internal) Image save plugin for the PDF format. @@ -60,8 +60,8 @@ def _save(im, fp, filename): xref = [0]*(5+1) # placeholders - fp.write("%PDF-1.2\n") - fp.write("% created by PIL PDF driver " + __version__ + "\n") + fp.write(b"%PDF-1.2\n") + fp.write(b"% created by PIL PDF driver " + __version__ + "\n") # # Get image characteristics @@ -76,34 +76,34 @@ def _save(im, fp, filename): params = None if im.mode == "1": - filter = "/ASCIIHexDecode" - colorspace = "/DeviceGray" - procset = "/ImageB" # grayscale + filter = b"/ASCIIHexDecode" + colorspace = b"/DeviceGray" + procset = b"/ImageB" # grayscale bits = 1 elif im.mode == "L": - filter = "/DCTDecode" + filter = b"/DCTDecode" # params = "<< /Predictor 15 /Columns %d >>" % (width-2) - colorspace = "/DeviceGray" - procset = "/ImageB" # grayscale + colorspace = b"/DeviceGray" + procset = b"/ImageB" # grayscale elif im.mode == "P": - filter = "/ASCIIHexDecode" - colorspace = "[ /Indexed /DeviceRGB 255 <" + filter = b"/ASCIIHexDecode" + colorspace = b"[ /Indexed /DeviceRGB 255 <" palette = im.im.getpalette("RGB") for i in range(256): r = ord(palette[i*3]) g = ord(palette[i*3+1]) b = ord(palette[i*3+2]) - colorspace = colorspace + "%02x%02x%02x " % (r, g, b) - colorspace = colorspace + "> ]" - procset = "/ImageI" # indexed color + colorspace = colorspace + b"%02x%02x%02x " % (r, g, b) + colorspace = colorspace + b"> ]" + procset = b"/ImageI" # indexed color elif im.mode == "RGB": - filter = "/DCTDecode" - colorspace = "/DeviceRGB" - procset = "/ImageC" # color images + filter = b"/DCTDecode" + colorspace = b"/DeviceRGB" + procset = b"/ImageC" # color images elif im.mode == "CMYK": - filter = "/DCTDecode" - colorspace = "/DeviceCMYK" - procset = "/ImageC" # color images + filter = b"/DCTDecode" + colorspace = b"/DeviceCMYK" + procset = b"/ImageC" # color images else: raise ValueError("cannot save mode %s" % im.mode) @@ -111,25 +111,25 @@ def _save(im, fp, filename): # catalogue xref[1] = fp.tell() - _obj(fp, 1, Type = "/Catalog", - Pages = "2 0 R") + _obj(fp, 1, Type = b"/Catalog", + Pages = b"2 0 R") _endobj(fp) # # pages xref[2] = fp.tell() - _obj(fp, 2, Type = "/Pages", + _obj(fp, 2, Type = b"/Pages", Count = 1, - Kids = "[4 0 R]") + Kids = b"[4 0 R]") _endobj(fp) # # image - op = StringIO.StringIO() + op = io.BytesIO() - if filter == "/ASCIIHexDecode": + if filter == b"/ASCIIHexDecode": if bits == 1: # FIXME: the hex encoder doesn't support packed 1-bit # images; do things the hard way... @@ -137,18 +137,18 @@ def _save(im, fp, filename): im = Image.new("L", (len(data), 1), None) im.putdata(data) ImageFile._save(im, op, [("hex", (0,0)+im.size, 0, im.mode)]) - elif filter == "/DCTDecode": + elif filter == b"/DCTDecode": ImageFile._save(im, op, [("jpeg", (0,0)+im.size, 0, im.mode)]) - elif filter == "/FlateDecode": + elif filter == b"/FlateDecode": ImageFile._save(im, op, [("zip", (0,0)+im.size, 0, im.mode)]) - elif filter == "/RunLengthDecode": + elif filter == b"/RunLengthDecode": ImageFile._save(im, op, [("packbits", (0,0)+im.size, 0, im.mode)]) else: raise ValueError("unsupported PDF filter (%s)" % filter) xref[3] = fp.tell() - _obj(fp, 3, Type = "/XObject", - Subtype = "/Image", + _obj(fp, 3, Type = b"/XObject", + Subtype = b"/Image", Width = width, # * 72.0 / resolution, Height = height, # * 72.0 / resolution, Length = len(op.getvalue()), @@ -157,9 +157,9 @@ def _save(im, fp, filename): DecodeParams = params, ColorSpace = colorspace) - fp.write("stream\n") + fp.write(b"stream\n") fp.write(op.getvalue()) - fp.write("\nendstream\n") + fp.write(b"\nendstream\n") _endobj(fp) @@ -168,37 +168,37 @@ def _save(im, fp, filename): xref[4] = fp.tell() _obj(fp, 4) - fp.write("<<\n/Type /Page\n/Parent 2 0 R\n"\ - "/Resources <<\n/ProcSet [ /PDF %s ]\n"\ - "/XObject << /image 3 0 R >>\n>>\n"\ - "/MediaBox [ 0 0 %d %d ]\n/Contents 5 0 R\n>>\n" %\ + fp.write(b"<<\n/Type /Page\n/Parent 2 0 R\n"\ + b"/Resources <<\n/ProcSet [ /PDF %s ]\n"\ + b"/XObject << /image 3 0 R >>\n>>\n"\ + b"/MediaBox [ 0 0 %d %d ]\n/Contents 5 0 R\n>>\n" %\ (procset, int(width * 72.0 /resolution) , int(height * 72.0 / resolution))) _endobj(fp) # # page contents - op = StringIO.StringIO() + op = io.BytesIO() - op.write("q %d 0 0 %d 0 0 cm /image Do Q\n" % (int(width * 72.0 / resolution), int(height * 72.0 / resolution))) + op.write(b"q %d 0 0 %d 0 0 cm /image Do Q\n" % (int(width * 72.0 / resolution), int(height * 72.0 / resolution))) xref[5] = fp.tell() _obj(fp, 5, Length = len(op.getvalue())) - fp.write("stream\n") + fp.write(b"stream\n") fp.write(op.getvalue()) - fp.write("\nendstream\n") + fp.write(b"\nendstream\n") _endobj(fp) # # trailer startxref = fp.tell() - fp.write("xref\n0 %d\n0000000000 65535 f \n" % len(xref)) + fp.write(b"xref\n0 %d\n0000000000 65535 f \n" % len(xref)) for x in xref[1:]: - fp.write("%010d 00000 n \n" % x) - fp.write("trailer\n<<\n/Size %d\n/Root 1 0 R\n>>\n" % len(xref)) - fp.write("startxref\n%d\n%%%%EOF\n" % startxref) + fp.write(b"%010d 00000 n \n" % x) + fp.write(b"trailer\n<<\n/Size %d\n/Root 1 0 R\n>>\n" % len(xref)) + fp.write(b"startxref\n%d\n%%%%EOF\n" % startxref) fp.flush() # From e2283c664b674554ceb0b8a50b77c293c5babff0 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Tue, 16 Oct 2012 22:07:08 -0500 Subject: [PATCH 32/61] py3k: Integer long literals are no longer valid syntax --- PIL/EpsImagePlugin.py | 4 ++-- PIL/GbrImagePlugin.py | 2 +- PIL/MpegImagePlugin.py | 2 +- PIL/OleFileIO.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py index 9910f382a..032a51baf 100644 --- a/PIL/EpsImagePlugin.py +++ b/PIL/EpsImagePlugin.py @@ -116,7 +116,7 @@ class PSFile: def _accept(prefix): - return prefix[:4] == "%!PS" or i32(prefix) == 0xC6D3D0C5L + return prefix[:4] == "%!PS" or i32(prefix) == 0xC6D3D0C5 ## # Image plugin for Encapsulated Postscript. This plugin supports only @@ -141,7 +141,7 @@ class EpsImageFile(ImageFile.ImageFile): offset = 0 fp.seek(0, 2) length = fp.tell() - elif i32(s) == 0xC6D3D0C5L: + elif i32(s) == 0xC6D3D0C5: offset = i32(s[4:]) length = i32(s[8:]) fp.seek(offset) diff --git a/PIL/GbrImagePlugin.py b/PIL/GbrImagePlugin.py index 3efcb730c..fb96ac9b4 100644 --- a/PIL/GbrImagePlugin.py +++ b/PIL/GbrImagePlugin.py @@ -16,7 +16,7 @@ from . import Image, ImageFile def i32(c): - return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24L) + return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) def _accept(prefix): return i32(prefix) >= 20 and i32(prefix[4:8]) == 1 diff --git a/PIL/MpegImagePlugin.py b/PIL/MpegImagePlugin.py index 310cd2363..39155d9f9 100644 --- a/PIL/MpegImagePlugin.py +++ b/PIL/MpegImagePlugin.py @@ -38,7 +38,7 @@ class BitStream: continue self.bitbuffer = (self.bitbuffer << 8) + c self.bits = self.bits + 8 - return self.bitbuffer >> (self.bits - bits) & (1L << bits) - 1 + return self.bitbuffer >> (self.bits - bits) & (1 << bits) - 1 def skip(self, bits): while self.bits < bits: diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index f8399eac4..c399b0286 100644 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -485,7 +485,7 @@ class OleFileIO: value = long(i32(s, offset+4)) + (long(i32(s, offset+8))<<32) # FIXME: this is a 64-bit int: "number of 100ns periods # since Jan 1,1601". Should map this to Python time - value = value / 10000000L # seconds + value = value / 10000000 # seconds elif type == VT_UI1: value = ord(s[offset+4]) elif type == VT_CLSID: From 48cf699fe6b4187d0c856523e3e2c4ba03dd4a12 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Sat, 20 Oct 2012 16:05:13 -0500 Subject: [PATCH 33/61] py3k: Change apply() to unpacking syntax apply() is no longer available in py3k. --- PIL/Image.py | 14 +++++++------- PIL/ImageFilter.py | 2 +- PIL/ImageTk.py | 4 ++-- PIL/ImageWin.py | 2 +- Scripts/pilconvert.py | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index f2a7de141..bc7c742f5 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -372,8 +372,8 @@ def _getdecoder(mode, decoder_name, args, extra=()): try: # get decoder decoder = getattr(core, decoder_name + "_decoder") - # print decoder, (mode,) + args + extra - return apply(decoder, (mode,) + args + extra) + # print(decoder, mode, args + extra) + return decoder(mode, *args + extra) except AttributeError: raise IOError("decoder %s not available" % decoder_name) @@ -388,8 +388,8 @@ def _getencoder(mode, encoder_name, args, extra=()): try: # get encoder encoder = getattr(core, encoder_name + "_encoder") - # print encoder, (mode,) + args + extra - return apply(encoder, (mode,) + args + extra) + # print(encoder, mode, args + extra) + return encoder(mode, *args + extra) except AttributeError: raise IOError("encoder %s not available" % encoder_name) @@ -601,7 +601,7 @@ class Image: "Explicitly load pixel data." if self.im and self.palette and self.palette.dirty: # realize palette - apply(self.im.putpalette, self.palette.getdata()) + self.im.putpalette(*self.palette.getdata()) self.palette.dirty = 0 self.palette.mode = "RGB" self.palette.rawmode = None @@ -2129,8 +2129,8 @@ def register_extension(id, extension): def _show(image, **options): # override me, as necessary - apply(_showxv, (image,), options) + _showxv(image, **options) def _showxv(image, title=None, **options): from . import ImageShow - apply(ImageShow.show, (image, title), options) + ImageShow.show(image, title, **options) diff --git a/PIL/ImageFilter.py b/PIL/ImageFilter.py index cfdad5fc5..f4f98be79 100644 --- a/PIL/ImageFilter.py +++ b/PIL/ImageFilter.py @@ -54,7 +54,7 @@ class Kernel(Filter): def filter(self, image): if image.mode == "P": raise ValueError("cannot filter palette images") - return apply(image.filter, self.filterargs) + return image.filter(*self.filterargs) class BuiltinFilter(Kernel): def __init__(self): diff --git a/PIL/ImageTk.py b/PIL/ImageTk.py index 8f3b4c3e5..34832d8d9 100644 --- a/PIL/ImageTk.py +++ b/PIL/ImageTk.py @@ -117,7 +117,7 @@ class PhotoImage: self.__mode = mode self.__size = size - self.__photo = apply(tkinter.PhotoImage, (), kw) + self.__photo = tkinter.PhotoImage(**kw) self.tk = self.__photo.tk if image: self.paste(image) @@ -239,7 +239,7 @@ class BitmapImage: else: # slow but safe way kw["data"] = image.tobitmap() - self.__photo = apply(tkinter.BitmapImage, (), kw) + self.__photo = tkinter.BitmapImage(**kw) def __del__(self): name = self.__photo.name diff --git a/PIL/ImageWin.py b/PIL/ImageWin.py index 8e0cd9728..c2d72b959 100644 --- a/PIL/ImageWin.py +++ b/PIL/ImageWin.py @@ -179,7 +179,7 @@ class Window: ) def __dispatcher(self, action, *args): - return apply(getattr(self, "ui_handle_" + action), args) + return getattr(self, "ui_handle_" + action)(*args) def ui_handle_clear(self, dc, x0, y0, x1, y1): pass diff --git a/Scripts/pilconvert.py b/Scripts/pilconvert.py index 64900db8a..949d277c3 100644 --- a/Scripts/pilconvert.py +++ b/Scripts/pilconvert.py @@ -90,9 +90,9 @@ try: im.draft(convert, im.size) im = im.convert(convert) if format: - apply(im.save, (argv[1], format), options) + im.save(argv[1], format, **options) else: - apply(im.save, (argv[1],), options) + im.save(argv[1], **options) except: print("cannot convert image", end=' ') print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1])) From 31c454b925db76c0fbea8d1e1179cfc24f057032 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Tue, 16 Oct 2012 22:39:56 -0500 Subject: [PATCH 34/61] py3k: 2to3's "idiom" filter This is, I guess, a few things the Python devs were just fed up with. * "while 1" is now "while True" * Types are compared with isinstance instead of == * Sort a list in one go with sorted() My own twist is to also replace type('') with str, type(()) with tuple, type([]) with list, type(1) with int, and type(5000.0) with float. --- PIL/ArgImagePlugin.py | 2 +- PIL/BdfFontFile.py | 10 +++++----- PIL/ContainerIO.py | 4 ++-- PIL/EpsImagePlugin.py | 2 +- PIL/GdImageFile.py | 2 +- PIL/GifImagePlugin.py | 2 +- PIL/ImImagePlugin.py | 2 +- PIL/Image.py | 5 ++--- PIL/ImageCms.py | 18 +++++++++--------- PIL/ImageColor.py | 2 +- PIL/ImageFile.py | 6 +++--- PIL/ImageMath.py | 2 +- PIL/ImageOps.py | 4 ++-- PIL/ImagePalette.py | 2 +- PIL/ImageStat.py | 2 +- PIL/ImtImagePlugin.py | 2 +- PIL/IptcImagePlugin.py | 6 +++--- PIL/JpegImagePlugin.py | 2 +- PIL/OleFileIO.py | 11 +++++------ PIL/PaletteFile.py | 2 +- PIL/PngImagePlugin.py | 4 ++-- PIL/PpmImagePlugin.py | 6 +++--- PIL/TarIO.py | 2 +- PIL/TiffImagePlugin.py | 9 ++++----- PIL/XVThumbImagePlugin.py | 2 +- PIL/XpmImagePlugin.py | 2 +- Sane/sane.py | 4 ++-- Scripts/explode.py | 2 +- Scripts/pilconvert.py | 3 +-- Scripts/pildriver.py | 4 ++-- Scripts/pilfile.py | 3 +-- Scripts/player.py | 4 ++-- 32 files changed, 64 insertions(+), 69 deletions(-) diff --git a/PIL/ArgImagePlugin.py b/PIL/ArgImagePlugin.py index c192dd955..21f05b7a7 100644 --- a/PIL/ArgImagePlugin.py +++ b/PIL/ArgImagePlugin.py @@ -458,7 +458,7 @@ class ArgImageFile(ImageFile.ImageFile): self.fp = self.arg.fp - while 1: + while True: # # process chunks diff --git a/PIL/BdfFontFile.py b/PIL/BdfFontFile.py index 5b38d5957..236dd1fdd 100644 --- a/PIL/BdfFontFile.py +++ b/PIL/BdfFontFile.py @@ -43,7 +43,7 @@ bdf_spacing = { def bdf_char(f): # skip to STARTCHAR - while 1: + while True: s = f.readline() if not s: return None @@ -53,7 +53,7 @@ def bdf_char(f): # load symbol properties props = {} - while 1: + while True: s = f.readline() if not s or s[:6] == "BITMAP": break @@ -62,7 +62,7 @@ def bdf_char(f): # load bitmap bitmap = [] - while 1: + while True: s = f.readline() if not s or s[:7] == "ENDCHAR": break @@ -98,7 +98,7 @@ class BdfFontFile(FontFile.FontFile): props = {} comments = [] - while 1: + while True: s = fp.readline() if not s or s[:13] == "ENDPROPERTIES": break @@ -123,7 +123,7 @@ class BdfFontFile(FontFile.FontFile): # print "#", i font = [] - while 1: + while True: c = bdf_char(fp) if not c: break diff --git a/PIL/ContainerIO.py b/PIL/ContainerIO.py index dff50e6bf..f4a15b813 100644 --- a/PIL/ContainerIO.py +++ b/PIL/ContainerIO.py @@ -92,7 +92,7 @@ class ContainerIO: def readline(self): s = "" - while 1: + while True: c = self.read(1) if not c: break @@ -108,7 +108,7 @@ class ContainerIO: def readlines(self): l = [] - while 1: + while True: s = self.readline() if not s: break diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py index 032a51baf..09e8802e9 100644 --- a/PIL/EpsImagePlugin.py +++ b/PIL/EpsImagePlugin.py @@ -261,7 +261,7 @@ class EpsImageFile(ImageFile.ImageFile): id = id[1:-1] # Scan forward to the actual image data - while 1: + while True: s = fp.readline() if not s: break diff --git a/PIL/GdImageFile.py b/PIL/GdImageFile.py index 0aebe857c..16749cbdb 100644 --- a/PIL/GdImageFile.py +++ b/PIL/GdImageFile.py @@ -78,7 +78,7 @@ def open(fp, mode = "r"): if mode != "r": raise ValueError("bad mode") - if type(fp) == type(""): + if isinstance(fp, str): filename = fp fp = builtins.open(fp, "rb") else: diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 138627574..c48a53608 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -125,7 +125,7 @@ class GifImageFile(ImageFile.ImageFile): self.palette = self.global_palette - while 1: + while True: s = self.fp.read(1) if not s or s == ";": diff --git a/PIL/ImImagePlugin.py b/PIL/ImImagePlugin.py index a105ecaef..a521eac27 100644 --- a/PIL/ImImagePlugin.py +++ b/PIL/ImImagePlugin.py @@ -125,7 +125,7 @@ class ImImageFile(ImageFile.ImageFile): self.rawmode = "L" - while 1: + while True: s = self.fp.read(1) diff --git a/PIL/Image.py b/PIL/Image.py index bc7c742f5..4b8fc2b5e 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -218,8 +218,7 @@ def _conv_type_shape(im): return shape+(extra,), typ -MODES = list(_MODEINFO.keys()) -MODES.sort() +MODES = sorted(_MODEINFO.keys()) # raw modes that may be memory mapped. NOTE: if you change this, you # may have to modify the stride calculation in map.c too! @@ -532,7 +531,7 @@ class Image: bufsize = max(65536, self.size[0] * 4) # see RawEncode.c data = [] - while 1: + while True: l, s, d = e.encode(bufsize) data.append(d) if s: diff --git a/PIL/ImageCms.py b/PIL/ImageCms.py index 0f2dd0e4d..747af51d1 100644 --- a/PIL/ImageCms.py +++ b/PIL/ImageCms.py @@ -124,7 +124,7 @@ FLAGS = { _MAX_FLAG = 0 for flag in FLAGS.values(): - if isinstance(flag, type(0)): + if isinstance(flag, int): _MAX_FLAG = _MAX_FLAG | flag # --------------------------------------------------------------------. @@ -290,10 +290,10 @@ def profileToProfile(im, inputProfile, outputProfile, renderingIntent=INTENT_PER if outputMode is None: outputMode = im.mode - if type(renderingIntent) != type(1) or not (0 <= renderingIntent <=3): + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <=3): raise PyCMSError("renderingIntent must be an integer between 0 and 3") - if type(flags) != type(1) or not (0 <= flags <= _MAX_FLAG): + if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG) try: @@ -398,10 +398,10 @@ def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent """ - if type(renderingIntent) != type(1) or not (0 <= renderingIntent <=3): + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <=3): raise PyCMSError("renderingIntent must be an integer between 0 and 3") - if type(flags) != type(1) or not (0 <= flags <= _MAX_FLAG): + if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG) try: @@ -489,10 +489,10 @@ def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMo """ - if type(renderingIntent) != type(1) or not (0 <= renderingIntent <=3): + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <=3): raise PyCMSError("renderingIntent must be an integer between 0 and 3") - if type(flags) != type(1) or not (0 <= flags <= _MAX_FLAG): + if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG) try: @@ -597,9 +597,9 @@ def createProfile(colorSpace, colorTemp=-1): raise PyCMSError("Color space not supported for on-the-fly profile creation (%s)" % colorSpace) if colorSpace == "LAB": - if type(colorTemp) == type(5000.0): + if isinstance(colorTemp, float): colorTemp = int(colorTemp + 0.5) - if type (colorTemp) != type (5000): + if not isinstance(colorTemp, int): raise PyCMSError("Color temperature must be a positive integer, \"%s\" not valid" % colorTemp) try: diff --git a/PIL/ImageColor.py b/PIL/ImageColor.py index 247a00efc..e1cfd5ab9 100644 --- a/PIL/ImageColor.py +++ b/PIL/ImageColor.py @@ -41,7 +41,7 @@ def getrgb(color): rgb = None # found color in cache if rgb: - if isinstance(rgb, type(())): + if isinstance(rgb, tuple): return rgb colormap[color] = rgb = getrgb(rgb) return rgb diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index 41782d5d6..31c904742 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -195,7 +195,7 @@ class ImageFile(Image.Image): continue b = prefix t = len(b) - while 1: + while True: s = read(self.decodermaxblock) if not s: self.tile = [] @@ -315,7 +315,7 @@ class _ParserFile: def readline(self): # FIXME: this is slow! s = "" - while 1: + while True: c = self.read(1) if not c: break @@ -483,7 +483,7 @@ def _save(im, fp, tile): if o > 0: fp.seek(o, 0) e.setimage(im.im, b) - while 1: + while True: l, s, d = e.encode(bufsize) fp.write(d) if s: diff --git a/PIL/ImageMath.py b/PIL/ImageMath.py index 86f6bdeab..6fb621962 100644 --- a/PIL/ImageMath.py +++ b/PIL/ImageMath.py @@ -28,7 +28,7 @@ except ImportError: VERBOSE = 0 def _isconstant(v): - return isinstance(v, type(0)) or isinstance(v, type(0.0)) + return isinstance(v, int) or isinstance(v, float) class _Operand: # wraps an image operand, providing standard operators diff --git a/PIL/ImageOps.py b/PIL/ImageOps.py index 496631aa9..35b1064c2 100644 --- a/PIL/ImageOps.py +++ b/PIL/ImageOps.py @@ -33,7 +33,7 @@ from functools import reduce # helpers def _border(border): - if type(border) is type(()): + if isinstance(border, tuple): if len(border) == 2: left, top = right, bottom = border elif len(border) == 4: @@ -275,7 +275,7 @@ def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)): # http://www.cazabon.com # ensure inputs are valid - if type(centering) != type([]): + if not isinstance(centering, list): centering = [centering[0], centering[1]] if centering[0] > 1.0 or centering[0] < 0.0: diff --git a/PIL/ImagePalette.py b/PIL/ImagePalette.py index 1a00813b7..9992e3fe0 100644 --- a/PIL/ImagePalette.py +++ b/PIL/ImagePalette.py @@ -76,7 +76,7 @@ class ImagePalette: # (experimental) save palette to text file if self.rawmode: raise ValueError("palette contains raw palette data") - if type(fp) == type(""): + if isinstance(fp, str): fp = open(fp, "w") fp.write("# Palette\n") fp.write("# Mode: %s\n" % self.mode) diff --git a/PIL/ImageStat.py b/PIL/ImageStat.py index e2e63cabc..7e343884b 100644 --- a/PIL/ImageStat.py +++ b/PIL/ImageStat.py @@ -53,7 +53,7 @@ class Stat: self.h = image_or_list.histogram() except AttributeError: self.h = image_or_list # assume it to be a histogram list - if type(self.h) != type([]): + if not isinstance(self.h, list): raise TypeError("first argument must be image or list") self.bands = list(range(len(self.h) // 256)) diff --git a/PIL/ImtImagePlugin.py b/PIL/ImtImagePlugin.py index 853344b24..8fcd46a1e 100644 --- a/PIL/ImtImagePlugin.py +++ b/PIL/ImtImagePlugin.py @@ -45,7 +45,7 @@ class ImtImageFile(ImageFile.ImageFile): xsize = ysize = 0 - while 1: + while True: s = self.fp.read(1) if not s: diff --git a/PIL/IptcImagePlugin.py b/PIL/IptcImagePlugin.py index 51e060a66..6a7ebc29f 100644 --- a/PIL/IptcImagePlugin.py +++ b/PIL/IptcImagePlugin.py @@ -98,7 +98,7 @@ class IptcImageFile(ImageFile.ImageFile): if sz != size[0]: return 0 y = 1 - while 1: + while True: self.fp.seek(sz, 1) t, s = self.field() if t != (8, 10): @@ -111,7 +111,7 @@ class IptcImageFile(ImageFile.ImageFile): def _open(self): # load descriptive fields - while 1: + while True: offset = self.fp.tell() tag, size = self.field() if not tag or tag == (8,10): @@ -180,7 +180,7 @@ class IptcImageFile(ImageFile.ImageFile): # To simplify access to the extracted file, # prepend a PPM header o.write("P5\n%d %d\n255\n" % self.size) - while 1: + while True: type, size = self.field() if type != (8, 10): break diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 2a71544d9..466b926db 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -287,7 +287,7 @@ class JpegImageFile(ImageFile.ImageFile): self.applist = [] self.icclist = [] - while 1: + while True: s = s + self.fp.read(1) diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index c399b0286..7fb45f077 100644 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -175,7 +175,7 @@ class _OleDirectoryEntry: if right != -1: # 0xFFFFFFFFL: # and then back to the left sid = right - while 1: + while True: left, right, child = sidlist[sid][4] if left == -1: # 0xFFFFFFFFL: break @@ -183,7 +183,7 @@ class _OleDirectoryEntry: sid = left else: # couldn't move right; move up instead - while 1: + while True: ptr = stack[-1] del stack[-1] left, right, child = sidlist[ptr][4] @@ -267,7 +267,7 @@ class OleFileIO: def open(self, filename): """Open an OLE2 file""" - if type(filename) == type(""): + if isinstance(filename, str): self.fp = open(filename, "rb") else: self.fp = filename @@ -345,7 +345,7 @@ class OleFileIO: # create list of sid entries self.sidlist = [] - while 1: + while True: entry = fp.read(128) if not entry: break @@ -525,8 +525,7 @@ if __name__ == "__main__": if file[-1][0] == "\005": print(file) props = ole.getproperties(file) - props = list(props.items()) - props.sort() + props = sorted(props.items()) for k, v in props: print(" ", k, v) except IOError as v: diff --git a/PIL/PaletteFile.py b/PIL/PaletteFile.py index 7df149301..c09245bf9 100644 --- a/PIL/PaletteFile.py +++ b/PIL/PaletteFile.py @@ -24,7 +24,7 @@ class PaletteFile: self.palette = [(i, i, i) for i in range(256)] - while 1: + while True: s = fp.readline() diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index be9ddcc30..d8d794ac1 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -138,7 +138,7 @@ class ChunkStream: cids = [] - while 1: + while True: cid, pos, len = self.read() if cid == endchunk: break @@ -323,7 +323,7 @@ class PngImageFile(ImageFile.ImageFile): self.png = PngStream(self.fp) - while 1: + while True: # # get next chunk diff --git a/PIL/PpmImagePlugin.py b/PIL/PpmImagePlugin.py index 7e50f8c67..ea665f7a9 100644 --- a/PIL/PpmImagePlugin.py +++ b/PIL/PpmImagePlugin.py @@ -49,7 +49,7 @@ class PpmImageFile(ImageFile.ImageFile): format_description = "Pbmplus image" def _token(self, s = ""): - while 1: # read until next whitespace + while True: # read until next whitespace c = self.fp.read(1) if not c or c in string.whitespace: break @@ -71,8 +71,8 @@ class PpmImageFile(ImageFile.ImageFile): self.mode = rawmode = mode for ix in range(3): - while 1: - while 1: + while True: + while True: s = self.fp.read(1) if s not in string.whitespace: break diff --git a/PIL/TarIO.py b/PIL/TarIO.py index f56987f6e..e0608c72f 100644 --- a/PIL/TarIO.py +++ b/PIL/TarIO.py @@ -32,7 +32,7 @@ class TarIO(ContainerIO.ContainerIO): fh = open(tarfile, "rb") - while 1: + while True: s = fh.read(512) if len(s) != 512: diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 960430f2f..93c188115 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -401,8 +401,7 @@ class ImageFileDirectory(collections.MutableMapping): fp.write(o16(len(self.tags))) # always write in ascending tag order - tags = list(self.tags.items()) - tags.sort() + tags = sorted(self.tags.items()) directory = [] append = directory.append @@ -425,7 +424,7 @@ class ImageFileDirectory(collections.MutableMapping): elif typ == 7: # untyped data data = value = "".join(value) - elif type(value[0]) is type(""): + elif isinstance(value[0], str): # string data typ = 2 data = value = "\0".join(value) + "\0" @@ -737,10 +736,10 @@ SAVE_INFO = { def _cvt_res(value): # convert value to TIFF rational number -- (numerator, denominator) - if type(value) in (type([]), type(())): + if isinstance(value, collections.Sequence): assert(len(value) % 2 == 0) return value - if type(value) == type(1): + if isinstance(value, int): return (value, 1) value = float(value) return (int(value * 65536), 65536) diff --git a/PIL/XVThumbImagePlugin.py b/PIL/XVThumbImagePlugin.py index 57de388f9..9d1d534fb 100644 --- a/PIL/XVThumbImagePlugin.py +++ b/PIL/XVThumbImagePlugin.py @@ -47,7 +47,7 @@ class XVThumbImageFile(ImageFile.ImageFile): self.fp.readline() # skip info comments - while 1: + while True: s = self.fp.readline() if not s: raise SyntaxError("Unexpected EOF reading XV thumbnail file") diff --git a/PIL/XpmImagePlugin.py b/PIL/XpmImagePlugin.py index 88723529b..ff7d3d2a7 100644 --- a/PIL/XpmImagePlugin.py +++ b/PIL/XpmImagePlugin.py @@ -42,7 +42,7 @@ class XpmImageFile(ImageFile.ImageFile): raise SyntaxError("not an XPM file") # skip forward to next string - while 1: + while True: s = self.fp.readline() if not s: raise SyntaxError("broken XPM file") diff --git a/Sane/sane.py b/Sane/sane.py index 387435b57..331776f95 100644 --- a/Sane/sane.py +++ b/Sane/sane.py @@ -55,7 +55,7 @@ class Option: def f(x): if x=='-': return '_' else: return x - if type(self.name)!=type(''): self.py_name=str(self.name) + if not isinstance(self.name, str): self.py_name=str(self.name) else: self.py_name=''.join(map(f, self.name)) def is_active(self): @@ -174,7 +174,7 @@ class SaneDev: raise AttributeError('Inactive option: '+key) if not _sane.OPTION_IS_SETTABLE(opt.cap): raise AttributeError("Option can't be set by software: "+key) - if type(value) == int and opt.type == TYPE_FIXED: + if isinstance(value, int) and opt.type == TYPE_FIXED: # avoid annoying errors of backend if int is given instead float: value = float(value) self.last_opt = dev.set_option(opt.index, value) diff --git a/Scripts/explode.py b/Scripts/explode.py index d6c2cd935..950ef7b50 100644 --- a/Scripts/explode.py +++ b/Scripts/explode.py @@ -89,7 +89,7 @@ if html: html = open(file+".html", "w") html.write("\n\n") -while 1: +while True: if frames[ix]: im.save(outfile % ix) diff --git a/Scripts/pilconvert.py b/Scripts/pilconvert.py index 949d277c3..588c0dfab 100644 --- a/Scripts/pilconvert.py +++ b/Scripts/pilconvert.py @@ -56,8 +56,7 @@ for o, a in opt: if o == "-f": Image.init() - id = Image.ID[:] - id.sort() + id = sorted(Image.ID) print("Supported formats (* indicates output format):") for i in id: if i in Image.SAVE: diff --git a/Scripts/pildriver.py b/Scripts/pildriver.py index 980919288..755b2d05d 100644 --- a/Scripts/pildriver.py +++ b/Scripts/pildriver.py @@ -485,7 +485,7 @@ class PILDriver: if self.verbose: print("Stack: " + repr(self.stack)) top = self.top() - if type(top) != type(""): + if not isinstance(top, str): continue; funcname = "do_" + top if not hasattr(self, funcname): @@ -510,7 +510,7 @@ if __name__ == '__main__': driver.execute(sys.argv[1:]) else: print("PILDriver says hello.") - while 1: + while True: try: line = raw_input('pildriver> '); except EOFError: diff --git a/Scripts/pilfile.py b/Scripts/pilfile.py index 4d33d23c0..33c7dc7e7 100644 --- a/Scripts/pilfile.py +++ b/Scripts/pilfile.py @@ -45,8 +45,7 @@ verbose = quiet = verify = 0 for o, a in opt: if o == "-f": Image.init() - id = Image.ID[:] - id.sort() + id = sorted(Image.ID) print("Supported formats:") for i in id: print(i, end=' ') diff --git a/Scripts/player.py b/Scripts/player.py index 016e8b1d3..b1435ba4c 100644 --- a/Scripts/player.py +++ b/Scripts/player.py @@ -42,7 +42,7 @@ class AppletDisplay: class UI(Label): def __init__(self, master, im): - if type(im) == type([]): + if isinstance(im, list): # list of images self.im = im[1:] im = self.im[0] @@ -71,7 +71,7 @@ class UI(Label): def next(self): - if type(self.im) == type([]): + if isinstance(self.im, list): try: im = self.im[0] From af94e2d93d0657a0df84fbbf60ed0b40cfbd5866 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Tue, 16 Oct 2012 22:54:38 -0500 Subject: [PATCH 35/61] py3k: Map long type to int int() is really now long() in py3k, but to avoid breaking 2.6/2.7, we leave the integer types where they are and just map long to int in py3k. Also, pretty proud of myself for finding an easy way of detecting py3k. --- PIL/OleFileIO.py | 3 +++ PIL/WmfImagePlugin.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index 7fb45f077..db1c04f06 100644 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -41,6 +41,9 @@ from __future__ import print_function import io import sys +if str != bytes: + long = int + def i16(c, o = 0): return ord(c[o])+(ord(c[o+1])<<8) diff --git a/PIL/WmfImagePlugin.py b/PIL/WmfImagePlugin.py index 93124be23..ca3f194a9 100644 --- a/PIL/WmfImagePlugin.py +++ b/PIL/WmfImagePlugin.py @@ -21,6 +21,9 @@ from . import Image, ImageFile _handler = None +if str != bytes: + long = int + ## # Install application-specific WMF image handler. # From 80c2fa60aef3831001c887d969c7543e0549ee18 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Tue, 16 Oct 2012 22:58:14 -0500 Subject: [PATCH 36/61] py3k: Sort by key, not cmp Py3k only supports key because it's more efficient. Not even sure 2to3 checks for this. --- PIL/ImageFile.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index 31c904742..60c732384 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -56,9 +56,9 @@ def raise_ioerror(error): # -------------------------------------------------------------------- # Helpers -def _tilesort(t1, t2): +def _tilesort(t): # sort on offset - return cmp(t1[2], t2[2]) + return t[2] # # -------------------------------------------------------------------- @@ -178,7 +178,7 @@ class ImageFile(Image.Image): if not self.map: # sort tiles in file order - self.tile.sort(_tilesort) + self.tile.sort(key=_tilesort) try: # FIXME: This is a hack to handle TIFF's JpegTables tag. @@ -470,7 +470,7 @@ def _save(im, fp, tile): im.load() if not hasattr(im, "encoderconfig"): im.encoderconfig = () - tile.sort(_tilesort) + tile.sort(key=_tilesort) # FIXME: make MAXBLOCK a configuration parameter bufsize = max(MAXBLOCK, im.size[0] * 4) # see RawEncode.c try: From 275a895952b334c012467263e32ff9782d38741c Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Fri, 19 Oct 2012 07:54:55 -0500 Subject: [PATCH 37/61] py3k: Type coercion is gone Types aren't automatically converted for operations for you. --- PIL/Image.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 4b8fc2b5e..f82e52981 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -396,11 +396,16 @@ def _getencoder(mode, encoder_name, args, extra=()): # -------------------------------------------------------------------- # Simple expression analyzer +def coerce_e(value): + return value if isinstance(value, _E) else _E(value) + class _E: - def __init__(self, data): self.data = data - def __coerce__(self, other): return self, _E(other) - def __add__(self, other): return _E((self.data, "__add__", other.data)) - def __mul__(self, other): return _E((self.data, "__mul__", other.data)) + def __init__(self, data): + self.data = data + def __add__(self, other): + return _E((self.data, "__add__", coerce_e(other).data)) + def __mul__(self, other): + return _E((self.data, "__mul__", coerce_e(other).data)) def _getscaleoffset(expr): stub = ["stub"] From 8b704e3005e3b7e7089ffee1c17c7117b293807a Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Sat, 20 Oct 2012 16:27:45 -0500 Subject: [PATCH 38/61] py3k: Publish both frombytes and fromstring in C modules for old Python To ease the transition, frombytes and tobytes need to be declared in both versions. --- _imagingcms.c | 3 +-- display.c | 15 +++++++-------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/_imagingcms.c b/_imagingcms.c index 910dc6eeb..65958f33a 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -501,9 +501,8 @@ cms_get_display_profile_win32(PyObject* self, PyObject* args) static PyMethodDef pyCMSdll_methods[] = { {"profile_open", cms_profile_open, 1}, -#if PY_VERSION_HEX >= 0x03000000 {"profile_frombytes", cms_profile_fromstring, 1}, -#else +#if PY_VERSION_HEX < 0x03000000 {"profile_fromstring", cms_profile_fromstring, 1}, #endif diff --git a/display.c b/display.c index 9571b1cbe..1ce537c64 100644 --- a/display.c +++ b/display.c @@ -180,7 +180,7 @@ _releasedc(ImagingDisplayObject* display, PyObject* args) } static PyObject* -_fromstring(ImagingDisplayObject* display, PyObject* args) +_frombytes(ImagingDisplayObject* display, PyObject* args) { char* ptr; int bytes; @@ -205,7 +205,7 @@ _fromstring(ImagingDisplayObject* display, PyObject* args) } static PyObject* -_tostring(ImagingDisplayObject* display, PyObject* args) +_tobytes(ImagingDisplayObject* display, PyObject* args) { #if PY_VERSION_HEX >= 0x03000000 if (!PyArg_ParseTuple(args, ":tobytes")) @@ -227,12 +227,11 @@ static struct PyMethodDef methods[] = { {"query_palette", (PyCFunction)_query_palette, 1}, {"getdc", (PyCFunction)_getdc, 1}, {"releasedc", (PyCFunction)_releasedc, 1}, -#if PY_VERSION_HEX >= 0x03000000 - {"frombytes", (PyCFunction)_fromstring, 1}, - {"tobytes", (PyCFunction)_tostring, 1}, -#else - {"fromstring", (PyCFunction)_fromstring, 1}, - {"tostring", (PyCFunction)_tostring, 1}, + {"frombytes", (PyCFunction)_frombytes, 1}, + {"tobytes", (PyCFunction)_tobytes, 1}, +#if PY_VERSION_HEX < 0x03000000 + {"fromstring", (PyCFunction)_frombytes, 1}, + {"tostring", (PyCFunction)_tobytes, 1}, #endif {NULL, NULL} /* sentinel */ }; From a7e3b2e47b2521ed15ea70db162393ca6ac00e0e Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Sat, 20 Oct 2012 16:01:53 -0500 Subject: [PATCH 39/61] py3k: The big push There are two main issues fixed with this commit: * bytes vs. str: All file, image, and palette data are now handled as bytes. A new _binary module consolidates the hacks needed to do this across Python versions. tostring/fromstring methods have been renamed to tobytes/frombytes, but the Python 2.6/2.7 versions alias them to the old names for compatibility. Users should move to tobytes/frombytes. One other potentially-breaking change is that text data in image files (such as tags, comments) are now explicitly handled with a specific character encoding in mind. This works well with the Unicode str in Python 3, but may trip up old code expecting a straight byte-for-byte translation to a Python string. This also required a change to Gohlke's tags tests (in Tests/test_file_png.py) to expect Unicode strings from the code. * True div vs. floor div: Many division operations used the "/" operator to do floor division, which is now the "//" operator in Python 3. These were fixed. As of this commit, on the first pass, I have one failing test (improper handling of a slice object in a C module, test_imagepath.py) in Python 3, and three that that I haven't tried running yet (test_imagegl, test_imagegrab, and test_imageqt). I also haven't tested anything on Windows. All but the three skipped tests run flawlessly against Pythons 2.6 and 2.7. --- PIL/ArgImagePlugin.py | 22 ++--- PIL/BdfFontFile.py | 30 +++---- PIL/BmpImagePlugin.py | 39 ++++----- PIL/BufrStubImagePlugin.py | 2 +- PIL/CurImagePlugin.py | 26 +++--- PIL/DcxImagePlugin.py | 5 +- PIL/EpsImagePlugin.py | 63 +++++++------- PIL/FitsStubImagePlugin.py | 2 +- PIL/FliImagePlugin.py | 24 +++--- PIL/FontFile.py | 16 ++-- PIL/FpxImagePlugin.py | 4 +- PIL/GbrImagePlugin.py | 9 +- PIL/GdImageFile.py | 5 +- PIL/GifImagePlugin.py | 89 ++++++++++---------- PIL/GimpGradientFile.py | 13 +-- PIL/GimpPaletteFile.py | 11 +-- PIL/GribStubImagePlugin.py | 2 +- PIL/Hdf5StubImagePlugin.py | 2 +- PIL/IcnsImagePlugin.py | 28 ++++--- PIL/IcoImagePlugin.py | 24 +++--- PIL/ImImagePlugin.py | 42 ++++++---- PIL/Image.py | 112 ++++++++++++++----------- PIL/ImageCms.py | 2 +- PIL/ImageColor.py | 2 +- PIL/ImageFile.py | 13 +-- PIL/ImageFilter.py | 4 +- PIL/ImageFont.py | 6 +- PIL/ImageGrab.py | 4 +- PIL/ImageMath.py | 27 +++--- PIL/ImageOps.py | 22 ++--- PIL/ImagePalette.py | 22 +++-- PIL/ImageQt.py | 6 +- PIL/ImageStat.py | 2 +- PIL/ImageWin.py | 21 +++-- PIL/ImtImagePlugin.py | 8 +- PIL/IptcImagePlugin.py | 30 ++++--- PIL/JpegImagePlugin.py | 53 ++++++------ PIL/McIdasImagePlugin.py | 2 +- PIL/MpegImagePlugin.py | 5 +- PIL/MspImagePlugin.py | 16 ++-- PIL/OleFileIO.py | 29 ++++--- PIL/PaletteFile.py | 8 +- PIL/PalmImagePlugin.py | 24 +++--- PIL/PcdImagePlugin.py | 8 +- PIL/PcfFontFile.py | 32 ++++---- PIL/PcxImagePlugin.py | 36 ++++---- PIL/PdfImagePlugin.py | 48 ++++++----- PIL/PixarImagePlugin.py | 11 +-- PIL/PngImagePlugin.py | 141 +++++++++++++++++--------------- PIL/PpmImagePlugin.py | 42 +++++----- PIL/PsdImagePlugin.py | 21 +++-- PIL/SgiImagePlugin.py | 15 ++-- PIL/SunImagePlugin.py | 12 +-- PIL/TarIO.py | 4 +- PIL/TgaImagePlugin.py | 47 +++++------ PIL/TiffImagePlugin.py | 87 +++++++++----------- PIL/WalImageFile.py | 107 ++++++++++++------------ PIL/WmfImagePlugin.py | 37 ++++----- PIL/XVThumbImagePlugin.py | 14 ++-- PIL/XbmImagePlugin.py | 28 +++---- PIL/XpmImagePlugin.py | 27 +++--- PIL/_binary.py | 52 ++++++++++++ Sane/demo_numarray.py | 4 +- Tests/test_file_png.py | 16 ++-- docs/pythondoc-PIL.Image.rst | 27 +++--- docs/pythondoc-PIL.ImageWin.rst | 9 +- 66 files changed, 882 insertions(+), 819 deletions(-) create mode 100644 PIL/_binary.py diff --git a/PIL/ArgImagePlugin.py b/PIL/ArgImagePlugin.py index 21f05b7a7..69b50acdc 100644 --- a/PIL/ArgImagePlugin.py +++ b/PIL/ArgImagePlugin.py @@ -24,9 +24,9 @@ __version__ = "0.4" from . import Image, ImageFile, ImagePalette -from .PngImagePlugin import i16, i32, ChunkStream, _MODES +from .PngImagePlugin import i8, i16, i32, ChunkStream, _MODES -MAGIC = "\212ARG\r\n\032\n" +MAGIC = b"\212ARG\r\n\032\n" # -------------------------------------------------------------------- # ARG parser @@ -67,7 +67,7 @@ class ArgStream(ChunkStream): s = self.fp.read(bytes) self.size = i32(s), i32(s[4:]) try: - self.mode, self.rawmode = _MODES[(ord(s[8]), ord(s[9]))] + self.mode, self.rawmode = _MODES[(i8(s[8]), i8(s[9]))] except: raise SyntaxError("unknown ARG mode") @@ -154,14 +154,14 @@ class ArgStream(ChunkStream): size = i32(s), i32(s[4:]) try: - mode, rawmode = _MODES[(ord(s[8]), ord(s[9]))] + mode, rawmode = _MODES[(i8(s[8]), i8(s[9]))] except: raise SyntaxError("unknown image mode") if full: - if ord(s[12]): + if i8(s[12]): pass # interlace not yet supported - if ord(s[11]): + if i8(s[11]): raise SyntaxError("unknown filter category") return size, mode, rawmode @@ -236,7 +236,7 @@ class ArgStream(ChunkStream): self.im = Image.core.new(mode, size) self.decoder = Image.core.zip_decoder(rawmode) self.decoder.setimage(self.im, (0,0) + size) - self.data = "" + self.data = b"" return s @@ -252,7 +252,7 @@ class ArgStream(ChunkStream): size, mode, rawmode = self.__getmodesize(s) # delta header - diff = ord(s[13]) + diff = i8(s[13]) offs = i32(s[14:18]), i32(s[18:22]) bbox = offs + (offs[0]+size[0], offs[1]+size[1]) @@ -269,7 +269,7 @@ class ArgStream(ChunkStream): self.decoder = Image.core.zip_decoder(rawmode) self.decoder.setimage(self.im, (0,0) + size) - self.data = "" + self.data = b"" return s @@ -289,7 +289,7 @@ class ArgStream(ChunkStream): self.im = Image.core.new(mode, size) self.decoder = Image.core.jpeg_decoder(rawmode) self.decoder.setimage(self.im, (0,0) + size) - self.data = "" + self.data = b"" return s @@ -309,7 +309,7 @@ class ArgStream(ChunkStream): self.im = Image.core.new(mode, size) self.decoder = Image.core.raw_decoder(rawmode) self.decoder.setimage(self.im, (0,0) + size) - self.data = "" + self.data = b"" return s diff --git a/PIL/BdfFontFile.py b/PIL/BdfFontFile.py index 236dd1fdd..3899f90b6 100644 --- a/PIL/BdfFontFile.py +++ b/PIL/BdfFontFile.py @@ -47,27 +47,27 @@ def bdf_char(f): s = f.readline() if not s: return None - if s[:9] == "STARTCHAR": + if s[:9] == b"STARTCHAR": break - id = s[9:].strip() + id = s[9:].strip().decode('ascii') # load symbol properties props = {} while True: s = f.readline() - if not s or s[:6] == "BITMAP": + if not s or s[:6] == b"BITMAP": break - i = s.find(" ") - props[s[:i]] = s[i+1:-1] + i = s.find(b" ") + props[s[:i].decode('ascii')] = s[i+1:-1].decode('ascii') # load bitmap bitmap = [] while True: s = f.readline() - if not s or s[:7] == "ENDCHAR": + if not s or s[:7] == b"ENDCHAR": break bitmap.append(s[:-1]) - bitmap = "".join(bitmap) + bitmap = b"".join(bitmap) [x, y, l, d] = [int(s) for s in props["BBX"].split()] [dx, dy] = [int(s) for s in props["DWIDTH"].split()] @@ -75,7 +75,7 @@ def bdf_char(f): bbox = (dx, dy), (l, -d-y, x+l, -d), (0, 0, x, y) try: - im = Image.fromstring("1", (x, y), bitmap, "hex", "1") + im = Image.frombytes("1", (x, y), bitmap, "hex", "1") except ValueError: # deal with zero-width characters im = Image.new("1", (x, y)) @@ -92,7 +92,7 @@ class BdfFontFile(FontFile.FontFile): FontFile.FontFile.__init__(self) s = fp.readline() - if s[:13] != "STARTFONT 2.1": + if s[:13] != b"STARTFONT 2.1": raise SyntaxError("not a valid BDF file") props = {} @@ -100,13 +100,13 @@ class BdfFontFile(FontFile.FontFile): while True: s = fp.readline() - if not s or s[:13] == "ENDPROPERTIES": + if not s or s[:13] == b"ENDPROPERTIES": break - i = s.find(" ") - props[s[:i]] = s[i+1:-1] - if s[:i] in ["COMMENT", "COPYRIGHT"]: - if s.find("LogicalFontDescription") < 0: - comments.append(s[i+1:-1]) + i = s.find(b" ") + props[s[:i].decode('ascii')] = s[i+1:-1].decode('ascii') + if s[:i] in [b"COMMENT", b"COPYRIGHT"]: + if s.find(b"LogicalFontDescription") < 0: + comments.append(s[i+1:-1].decode('ascii')) font = props["FONT"].split("-") diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index ad59d5de9..465904e77 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -27,20 +27,19 @@ __version__ = "0.7" -from . import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette, _binary +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le +o8 = _binary.o8 +o16 = _binary.o16le +o32 = _binary.o32le # # -------------------------------------------------------------------- # Read BMP file -def i16(c): - return ord(c[0]) + (ord(c[1])<<8) - -def i32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) - - BIT2MODE = { # bits => mode, rawmode 1: ("P", "P;1"), @@ -52,7 +51,7 @@ BIT2MODE = { } def _accept(prefix): - return prefix[:2] == "BM" + return prefix[:2] == b"BM" ## # Image plugin for the Windows BMP format. @@ -134,7 +133,7 @@ class BmpImageFile(ImageFile.ImageFile): indices = list(range(colors)) for i in indices: rgb = read(lutsize)[:3] - if rgb != chr(i)*3: + if rgb != o8(i)*3: greyscale = 0 palette.append(rgb) if greyscale: @@ -145,7 +144,7 @@ class BmpImageFile(ImageFile.ImageFile): else: self.mode = "P" self.palette = ImagePalette.raw( - "BGR", "".join(palette) + "BGR", b"".join(palette) ) if not offset: @@ -162,7 +161,7 @@ class BmpImageFile(ImageFile.ImageFile): # HEAD s = self.fp.read(14) - if s[:2] != "BM": + if s[:2] != b"BM": raise SyntaxError("Not a BMP file") offset = i32(s[10:]) @@ -181,12 +180,6 @@ class DibImageFile(BmpImageFile): # -------------------------------------------------------------------- # Write BMP file -def o16(i): - return chr(i&255) + chr(i>>8&255) - -def o32(i): - return chr(i&255) + chr(i>>8&255) + chr(i>>16&255) + chr(i>>24&255) - SAVE = { "1": ("1", 1, 2), "L": ("L", 8, 256), @@ -204,13 +197,13 @@ def _save(im, fp, filename, check=0): if check: return check - stride = ((im.size[0]*bits+7)/8+3)&(~3) + stride = ((im.size[0]*bits+7)//8+3)&(~3) header = 40 # or 64 for OS/2 version 2 offset = 14 + header + colors * 4 image = stride * im.size[1] # bitmap header - fp.write("BM" + # file type (magic) + fp.write(b"BM" + # file type (magic) o32(offset+image) + # file size o32(0) + # reserved o32(offset)) # image data offset @@ -227,14 +220,14 @@ def _save(im, fp, filename, check=0): o32(colors) + # colors used o32(colors)) # colors important - fp.write("\000" * (header - 40)) # padding (for OS/2 format) + fp.write(b"\0" * (header - 40)) # padding (for OS/2 format) if im.mode == "1": for i in (0, 255): - fp.write(chr(i) * 4) + fp.write(o8(i) * 4) elif im.mode == "L": for i in range(256): - fp.write(chr(i) * 4) + fp.write(o8(i) * 4) elif im.mode == "P": fp.write(im.im.getpalette("RGB", "BGRX")) diff --git a/PIL/BufrStubImagePlugin.py b/PIL/BufrStubImagePlugin.py index e17bada90..df716a238 100644 --- a/PIL/BufrStubImagePlugin.py +++ b/PIL/BufrStubImagePlugin.py @@ -26,7 +26,7 @@ def register_handler(handler): # Image adapter def _accept(prefix): - return prefix[:4] == "BUFR" or prefix[:4] == "ZCZC" + return prefix[:4] == b"BUFR" or prefix[:4] == b"ZCZC" class BufrStubImageFile(ImageFile.StubImageFile): diff --git a/PIL/CurImagePlugin.py b/PIL/CurImagePlugin.py index c2c46e270..c35537dcb 100644 --- a/PIL/CurImagePlugin.py +++ b/PIL/CurImagePlugin.py @@ -19,21 +19,19 @@ __version__ = "0.1" -from . import Image, BmpImagePlugin +from . import Image, BmpImagePlugin, _binary # # -------------------------------------------------------------------- -def i16(c): - return ord(c[0]) + (ord(c[1])<<8) - -def i32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le def _accept(prefix): - return prefix[:4] == "\0\0\2\0" + return prefix[:4] == b"\0\0\2\0" ## # Image plugin for Windows Cursor files. @@ -53,17 +51,17 @@ class CurImageFile(BmpImagePlugin.BmpImageFile): raise SyntaxError("not an CUR file") # pick the largest cursor in the file - m = "" + m = b"" for i in range(i16(s[4:])): s = self.fp.read(16) if not m: m = s - elif ord(s[0]) > ord(m[0]) and ord(s[1]) > ord(m[1]): + elif i8(s[0]) > i8(m[0]) and i8(s[1]) > i8(m[1]): m = s - #print "width", ord(s[0]) - #print "height", ord(s[1]) - #print "colors", ord(s[2]) - #print "reserved", ord(s[3]) + #print "width", i8(s[0]) + #print "height", i8(s[1]) + #print "colors", i8(s[2]) + #print "reserved", i8(s[3]) #print "hotspot x", i16(s[4:]) #print "hotspot y", i16(s[6:]) #print "bytes", i32(s[8:]) @@ -73,7 +71,7 @@ class CurImageFile(BmpImagePlugin.BmpImageFile): self._bitmap(i32(m[12:]) + offset) # patch up the bitmap height - self.size = self.size[0], self.size[1]/2 + self.size = self.size[0], self.size[1]//2 d, e, o, a = self.tile[0] self.tile[0] = d, (0,0)+self.size, o, a diff --git a/PIL/DcxImagePlugin.py b/PIL/DcxImagePlugin.py index 9a512c28c..0a08b0bf8 100644 --- a/PIL/DcxImagePlugin.py +++ b/PIL/DcxImagePlugin.py @@ -23,14 +23,13 @@ __version__ = "0.2" -from . import Image +from . import Image, _binary from .PcxImagePlugin import PcxImageFile MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then? -def i32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) +i32 = _binary.i32le def _accept(prefix): return i32(prefix) == MAGIC diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py index 09e8802e9..b1df67fd6 100644 --- a/PIL/EpsImagePlugin.py +++ b/PIL/EpsImagePlugin.py @@ -21,16 +21,13 @@ __version__ = "0.5" import re -from . import Image, ImageFile +from . import Image, ImageFile, _binary # # -------------------------------------------------------------------- -def i32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) - -def o32(i): - return chr(i&255) + chr(i>>8&255) + chr(i>>16&255) + chr(i>>24&255) +i32 = _binary.i32le +o32 = _binary.o32le split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$") field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$") @@ -99,24 +96,24 @@ class PSFile: pos = pos - 1 return pos def readline(self): - s = "" + s = b"" if self.char: c = self.char self.char = None else: c = self.fp.read(1) - while c not in "\r\n": + while c not in b"\r\n": s = s + c c = self.fp.read(1) - if c == "\r": + if c == b"\r": self.char = self.fp.read(1) - if self.char == "\n": + if self.char == b"\n": self.char = None - return s + "\n" + return s + b"\n" def _accept(prefix): - return prefix[:4] == "%!PS" or i32(prefix) == 0xC6D3D0C5 + return prefix[:4] == b"%!PS" or i32(prefix) == 0xC6D3D0C5 ## # Image plugin for Encapsulated Postscript. This plugin supports only @@ -302,42 +299,42 @@ def _save(im, fp, filename, eps=1): # # determine postscript image mode if im.mode == "L": - operator = (8, 1, "image") + operator = (8, 1, b"image") elif im.mode == "RGB": - operator = (8, 3, "false 3 colorimage") + operator = (8, 3, b"false 3 colorimage") elif im.mode == "CMYK": - operator = (8, 4, "false 4 colorimage") + operator = (8, 4, b"false 4 colorimage") else: raise ValueError("image mode is not supported") if eps: # # write EPS header - fp.write("%!PS-Adobe-3.0 EPSF-3.0\n") - fp.write("%%Creator: PIL 0.1 EpsEncode\n") + fp.write(b"%!PS-Adobe-3.0 EPSF-3.0\n") + fp.write(b"%%Creator: PIL 0.1 EpsEncode\n") #fp.write("%%CreationDate: %s"...) - fp.write("%%%%BoundingBox: 0 0 %d %d\n" % im.size) - fp.write("%%Pages: 1\n") - fp.write("%%EndComments\n") - fp.write("%%Page: 1 1\n") - fp.write("%%ImageData: %d %d " % im.size) - fp.write("%d %d 0 1 1 \"%s\"\n" % operator) + fp.write(("%%%%BoundingBox: 0 0 %d %d\n" % im.size).encode('ascii')) + fp.write(b"%%Pages: 1\n") + fp.write(b"%%EndComments\n") + fp.write(b"%%Page: 1 1\n") + fp.write(("%%ImageData: %d %d " % im.size).encode('ascii')) + fp.write(("%d %d 0 1 1 \"%s\"\n" % operator).encode('ascii')) # # image header - fp.write("gsave\n") - fp.write("10 dict begin\n") - fp.write("/buf %d string def\n" % (im.size[0] * operator[1])) - fp.write("%d %d scale\n" % im.size) - fp.write("%d %d 8\n" % im.size) # <= bits - fp.write("[%d 0 0 -%d 0 %d]\n" % (im.size[0], im.size[1], im.size[1])) - fp.write("{ currentfile buf readhexstring pop } bind\n") - fp.write("%s\n" % operator[2]) + fp.write(b"gsave\n") + fp.write(b"10 dict begin\n") + fp.write(("/buf %d string def\n" % (im.size[0] * operator[1])).encode('ascii')) + fp.write(("%d %d scale\n" % im.size).encode('ascii')) + fp.write(("%d %d 8\n" % im.size).encode('ascii')) # <= bits + fp.write(("[%d 0 0 -%d 0 %d]\n" % (im.size[0], im.size[1], im.size[1])).encode('ascii')) + fp.write(b"{ currentfile buf readhexstring pop } bind\n") + fp.write(operator[2] + b"\n") ImageFile._save(im, fp, [("eps", (0,0)+im.size, 0, None)]) - fp.write("\n%%%%EndBinary\n") - fp.write("grestore end\n") + fp.write(b"\n%%%%EndBinary\n") + fp.write(b"grestore end\n") fp.flush() # diff --git a/PIL/FitsStubImagePlugin.py b/PIL/FitsStubImagePlugin.py index f442caefc..6dee89a4b 100644 --- a/PIL/FitsStubImagePlugin.py +++ b/PIL/FitsStubImagePlugin.py @@ -26,7 +26,7 @@ def register_handler(handler): # Image adapter def _accept(prefix): - return prefix[:6] == "SIMPLE" + return prefix[:6] == b"SIMPLE" class FITSStubImageFile(ImageFile.StubImageFile): diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py index 1404cb9e5..4c16b0e04 100644 --- a/PIL/FliImagePlugin.py +++ b/PIL/FliImagePlugin.py @@ -18,14 +18,12 @@ __version__ = "0.2" -from . import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette, _binary - -def i16(c): - return ord(c[0]) + (ord(c[1])<<8) - -def i32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le +o8 = _binary.o8 # # decoder @@ -82,7 +80,7 @@ class FliImageFile(ImageFile.ImageFile): elif i16(s[4:6]) == 4: self._palette(palette, 0) - palette = [chr(r)+chr(g)+chr(b) for (r,g,b) in palette] + palette = [o8(r)+o8(g)+o8(b) for (r,g,b) in palette] self.palette = ImagePalette.raw("RGB", "".join(palette)) # set things up to decode first frame @@ -97,15 +95,15 @@ class FliImageFile(ImageFile.ImageFile): i = 0 for e in range(i16(self.fp.read(2))): s = self.fp.read(2) - i = i + ord(s[0]) - n = ord(s[1]) + i = i + i8(s[0]) + n = i8(s[1]) if n == 0: n = 256 s = self.fp.read(n * 3) for n in range(0, len(s), 3): - r = ord(s[n]) << shift - g = ord(s[n+1]) << shift - b = ord(s[n+2]) << shift + r = i8(s[n]) << shift + g = i8(s[n+1]) << shift + b = i8(s[n+2]) << shift palette[i] = (r, g, b) i = i + 1 diff --git a/PIL/FontFile.py b/PIL/FontFile.py index a7912fefc..7e3905c92 100644 --- a/PIL/FontFile.py +++ b/PIL/FontFile.py @@ -15,7 +15,7 @@ # import os -from . import Image +from . import Image, _binary import marshal @@ -31,7 +31,7 @@ def puti16(fp, values): for v in values: if v < 0: v = v + 65536 - fp.write(chr(v>>8&255) + chr(v&255)) + fp.write(_binary.o16be(v)) ## # Base class for raster font file handlers. @@ -106,9 +106,9 @@ class FontFile: # font metrics fp = open(os.path.splitext(filename)[0] + ".pil", "wb") - fp.write("PILfont\n") - fp.write(";;;;;;%d;\n" % self.ysize) # HACK!!! - fp.write("DATA\n") + fp.write(b"PILfont\n") + fp.write((";;;;;;%d;\n" % self.ysize).encode('ascii')) # HACK!!! + fp.write(b"DATA\n") for id in range(256): m = self.metrics[id] if not m: @@ -128,13 +128,13 @@ class FontFile: data = marshal.dumps((self.metrics, self.info)) if zlib: - data = "z" + zlib.compress(data, 9) + data = b"z" + zlib.compress(data, 9) else: - data = "u" + data + data = b"u" + data fp = open(os.path.splitext(filename)[0] + ".pil", "wb") - fp.write("PILfont2\n" + self.name + "\n" + "DATA\n") + fp.write(b"PILfont2\n" + self.name + "\n" + "DATA\n") fp.write(data) diff --git a/PIL/FpxImagePlugin.py b/PIL/FpxImagePlugin.py index 6612c3e1a..7dc35b6e7 100644 --- a/PIL/FpxImagePlugin.py +++ b/PIL/FpxImagePlugin.py @@ -170,8 +170,8 @@ class FpxImageFile(ImageFile.ImageFile): elif compression == 2: - internal_color_conversion = ord(s[14]) - jpeg_tables = ord(s[15]) + internal_color_conversion = i8(s[14]) + jpeg_tables = i8(s[15]) rawmode = self.rawmode if internal_color_conversion: diff --git a/PIL/GbrImagePlugin.py b/PIL/GbrImagePlugin.py index fb96ac9b4..e7d611f00 100644 --- a/PIL/GbrImagePlugin.py +++ b/PIL/GbrImagePlugin.py @@ -13,10 +13,9 @@ # See the README file for information on usage and redistribution. # -from . import Image, ImageFile +from . import Image, ImageFile, _binary -def i32(c): - return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) +i32 = _binary.i32be def _accept(prefix): return i32(prefix) >= 20 and i32(prefix[4:8]) == 1 @@ -59,8 +58,8 @@ class GbrImageFile(ImageFile.ImageFile): # create an image out of the brush data block self.im = Image.core.new(self.mode, self.size) - self.im.fromstring(self.data) - self.data = "" + self.im.frombytes(self.data) + self.data = b"" # # registry diff --git a/PIL/GdImageFile.py b/PIL/GdImageFile.py index 16749cbdb..01f30141f 100644 --- a/PIL/GdImageFile.py +++ b/PIL/GdImageFile.py @@ -25,7 +25,7 @@ __version__ = "0.1" -from . import ImageFile, ImagePalette +from . import ImageFile, ImagePalette, _binary try: import builtins @@ -33,8 +33,7 @@ except ImportError: import __builtin__ builtins = __builtin__ -def i16(c): - return ord(c[1]) + (ord(c[0])<<8) +i16 = _binary.i16be ## # Image plugin for the GD uncompressed format. Note that this format diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index c48a53608..97bd416e5 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -28,24 +28,23 @@ __version__ = "0.9" -from . import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette, _binary # -------------------------------------------------------------------- # Helpers -def i16(c): - return ord(c[0]) + (ord(c[1])<<8) - -def o16(i): - return chr(i&255) + chr(i>>8&255) +i8 = _binary.i8 +i16 = _binary.i16le +o8 = _binary.o8 +o16 = _binary.o16le # -------------------------------------------------------------------- # Identify/read GIF files def _accept(prefix): - return prefix[:6] in ["GIF87a", "GIF89a"] + return prefix[:6] in [b"GIF87a", b"GIF89a"] ## # Image plugin for GIF images. This plugin supports both GIF87 and @@ -60,15 +59,15 @@ class GifImageFile(ImageFile.ImageFile): def data(self): s = self.fp.read(1) - if s and ord(s): - return self.fp.read(ord(s)) + if s and i8(s): + return self.fp.read(i8(s)) return None def _open(self): # Screen s = self.fp.read(13) - if s[:6] not in ["GIF87a", "GIF89a"]: + if s[:6] not in [b"GIF87a", b"GIF89a"]: raise SyntaxError("not a GIF file") self.info["version"] = s[:6] @@ -77,17 +76,17 @@ class GifImageFile(ImageFile.ImageFile): self.tile = [] - flags = ord(s[10]) + flags = i8(s[10]) bits = (flags & 7) + 1 if flags & 128: # get global palette - self.info["background"] = ord(s[11]) + self.info["background"] = i8(s[11]) # check if palette contains colour indices p = self.fp.read(3<= 3 and ord(block[0]) == 1: + if len(block) >= 3 and i8(block[0]) == 1: self.info["loop"] = i16(block[1:3]) while self.data(): pass - elif s == ",": + elif s == b",": # # local image # @@ -177,7 +176,7 @@ class GifImageFile(ImageFile.ImageFile): # extent x0, y0 = i16(s[0:]), i16(s[2:]) x1, y1 = x0 + i16(s[4:]), y0 + i16(s[6:]) - flags = ord(s[8]) + flags = i8(s[8]) interlace = (flags & 64) != 0 @@ -187,7 +186,7 @@ class GifImageFile(ImageFile.ImageFile): ImagePalette.raw("RGB", self.fp.read(3< 100: raise SyntaxError("bad palette file") @@ -49,11 +50,11 @@ class GimpPaletteFile: raise ValueError("bad palette entry") if 0 <= i <= 255: - self.palette[i] = chr(v[0]) + chr(v[1]) + chr(v[2]) + self.palette[i] = o8(v[0]) + o8(v[1]) + o8(v[2]) i = i + 1 - self.palette = "".join(self.palette) + self.palette = b"".join(self.palette) def getpalette(self): diff --git a/PIL/GribStubImagePlugin.py b/PIL/GribStubImagePlugin.py index 7125751bd..af573bf5d 100644 --- a/PIL/GribStubImagePlugin.py +++ b/PIL/GribStubImagePlugin.py @@ -26,7 +26,7 @@ def register_handler(handler): # Image adapter def _accept(prefix): - return prefix[0:4] == "GRIB" and prefix[7] == chr(1) + return prefix[0:4] == b"GRIB" and prefix[7] == b'\x01' class GribStubImageFile(ImageFile.StubImageFile): diff --git a/PIL/Hdf5StubImagePlugin.py b/PIL/Hdf5StubImagePlugin.py index d4b1f7932..0875b9866 100644 --- a/PIL/Hdf5StubImagePlugin.py +++ b/PIL/Hdf5StubImagePlugin.py @@ -26,7 +26,7 @@ def register_handler(handler): # Image adapter def _accept(prefix): - return prefix[:8] == "\x89HDF\r\n\x1a\n" + return prefix[:8] == b"\x89HDF\r\n\x1a\n" class HDF5StubImageFile(ImageFile.StubImageFile): diff --git a/PIL/IcnsImagePlugin.py b/PIL/IcnsImagePlugin.py index ac45b99f7..3478a9643 100644 --- a/PIL/IcnsImagePlugin.py +++ b/PIL/IcnsImagePlugin.py @@ -14,9 +14,11 @@ # See the README file for information on usage and redistribution. # -from . import Image, ImageFile +from . import Image, ImageFile, _binary import struct +i8 = _binary.i8 + HEADERSIZE = 8 def nextheader(fobj): @@ -27,7 +29,7 @@ def read_32t(fobj, start_length, size): (start, length) = start_length fobj.seek(start) sig = fobj.read(4) - if sig != '\x00\x00\x00\x00': + if sig != b'\x00\x00\x00\x00': raise SyntaxError('Unknown signature, expecting 0x00000000') return read_32(fobj, (start + 4, length - 4), size) @@ -53,7 +55,7 @@ def read_32(fobj, start_length, size): byte = fobj.read(1) if not byte: break - byte = ord(byte) + byte = i8(byte) if byte & 0x80: blocksize = byte - 125 byte = fobj.read(1) @@ -70,7 +72,7 @@ def read_32(fobj, start_length, size): "Error reading channel [%r left]" % bytesleft ) band = Image.frombuffer( - "L", size, "".join(data), "raw", "L", 0, 1 + "L", size, b"".join(data), "raw", "L", 0, 1 ) im.im.putband(band.im, band_ix) return {"RGB": im} @@ -88,20 +90,20 @@ class IcnsFile: SIZES = { (128, 128): [ - ('it32', read_32t), - ('t8mk', read_mk), + (b'it32', read_32t), + (b't8mk', read_mk), ], (48, 48): [ - ('ih32', read_32), - ('h8mk', read_mk), + (b'ih32', read_32), + (b'h8mk', read_mk), ], (32, 32): [ - ('il32', read_32), - ('l8mk', read_mk), + (b'il32', read_32), + (b'l8mk', read_mk), ], (16, 16): [ - ('is32', read_32), - ('s8mk', read_mk), + (b'is32', read_32), + (b's8mk', read_mk), ], } @@ -204,7 +206,7 @@ class IcnsImageFile(ImageFile.ImageFile): self.load_end() -Image.register_open("ICNS", IcnsImageFile, lambda x: x[:4] == 'icns') +Image.register_open("ICNS", IcnsImageFile, lambda x: x[:4] == b'icns') Image.register_extension("ICNS", '.icns') if __name__ == '__main__': diff --git a/PIL/IcoImagePlugin.py b/PIL/IcoImagePlugin.py index 7549a2771..0068db47f 100644 --- a/PIL/IcoImagePlugin.py +++ b/PIL/IcoImagePlugin.py @@ -19,21 +19,19 @@ __version__ = "0.1" -from . import Image, BmpImagePlugin +from . import Image, BmpImagePlugin, _binary # # -------------------------------------------------------------------- -def i16(c): - return ord(c[0]) + (ord(c[1])<<8) - -def i32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le def _accept(prefix): - return prefix[:4] == "\0\0\1\0" + return prefix[:4] == b"\0\0\1\0" ## # Image plugin for Windows Icon files. @@ -56,12 +54,12 @@ class IcoImageFile(BmpImagePlugin.BmpImageFile): s = self.fp.read(16) if not m: m = s - elif ord(s[0]) > ord(m[0]) and ord(s[1]) > ord(m[1]): + elif i8(s[0]) > i8(m[0]) and i8(s[1]) > i8(m[1]): m = s - #print "width", ord(s[0]) - #print "height", ord(s[1]) - #print "colors", ord(s[2]) - #print "reserved", ord(s[3]) + #print "width", i8(s[0]) + #print "height", i8(s[1]) + #print "colors", i8(s[2]) + #print "reserved", i8(s[3]) #print "planes", i16(s[4:]) #print "bitcount", i16(s[6:]) #print "bytes", i32(s[8:]) @@ -71,7 +69,7 @@ class IcoImageFile(BmpImagePlugin.BmpImageFile): self._bitmap(i32(m[12:])) # patch up the bitmap height - self.size = self.size[0], self.size[1]/2 + self.size = self.size[0], self.size[1]//2 d, e, o, a = self.tile[0] self.tile[0] = d, (0,0)+self.size, o, a diff --git a/PIL/ImImagePlugin.py b/PIL/ImImagePlugin.py index a521eac27..84a59c779 100644 --- a/PIL/ImImagePlugin.py +++ b/PIL/ImImagePlugin.py @@ -30,6 +30,7 @@ __version__ = "0.7" import re from . import Image, ImageFile, ImagePalette +from ._binary import i8, o8 # -------------------------------------------------------------------- @@ -91,7 +92,7 @@ for i in range(2, 33): # -------------------------------------------------------------------- # Read IM directory -split = re.compile(r"^([A-Za-z][^:]*):[ \t]*(.*)[ \t]*$") +split = re.compile(br"^([A-Za-z][^:]*):[ \t]*(.*)[ \t]*$") def number(s): try: @@ -112,7 +113,7 @@ class ImImageFile(ImageFile.ImageFile): # Quick rejection: if there's not an LF among the first # 100 bytes, this is (probably) not a text header. - if not "\n" in self.fp.read(100): + if not b"\n" in self.fp.read(100): raise SyntaxError("not an IM file") self.fp.seek(0) @@ -130,10 +131,10 @@ class ImImageFile(ImageFile.ImageFile): s = self.fp.read(1) # Some versions of IFUNC uses \n\r instead of \r\n... - if s == "\r": + if s == b"\r": continue - if not s or s[0] == chr(0) or s[0] == chr(26): + if not s or s == b'\0' or s == b'\x1A': break # FIXME: this may read whole file if not a text file @@ -142,9 +143,9 @@ class ImImageFile(ImageFile.ImageFile): if len(s) > 100: raise SyntaxError("not an IM file") - if s[-2:] == '\r\n': + if s[-2:] == b'\r\n': s = s[:-2] - elif s[-1:] == '\n': + elif s[-1:] == b'\n': s = s[:-1] try: @@ -156,6 +157,11 @@ class ImImageFile(ImageFile.ImageFile): k, v = m.group(1,2) + # Don't know if this is the correct encoding, but a decent guess + # (I guess) + k = k.decode('latin-1', 'replace') + v = v.decode('latin-1', 'replace') + # Convert value as appropriate if k in [FRAMES, SCALE, SIZE]: v = v.replace("*", ",") @@ -180,7 +186,7 @@ class ImImageFile(ImageFile.ImageFile): else: - raise SyntaxError("Syntax error in IM header: " + s) + raise SyntaxError("Syntax error in IM header: " + s.decode('ascii', 'replace')) if not n: raise SyntaxError("Not an IM file") @@ -190,7 +196,7 @@ class ImImageFile(ImageFile.ImageFile): self.mode = self.info[MODE] # Skip forward to start of image data - while s and s[0] != chr(26): + while s and s[0:1] != b'\x1A': s = self.fp.read(1) if not s: raise SyntaxError("File truncated") @@ -202,14 +208,14 @@ class ImImageFile(ImageFile.ImageFile): linear = 1 # linear greyscale palette for i in range(256): if palette[i] == palette[i+256] == palette[i+512]: - if palette[i] != chr(i): + if i8(palette[i]) != i: linear = 0 else: greyscale = 0 if self.mode == "L" or self.mode == "LA": if greyscale: if not linear: - self.lut = [ord(c) for c in palette[:256]] + self.lut = [i8(c) for c in palette[:256]] else: if self.mode == "L": self.mode = self.rawmode = "P" @@ -218,7 +224,7 @@ class ImImageFile(ImageFile.ImageFile): self.palette = ImagePalette.raw("RGB;L", palette) elif self.mode == "RGB": if not greyscale or not linear: - self.lut = [ord(c) for c in palette] + self.lut = [i8(c) for c in palette] self.frame = 0 @@ -265,7 +271,7 @@ class ImImageFile(ImageFile.ImageFile): else: bits = 8 * len(self.mode) - size = ((self.size[0] * bits + 7) / 8) * self.size[1] + size = ((self.size[0] * bits + 7) // 8) * self.size[1] offs = self.__offset + frame * size self.fp = self.__fp @@ -314,14 +320,14 @@ def _save(im, fp, filename, check=0): if check: return check - fp.write("Image type: %s image\r\n" % type) + fp.write(("Image type: %s image\r\n" % type).encode('ascii')) if filename: - fp.write("Name: %s\r\n" % filename) - fp.write("Image size (x*y): %d*%d\r\n" % im.size) - fp.write("File size (no of images): %d\r\n" % frames) + fp.write(("Name: %s\r\n" % filename).encode('ascii')) + fp.write(("Image size (x*y): %d*%d\r\n" % im.size).encode('ascii')) + fp.write(("File size (no of images): %d\r\n" % frames).encode('ascii')) if im.mode == "P": - fp.write("Lut: 1\r\n") - fp.write("\000" * (511-fp.tell()) + "\032") + fp.write(b"Lut: 1\r\n") + fp.write(b"\000" * (511-fp.tell()) + b"\032") if im.mode == "P": fp.write(im.im.getpalette("RGB", "RGB;L")) # 768 bytes ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode, 0, -1))]) diff --git a/PIL/Image.py b/PIL/Image.py index f82e52981..2f2eac3b4 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -73,6 +73,7 @@ except ImportError: builtins = __builtin__ from . import ImageMode +from ._binary import i8, o8 import os, sys @@ -80,12 +81,12 @@ import os, sys import collections import numbers -if sys.version_info >= (3,0): - def isStringType(t): - return isinstance(t, str) -else: +if bytes is str: def isStringType(t): return isinstance(t, basestring) +else: + def isStringType(t): + return isinstance(t, str) ## # (Internal) Checks if an object is an image object. @@ -181,16 +182,7 @@ _MODEINFO = { } -try: - byteorder = sys.byteorder -except AttributeError: - import struct - if struct.unpack("h", "\0\1")[0] == 1: - byteorder = "big" - else: - byteorder = "little" - -if byteorder == 'little': +if sys.byteorder == 'little': _ENDIAN = '<' else: _ENDIAN = '>' @@ -358,7 +350,7 @@ def init(): return 1 # -------------------------------------------------------------------- -# Codec factories (used by tostring/fromstring and ImageFile.load) +# Codec factories (used by tobytes/frombytes and ImageFile.load) def _getdecoder(mode, decoder_name, args, extra=()): @@ -436,7 +428,7 @@ def _getscaleoffset(expr): # # @see #open # @see #new -# @see #fromstring +# @see #frombytes class Image: @@ -505,7 +497,7 @@ class Image: shape, typestr = _conv_type_shape(self) new['shape'] = shape new['typestr'] = typestr - new['data'] = self.tostring() + new['data'] = self.tobytes() return new raise AttributeError(name) @@ -515,10 +507,10 @@ class Image: # @param encoder_name What encoder to use. The default is to # use the standard "raw" encoder. # @param *args Extra arguments to the encoder. - # @return An 8-bit string. + # @return A bytes object. - def tostring(self, encoder_name="raw", *args): - "Return image as a binary string" + def tobytes(self, encoder_name="raw", *args): + "Return image as a bytes object" # may pass tuple instead of argument list if len(args) == 1 and isinstance(args[0], tuple): @@ -542,9 +534,13 @@ class Image: if s: break if s < 0: - raise RuntimeError("encoder error %d in tostring" % s) + raise RuntimeError("encoder error %d in tobytes" % s) - return "".join(data) + return b"".join(data) + + if bytes is str: + # Declare tostring as alias to tobytes + tostring = tobytes ## # Returns the image converted to an X11 bitmap. This method @@ -560,20 +556,20 @@ class Image: self.load() if self.mode != "1": raise ValueError("not a bitmap") - data = self.tostring("xbm") - return "".join(["#define %s_width %d\n" % (name, self.size[0]), - "#define %s_height %d\n"% (name, self.size[1]), - "static char %s_bits[] = {\n" % name, data, "};"]) + data = self.tobytes("xbm") + return b"".join([("#define %s_width %d\n" % (name, self.size[0])).encode('ascii'), + ("#define %s_height %d\n"% (name, self.size[1])).encode('ascii'), + ("static char %s_bits[] = {\n" % name).encode('ascii'), data, b"};"]) ## - # Loads this image with pixel data from a string. + # Loads this image with pixel data from a bytes object. #

- # This method is similar to the {@link #fromstring} function, but + # This method is similar to the {@link #frombytes} function, but # loads data into this image instead of creating a new image # object. - def fromstring(self, data, decoder_name="raw", *args): - "Load data to image from binary string" + def frombytes(self, data, decoder_name="raw", *args): + "Load data to image from a bytes object" # may pass tuple instead of argument list if len(args) == 1 and isinstance(args[0], tuple): @@ -593,6 +589,10 @@ class Image: if s[1] != 0: raise ValueError("cannot decode image data") + if bytes is str: + # Declare fromstring as alias to frombytes + fromstring = frombytes + ## # Allocates storage for the image and loads the pixel data. In # normal cases, you don't need to call this method, since the @@ -929,7 +929,10 @@ class Image: self.load() try: - return [ord(c) for c in self.im.getpalette()] + if bytes is str: + return [i8(c) for c in self.im.getpalette()] + else: + return list(self.im.getpalette()) except ValueError: return None # no palette @@ -958,7 +961,7 @@ class Image: self.load() x, y = self.im.getprojection() - return [ord(c) for c in x], [ord(c) for c in y] + return [i8(c) for c in x], [i8(c) for c in y] ## # Returns a histogram for the image. The histogram is returned as @@ -1233,8 +1236,11 @@ class Image: if isinstance(data, ImagePalette.ImagePalette): palette = ImagePalette.raw(data.rawmode, data.palette) else: - if not isStringType(data): - data = "".join(map(chr, data)) + if not isinstance(data, bytes): + if bytes is str: + data = "".join(chr(x) for x in data) + else: + data = bytes(data) palette = ImagePalette.raw(rawmode, data) self.mode = "P" self.palette = palette @@ -1762,7 +1768,7 @@ def new(mode, size, color=0): return Image()._new(core.fill(mode, size, color)) ## -# Creates an image memory from pixel data in a string. +# Creates a copy of an image memory from pixel data in a buffer. #

# In its simplest form, this function takes three arguments # (mode, size, and unpacked pixel data). @@ -1773,17 +1779,17 @@ def new(mode, size, color=0): #

# Note that this function decodes pixel data only, not entire images. # If you have an entire image in a string, wrap it in a -# StringIO object, and use {@link #open} to load it. +# BytesIO object, and use {@link #open} to load it. # # @param mode The image mode. # @param size The image size. -# @param data An 8-bit string containing raw data for the given mode. +# @param data A byte buffer containing raw data for the given mode. # @param decoder_name What decoder to use. # @param *args Additional parameters for the given decoder. # @return An Image object. -def fromstring(mode, size, data, decoder_name="raw", *args): - "Load image from string" +def frombytes(mode, size, data, decoder_name="raw", *args): + "Load image from byte buffer" # may pass tuple instead of argument list if len(args) == 1 and isinstance(args[0], tuple): @@ -1793,14 +1799,18 @@ def fromstring(mode, size, data, decoder_name="raw", *args): args = mode im = new(mode, size) - im.fromstring(data, decoder_name, args) + im.frombytes(data, decoder_name, args) return im +if bytes is str: + # Declare fromstring as an alias for frombytes + fromstring = frombytes + ## -# (New in 1.1.4) Creates an image memory from pixel data in a string -# or byte buffer. +# (New in 1.1.4) Creates an image memory referencing pixel data in a +# byte buffer. #

-# This function is similar to {@link #fromstring}, but uses data in +# This function is similar to {@link #frombytes}, but uses data in # the byte buffer, where possible. This means that changes to the # original buffer object are reflected in this image). Not all modes # can share memory; supported modes include "L", "RGBX", "RGBA", and @@ -1808,7 +1818,7 @@ def fromstring(mode, size, data, decoder_name="raw", *args): #

# Note that this function decodes pixel data only, not entire images. # If you have an entire image file in a string, wrap it in a -# StringIO object, and use {@link #open} to load it. +# BytesIO object, and use {@link #open} to load it. #

# In the current version, the default parameters used for the "raw" # decoder differs from that used for {@link fromstring}. This is a @@ -1819,7 +1829,7 @@ def fromstring(mode, size, data, decoder_name="raw", *args): # # @param mode The image mode. # @param size The image size. -# @param data An 8-bit string or other buffer object containing raw +# @param data A bytes or other buffer object containing raw # data for the given mode. # @param decoder_name What decoder to use. # @param *args Additional parameters for the given decoder. For the @@ -1830,7 +1840,7 @@ def fromstring(mode, size, data, decoder_name="raw", *args): # @since 1.1.4 def frombuffer(mode, size, data, decoder_name="raw", *args): - "Load image from string or buffer" + "Load image from bytes or buffer" # may pass tuple instead of argument list if len(args) == 1 and isinstance(args[0], tuple): @@ -1854,14 +1864,14 @@ def frombuffer(mode, size, data, decoder_name="raw", *args): im.readonly = 1 return im - return fromstring(mode, size, data, decoder_name, args) + return frombytes(mode, size, data, decoder_name, args) ## # (New in 1.1.6) Creates an image memory from an object exporting # the array interface (using the buffer protocol). # -# If obj is not contiguous, then the tostring method is called +# If obj is not contiguous, then the tobytes method is called # and {@link frombuffer} is used. # # @param obj Object with array interface @@ -1896,7 +1906,7 @@ def fromarray(obj, mode=None): size = shape[1], shape[0] if strides is not None: - obj = obj.tostring() + obj = obj.tobytes() return frombuffer(mode, size, obj, "raw", rawmode, 0, 1) @@ -1962,6 +1972,8 @@ def open(fp, mode="r"): fp.seek(0) return factory(fp, filename) except (SyntaxError, IndexError, TypeError): + #import traceback + #traceback.print_exc() pass if init(): @@ -1973,6 +1985,8 @@ def open(fp, mode="r"): fp.seek(0) return factory(fp, filename) except (SyntaxError, IndexError, TypeError): + #import traceback + #traceback.print_exc() pass raise IOError("cannot identify image file") diff --git a/PIL/ImageCms.py b/PIL/ImageCms.py index 747af51d1..7f2d5f675 100644 --- a/PIL/ImageCms.py +++ b/PIL/ImageCms.py @@ -142,7 +142,7 @@ class ImageCmsProfile: if Image.isStringType(profile): self._set(core.profile_open(profile), profile) elif hasattr(profile, "read"): - self._set(core.profile_fromstring(profile.read())) + self._set(core.profile_frombytes(profile.read())) else: self._set(profile) # assume it's already a profile diff --git a/PIL/ImageColor.py b/PIL/ImageColor.py index e1cfd5ab9..f1be7092c 100644 --- a/PIL/ImageColor.py +++ b/PIL/ImageColor.py @@ -99,7 +99,7 @@ def getcolor(color, mode): return r, g, b, 255 if Image.getmodebase(mode) == "L": r, g, b = color - return (r*299 + g*587 + b*114)/1000 + return (r*299 + g*587 + b*114)//1000 return color colormap = { diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index 60c732384..1a5a8f11e 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -184,7 +184,7 @@ class ImageFile(Image.Image): # FIXME: This is a hack to handle TIFF's JpegTables tag. prefix = self.tile_prefix except AttributeError: - prefix = "" + prefix = b"" for d, e, o, a in self.tile: d = Image._getdecoder(self.mode, d, a, self.decoderconfig) @@ -314,13 +314,13 @@ class _ParserFile: def readline(self): # FIXME: this is slow! - s = "" + s = b"" while True: c = self.read(1) if not c: break s = s + c - if c == "\n": + if c == b"\n": break return s @@ -404,6 +404,7 @@ class Parser: finally: fp.close() # explicitly close the virtual file except IOError: + # traceback.print_exc() pass # not enough data else: flag = hasattr(im, "load_seek") or hasattr(im, "load_read") @@ -438,7 +439,7 @@ class Parser: # finish decoding if self.decoder: # get rid of what's left in the buffers - self.feed("") + self.feed(b"") self.data = self.decoder = None if not self.finished: raise IOError("image was incomplete") @@ -516,7 +517,7 @@ def _save(im, fp, tile): def _safe_read(fp, size): if size <= 0: - return "" + return b"" if size <= SAFEBLOCK: return fp.read(size) data = [] @@ -526,4 +527,4 @@ def _safe_read(fp, size): break data.append(block) size = size - len(block) - return "".join(data) + return b"".join(data) diff --git a/PIL/ImageFilter.py b/PIL/ImageFilter.py index f4f98be79..71f42c3ac 100644 --- a/PIL/ImageFilter.py +++ b/PIL/ImageFilter.py @@ -82,7 +82,7 @@ class RankFilter(Filter): def filter(self, image): if image.mode == "P": raise ValueError("cannot filter palette images") - image = image.expand(self.size/2, self.size/2) + image = image.expand(self.size//2, self.size//2) return image.rankfilter(self.size, self.rank) ## @@ -99,7 +99,7 @@ class MedianFilter(RankFilter): def __init__(self, size=3): self.size = size - self.rank = size*size/2 + self.rank = size*size//2 ## # Min filter. Picks the lowest pixel value in a window with the given diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index 0bb846fd3..9a442b6dd 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -99,13 +99,13 @@ class ImageFont: def _load_pilfont_data(self, file, image): # read PILfont header - if file.readline() != "PILfont\n": + if file.readline() != b"PILfont\n": raise SyntaxError("Not a PILfont file") - d = file.readline().split(";") + d = file.readline().split(b";") self.info = [] # FIXME: should be a dictionary while True: s = file.readline() - if not s or s == "DATA\n": + if not s or s == b"DATA\n": break self.info.append(s) diff --git a/PIL/ImageGrab.py b/PIL/ImageGrab.py index f938efa6f..6543b0d88 100644 --- a/PIL/ImageGrab.py +++ b/PIL/ImageGrab.py @@ -45,7 +45,7 @@ except AttributeError: def grab(bbox=None): size, data = grabber() - im = Image.fromstring( + im = Image.frombytes( "RGB", size, data, # RGB, 32-bit line padding, origo in lower left corner "raw", "BGR", (size[0]*3 + 3) & -4, -1 @@ -65,7 +65,7 @@ def grab(bbox=None): def grabclipboard(): debug = 0 # temporary interface data = Image.core.grabclipboard(debug) - if Image.isStringType(data): + if isinstance(data, bytes): from . import BmpImagePlugin import io return BmpImagePlugin.DibImageFile(io.BytesIO(data)) diff --git a/PIL/ImageMath.py b/PIL/ImageMath.py index 6fb621962..503306034 100644 --- a/PIL/ImageMath.py +++ b/PIL/ImageMath.py @@ -91,14 +91,14 @@ class _Operand: return _Operand(out) # unary operators - if sys.version_info >= (3,0): - def __bool__(self): - # an image is "true" if it contains at least one non-zero pixel - return self.im.getbbox() is not None - else: - def __nonzero__(self): - # an image is "true" if it contains at least one non-zero pixel - return self.im.getbbox() is not None + def __bool__(self): + # an image is "true" if it contains at least one non-zero pixel + return self.im.getbbox() is not None + + if bytes is str: + # Provide __nonzero__ for pre-Py3k + __nonzero__ = __bool__ + del __bool__ def __abs__(self): return self.apply("abs", self) @@ -120,9 +120,9 @@ class _Operand: return self.apply("mul", self, other) def __rmul__(self, other): return self.apply("mul", other, self) - def __div__(self, other): + def __truediv__(self, other): return self.apply("div", self, other) - def __rdiv__(self, other): + def __rtruediv__(self, other): return self.apply("div", other, self) def __mod__(self, other): return self.apply("mod", self, other) @@ -133,6 +133,13 @@ class _Operand: def __rpow__(self, other): return self.apply("pow", other, self) + if bytes is str: + # Provide __div__ and __rdiv__ for pre-Py3k + __div__ = __truediv__ + __rdiv__ = __rtruediv__ + del __truediv__ + del __rtruediv__ + # bitwise def __invert__(self): return self.apply("invert", self) diff --git a/PIL/ImageOps.py b/PIL/ImageOps.py index 35b1064c2..4adc59435 100644 --- a/PIL/ImageOps.py +++ b/PIL/ImageOps.py @@ -95,7 +95,7 @@ def autocontrast(image, cutoff=0, ignore=None): for ix in range(256): n = n + h[ix] # remove cutoff% pixels from the low end - cut = n * cutoff / 100 + cut = n * cutoff // 100 for lo in range(256): if cut > h[lo]: cut = cut - h[lo] @@ -106,7 +106,7 @@ def autocontrast(image, cutoff=0, ignore=None): if cut <= 0: break # remove cutoff% samples from the hi end - cut = n * cutoff / 100 + cut = n * cutoff // 100 for hi in range(255, -1, -1): if cut > h[hi]: cut = cut - h[hi] @@ -125,7 +125,7 @@ def autocontrast(image, cutoff=0, ignore=None): break if hi <= lo: # don't bother - lut.extend(range(256)) + lut.extend(list(range(256))) else: scale = 255.0 / (hi - lo) offset = -lo * scale @@ -156,9 +156,9 @@ def colorize(image, black, white): white = _color(white, "RGB") red = []; green = []; blue = [] for i in range(256): - red.append(black[0]+i*(white[0]-black[0])/255) - green.append(black[1]+i*(white[1]-black[1])/255) - blue.append(black[2]+i*(white[2]-black[2])/255) + red.append(black[0]+i*(white[0]-black[0])//255) + green.append(black[1]+i*(white[1]-black[1])//255) + blue.append(black[2]+i*(white[2]-black[2])//255) image = image.convert("RGB") return _lut(image, red + green + blue) @@ -212,15 +212,15 @@ def equalize(image, mask=None): for b in range(0, len(h), 256): histo = [_f for _f in h[b:b+256] if _f] if len(histo) <= 1: - lut.extend(range(256)) + lut.extend(list(range(256))) else: - step = (reduce(operator.add, histo) - histo[-1]) / 255 + step = (reduce(operator.add, histo) - histo[-1]) // 255 if not step: - lut.extend(range(256)) + lut.extend(list(range(256))) else: - n = step / 2 + n = step // 2 for i in range(256): - lut.append(n / step) + lut.append(n // step) n = n + h[i+b] return _lut(image, lut) diff --git a/PIL/ImagePalette.py b/PIL/ImagePalette.py index 9992e3fe0..689910521 100644 --- a/PIL/ImagePalette.py +++ b/PIL/ImagePalette.py @@ -39,16 +39,20 @@ class ImagePalette: # for the low-level im.putpalette primitive if self.rawmode: return self.rawmode, self.palette - return self.mode + ";L", self.tostring() + return self.mode + ";L", self.tobytes() - def tostring(self): - # experimental: convert palette to string + def tobytes(self): + # experimental: convert palette to bytes if self.rawmode: raise ValueError("palette contains raw palette data") - if Image.isStringType(self.palette): + if isinstance(self.palette, bytes): return self.palette return array.array("B", self.palette).tostring() + if bytes is str: + # Declare tostring as an alias for tobytes + tostring = tobytes + def getcolor(self, color): # experimental: given an rgb tuple, allocate palette entry if self.rawmode: @@ -58,7 +62,7 @@ class ImagePalette: return self.colors[color] except KeyError: # allocate new color slot - if Image.isStringType(self.palette): + if isinstance(self.palette, bytes): self.palette = [int(x) for x in self.palette] index = len(self.colors) if index >= 256: @@ -104,7 +108,7 @@ def _make_linear_lut(black, white): lut = [] if black == 0: for i in range(256): - lut.append(white*i/255) + lut.append(white*i//255) else: raise NotImplementedError # FIXME return lut @@ -155,6 +159,8 @@ def load(filename): p = GimpPaletteFile.GimpPaletteFile(fp) lut = p.getpalette() except (SyntaxError, ValueError): + #import traceback + #traceback.print_exc() pass if not lut: @@ -164,6 +170,8 @@ def load(filename): p = GimpGradientFile.GimpGradientFile(fp) lut = p.getpalette() except (SyntaxError, ValueError): + #import traceback + #traceback.print_exc() pass if not lut: @@ -173,6 +181,8 @@ def load(filename): p = PaletteFile.PaletteFile(fp) lut = p.getpalette() except (SyntaxError, ValueError): + import traceback + traceback.print_exc() pass if not lut: diff --git a/PIL/ImageQt.py b/PIL/ImageQt.py index 1388dbefa..9cc2cfad6 100644 --- a/PIL/ImageQt.py +++ b/PIL/ImageQt.py @@ -62,11 +62,11 @@ class ImageQt(QImage): for i in range(0, len(palette), 3): colortable.append(rgb(*palette[i:i+3])) elif im.mode == "RGB": - data = im.tostring("raw", "BGRX") + data = im.tobytes("raw", "BGRX") format = QImage.Format_RGB32 elif im.mode == "RGBA": try: - data = im.tostring("raw", "BGRA") + data = im.tobytes("raw", "BGRA") except SystemError: # workaround for earlier versions r, g, b, a = im.split() @@ -76,7 +76,7 @@ class ImageQt(QImage): raise ValueError("unsupported image mode %r" % im.mode) # must keep a reference, or Qt will crash! - self.__data = data or im.tostring() + self.__data = data or im.tobytes() QImage.__init__(self, self.__data, im.size[0], im.size[1], format) diff --git a/PIL/ImageStat.py b/PIL/ImageStat.py index 7e343884b..1c0340537 100644 --- a/PIL/ImageStat.py +++ b/PIL/ImageStat.py @@ -127,7 +127,7 @@ class Stat: v = [] for i in self.bands: s = 0 - l = self.count[i]/2 + l = self.count[i]//2 b = i * 256 for j in range(256): s = s + self.h[b+j] diff --git a/PIL/ImageWin.py b/PIL/ImageWin.py index c2d72b959..3d342b409 100644 --- a/PIL/ImageWin.py +++ b/PIL/ImageWin.py @@ -151,22 +151,25 @@ class Dib: self.image.paste(im.im) ## - # Load display memory contents from string buffer. + # Load display memory contents from byte data. # - # @param buffer A string buffer containing display data (usually - # data returned from tostring) + # @param buffer A buffer containing display data (usually + # data returned from tobytes) - def fromstring(self, buffer): - return self.image.fromstring(buffer) + def frombytes(self, buffer): + return self.image.frombytes(buffer) ## - # Copy display memory contents to string buffer. + # Copy display memory contents to bytes object. # - # @return A string buffer containing display data. + # @return A bytes object containing display data. - def tostring(self): - return self.image.tostring() + def tobytes(self): + return self.image.tobytes() + if bytes is str: + tostring = tobytes + fromstring = frombytes ## # Create a Window with the given title size. diff --git a/PIL/ImtImagePlugin.py b/PIL/ImtImagePlugin.py index 8fcd46a1e..7620933a6 100644 --- a/PIL/ImtImagePlugin.py +++ b/PIL/ImtImagePlugin.py @@ -24,7 +24,7 @@ from . import Image, ImageFile # # -------------------------------------------------------------------- -field = re.compile(r"([a-z]*) ([^ \r\n]*)") +field = re.compile(br"([a-z]*) ([^ \r\n]*)") ## # Image plugin for IM Tools images. @@ -39,7 +39,7 @@ class ImtImageFile(ImageFile.ImageFile): # Quick rejection: if there's not a LF among the first # 100 bytes, this is (probably) not a text header. - if not "\n" in self.fp.read(100): + if not b"\n" in self.fp.read(100): raise SyntaxError("not an IM file") self.fp.seek(0) @@ -51,7 +51,7 @@ class ImtImageFile(ImageFile.ImageFile): if not s: break - if s == chr(12): + if s == b'\x0C': # image data begins self.tile = [("raw", (0,0)+self.size, @@ -67,7 +67,7 @@ class ImtImageFile(ImageFile.ImageFile): s = s + self.fp.readline() if len(s) == 1 or len(s) > 100: break - if s[0] == "*": + if s[0] == b"*": continue # comment m = field.match(s) diff --git a/PIL/IptcImagePlugin.py b/PIL/IptcImagePlugin.py index 6a7ebc29f..c9bca964e 100644 --- a/PIL/IptcImagePlugin.py +++ b/PIL/IptcImagePlugin.py @@ -20,32 +20,30 @@ from __future__ import print_function __version__ = "0.3" -from . import Image, ImageFile +from . import Image, ImageFile, _binary import os, tempfile +i8 = _binary.i8 +i16 = _binary.i16be +i32 = _binary.i32be +o8 = _binary.o8 COMPRESSION = { 1: "raw", 5: "jpeg" } -PAD = chr(0) * 4 +PAD = o8(0) * 4 # # Helpers -def i16(c): - return ord(c[1]) + (ord(c[0])<<8) - -def i32(c): - return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) - def i(c): return i32((PAD + c)[-4:]) def dump(c): for i in c: - print("%02x" % ord(i), end=' ') + print("%02x" % i8(i), end=' ') print() ## @@ -67,14 +65,14 @@ class IptcImageFile(ImageFile.ImageFile): if not len(s): return None, 0 - tag = ord(s[1]), ord(s[2]) + tag = i8(s[1]), i8(s[2]) # syntax - if ord(s[0]) != 0x1C or tag[0] < 1 or tag[0] > 9: + if i8(s[0]) != 0x1C or tag[0] < 1 or tag[0] > 9: raise SyntaxError("invalid IPTC/NAA file") # field size - size = ord(s[3]) + size = i8(s[3]) if size > 132: raise IOError("illegal field length in IPTC/NAA file") elif size == 128: @@ -131,10 +129,10 @@ class IptcImageFile(ImageFile.ImageFile): # print tag, self.info[tag] # mode - layers = ord(self.info[(3,60)][0]) - component = ord(self.info[(3,60)][1]) + layers = i8(self.info[(3,60)][0]) + component = i8(self.info[(3,60)][1]) if (3,65) in self.info: - id = ord(self.info[(3,65)][0])-1 + id = i8(self.info[(3,65)][0])-1 else: id = 0 if layers == 1 and not component: @@ -242,7 +240,7 @@ def getiptcinfo(im): code = JpegImagePlugin.i16(app, offset) offset = offset + 2 # resource name (usually empty) - name_len = ord(app[offset]) + name_len = i8(app[offset]) name = app[offset+1:offset+1+name_len] offset = 1 + offset + name_len if offset & 1: diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 466b926db..aa6745ee8 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -35,13 +35,12 @@ __version__ = "0.6" import array, struct -from . import Image, ImageFile +from . import Image, ImageFile, _binary -def i16(c,o=0): - return ord(c[o+1]) + (ord(c[o])<<8) - -def i32(c,o=0): - return ord(c[o+3]) + (ord(c[o+2])<<8) + (ord(c[o+1])<<16) + (ord(c[o])<<24) +i8 = _binary.i8 +o8 = _binary.o8 +i16 = _binary.i16be +i32 = _binary.i32be # # Parser @@ -63,13 +62,13 @@ def APP(self, marker): self.app[app] = s # compatibility self.applist.append((app, s)) - if marker == 0xFFE0 and s[:4] == "JFIF": + if marker == 0xFFE0 and s[:4] == b"JFIF": # extract JFIF information self.info["jfif"] = version = i16(s, 5) # version self.info["jfif_version"] = divmod(version, 256) # extract JFIF properties try: - jfif_unit = ord(s[7]) + jfif_unit = i8(s[7]) jfif_density = i16(s, 8), i16(s, 10) except: pass @@ -78,13 +77,13 @@ def APP(self, marker): self.info["dpi"] = jfif_density self.info["jfif_unit"] = jfif_unit self.info["jfif_density"] = jfif_density - elif marker == 0xFFE1 and s[:5] == "Exif\0": + elif marker == 0xFFE1 and s[:5] == b"Exif\0": # extract Exif information (incomplete) self.info["exif"] = s # FIXME: value will change - elif marker == 0xFFE2 and s[:5] == "FPXR\0": + elif marker == 0xFFE2 and s[:5] == b"FPXR\0": # extract FlashPix information (incomplete) self.info["flashpix"] = s # FIXME: value will change - elif marker == 0xFFE2 and s[:12] == "ICC_PROFILE\0": + elif marker == 0xFFE2 and s[:12] == b"ICC_PROFILE\0": # Since an ICC profile can be larger than the maximum size of # a JPEG marker (64K), we need provisions to split it into # multiple markers. The format defined by the ICC specifies @@ -97,11 +96,11 @@ def APP(self, marker): # reassemble the profile, rather than assuming that the APP2 # markers appear in the correct sequence. self.icclist.append(s) - elif marker == 0xFFEE and s[:5] == "Adobe": + elif marker == 0xFFEE and s[:5] == b"Adobe": self.info["adobe"] = i16(s, 5) # extract Adobe custom properties try: - adobe_transform = ord(s[1]) + adobe_transform = i8(s[1]) except: pass else: @@ -129,11 +128,11 @@ def SOF(self, marker): s = ImageFile._safe_read(self.fp, n) self.size = i16(s[3:]), i16(s[1:]) - self.bits = ord(s[0]) + self.bits = i8(s[0]) if self.bits != 8: raise SyntaxError("cannot handle %d-bit layers" % self.bits) - self.layers = ord(s[5]) + self.layers = i8(s[5]) if self.layers == 1: self.mode = "L" elif self.layers == 3: @@ -149,11 +148,11 @@ def SOF(self, marker): if self.icclist: # fixup icc profile self.icclist.sort() # sort by sequence number - if ord(self.icclist[0][13]) == len(self.icclist): + if i8(self.icclist[0][13]) == len(self.icclist): profile = [] for p in self.icclist: profile.append(p[14:]) - icc_profile = "".join(profile) + icc_profile = b"".join(profile) else: icc_profile = None # wrong number of fragments self.info["icc_profile"] = icc_profile @@ -162,7 +161,7 @@ def SOF(self, marker): for i in range(6, len(s), 3): t = s[i:i+3] # 4-tuples: id, vsamp, hsamp, qtable - self.layer.append((t[0], ord(t[1])/16, ord(t[1])&15, ord(t[2]))) + self.layer.append((t[0], i8(t[1])//16, i8(t[1])&15, i8(t[2]))) def DQT(self, marker): # @@ -178,8 +177,8 @@ def DQT(self, marker): while len(s): if len(s) < 65: raise SyntaxError("bad quantization table marker") - v = ord(s[0]) - if v/16 == 0: + v = i8(s[0]) + if v//16 == 0: self.quantization[v&15] = array.array("b", s[1:65]) s = s[65:] else: @@ -258,7 +257,7 @@ MARKER = { def _accept(prefix): - return prefix[0] == "\377" + return prefix[0:1] == b"\377" ## # Image plugin for JPEG and JFIF images. @@ -272,7 +271,7 @@ class JpegImageFile(ImageFile.ImageFile): s = self.fp.read(1) - if ord(s[0]) != 255: + if i8(s[0]) != 255: raise SyntaxError("not a JPEG file") # Create attributes @@ -325,12 +324,12 @@ class JpegImageFile(ImageFile.ImageFile): a = mode, "" if size: - scale = max(self.size[0] / size[0], self.size[1] / size[1]) + scale = max(self.size[0] // size[0], self.size[1] // size[1]) for s in [8, 4, 2, 1]: if scale >= s: break - e = e[0], e[1], (e[2]-e[0]+s-1)/s+e[0], (e[3]-e[1]+s-1)/s+e[1] - self.size = ((self.size[0]+s-1)/s, (self.size[1]+s-1)/s) + e = e[0], e[1], (e[2]-e[0]+s-1)//s+e[0], (e[3]-e[1]+s-1)//s+e[1] + self.size = ((self.size[0]+s-1)//s, (self.size[1]+s-1)//s) scale = s self.tile = [(d, e, o, a)] @@ -436,7 +435,7 @@ def _save(im, fp, filename): elif subsampling == "4:1:1": subsampling = 2 - extra = "" + extra = b"" icc_profile = info.get("icc_profile") if icc_profile: @@ -450,7 +449,7 @@ def _save(im, fp, filename): i = 1 for marker in markers: size = struct.pack(">H", 2 + ICC_OVERHEAD_LEN + len(marker)) - extra = extra + ("\xFF\xE2" + size + "ICC_PROFILE\0" + chr(i) + chr(len(markers)) + marker) + extra = extra + (b"\xFF\xE2" + size + b"ICC_PROFILE\0" + o8(i) + o8(len(markers)) + marker) i = i + 1 # get keyword arguments diff --git a/PIL/McIdasImagePlugin.py b/PIL/McIdasImagePlugin.py index 3f77ea198..36982d7de 100644 --- a/PIL/McIdasImagePlugin.py +++ b/PIL/McIdasImagePlugin.py @@ -22,7 +22,7 @@ import struct from . import Image, ImageFile def _accept(s): - return s[:8] == "\x00\x00\x00\x00\x00\x00\x00\x04" + return s[:8] == b"\x00\x00\x00\x00\x00\x00\x00\x04" ## # Image plugin for McIdas area images. diff --git a/PIL/MpegImagePlugin.py b/PIL/MpegImagePlugin.py index 39155d9f9..7c1fac273 100644 --- a/PIL/MpegImagePlugin.py +++ b/PIL/MpegImagePlugin.py @@ -16,6 +16,7 @@ __version__ = "0.1" from . import Image, ImageFile +from ._binary import i8 # # Bitstream parser @@ -28,7 +29,7 @@ class BitStream: self.bitbuffer = 0 def next(self): - return ord(self.fp.read(1)) + return i8(self.fp.read(1)) def peek(self, bits): while self.bits < bits: @@ -42,7 +43,7 @@ class BitStream: def skip(self, bits): while self.bits < bits: - self.bitbuffer = (self.bitbuffer << 8) + ord(self.fp.read(1)) + self.bitbuffer = (self.bitbuffer << 8) + i8(self.fp.read(1)) self.bits = self.bits + 8 self.bits = self.bits - bits diff --git a/PIL/MspImagePlugin.py b/PIL/MspImagePlugin.py index 0d9ee1686..a6728067b 100644 --- a/PIL/MspImagePlugin.py +++ b/PIL/MspImagePlugin.py @@ -19,17 +19,16 @@ __version__ = "0.1" -from . import Image, ImageFile +from . import Image, ImageFile, _binary # # read MSP files -def i16(c): - return ord(c[0]) + (ord(c[1])<<8) +i16 = _binary.i16le def _accept(prefix): - return prefix[:4] in ["DanM", "LinS"] + return prefix[:4] in [b"DanM", b"LinS"] ## # Image plugin for Windows MSP images. This plugin supports both @@ -44,7 +43,7 @@ class MspImageFile(ImageFile.ImageFile): # Header s = self.fp.read(32) - if s[:4] not in ["DanM", "LinS"]: + if s[:4] not in [b"DanM", b"LinS"]: raise SyntaxError("not an MSP file") # Header checksum @@ -57,7 +56,7 @@ class MspImageFile(ImageFile.ImageFile): self.mode = "1" self.size = i16(s[4:]), i16(s[6:]) - if s[:4] == "DanM": + if s[:4] == b"DanM": self.tile = [("raw", (0,0)+self.size, 32, ("1", 0, 1))] else: self.tile = [("msp", (0,0)+self.size, 32+2*self.size[1], None)] @@ -65,8 +64,7 @@ class MspImageFile(ImageFile.ImageFile): # # write MSP files (uncompressed only) -def o16(i): - return chr(i&255) + chr(i>>8&255) +o16 = _binary.o16le def _save(im, fp, filename): @@ -76,7 +74,7 @@ def _save(im, fp, filename): # create MSP header header = [0] * 16 - header[0], header[1] = i16("Da"), i16("nM") # version 1 + header[0], header[1] = i16(b"Da"), i16(b"nM") # version 1 header[2], header[3] = im.size header[4], header[5] = 1, 1 header[6], header[7] = 1, 1 diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index db1c04f06..d5d265c6a 100644 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -40,18 +40,17 @@ from __future__ import print_function import io import sys +from . import _binary -if str != bytes: +if str is not bytes: long = int -def i16(c, o = 0): - return ord(c[o])+(ord(c[o+1])<<8) - -def i32(c, o = 0): - return ord(c[o])+(ord(c[o+1])<<8)+(ord(c[o+2])<<16)+(ord(c[o+3])<<24) +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le -MAGIC = '\320\317\021\340\241\261\032\341' +MAGIC = b'\320\317\021\340\241\261\032\341' # # -------------------------------------------------------------------- @@ -332,12 +331,12 @@ class OleFileIO: def _unicode(self, s): # Map unicode string to Latin 1 - if sys.version_info >= (3,0): - # Provide actual Unicode string - return s.decode('utf-16') - else: + if bytes is str: # Old version tried to produce a Latin-1 str return s.decode('utf-16').encode('latin-1', 'replace') + else: + # Provide actual Unicode string + return s.decode('utf-16') def loaddirectory(self, sect): # Load the directory. The directory is stored in a standard @@ -352,7 +351,7 @@ class OleFileIO: entry = fp.read(128) if not entry: break - type = ord(entry[66]) + type = i8(entry[66]) name = self._unicode(entry[0:0+i16(entry, 64)]) ptrs = i32(entry, 68), i32(entry, 72), i32(entry, 76) sect, size = i32(entry, 116), i32(entry, 120) @@ -372,7 +371,7 @@ class OleFileIO: return "" return (("%08X-%04X-%04X-%02X%02X-" + "%02X" * 6) % ((i32(clsid, 0), i16(clsid, 4), i16(clsid, 6)) + - tuple(map(ord, clsid[8:16])))) + tuple(map(i8, clsid[8:16])))) def _list(self, files, prefix, node): # listdir helper @@ -488,9 +487,9 @@ class OleFileIO: value = long(i32(s, offset+4)) + (long(i32(s, offset+8))<<32) # FIXME: this is a 64-bit int: "number of 100ns periods # since Jan 1,1601". Should map this to Python time - value = value / 10000000 # seconds + value = value // 10000000 # seconds elif type == VT_UI1: - value = ord(s[offset+4]) + value = i8(s[offset+4]) elif type == VT_CLSID: value = self._clsid(s[offset+4:offset+20]) elif type == VT_CF: diff --git a/PIL/PaletteFile.py b/PIL/PaletteFile.py index c09245bf9..c6a3f36e2 100644 --- a/PIL/PaletteFile.py +++ b/PIL/PaletteFile.py @@ -13,6 +13,8 @@ # See the README file for information on usage and redistribution. # +from ._binary import o8 + ## # File handler for Teragon-style palette files. @@ -30,7 +32,7 @@ class PaletteFile: if not s: break - if s[0] == "#": + if s[0:1] == b"#": continue if len(s) > 100: raise SyntaxError("bad palette file") @@ -43,9 +45,9 @@ class PaletteFile: g = b = r if 0 <= i <= 255: - self.palette[i] = chr(r) + chr(g) + chr(b) + self.palette[i] = o8(r) + o8(g) + o8(b) - self.palette = "".join(self.palette) + self.palette = b"".join(self.palette) def getpalette(self): diff --git a/PIL/PalmImagePlugin.py b/PIL/PalmImagePlugin.py index 6c6376830..4f1be0daa 100644 --- a/PIL/PalmImagePlugin.py +++ b/PIL/PalmImagePlugin.py @@ -9,7 +9,7 @@ __version__ = "1.0" -from . import Image, ImageFile +from . import Image, ImageFile, _binary _Palm8BitColormapValues = ( ( 255, 255, 255 ), ( 255, 204, 255 ), ( 255, 153, 255 ), ( 255, 102, 255 ), @@ -107,8 +107,8 @@ _COMPRESSION_TYPES = { "scanline": 0x00, } -def o16b(i): - return chr(i>>8&255) + chr(i&255) +o8 = _binary.o8 +o16b = _binary.o16be # # -------------------------------------------------------------------- @@ -172,7 +172,7 @@ def _save(im, fp, filename, check=0): cols = im.size[0] rows = im.size[1] - rowbytes = ((cols + (16/bpp - 1)) / (16 / bpp)) * 2; + rowbytes = ((cols + (16//bpp - 1)) / (16 // bpp)) * 2; transparent_index = 0 compression_type = _COMPRESSION_TYPES["none"] @@ -186,16 +186,16 @@ def _save(im, fp, filename, check=0): colormapsize = 0 if "offset" in im.info: - offset = (rowbytes * rows + 16 + 3 + colormapsize) / 4; + offset = (rowbytes * rows + 16 + 3 + colormapsize) // 4; else: offset = 0 fp.write(o16b(cols) + o16b(rows) + o16b(rowbytes) + o16b(flags)) - fp.write(chr(bpp)) - fp.write(chr(version)) + fp.write(o8(bpp)) + fp.write(o8(version)) fp.write(o16b(offset)) - fp.write(chr(transparent_index)) - fp.write(chr(compression_type)) + fp.write(o8(transparent_index)) + fp.write(o8(compression_type)) fp.write(o16b(0)) # reserved by Palm # now write colormap if necessary @@ -203,11 +203,11 @@ def _save(im, fp, filename, check=0): if colormapsize > 0: fp.write(o16b(256)) for i in range(256): - fp.write(chr(i)) + fp.write(o8(i)) if colormapmode == 'RGB': - fp.write(chr(colormap[3 * i]) + chr(colormap[3 * i + 1]) + chr(colormap[3 * i + 2])) + fp.write(o8(colormap[3 * i]) + o8(colormap[3 * i + 1]) + o8(colormap[3 * i + 2])) elif colormapmode == 'RGBA': - fp.write(chr(colormap[4 * i]) + chr(colormap[4 * i + 1]) + chr(colormap[4 * i + 2])) + fp.write(o8(colormap[4 * i]) + o8(colormap[4 * i + 1]) + o8(colormap[4 * i + 2])) # now convert data to raw form ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode, rowbytes, 1))]) diff --git a/PIL/PcdImagePlugin.py b/PIL/PcdImagePlugin.py index 3c0ba9ed0..4ce2b2439 100644 --- a/PIL/PcdImagePlugin.py +++ b/PIL/PcdImagePlugin.py @@ -18,7 +18,9 @@ __version__ = "0.1" -from . import Image, ImageFile +from . import Image, ImageFile, _binary + +i8 = _binary.i8 ## # Image plugin for PhotoCD images. This plugin only reads the 768x512 @@ -36,10 +38,10 @@ class PcdImageFile(ImageFile.ImageFile): self.fp.seek(2048) s = self.fp.read(2048) - if s[:4] != "PCD_": + if s[:4] != b"PCD_": raise SyntaxError("not a PCD file") - orientation = ord(s[1538]) & 3 + orientation = i8(s[1538]) & 3 if orientation == 1: self.tile_post_rotate = 90 # hack elif orientation == 3: diff --git a/PIL/PcfFontFile.py b/PIL/PcfFontFile.py index 0bc53aa5b..ce67025ee 100644 --- a/PIL/PcfFontFile.py +++ b/PIL/PcfFontFile.py @@ -18,6 +18,7 @@ from . import Image from . import FontFile +from . import _binary # -------------------------------------------------------------------- # declarations @@ -41,19 +42,14 @@ BYTES_PER_ROW = [ lambda bits: ((bits+63) >> 3) & ~7, ] - -def l16(c): - return ord(c[0]) + (ord(c[1])<<8) -def l32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) - -def b16(c): - return ord(c[1]) + (ord(c[0])<<8) -def b32(c): - return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) +i8 = _binary.i8 +l16 = _binary.i16le +l32 = _binary.i32le +b16 = _binary.i16be +b32 = _binary.i32be def sz(s, o): - return s[o:s.index("\0", o)] + return s[o:s.index(b"\0", o)] ## # Font file plugin for the X11 PCF format. @@ -124,7 +120,7 @@ class PcfFontFile(FontFile.FontFile): # read property description p = [] for i in range(nprops): - p.append((i32(fp.read(4)), ord(fp.read(1)), i32(fp.read(4)))) + p.append((i32(fp.read(4)), i8(fp.read(1)), i32(fp.read(4)))) if nprops & 3: fp.seek(4 - (nprops & 3), 1) # pad @@ -153,11 +149,11 @@ class PcfFontFile(FontFile.FontFile): # "compressed" metrics for i in range(i16(fp.read(2))): - left = ord(fp.read(1)) - 128 - right = ord(fp.read(1)) - 128 - width = ord(fp.read(1)) - 128 - ascent = ord(fp.read(1)) - 128 - descent = ord(fp.read(1)) - 128 + left = i8(fp.read(1)) - 128 + right = i8(fp.read(1)) - 128 + width = i8(fp.read(1)) - 128 + ascent = i8(fp.read(1)) - 128 + descent = i8(fp.read(1)) - 128 xsize = right - left ysize = ascent + descent append( @@ -224,7 +220,7 @@ class PcfFontFile(FontFile.FontFile): x, y, l, r, w, a, d, f = metrics[i] b, e = offsets[i], offsets[i+1] bitmaps.append( - Image.fromstring("1", (x, y), data[b:e], "raw", mode, pad(x)) + Image.frombytes("1", (x, y), data[b:e], "raw", mode, pad(x)) ) return bitmaps diff --git a/PIL/PcxImagePlugin.py b/PIL/PcxImagePlugin.py index 597cc6d9e..f556ceced 100644 --- a/PIL/PcxImagePlugin.py +++ b/PIL/PcxImagePlugin.py @@ -27,13 +27,14 @@ __version__ = "0.6" -from . import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette, _binary -def i16(c,o): - return ord(c[o]) + (ord(c[o+1])<<8) +i8 = _binary.i8 +i16 = _binary.i16le +o8 = _binary.o8 def _accept(prefix): - return ord(prefix[0]) == 10 and ord(prefix[1]) in [0, 2, 3, 5] + return i8(prefix[0]) == 10 and i8(prefix[1]) in [0, 2, 3, 5] ## # Image plugin for Paintbrush images. @@ -56,9 +57,9 @@ class PcxImageFile(ImageFile.ImageFile): raise SyntaxError("bad PCX image size") # format - version = ord(s[1]) - bits = ord(s[3]) - planes = ord(s[65]) + version = i8(s[1]) + bits = i8(s[3]) + planes = i8(s[65]) stride = i16(s,66) self.info["dpi"] = i16(s,12), i16(s,14) @@ -76,10 +77,10 @@ class PcxImageFile(ImageFile.ImageFile): # FIXME: hey, this doesn't work with the incremental loader !!! self.fp.seek(-769, 2) s = self.fp.read(769) - if len(s) == 769 and ord(s[0]) == 12: + if len(s) == 769 and i8(s[0]) == 12: # check if the palette is linear greyscale for i in range(256): - if s[i*3+1:i*3+4] != chr(i)*3: + if s[i*3+1:i*3+4] != o8(i)*3: mode = rawmode = "P" break if mode == "P": @@ -111,8 +112,7 @@ SAVE = { "RGB": (5, 8, 3, "RGB;L"), } -def o16(i): - return chr(i&255) + chr(i>>8&255) +o16 = _binary.o16le def _save(im, fp, filename, check=0): @@ -125,7 +125,7 @@ def _save(im, fp, filename, check=0): return check # bytes per plane - stride = (im.size[0] * bits + 7) / 8 + stride = (im.size[0] * bits + 7) // 8 # under windows, we could determine the current screen size with # "Image.core.display_mode()[1]", but I think that's overkill... @@ -136,11 +136,11 @@ def _save(im, fp, filename, check=0): # PCX header fp.write( - chr(10) + chr(version) + chr(1) + chr(bits) + o16(0) + + o8(10) + o8(version) + o8(1) + o8(bits) + o16(0) + o16(0) + o16(im.size[0]-1) + o16(im.size[1]-1) + o16(dpi[0]) + - o16(dpi[1]) + chr(0)*24 + chr(255)*24 + chr(0) + chr(planes) + + o16(dpi[1]) + b"\0"*24 + b"\xFF"*24 + b"\0" + o8(planes) + o16(stride) + o16(1) + o16(screen[0]) + o16(screen[1]) + - chr(0)*54 + b"\0"*54 ) assert fp.tell() == 128 @@ -150,13 +150,13 @@ def _save(im, fp, filename, check=0): if im.mode == "P": # colour palette - fp.write(chr(12)) + fp.write(o8(12)) fp.write(im.im.getpalette("RGB", "RGB")) # 768 bytes elif im.mode == "L": # greyscale palette - fp.write(chr(12)) + fp.write(o8(12)) for i in range(256): - fp.write(chr(i)*3) + fp.write(o8(i)*3) # -------------------------------------------------------------------- # registry diff --git a/PIL/PdfImagePlugin.py b/PIL/PdfImagePlugin.py index f54491871..b83e2d41c 100644 --- a/PIL/PdfImagePlugin.py +++ b/PIL/PdfImagePlugin.py @@ -23,6 +23,7 @@ __version__ = "0.4" from . import Image, ImageFile +from ._binary import i8 import io @@ -37,12 +38,15 @@ import io # 5. page contents def _obj(fp, obj, **dict): - fp.write(b"%d 0 obj\n" % obj) + fp.write(("%d 0 obj\n" % obj).encode('ascii')) if dict: fp.write(b"<<\n") for k, v in dict.items(): if v is not None: - fp.write(b"/%s %s\n" % (k, v)) + if not isinstance(v, bytes): + v = str(v).encode('ascii') + + fp.write(b"/" + k.encode('ascii') + b" " + v + b"\n") fp.write(b">>\n") def _endobj(fp): @@ -61,7 +65,7 @@ def _save(im, fp, filename): xref = [0]*(5+1) # placeholders fp.write(b"%PDF-1.2\n") - fp.write(b"% created by PIL PDF driver " + __version__ + "\n") + fp.write(b"% created by PIL PDF driver " + __version__.encode('ascii') + b"\n") # # Get image characteristics @@ -78,32 +82,32 @@ def _save(im, fp, filename): if im.mode == "1": filter = b"/ASCIIHexDecode" colorspace = b"/DeviceGray" - procset = b"/ImageB" # grayscale + procset = "/ImageB" # grayscale bits = 1 elif im.mode == "L": filter = b"/DCTDecode" # params = "<< /Predictor 15 /Columns %d >>" % (width-2) colorspace = b"/DeviceGray" - procset = b"/ImageB" # grayscale + procset = "/ImageB" # grayscale elif im.mode == "P": filter = b"/ASCIIHexDecode" colorspace = b"[ /Indexed /DeviceRGB 255 <" palette = im.im.getpalette("RGB") for i in range(256): - r = ord(palette[i*3]) - g = ord(palette[i*3+1]) - b = ord(palette[i*3+2]) - colorspace = colorspace + b"%02x%02x%02x " % (r, g, b) + r = i8(palette[i*3]) + g = i8(palette[i*3+1]) + b = i8(palette[i*3+2]) + colorspace = colorspace + ("%02x%02x%02x " % (r, g, b)).encode('ascii') colorspace = colorspace + b"> ]" - procset = b"/ImageI" # indexed color + procset = "/ImageI" # indexed color elif im.mode == "RGB": filter = b"/DCTDecode" colorspace = b"/DeviceRGB" - procset = b"/ImageC" # color images + procset = "/ImageC" # color images elif im.mode == "CMYK": filter = b"/DCTDecode" colorspace = b"/DeviceCMYK" - procset = b"/ImageC" # color images + procset = "/ImageC" # color images else: raise ValueError("cannot save mode %s" % im.mode) @@ -168,11 +172,11 @@ def _save(im, fp, filename): xref[4] = fp.tell() _obj(fp, 4) - fp.write(b"<<\n/Type /Page\n/Parent 2 0 R\n"\ - b"/Resources <<\n/ProcSet [ /PDF %s ]\n"\ - b"/XObject << /image 3 0 R >>\n>>\n"\ - b"/MediaBox [ 0 0 %d %d ]\n/Contents 5 0 R\n>>\n" %\ - (procset, int(width * 72.0 /resolution) , int(height * 72.0 / resolution))) + fp.write(("<<\n/Type /Page\n/Parent 2 0 R\n"\ + "/Resources <<\n/ProcSet [ /PDF %s ]\n"\ + "/XObject << /image 3 0 R >>\n>>\n"\ + "/MediaBox [ 0 0 %d %d ]\n/Contents 5 0 R\n>>\n" %\ + (procset, int(width * 72.0 /resolution) , int(height * 72.0 / resolution))).encode('ascii')) _endobj(fp) # @@ -180,7 +184,7 @@ def _save(im, fp, filename): op = io.BytesIO() - op.write(b"q %d 0 0 %d 0 0 cm /image Do Q\n" % (int(width * 72.0 / resolution), int(height * 72.0 / resolution))) + op.write(("q %d 0 0 %d 0 0 cm /image Do Q\n" % (int(width * 72.0 / resolution), int(height * 72.0 / resolution))).encode('ascii')) xref[5] = fp.tell() _obj(fp, 5, Length = len(op.getvalue())) @@ -194,11 +198,11 @@ def _save(im, fp, filename): # # trailer startxref = fp.tell() - fp.write(b"xref\n0 %d\n0000000000 65535 f \n" % len(xref)) + fp.write(("xref\n0 %d\n0000000000 65535 f \n" % len(xref)).encode('ascii')) for x in xref[1:]: - fp.write(b"%010d 00000 n \n" % x) - fp.write(b"trailer\n<<\n/Size %d\n/Root 1 0 R\n>>\n" % len(xref)) - fp.write(b"startxref\n%d\n%%%%EOF\n" % startxref) + fp.write(("%010d 00000 n \n" % x).encode('ascii')) + fp.write(("trailer\n<<\n/Size %d\n/Root 1 0 R\n>>\n" % len(xref)).encode('ascii')) + fp.write(("startxref\n%d\n%%%%EOF\n" % startxref).encode('ascii')) fp.flush() # diff --git a/PIL/PixarImagePlugin.py b/PIL/PixarImagePlugin.py index 60abd3c12..9322faaec 100644 --- a/PIL/PixarImagePlugin.py +++ b/PIL/PixarImagePlugin.py @@ -21,16 +21,13 @@ __version__ = "0.1" -from . import Image, ImageFile +from . import Image, ImageFile, _binary # # helpers -def i16(c): - return ord(c[0]) + (ord(c[1])<<8) - -def i32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) +i16 = _binary.i16le +i32 = _binary.i32le ## # Image plugin for PIXAR raster images. @@ -44,7 +41,7 @@ class PixarImageFile(ImageFile.ImageFile): # assuming a 4-byte magic label (FIXME: add "_accept" hook) s = self.fp.read(4) - if s != "\200\350\000\000": + if s != b"\200\350\000\000": raise SyntaxError("not a PIXAR file") # read rest of header diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index d8d794ac1..12d9fe018 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -37,19 +37,17 @@ __version__ = "0.9" import re -from . import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette, _binary import zlib +i8 = _binary.i8 +i16 = _binary.i16be +i32 = _binary.i32be -def i16(c): - return ord(c[1]) + (ord(c[0])<<8) -def i32(c): - return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) - -is_cid = re.compile("\w\w\w\w").match +is_cid = re.compile(b"\w\w\w\w").match -_MAGIC = "\211PNG\r\n\032\n" +_MAGIC = b"\211PNG\r\n\032\n" _MODES = { @@ -115,7 +113,7 @@ class ChunkStream: if Image.DEBUG: print("STREAM", cid, pos, len) - return getattr(self, "chunk_" + cid)(pos, len) + return getattr(self, "chunk_" + cid.decode('ascii'))(pos, len) def crc(self, cid, data): "Read and verify checksum" @@ -131,7 +129,7 @@ class ChunkStream: self.fp.read(4) - def verify(self, endchunk = "IEND"): + def verify(self, endchunk = b"IEND"): # Simple approach; just calculate checksum for all remaining # blocks. Must be called directly after open. @@ -160,11 +158,18 @@ class PngInfo: self.chunks.append((cid, data)) def add_text(self, key, value, zip=0): + # The tEXt chunk stores latin-1 text + if not isinstance(key, bytes): + key = key.encode('latin-1', 'strict') + + if not isinstance(value, bytes): + value = value.encode('latin-1', 'replace') + if zip: import zlib - self.add("zTXt", key + "\0\0" + zlib.compress(value)) + self.add(b"zTXt", key + b"\0\0" + zlib.compress(value)) else: - self.add("tEXt", key + "\0" + value) + self.add(b"tEXt", key + b"\0" + value) # -------------------------------------------------------------------- # PNG image stream (IHDR/IEND) @@ -192,11 +197,11 @@ class PngStream(ChunkStream): # Null separator 1 byte (null character) # Compression method 1 byte (0) # Compressed profile n bytes (zlib with deflate compression) - i = s.find(chr(0)) + i = s.find(b"\0") if Image.DEBUG: print("iCCP profile name", s[:i]) - print("Compression method", ord(s[i])) - comp_method = ord(s[i]) + print("Compression method", i8(s[i])) + comp_method = i8(s[i]) if comp_method != 0: raise SyntaxError("Unknown compression method %s in iCCP chunk" % comp_method) try: @@ -212,12 +217,12 @@ class PngStream(ChunkStream): s = ImageFile._safe_read(self.fp, len) self.im_size = i32(s), i32(s[4:]) try: - self.im_mode, self.im_rawmode = _MODES[(ord(s[8]), ord(s[9]))] + self.im_mode, self.im_rawmode = _MODES[(i8(s[8]), i8(s[9]))] except: pass - if ord(s[12]): + if i8(s[12]): self.im_info["interlace"] = 1 - if ord(s[11]): + if i8(s[11]): raise SyntaxError("unknown filter category") return s @@ -246,7 +251,7 @@ class PngStream(ChunkStream): # transparency s = ImageFile._safe_read(self.fp, len) if self.im_mode == "P": - i = s.find(chr(0)) + i = s.find(b"\0") if i >= 0: self.im_info["transparency"] = i elif self.im_mode == "L": @@ -267,7 +272,7 @@ class PngStream(ChunkStream): # pixels per unit s = ImageFile._safe_read(self.fp, len) px, py = i32(s), i32(s[4:]) - unit = ord(s[8]) + unit = i8(s[8]) if unit == 1: # meter dpi = int(px * 0.0254 + 0.5), int(py * 0.0254 + 0.5) self.im_info["dpi"] = dpi @@ -280,10 +285,14 @@ class PngStream(ChunkStream): # text s = ImageFile._safe_read(self.fp, len) try: - k, v = s.split("\0", 1) + k, v = s.split(b"\0", 1) except ValueError: - k = s; v = "" # fallback for broken tEXt tags + k = s; v = b"" # fallback for broken tEXt tags if k: + if bytes is not str: + k = k.decode('latin-1', 'strict') + v = v.decode('latin-1', 'replace') + self.im_info[k] = self.im_text[k] = v return s @@ -291,12 +300,18 @@ class PngStream(ChunkStream): # compressed text s = ImageFile._safe_read(self.fp, len) - k, v = s.split("\0", 1) - comp_method = ord(v[0]) + k, v = s.split(b"\0", 1) + comp_method = i8(v[0]) if comp_method != 0: raise SyntaxError("Unknown compression method %s in zTXt chunk" % comp_method) import zlib - self.im_info[k] = self.im_text[k] = zlib.decompress(v[1:]) + v = zlib.decompress(v[1:]) + + if bytes is not str: + k = k.decode('latin-1', 'strict') + v = v.decode('latin-1', 'replace') + + self.im_info[k] = self.im_text[k] = v return s # -------------------------------------------------------------------- @@ -393,9 +408,9 @@ class PngImageFile(ImageFile.ImageFile): cid, pos, len = self.png.read() - if cid not in ["IDAT", "DDAT"]: + if cid not in [b"IDAT", b"DDAT"]: self.png.push(cid, pos, len) - return "" + return b"" self.__idat = len # empty chunks are allowed @@ -420,33 +435,31 @@ class PngImageFile(ImageFile.ImageFile): # -------------------------------------------------------------------- # PNG writer -def o16(i): - return chr(i>>8&255) + chr(i&255) - -def o32(i): - return chr(i>>24&255) + chr(i>>16&255) + chr(i>>8&255) + chr(i&255) +o8 = _binary.o8 +o16 = _binary.o16be +o32 = _binary.o32be _OUTMODES = { # supported PIL modes, and corresponding rawmodes/bits/color combinations - "1": ("1", chr(1)+chr(0)), - "L;1": ("L;1", chr(1)+chr(0)), - "L;2": ("L;2", chr(2)+chr(0)), - "L;4": ("L;4", chr(4)+chr(0)), - "L": ("L", chr(8)+chr(0)), - "LA": ("LA", chr(8)+chr(4)), - "I": ("I;16B", chr(16)+chr(0)), - "P;1": ("P;1", chr(1)+chr(3)), - "P;2": ("P;2", chr(2)+chr(3)), - "P;4": ("P;4", chr(4)+chr(3)), - "P": ("P", chr(8)+chr(3)), - "RGB": ("RGB", chr(8)+chr(2)), - "RGBA":("RGBA", chr(8)+chr(6)), + "1": ("1", b'\x01\x00'), + "L;1": ("L;1", b'\x01\x00'), + "L;2": ("L;2", b'\x02\x00'), + "L;4": ("L;4", b'\x04\x00'), + "L": ("L", b'\x08\x00'), + "LA": ("LA", b'\x08\x04'), + "I": ("I;16B", b'\x10\x00'), + "P;1": ("P;1", b'\x01\x03'), + "P;2": ("P;2", b'\x02\x03'), + "P;4": ("P;4", b'\x04\x03'), + "P": ("P", b'\x08\x03'), + "RGB": ("RGB", b'\x08\x02'), + "RGBA":("RGBA", b'\x08\x06'), } def putchunk(fp, cid, *data): "Write a PNG chunk (including CRC field)" - data = "".join(data) + data = b"".join(data) fp.write(o32(len(data)) + cid) fp.write(data) @@ -460,7 +473,7 @@ class _idat: self.fp = fp self.chunk = chunk def write(self, data): - self.chunk(self.fp, "IDAT", data) + self.chunk(self.fp, b"IDAT", data) def _save(im, fp, filename, chunk=putchunk, check=0): # save an image to disk (called by the save method) @@ -498,7 +511,7 @@ def _save(im, fp, filename, chunk=putchunk, check=0): if "dictionary" in im.encoderinfo: dictionary = im.encoderinfo["dictionary"] else: - dictionary = "" + dictionary = b"" im.encoderconfig = ("optimize" in im.encoderinfo, dictionary) @@ -516,39 +529,39 @@ def _save(im, fp, filename, chunk=putchunk, check=0): fp.write(_MAGIC) - chunk(fp, "IHDR", + chunk(fp, b"IHDR", o32(im.size[0]), o32(im.size[1]), # 0: size mode, # 8: depth/type - chr(0), # 10: compression - chr(0), # 11: filter category - chr(0)) # 12: interlace flag + b'\0', # 10: compression + b'\0', # 11: filter category + b'\0') # 12: interlace flag if im.mode == "P": - chunk(fp, "PLTE", im.im.getpalette("RGB")) + chunk(fp, b"PLTE", im.im.getpalette("RGB")) if "transparency" in im.encoderinfo: if im.mode == "P": transparency = max(0, min(255, im.encoderinfo["transparency"])) - chunk(fp, "tRNS", chr(255) * transparency + chr(0)) + chunk(fp, b"tRNS", b'\xFF' * transparency + b'\0') elif im.mode == "L": transparency = max(0, min(65535, im.encoderinfo["transparency"])) - chunk(fp, "tRNS", o16(transparency)) + chunk(fp, b"tRNS", o16(transparency)) elif im.mode == "RGB": red, green, blue = im.encoderinfo["transparency"] - chunk(fp, "tRNS", o16(red) + o16(green) + o16(blue)) + chunk(fp, b"tRNS", o16(red) + o16(green) + o16(blue)) else: raise IOError("cannot use transparency for this mode") if 0: # FIXME: to be supported some day - chunk(fp, "gAMA", o32(int(gamma * 100000.0))) + chunk(fp, b"gAMA", o32(int(gamma * 100000.0))) dpi = im.encoderinfo.get("dpi") if dpi: - chunk(fp, "pHYs", + chunk(fp, b"pHYs", o32(int(dpi[0] / 0.0254 + 0.5)), o32(int(dpi[1] / 0.0254 + 0.5)), - chr(1)) + b'\x01') info = im.encoderinfo.get("pnginfo") if info: @@ -568,13 +581,13 @@ def _save(im, fp, filename, chunk=putchunk, check=0): p = ICCProfile.ICCProfile(im.info["icc_profile"]) name = p.tags.desc.get("ASCII", p.tags.desc.get("Unicode", p.tags.desc.get("Macintosh", p.tags.desc.get("en", {}).get("US", "ICC Profile")))).encode("latin1", "replace")[:79] except ImportError: - name = "ICC Profile" - data = name + "\0\0" + zlib.compress(im.info["icc_profile"]) - chunk(fp, "iCCP", data) + name = b"ICC Profile" + data = name + b"\0\0" + zlib.compress(im.info["icc_profile"]) + chunk(fp, b"iCCP", data) ImageFile._save(im, _idat(fp, chunk), [("zip", (0,0)+im.size, 0, rawmode)]) - chunk(fp, "IEND", "") + chunk(fp, b"IEND", b"") try: fp.flush() @@ -596,7 +609,7 @@ def getchunks(im, **params): self.data.append(chunk) def append(fp, cid, *data): - data = "".join(data) + data = b"".join(data) hi, lo = Image.core.crc32(data, Image.core.crc32(cid)) crc = o16(hi) + o16(lo) fp.append((cid, data, crc)) diff --git a/PIL/PpmImagePlugin.py b/PIL/PpmImagePlugin.py index ea665f7a9..ea4266226 100644 --- a/PIL/PpmImagePlugin.py +++ b/PIL/PpmImagePlugin.py @@ -24,21 +24,23 @@ from . import Image, ImageFile # # -------------------------------------------------------------------- +b_whitespace = string.whitespace.encode() + MODES = { # standard - "P4": "1", - "P5": "L", - "P6": "RGB", + b"P4": "1", + b"P5": "L", + b"P6": "RGB", # extensions - "P0CMYK": "CMYK", + b"P0CMYK": "CMYK", # PIL extensions (for test purposes only) - "PyP": "P", - "PyRGBA": "RGBA", - "PyCMYK": "CMYK" + b"PyP": "P", + b"PyRGBA": "RGBA", + b"PyCMYK": "CMYK" } def _accept(prefix): - return prefix[0] == "P" and prefix[1] in "0456y" + return prefix[0:1] == b"P" and prefix[1] in b"0456y" ## # Image plugin for PBM, PGM, and PPM images. @@ -48,10 +50,10 @@ class PpmImageFile(ImageFile.ImageFile): format = "PPM" format_description = "Pbmplus image" - def _token(self, s = ""): + def _token(self, s = b""): while True: # read until next whitespace c = self.fp.read(1) - if not c or c in string.whitespace: + if not c or c in b_whitespace: break s = s + c return s @@ -60,7 +62,7 @@ class PpmImageFile(ImageFile.ImageFile): # check magic s = self.fp.read(1) - if s != "P": + if s != b"P": raise SyntaxError("not a PPM file") mode = MODES[self._token(s)] @@ -74,9 +76,9 @@ class PpmImageFile(ImageFile.ImageFile): while True: while True: s = self.fp.read(1) - if s not in string.whitespace: + if s not in b_whitespace: break - if s != "#": + if s != b"#": break s = self.fp.readline() s = int(self._token(s)) @@ -103,18 +105,18 @@ class PpmImageFile(ImageFile.ImageFile): def _save(im, fp, filename): if im.mode == "1": - rawmode, head = "1;I", "P4" + rawmode, head = "1;I", b"P4" elif im.mode == "L": - rawmode, head = "L", "P5" + rawmode, head = "L", b"P5" elif im.mode == "RGB": - rawmode, head = "RGB", "P6" + rawmode, head = "RGB", b"P6" elif im.mode == "RGBA": - rawmode, head = "RGB", "P6" + rawmode, head = "RGB", b"P6" else: raise IOError("cannot write mode %s as PPM" % im.mode) - fp.write(head + "\n%d %d\n" % im.size) - if head != "P4": - fp.write("255\n") + fp.write(head + ("\n%d %d\n" % im.size).encode('ascii')) + if head != b"P4": + fp.write(b"255\n") ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode, 0, 1))]) # ALTERNATIVE: save via builtin debug function diff --git a/PIL/PsdImagePlugin.py b/PIL/PsdImagePlugin.py index 0fc07d488..e92aeeab0 100644 --- a/PIL/PsdImagePlugin.py +++ b/PIL/PsdImagePlugin.py @@ -18,7 +18,7 @@ __version__ = "0.4" -from . import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette, _binary MODES = { # (photoshop mode, bits) -> (pil mode, required channels) @@ -36,17 +36,15 @@ MODES = { # # helpers -def i16(c): - return ord(c[1]) + (ord(c[0])<<8) - -def i32(c): - return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) +i8 = _binary.i8 +i16 = _binary.i16be +i32 = _binary.i32be # --------------------------------------------------------------------. # read PSD images def _accept(prefix): - return prefix[:4] == "8BPS" + return prefix[:4] == b"8BPS" ## # Image plugin for Photoshop images. @@ -64,7 +62,7 @@ class PsdImageFile(ImageFile.ImageFile): # header s = read(26) - if s[:4] != "8BPS" or i16(s[4:]) != 1: + if s[:4] != b"8BPS" or i16(s[4:]) != 1: raise SyntaxError("not a PSD file") psd_bits = i16(s[22:]) @@ -100,7 +98,7 @@ class PsdImageFile(ImageFile.ImageFile): while self.fp.tell() < end: signature = read(4) id = i16(read(2)) - name = read(ord(read(1))) + name = read(i8(read(1))) if not (len(name) & 1): read(1) # padding data = read(i32(read(4))) @@ -219,9 +217,10 @@ def _layerinfo(file): file.seek(length, 1) combined += length + 4 - length = ord(read(1)) + length = i8(read(1)) if length: - name = read(length) + # Don't know the proper encoding, Latin-1 should be a good guess + name = read(length).decode('latin-1', 'replace') combined += length + 1 file.seek(size - combined, 1) diff --git a/PIL/SgiImagePlugin.py b/PIL/SgiImagePlugin.py index 94b51fc17..ad8c3e847 100644 --- a/PIL/SgiImagePlugin.py +++ b/PIL/SgiImagePlugin.py @@ -21,14 +21,11 @@ __version__ = "0.2" -from . import Image, ImageFile +from . import Image, ImageFile, _binary - -def i16(c): - return ord(c[1]) + (ord(c[0])<<8) - -def i32(c): - return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) +i8 = _binary.i8 +i16 = _binary.i16be +i32 = _binary.i32be def _accept(prefix): @@ -50,10 +47,10 @@ class SgiImageFile(ImageFile.ImageFile): raise SyntaxError("not an SGI image file") # relevant header entries - compression = ord(s[2]) + compression = i8(s[2]) # bytes, dimension, zsize - layout = ord(s[3]), i16(s[4:]), i16(s[10:]) + layout = i8(s[3]), i16(s[4:]), i16(s[10:]) # determine mode from bytes/zsize if layout == (1, 2, 1) or layout == (1, 1, 1): diff --git a/PIL/SunImagePlugin.py b/PIL/SunImagePlugin.py index 1e03a028e..9a0ca5a01 100644 --- a/PIL/SunImagePlugin.py +++ b/PIL/SunImagePlugin.py @@ -20,14 +20,10 @@ __version__ = "0.3" -from . import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette, _binary - -def i16(c): - return ord(c[1]) + (ord(c[0])<<8) - -def i32(c): - return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) +i16 = _binary.i16be +i32 = _binary.i32be def _accept(prefix): @@ -71,7 +67,7 @@ class SunImageFile(ImageFile.ImageFile): if self.mode == "L": self.mode = rawmode = "P" - stride = (((self.size[0] * depth + 7) / 8) + 3) & (~3) + stride = (((self.size[0] * depth + 7) // 8) + 3) & (~3) if compression == 1: self.tile = [("raw", (0,0)+self.size, offset, (rawmode, stride))] diff --git a/PIL/TarIO.py b/PIL/TarIO.py index e0608c72f..73e3fc48a 100644 --- a/PIL/TarIO.py +++ b/PIL/TarIO.py @@ -38,8 +38,8 @@ class TarIO(ContainerIO.ContainerIO): if len(s) != 512: raise IOError("unexpected end of tar file") - name = s[:100] - i = name.find(chr(0)) + name = s[:100].decode('utf-8') + i = name.find(b'\0') if i == 0: raise IOError("cannot find subfile") if i > 0: diff --git a/PIL/TgaImagePlugin.py b/PIL/TgaImagePlugin.py index 945e8d1e7..82d469db3 100644 --- a/PIL/TgaImagePlugin.py +++ b/PIL/TgaImagePlugin.py @@ -19,18 +19,16 @@ __version__ = "0.3" -from . import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette, _binary # # -------------------------------------------------------------------- # Read RGA file -def i16(c): - return ord(c[0]) + (ord(c[1])<<8) - -def i32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le MODES = { @@ -45,7 +43,7 @@ MODES = { def _accept(prefix): - return prefix[0] == "\0" + return prefix[0:1] == b"\0" ## # Image plugin for Targa files. @@ -60,14 +58,14 @@ class TgaImageFile(ImageFile.ImageFile): # process header s = self.fp.read(18) - id = ord(s[0]) + id = i8(s[0]) - colormaptype = ord(s[1]) - imagetype = ord(s[2]) + colormaptype = i8(s[1]) + imagetype = i8(s[2]) - depth = ord(s[16]) + depth = i8(s[16]) - flags = ord(s[17]) + flags = i8(s[17]) self.size = i16(s[12:]), i16(s[14:]) @@ -110,13 +108,13 @@ class TgaImageFile(ImageFile.ImageFile): start, size, mapdepth = i16(s[3:]), i16(s[5:]), i16(s[7:]) if mapdepth == 16: self.palette = ImagePalette.raw("BGR;16", - "\0"*2*start + self.fp.read(2*size)) + b"\0"*2*start + self.fp.read(2*size)) elif mapdepth == 24: self.palette = ImagePalette.raw("BGR", - "\0"*3*start + self.fp.read(3*size)) + b"\0"*3*start + self.fp.read(3*size)) elif mapdepth == 32: self.palette = ImagePalette.raw("BGRA", - "\0"*4*start + self.fp.read(4*size)) + b"\0"*4*start + self.fp.read(4*size)) # setup tile descriptor try: @@ -135,11 +133,8 @@ class TgaImageFile(ImageFile.ImageFile): # -------------------------------------------------------------------- # Write TGA file -def o16(i): - return chr(i&255) + chr(i>>8&255) - -def o32(i): - return chr(i&255) + chr(i>>8&255) + chr(i>>16&255) + chr(i>>24&255) +o16 = _binary.o16le +o32 = _binary.o32le SAVE = { "1": ("1", 1, 0, 3), @@ -173,18 +168,18 @@ def _save(im, fp, filename, check=0): if orientation > 0: flags = flags | 0x20 - fp.write("\000" + - chr(colormaptype) + - chr(imagetype) + + fp.write(b"\000" + + o8(colormaptype) + + o8(imagetype) + o16(colormapfirst) + o16(colormaplength) + - chr(colormapentry) + + o8(colormapentry) + o16(0) + o16(0) + o16(im.size[0]) + o16(im.size[1]) + - chr(bits) + - chr(flags)) + o8(bits) + + o8(flags)) if colormaptype: fp.write(im.im.getpalette("RGB", "BGR")) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 93c188115..84ce1817a 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -45,46 +45,36 @@ __version__ = "1.3.5" from . import Image, ImageFile from . import ImagePalette +from . import _binary import array, sys import collections import itertools -II = "II" # little-endian (intel-style) -MM = "MM" # big-endian (motorola-style) +II = b"II" # little-endian (intel-style) +MM = b"MM" # big-endian (motorola-style) -try: - if sys.byteorder == "little": - native_prefix = II - else: - native_prefix = MM -except AttributeError: - if ord(array.array("i",[1]).tostring()[0]): - native_prefix = II - else: - native_prefix = MM +i8 = _binary.i8 +o8 = _binary.o8 + +if sys.byteorder == "little": + native_prefix = II +else: + native_prefix = MM # # -------------------------------------------------------------------- # Read TIFF files -def il16(c,o=0): - return ord(c[o]) + (ord(c[o+1])<<8) -def il32(c,o=0): - return ord(c[o]) + (ord(c[o+1])<<8) + (ord(c[o+2])<<16) + (ord(c[o+3])<<24) -def ol16(i): - return chr(i&255) + chr(i>>8&255) -def ol32(i): - return chr(i&255) + chr(i>>8&255) + chr(i>>16&255) + chr(i>>24&255) +il16 = _binary.i16le +il32 = _binary.i32le +ol16 = _binary.o16le +ol32 = _binary.o32le -def ib16(c,o=0): - return ord(c[o+1]) + (ord(c[o])<<8) -def ib32(c,o=0): - return ord(c[o+3]) + (ord(c[o+2])<<8) + (ord(c[o+1])<<16) + (ord(c[o])<<24) -def ob16(i): - return chr(i>>8&255) + chr(i&255) -def ob32(i): - return chr(i>>24&255) + chr(i>>16&255) + chr(i>>8&255) + chr(i&255) +ib16 = _binary.i16be +ib32 = _binary.i32be +ob16 = _binary.o16be +ob32 = _binary.o32be # a few tag names, just to make the code below a bit more readable IMAGEWIDTH = 256 @@ -200,7 +190,7 @@ OPEN_INFO = { } -PREFIXES = ["MM\000\052", "II\052\000", "II\xBC\000"] +PREFIXES = [b"MM\000\052", b"II\052\000", b"II\xBC\000"] def _accept(prefix): return prefix[:4] in PREFIXES @@ -264,7 +254,7 @@ class ImageFileDirectory(collections.MutableMapping): def __contains__(self, tag): return tag in self.tags or tag in self.tagdata - if sys.version_info < (3,0): + if bytes is str: def has_key(self, tag): return tag in self @@ -284,16 +274,13 @@ class ImageFileDirectory(collections.MutableMapping): load_dispatch = {} def load_byte(self, data): - l = [] - for i in range(len(data)): - l.append(ord(data[i])) - return tuple(l) + return data load_dispatch[1] = (1, load_byte) def load_string(self, data): - if data[-1:] == '\0': + if data[-1:] == b'\0': data = data[:-1] - return data + return data.decode('latin-1', 'replace') load_dispatch[2] = (1, load_string) def load_short(self, data): @@ -420,14 +407,14 @@ class ImageFileDirectory(collections.MutableMapping): if typ == 1: # byte data - data = value = "".join(map(chr, value)) + data = value elif typ == 7: # untyped data - data = value = "".join(value) + data = value = b"".join(value) elif isinstance(value[0], str): # string data typ = 2 - data = value = "\0".join(value) + "\0" + data = value = b"\0".join(value.encode('ascii', 'replace')) + b"\0" else: # integer data if tag == STRIPOFFSETS: @@ -442,9 +429,9 @@ class ImageFileDirectory(collections.MutableMapping): if v >= 65536: typ = 4 if typ == 3: - data = "".join(map(o16, value)) + data = b"".join(map(o16, value)) else: - data = "".join(map(o32, value)) + data = b"".join(map(o32, value)) if Image.DEBUG: from . import TiffTags @@ -460,13 +447,13 @@ class ImageFileDirectory(collections.MutableMapping): # figure out if data fits into the directory if len(data) == 4: - append((tag, typ, len(value), data, "")) + append((tag, typ, len(value), data, b"")) elif len(data) < 4: - append((tag, typ, len(value), data + (4-len(data))*"\0", "")) + append((tag, typ, len(value), data + (4-len(data))*b"\0", b"")) else: count = len(value) if typ == 5: - count = count / 2 # adjust for rational data field + count = count // 2 # adjust for rational data field append((tag, typ, count, o32(offset), data)) offset = offset + len(data) if offset & 1: @@ -486,13 +473,13 @@ class ImageFileDirectory(collections.MutableMapping): fp.write(o16(tag) + o16(typ) + o32(count) + value) # -- overwrite here for multi-page -- - fp.write("\0\0\0\0") # end of directory + fp.write(b"\0\0\0\0") # end of directory # pass 3: write auxiliary data to file for tag, typ, count, value, data in directory: fp.write(data) if len(data) & 1: - fp.write("\0") + fp.write(b"\0") return offset @@ -702,8 +689,8 @@ class TiffImageFile(ImageFile.ImageFile): # fixup palette descriptor if self.mode == "P": - palette = [chr(a / 256) for a in self.tag[COLORMAP]] - self.palette = ImagePalette.raw("RGB;L", "".join(palette)) + palette = [o8(a // 256) for a in self.tag[COLORMAP]] + self.palette = ImagePalette.raw("RGB;L", b"".join(palette)) # # -------------------------------------------------------------------- # Write TIFF files @@ -824,10 +811,10 @@ def _save(im, fp, filename): if im.mode == "P": lut = im.im.getpalette("RGB", "RGB;L") - ifd[COLORMAP] = tuple(ord(v) * 256 for v in lut) + ifd[COLORMAP] = tuple(i8(v) * 256 for v in lut) # data orientation - stride = len(bits) * ((im.size[0]*bits[0]+7)/8) + stride = len(bits) * ((im.size[0]*bits[0]+7)//8) ifd[ROWSPERSTRIP] = im.size[1] ifd[STRIPBYTECOUNTS] = stride * im.size[1] ifd[STRIPOFFSETS] = 0 # this is adjusted by IFD writer diff --git a/PIL/WalImageFile.py b/PIL/WalImageFile.py index d67d6d62d..5d2b701ab 100644 --- a/PIL/WalImageFile.py +++ b/PIL/WalImageFile.py @@ -23,7 +23,7 @@ from __future__ import print_function -from . import Image +from . import Image, _binary try: import builtins @@ -31,8 +31,7 @@ except ImportError: import __builtin__ builtins = __builtin__ -def i32(c, o=0): - return ord(c[o])+(ord(c[o+1])<<8)+(ord(c[o+2])<<16)+(ord(c[o+3])<<24) +i32 = _binary.i32le ## # Load texture from a Quake2 WAL texture file. @@ -60,15 +59,15 @@ def open(filename): # load pixel data fp.seek(offset) - im = Image.fromstring("P", size, fp.read(size[0] * size[1])) + im = Image.frombytes("P", size, fp.read(size[0] * size[1])) im.putpalette(quake2palette) im.format = "WAL" im.format_description = "Quake2 Texture" # strings are null-terminated - im.info["name"] = header[:32].split("\0", 1)[0] - next_name = header[56:56+32].split("\0", 1)[0] + im.info["name"] = header[:32].split(b"\0", 1)[0] + next_name = header[56:56+32].split(b"\0", 1)[0] if next_name: im.info["next_name"] = next_name @@ -77,54 +76,54 @@ def open(filename): quake2palette = ( # default palette taken from piffo 0.93 by Hans Häggström - "\x01\x01\x01\x0b\x0b\x0b\x12\x12\x12\x17\x17\x17\x1b\x1b\x1b\x1e" - "\x1e\x1e\x22\x22\x22\x26\x26\x26\x29\x29\x29\x2c\x2c\x2c\x2f\x2f" - "\x2f\x32\x32\x32\x35\x35\x35\x37\x37\x37\x3a\x3a\x3a\x3c\x3c\x3c" - "\x24\x1e\x13\x22\x1c\x12\x20\x1b\x12\x1f\x1a\x10\x1d\x19\x10\x1b" - "\x17\x0f\x1a\x16\x0f\x18\x14\x0d\x17\x13\x0d\x16\x12\x0d\x14\x10" - "\x0b\x13\x0f\x0b\x10\x0d\x0a\x0f\x0b\x0a\x0d\x0b\x07\x0b\x0a\x07" - "\x23\x23\x26\x22\x22\x25\x22\x20\x23\x21\x1f\x22\x20\x1e\x20\x1f" - "\x1d\x1e\x1d\x1b\x1c\x1b\x1a\x1a\x1a\x19\x19\x18\x17\x17\x17\x16" - "\x16\x14\x14\x14\x13\x13\x13\x10\x10\x10\x0f\x0f\x0f\x0d\x0d\x0d" - "\x2d\x28\x20\x29\x24\x1c\x27\x22\x1a\x25\x1f\x17\x38\x2e\x1e\x31" - "\x29\x1a\x2c\x25\x17\x26\x20\x14\x3c\x30\x14\x37\x2c\x13\x33\x28" - "\x12\x2d\x24\x10\x28\x1f\x0f\x22\x1a\x0b\x1b\x14\x0a\x13\x0f\x07" - "\x31\x1a\x16\x30\x17\x13\x2e\x16\x10\x2c\x14\x0d\x2a\x12\x0b\x27" - "\x0f\x0a\x25\x0f\x07\x21\x0d\x01\x1e\x0b\x01\x1c\x0b\x01\x1a\x0b" - "\x01\x18\x0a\x01\x16\x0a\x01\x13\x0a\x01\x10\x07\x01\x0d\x07\x01" - "\x29\x23\x1e\x27\x21\x1c\x26\x20\x1b\x25\x1f\x1a\x23\x1d\x19\x21" - "\x1c\x18\x20\x1b\x17\x1e\x19\x16\x1c\x18\x14\x1b\x17\x13\x19\x14" - "\x10\x17\x13\x0f\x14\x10\x0d\x12\x0f\x0b\x0f\x0b\x0a\x0b\x0a\x07" - "\x26\x1a\x0f\x23\x19\x0f\x20\x17\x0f\x1c\x16\x0f\x19\x13\x0d\x14" - "\x10\x0b\x10\x0d\x0a\x0b\x0a\x07\x33\x22\x1f\x35\x29\x26\x37\x2f" - "\x2d\x39\x35\x34\x37\x39\x3a\x33\x37\x39\x30\x34\x36\x2b\x31\x34" - "\x27\x2e\x31\x22\x2b\x2f\x1d\x28\x2c\x17\x25\x2a\x0f\x20\x26\x0d" - "\x1e\x25\x0b\x1c\x22\x0a\x1b\x20\x07\x19\x1e\x07\x17\x1b\x07\x14" - "\x18\x01\x12\x16\x01\x0f\x12\x01\x0b\x0d\x01\x07\x0a\x01\x01\x01" - "\x2c\x21\x21\x2a\x1f\x1f\x29\x1d\x1d\x27\x1c\x1c\x26\x1a\x1a\x24" - "\x18\x18\x22\x17\x17\x21\x16\x16\x1e\x13\x13\x1b\x12\x12\x18\x10" - "\x10\x16\x0d\x0d\x12\x0b\x0b\x0d\x0a\x0a\x0a\x07\x07\x01\x01\x01" - "\x2e\x30\x29\x2d\x2e\x27\x2b\x2c\x26\x2a\x2a\x24\x28\x29\x23\x27" - "\x27\x21\x26\x26\x1f\x24\x24\x1d\x22\x22\x1c\x1f\x1f\x1a\x1c\x1c" - "\x18\x19\x19\x16\x17\x17\x13\x13\x13\x10\x0f\x0f\x0d\x0b\x0b\x0a" - "\x30\x1e\x1b\x2d\x1c\x19\x2c\x1a\x17\x2a\x19\x14\x28\x17\x13\x26" - "\x16\x10\x24\x13\x0f\x21\x12\x0d\x1f\x10\x0b\x1c\x0f\x0a\x19\x0d" - "\x0a\x16\x0b\x07\x12\x0a\x07\x0f\x07\x01\x0a\x01\x01\x01\x01\x01" - "\x28\x29\x38\x26\x27\x36\x25\x26\x34\x24\x24\x31\x22\x22\x2f\x20" - "\x21\x2d\x1e\x1f\x2a\x1d\x1d\x27\x1b\x1b\x25\x19\x19\x21\x17\x17" - "\x1e\x14\x14\x1b\x13\x12\x17\x10\x0f\x13\x0d\x0b\x0f\x0a\x07\x07" - "\x2f\x32\x29\x2d\x30\x26\x2b\x2e\x24\x29\x2c\x21\x27\x2a\x1e\x25" - "\x28\x1c\x23\x26\x1a\x21\x25\x18\x1e\x22\x14\x1b\x1f\x10\x19\x1c" - "\x0d\x17\x1a\x0a\x13\x17\x07\x10\x13\x01\x0d\x0f\x01\x0a\x0b\x01" - "\x01\x3f\x01\x13\x3c\x0b\x1b\x39\x10\x20\x35\x14\x23\x31\x17\x23" - "\x2d\x18\x23\x29\x18\x3f\x3f\x3f\x3f\x3f\x39\x3f\x3f\x31\x3f\x3f" - "\x2a\x3f\x3f\x20\x3f\x3f\x14\x3f\x3c\x12\x3f\x39\x0f\x3f\x35\x0b" - "\x3f\x32\x07\x3f\x2d\x01\x3d\x2a\x01\x3b\x26\x01\x39\x21\x01\x37" - "\x1d\x01\x34\x1a\x01\x32\x16\x01\x2f\x12\x01\x2d\x0f\x01\x2a\x0b" - "\x01\x27\x07\x01\x23\x01\x01\x1d\x01\x01\x17\x01\x01\x10\x01\x01" - "\x3d\x01\x01\x19\x19\x3f\x3f\x01\x01\x01\x01\x3f\x16\x16\x13\x10" - "\x10\x0f\x0d\x0d\x0b\x3c\x2e\x2a\x36\x27\x20\x30\x21\x18\x29\x1b" - "\x10\x3c\x39\x37\x37\x32\x2f\x31\x2c\x28\x2b\x26\x21\x30\x22\x20" + b"\x01\x01\x01\x0b\x0b\x0b\x12\x12\x12\x17\x17\x17\x1b\x1b\x1b\x1e" + b"\x1e\x1e\x22\x22\x22\x26\x26\x26\x29\x29\x29\x2c\x2c\x2c\x2f\x2f" + b"\x2f\x32\x32\x32\x35\x35\x35\x37\x37\x37\x3a\x3a\x3a\x3c\x3c\x3c" + b"\x24\x1e\x13\x22\x1c\x12\x20\x1b\x12\x1f\x1a\x10\x1d\x19\x10\x1b" + b"\x17\x0f\x1a\x16\x0f\x18\x14\x0d\x17\x13\x0d\x16\x12\x0d\x14\x10" + b"\x0b\x13\x0f\x0b\x10\x0d\x0a\x0f\x0b\x0a\x0d\x0b\x07\x0b\x0a\x07" + b"\x23\x23\x26\x22\x22\x25\x22\x20\x23\x21\x1f\x22\x20\x1e\x20\x1f" + b"\x1d\x1e\x1d\x1b\x1c\x1b\x1a\x1a\x1a\x19\x19\x18\x17\x17\x17\x16" + b"\x16\x14\x14\x14\x13\x13\x13\x10\x10\x10\x0f\x0f\x0f\x0d\x0d\x0d" + b"\x2d\x28\x20\x29\x24\x1c\x27\x22\x1a\x25\x1f\x17\x38\x2e\x1e\x31" + b"\x29\x1a\x2c\x25\x17\x26\x20\x14\x3c\x30\x14\x37\x2c\x13\x33\x28" + b"\x12\x2d\x24\x10\x28\x1f\x0f\x22\x1a\x0b\x1b\x14\x0a\x13\x0f\x07" + b"\x31\x1a\x16\x30\x17\x13\x2e\x16\x10\x2c\x14\x0d\x2a\x12\x0b\x27" + b"\x0f\x0a\x25\x0f\x07\x21\x0d\x01\x1e\x0b\x01\x1c\x0b\x01\x1a\x0b" + b"\x01\x18\x0a\x01\x16\x0a\x01\x13\x0a\x01\x10\x07\x01\x0d\x07\x01" + b"\x29\x23\x1e\x27\x21\x1c\x26\x20\x1b\x25\x1f\x1a\x23\x1d\x19\x21" + b"\x1c\x18\x20\x1b\x17\x1e\x19\x16\x1c\x18\x14\x1b\x17\x13\x19\x14" + b"\x10\x17\x13\x0f\x14\x10\x0d\x12\x0f\x0b\x0f\x0b\x0a\x0b\x0a\x07" + b"\x26\x1a\x0f\x23\x19\x0f\x20\x17\x0f\x1c\x16\x0f\x19\x13\x0d\x14" + b"\x10\x0b\x10\x0d\x0a\x0b\x0a\x07\x33\x22\x1f\x35\x29\x26\x37\x2f" + b"\x2d\x39\x35\x34\x37\x39\x3a\x33\x37\x39\x30\x34\x36\x2b\x31\x34" + b"\x27\x2e\x31\x22\x2b\x2f\x1d\x28\x2c\x17\x25\x2a\x0f\x20\x26\x0d" + b"\x1e\x25\x0b\x1c\x22\x0a\x1b\x20\x07\x19\x1e\x07\x17\x1b\x07\x14" + b"\x18\x01\x12\x16\x01\x0f\x12\x01\x0b\x0d\x01\x07\x0a\x01\x01\x01" + b"\x2c\x21\x21\x2a\x1f\x1f\x29\x1d\x1d\x27\x1c\x1c\x26\x1a\x1a\x24" + b"\x18\x18\x22\x17\x17\x21\x16\x16\x1e\x13\x13\x1b\x12\x12\x18\x10" + b"\x10\x16\x0d\x0d\x12\x0b\x0b\x0d\x0a\x0a\x0a\x07\x07\x01\x01\x01" + b"\x2e\x30\x29\x2d\x2e\x27\x2b\x2c\x26\x2a\x2a\x24\x28\x29\x23\x27" + b"\x27\x21\x26\x26\x1f\x24\x24\x1d\x22\x22\x1c\x1f\x1f\x1a\x1c\x1c" + b"\x18\x19\x19\x16\x17\x17\x13\x13\x13\x10\x0f\x0f\x0d\x0b\x0b\x0a" + b"\x30\x1e\x1b\x2d\x1c\x19\x2c\x1a\x17\x2a\x19\x14\x28\x17\x13\x26" + b"\x16\x10\x24\x13\x0f\x21\x12\x0d\x1f\x10\x0b\x1c\x0f\x0a\x19\x0d" + b"\x0a\x16\x0b\x07\x12\x0a\x07\x0f\x07\x01\x0a\x01\x01\x01\x01\x01" + b"\x28\x29\x38\x26\x27\x36\x25\x26\x34\x24\x24\x31\x22\x22\x2f\x20" + b"\x21\x2d\x1e\x1f\x2a\x1d\x1d\x27\x1b\x1b\x25\x19\x19\x21\x17\x17" + b"\x1e\x14\x14\x1b\x13\x12\x17\x10\x0f\x13\x0d\x0b\x0f\x0a\x07\x07" + b"\x2f\x32\x29\x2d\x30\x26\x2b\x2e\x24\x29\x2c\x21\x27\x2a\x1e\x25" + b"\x28\x1c\x23\x26\x1a\x21\x25\x18\x1e\x22\x14\x1b\x1f\x10\x19\x1c" + b"\x0d\x17\x1a\x0a\x13\x17\x07\x10\x13\x01\x0d\x0f\x01\x0a\x0b\x01" + b"\x01\x3f\x01\x13\x3c\x0b\x1b\x39\x10\x20\x35\x14\x23\x31\x17\x23" + b"\x2d\x18\x23\x29\x18\x3f\x3f\x3f\x3f\x3f\x39\x3f\x3f\x31\x3f\x3f" + b"\x2a\x3f\x3f\x20\x3f\x3f\x14\x3f\x3c\x12\x3f\x39\x0f\x3f\x35\x0b" + b"\x3f\x32\x07\x3f\x2d\x01\x3d\x2a\x01\x3b\x26\x01\x39\x21\x01\x37" + b"\x1d\x01\x34\x1a\x01\x32\x16\x01\x2f\x12\x01\x2d\x0f\x01\x2a\x0b" + b"\x01\x27\x07\x01\x23\x01\x01\x1d\x01\x01\x17\x01\x01\x10\x01\x01" + b"\x3d\x01\x01\x19\x19\x3f\x3f\x01\x01\x01\x01\x3f\x16\x16\x13\x10" + b"\x10\x0f\x0d\x0d\x0b\x3c\x2e\x2a\x36\x27\x20\x30\x21\x18\x29\x1b" + b"\x10\x3c\x39\x37\x37\x32\x2f\x31\x2c\x28\x2b\x26\x21\x30\x22\x20" ) if __name__ == "__main__": diff --git a/PIL/WmfImagePlugin.py b/PIL/WmfImagePlugin.py index ca3f194a9..e77d1eb77 100644 --- a/PIL/WmfImagePlugin.py +++ b/PIL/WmfImagePlugin.py @@ -17,7 +17,7 @@ __version__ = "0.2" -from . import Image, ImageFile +from . import Image, ImageFile, _binary _handler = None @@ -44,7 +44,7 @@ if hasattr(Image.core, "drawwmf"): def load(self, im): im.fp.seek(0) # rewind - return Image.fromstring( + return Image.frombytes( "RGB", im.size, Image.core.drawwmf(im.fp.read(), im.size, self.bbox), "raw", "BGR", (im.size[0]*3 + 3) & -4, -1 @@ -54,20 +54,15 @@ if hasattr(Image.core, "drawwmf"): # -------------------------------------------------------------------- -def word(c, o=0): - return ord(c[o]) + (ord(c[o+1])<<8) +word = _binary.i16le def short(c, o=0): - v = ord(c[o]) + (ord(c[o+1])<<8) + v = word(c, o) if v >= 32768: v = v - 65536 return v -def dword(c, o=0): - return ord(c[o]) + (ord(c[o+1])<<8) + (ord(c[o+2])<<16) + (ord(c[o+3])<<24) - -def long(c, o=0): - return dword(c, o) +dword = _binary.i32le # # -------------------------------------------------------------------- @@ -75,8 +70,8 @@ def long(c, o=0): def _accept(prefix): return ( - prefix[:6] == "\xd7\xcd\xc6\x9a\x00\x00" or - prefix[:4] == "\x01\x00\x00\x00" + prefix[:6] == b"\xd7\xcd\xc6\x9a\x00\x00" or + prefix[:4] == b"\x01\x00\x00\x00" ) ## @@ -92,7 +87,7 @@ class WmfStubImageFile(ImageFile.StubImageFile): # check placable header s = self.fp.read(80) - if s[:6] == "\xd7\xcd\xc6\x9a\x00\x00": + if s[:6] == b"\xd7\xcd\xc6\x9a\x00\x00": # placeable windows metafile @@ -104,7 +99,7 @@ class WmfStubImageFile(ImageFile.StubImageFile): x1 = short(s, 10); y1 = short(s, 12) # normalize size to 72 dots per inch - size = (x1 - x0) * 72 / inch, (y1 - y0) * 72 / inch + size = (x1 - x0) * 72 // inch, (y1 - y0) * 72 // inch self.info["wmf_bbox"] = x0, y0, x1, y1 @@ -113,25 +108,25 @@ class WmfStubImageFile(ImageFile.StubImageFile): # print self.mode, self.size, self.info # sanity check (standard metafile header) - if s[22:26] != "\x01\x00\t\x00": + if s[22:26] != b"\x01\x00\t\x00": raise SyntaxError("Unsupported WMF file format") - elif long(s) == 1 and s[40:44] == " EMF": + elif dword(s) == 1 and s[40:44] == b" EMF": # enhanced metafile # get bounding box - x0 = long(s, 8); y0 = long(s, 12) - x1 = long(s, 16); y1 = long(s, 20) + x0 = dword(s, 8); y0 = dword(s, 12) + x1 = dword(s, 16); y1 = dword(s, 20) # get frame (in 0.01 millimeter units) - frame = long(s, 24), long(s, 28), long(s, 32), long(s, 36) + frame = dword(s, 24), dword(s, 28), dword(s, 32), dword(s, 36) # normalize size to 72 dots per inch size = x1 - x0, y1 - y0 # calculate dots per inch from bbox and frame - xdpi = 2540 * (x1 - y0) / (frame[2] - frame[0]) - ydpi = 2540 * (y1 - y0) / (frame[3] - frame[1]) + xdpi = 2540 * (x1 - y0) // (frame[2] - frame[0]) + ydpi = 2540 * (y1 - y0) // (frame[3] - frame[1]) self.info["wmf_bbox"] = x0, y0, x1, y1 diff --git a/PIL/XVThumbImagePlugin.py b/PIL/XVThumbImagePlugin.py index 9d1d534fb..e7f8c90cd 100644 --- a/PIL/XVThumbImagePlugin.py +++ b/PIL/XVThumbImagePlugin.py @@ -19,14 +19,16 @@ __version__ = "0.1" -from . import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette, _binary + +o8 = _binary.o8 # standard color palette for thumbnails (RGB332) -PALETTE = "" +PALETTE = b"" for r in range(8): for g in range(8): for b in range(4): - PALETTE = PALETTE + (chr((r*255)/7)+chr((g*255)/7)+chr((b*255)/3)) + PALETTE = PALETTE + (o8((r*255)//7)+o8((g*255)//7)+o8((b*255)//3)) ## # Image plugin for XV thumbnail images. @@ -40,7 +42,7 @@ class XVThumbImageFile(ImageFile.ImageFile): # check magic s = self.fp.read(6) - if s != "P7 332": + if s != b"P7 332": raise SyntaxError("not an XV thumbnail file") # Skip to beginning of next line @@ -51,14 +53,14 @@ class XVThumbImageFile(ImageFile.ImageFile): s = self.fp.readline() if not s: raise SyntaxError("Unexpected EOF reading XV thumbnail file") - if s[0] != '#': + if s[0] != b'#': break # parse header line (already read) s = s.strip().split() self.mode = "P" - self.size = int(s[0]), int(s[1]) + self.size = int(s[0:1]), int(s[1:2]) self.palette = ImagePalette.raw("RGB", PALETTE) diff --git a/PIL/XbmImagePlugin.py b/PIL/XbmImagePlugin.py index 1c6bbcbc3..e89b83c05 100644 --- a/PIL/XbmImagePlugin.py +++ b/PIL/XbmImagePlugin.py @@ -26,17 +26,17 @@ from . import Image, ImageFile # XBM header xbm_head = re.compile( - "\s*#define[ \t]+[^_]*_width[ \t]+(?P[0-9]+)[\r\n]+" - "#define[ \t]+[^_]*_height[ \t]+(?P[0-9]+)[\r\n]+" - "(?P" - "#define[ \t]+[^_]*_x_hot[ \t]+(?P[0-9]+)[\r\n]+" - "#define[ \t]+[^_]*_y_hot[ \t]+(?P[0-9]+)[\r\n]+" - ")?" - "[\\000-\\377]*_bits\\[\\]" + b"\s*#define[ \t]+[^_]*_width[ \t]+(?P[0-9]+)[\r\n]+" + b"#define[ \t]+[^_]*_height[ \t]+(?P[0-9]+)[\r\n]+" + b"(?P" + b"#define[ \t]+[^_]*_x_hot[ \t]+(?P[0-9]+)[\r\n]+" + b"#define[ \t]+[^_]*_y_hot[ \t]+(?P[0-9]+)[\r\n]+" + b")?" + b"[\\000-\\377]*_bits\\[\\]" ) def _accept(prefix): - return prefix.lstrip()[:7] == "#define" + return prefix.lstrip()[:7] == b"#define" ## # Image plugin for X11 bitmaps. @@ -71,19 +71,19 @@ def _save(im, fp, filename): if im.mode != "1": raise IOError("cannot write mode %s as XBM" % im.mode) - fp.write("#define im_width %d\n" % im.size[0]) - fp.write("#define im_height %d\n" % im.size[1]) + fp.write(("#define im_width %d\n" % im.size[0]).encode('ascii')) + fp.write(("#define im_height %d\n" % im.size[1]).encode('ascii')) hotspot = im.encoderinfo.get("hotspot") if hotspot: - fp.write("#define im_x_hot %d\n" % hotspot[0]) - fp.write("#define im_y_hot %d\n" % hotspot[1]) + fp.write(("#define im_x_hot %d\n" % hotspot[0]).encode('ascii')) + fp.write(("#define im_y_hot %d\n" % hotspot[1]).encode('ascii')) - fp.write("static char im_bits[] = {\n") + fp.write(b"static char im_bits[] = {\n") ImageFile._save(im, fp, [("xbm", (0,0)+im.size, 0, None)]) - fp.write("};\n") + fp.write(b"};\n") Image.register_open("XBM", XbmImageFile, _accept) diff --git a/PIL/XpmImagePlugin.py b/PIL/XpmImagePlugin.py index ff7d3d2a7..c2a52f9b2 100644 --- a/PIL/XpmImagePlugin.py +++ b/PIL/XpmImagePlugin.py @@ -20,13 +20,14 @@ __version__ = "0.2" import re from . import Image, ImageFile, ImagePalette +from ._binary import i8, o8 # XPM header -xpm_head = re.compile("\"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)") +xpm_head = re.compile(b"\"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)") def _accept(prefix): - return prefix[:9] == "/* XPM */" + return prefix[:9] == b"/* XPM */" ## # Image plugin for X11 pixel maps. @@ -61,17 +62,17 @@ class XpmImageFile(ImageFile.ImageFile): # # load palette description - palette = ["\0\0\0"] * 256 + palette = [b"\0\0\0"] * 256 for i in range(pal): s = self.fp.readline() - if s[-2:] == '\r\n': + if s[-2:] == b'\r\n': s = s[:-2] - elif s[-1:] in '\r\n': + elif s[-1:] in b'\r\n': s = s[:-1] - c = ord(s[1]) + c = i8(s[1]) s = s[2:-2].split() for i in range(0, len(s), 2): @@ -80,14 +81,14 @@ class XpmImageFile(ImageFile.ImageFile): # process colour key rgb = s[i+1] - if rgb == "None": + if rgb == b"None": self.info["transparency"] = c - elif rgb[0] == "#": + elif rgb[0] == b"#": # FIXME: handle colour names (see ImagePalette.py) rgb = int(rgb[1:], 16) - palette[c] = chr((rgb >> 16) & 255) +\ - chr((rgb >> 8) & 255) +\ - chr(rgb & 255) + palette[c] = o8((rgb >> 16) & 255) +\ + o8((rgb >> 8) & 255) +\ + o8(rgb & 255) else: # unknown colour raise ValueError("cannot read this XPM file") @@ -99,7 +100,7 @@ class XpmImageFile(ImageFile.ImageFile): raise ValueError("cannot read this XPM file") self.mode = "P" - self.palette = ImagePalette.raw("RGB", "".join(palette)) + self.palette = ImagePalette.raw("RGB", b"".join(palette)) self.tile = [("raw", (0, 0)+self.size, self.fp.tell(), ("P", 0, 1))] @@ -117,7 +118,7 @@ class XpmImageFile(ImageFile.ImageFile): self.fp = None - return "".join(s) + return b"".join(s) # # Registry diff --git a/PIL/_binary.py b/PIL/_binary.py new file mode 100644 index 000000000..37318a21a --- /dev/null +++ b/PIL/_binary.py @@ -0,0 +1,52 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Binary input/output support routines. +# +# Copyright (c) 1997-2003 by Secret Labs AB +# Copyright (c) 1995-2003 by Fredrik Lundh +# Copyright (c) 2012 by Brian Crowell +# +# See the README file for information on usage and redistribution. +# + +if bytes is str: + def i8(c): + return ord(c) + + def o8(i): + return chr(i&255) +else: + def i8(c): + return c if c.__class__ is int else c[0] + + def o8(i): + return bytes((i&255,)) + +# Input, le = little endian, be = big endian +def i16le(c, o=0): + return i8(c[o]) | (i8(c[o+1])<<8) + +def i32le(c, o=0): + return i8(c[o]) | (i8(c[o+1])<<8) | (i8(c[o+2])<<16) | (i8(c[o+3])<<24) + +def i16be(c, o=0): + return (i8(c[o])<<8) | i8(c[o+1]) + +def i32be(c, o=0): + return (i8(c[o])<<24) | (i8(c[o+1])<<16) | (i8(c[o+2])<<8) | i8(c[o+3]) + +# Output, le = little endian, be = big endian +def o16le(i): + return o8(i) + o8(i>>8) + +def o32le(i): + return o8(i) + o8(i>>8) + o8(i>>16) + o8(i>>24) + +def o16be(i): + return o8(i>>8) + o8(i) + +def o32be(i): + return o8(i>>24) + o8(i>>16) + o8(i>>8) + o8(i) + diff --git a/Sane/demo_numarray.py b/Sane/demo_numarray.py index 2eccb72a3..57fcc4407 100644 --- a/Sane/demo_numarray.py +++ b/Sane/demo_numarray.py @@ -16,13 +16,13 @@ import Image def toImage(arr): if arr.type().bytes == 1: # need to swap coordinates btw array and image (with [::-1]) - im = Image.fromstring('L', arr.shape[::-1], arr.tostring()) + im = Image.frombytes('L', arr.shape[::-1], arr.tostring()) else: arr_c = arr - arr.min() arr_c *= (255./arr_c.max()) arr = arr_c.astype(UInt8) # need to swap coordinates btw array and image (with [::-1]) - im = Image.fromstring('L', arr.shape[::-1], arr.tostring()) + im = Image.frombytes('L', arr.shape[::-1], arr.tostring()) return im print('SANE version:', sane.init()) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 6fb67fc02..7086d8709 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -88,16 +88,16 @@ def test_bad_text(): assert_equal(im.info, {}) im = load(HEAD + chunk(b'tEXt', b'spam') + TAIL) - assert_equal(im.info, {b'spam': b''}) + assert_equal(im.info, {'spam': ''}) im = load(HEAD + chunk(b'tEXt', b'spam\0') + TAIL) - assert_equal(im.info, {b'spam': b''}) + assert_equal(im.info, {'spam': ''}) im = load(HEAD + chunk(b'tEXt', b'spam\0egg') + TAIL) - assert_equal(im.info, {b'spam': b'egg'}) + assert_equal(im.info, {'spam': 'egg'}) im = load(HEAD + chunk(b'tEXt', b'spam\0egg\0') + TAIL) - assert_equal(im.info, {b'spam': b'egg\x00'}) + assert_equal(im.info, {'spam': 'egg\x00'}) def test_interlace(): @@ -141,12 +141,12 @@ def test_roundtrip_text(): im = Image.open(file) info = PngImagePlugin.PngInfo() - info.add_text(b"TXT", b"VALUE") - info.add_text(b"ZIP", b"VALUE", 1) + info.add_text("TXT", "VALUE") + info.add_text("ZIP", "VALUE", 1) im = roundtrip(im, pnginfo=info) - assert_equal(im.info, {b'TXT': b'VALUE', b'ZIP': b'VALUE'}) - assert_equal(im.text, {b'TXT': b'VALUE', b'ZIP': b'VALUE'}) + assert_equal(im.info, {'TXT': 'VALUE', 'ZIP': 'VALUE'}) + assert_equal(im.text, {'TXT': 'VALUE', 'ZIP': 'VALUE'}) def test_scary(): # Check reading of evil PNG file. For information, see: diff --git a/docs/pythondoc-PIL.Image.rst b/docs/pythondoc-PIL.Image.rst index 5ddb7bd51..1c2882374 100644 --- a/docs/pythondoc-PIL.Image.rst +++ b/docs/pythondoc-PIL.Image.rst @@ -33,11 +33,11 @@ The PIL.Image Module **frombuffer(mode, size, data, decoder\_name="raw", \*args)** [`# <#PIL.Image.frombuffer-function>`_] - (New in 1.1.4) Creates an image memory from pixel data in a string - or byte buffer. + (New in 1.1.4) Creates an image memory referencing pixel data in a + byte buffer. This function is similar to - `**fromstring** <#PIL.Image.fromstring-function>`_, but uses data in + `**frombytes** <#PIL.Image.frombytes-function>`_, but uses data in the byte buffer, where possible. This means that changes to the original buffer object are reflected in this image). Not all modes can share memory; support modes include "L", "RGBX", "RGBA", and @@ -56,9 +56,9 @@ The PIL.Image Module *\*args* Returns: -**fromstring(mode, size, data, decoder\_name="raw", \*args)** -[`# <#PIL.Image.fromstring-function>`_] - Creates an image memory from pixel data in a string. +**frombytes(mode, size, data, decoder\_name="raw", \*args)** +[`# <#PIL.Image.frombytes-function>`_] + Creates a copy of an image memory from pixel data in a buffer. In its simplest form, this function takes three arguments (mode, size, and unpacked pixel data). @@ -233,14 +233,16 @@ The Image Class *filter* Returns: -**fromstring(data, decoder\_name="raw", \*args)** -[`# <#PIL.Image.Image.fromstring-method>`_] - Loads this image with pixel data from a string. +**frombytes(data, decoder\_name="raw", \*args)** +[`# <#PIL.Image.Image.frombytes-method>`_] + Loads this image with pixel data from a byte uffer. This method is similar to the - `**fromstring** <#PIL.Image.fromstring-function>`_ function, but + `**frombytes** <#PIL.Image.frombytes-function>`_ function, but loads data into this image instead of creating a new image object. + (In Python 2.6 and 2.7, this is also available as fromstring().) + **getbands()** [`# <#PIL.Image.Image.getbands-method>`_] Returns a tuple containing the name of each band in this image. For example, **getbands** on an RGB image returns ("R", "G", "B"). @@ -516,8 +518,9 @@ The Image Class Returns: Raises **ValueError**: -**tostring(encoder\_name="raw", \*args)** -[`# <#PIL.Image.Image.tostring-method>`_] +**tobytes(encoder\_name="raw", \*args)** +[`# <#PIL.Image.Image.tobytes-method>`_] + (In Python 2.6 and 2.7, this is also available as tostring().) *encoder\_name* *\*args* diff --git a/docs/pythondoc-PIL.ImageWin.rst b/docs/pythondoc-PIL.ImageWin.rst index 05558a3a5..2bcb8fbb2 100644 --- a/docs/pythondoc-PIL.ImageWin.rst +++ b/docs/pythondoc-PIL.ImageWin.rst @@ -58,11 +58,12 @@ The Dib Class instance. In PythonWin, you can use the **GetHandleAttrib** method of the **CDC** class to get a suitable handle. -**fromstring(buffer)** [`# <#PIL.ImageWin.Dib.fromstring-method>`_] +**frombytes(buffer)** [`# <#PIL.ImageWin.Dib.frombytes-method>`_] + (For Python 2.6/2.7, this is also available as fromstring(buffer).) *buffer* - A string buffer containing display data (usually data returned - from **tostring**) + A byte buffer containing display data (usually data returned + from **tobytes**) **paste(im, box=None)** [`# <#PIL.ImageWin.Dib.paste-method>`_] @@ -82,7 +83,7 @@ The Dib Class *handle* Returns: -**tostring()** [`# <#PIL.ImageWin.Dib.tostring-method>`_] +**tobytes()** [`# <#PIL.ImageWin.Dib.tobytes-method>`_] Returns: From be560f00f5ae964fd7c0342390a3fa23c8e7c312 Mon Sep 17 00:00:00 2001 From: Christoph Gohlke Date: Sun, 21 Oct 2012 11:01:14 -0500 Subject: [PATCH 40/61] py3k: Allow slicing for paths This is Gohlke's fix for two issues: negative indexes on paths were not resolved to the correct index, and path slicing didn't work. His fix for slicing is related to the one found at: http://renesd.blogspot.com/2009/07/python3-c-api-simple-slicing-sqslice.html With this commit, all 79 tests (82 minus the three skipped ones) run successfully on Python 2.6.8, Python 2.7.3rc2, and Python 3.2.3. --- path.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/path.c b/path.c index af0f4951d..a27b59156 100644 --- a/path.c +++ b/path.c @@ -370,6 +370,8 @@ path_getbbox(PyPathObject* self, PyObject* args) static PyObject* path_getitem(PyPathObject* self, int i) { + if (i < 0) + i = self->count + i; if (i < 0 || i >= self->count) { PyErr_SetString(PyExc_IndexError, "path index out of range"); return NULL; @@ -559,6 +561,47 @@ static struct PyGetSetDef getsetters[] = { { NULL } }; +static PyObject* +path_subscript(PyPathObject* self, PyObject* item) { + if (PyIndex_Check(item)) { + Py_ssize_t i; + i = PyNumber_AsSsize_t(item, PyExc_IndexError); + if (i == -1 && PyErr_Occurred()) + return NULL; + return path_getitem(self, i); + } + if (PySlice_Check(item)) { + int len = 4; + Py_ssize_t start, stop, step, slicelength; + +#if PY_VERSION_HEX >= 0x03000000 + if (PySlice_GetIndicesEx(item, len, &start, &stop, &step, &slicelength) < 0) + return NULL; +#else + if (PySlice_GetIndicesEx((PySliceObject*)item, len, &start, &stop, &step, &slicelength) < 0) + return NULL; +#endif + + if (slicelength <= 0) { + double *xy = alloc_array(0); + return (PyObject*) path_new(0, xy, 0); + } + else if (step == 1) { + return path_getslice(self, start, stop); + } + else { + PyErr_SetString(PyExc_TypeError, "slice steps not supported"); + return NULL; + } + } + else { + PyErr_Format(PyExc_TypeError, + "Path indices must be integers, not %.200s", + Py_TYPE(&item)->tp_name); + return NULL; + } +} + static PySequenceMethods path_as_sequence = { (lenfunc)path_len, /*sq_length*/ (binaryfunc)0, /*sq_concat*/ @@ -569,6 +612,12 @@ static PySequenceMethods path_as_sequence = { (ssizessizeobjargproc)0, /*sq_ass_slice*/ }; +static PyMappingMethods path_as_mapping = { + (lenfunc)path_len, + (binaryfunc)path_subscript, + NULL +}; + static PyTypeObject PyPathType = { PyVarObject_HEAD_INIT(NULL, 0) "Path", /*tp_name*/ @@ -583,7 +632,7 @@ static PyTypeObject PyPathType = { 0, /*tp_repr*/ 0, /*tp_as_number */ &path_as_sequence, /*tp_as_sequence */ - 0, /*tp_as_mapping */ + &path_as_mapping, /*tp_as_mapping */ 0, /*tp_hash*/ 0, /*tp_call*/ 0, /*tp_str*/ From a8599e8bb2cc109ffb932b70f4565a94da6ab425 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Sun, 21 Oct 2012 11:36:47 -0500 Subject: [PATCH 41/61] py3k: Remove ancient Python hacks --- _imagingcms.c | 5 ----- _imagingft.c | 9 --------- decode.c | 5 ----- display.c | 5 ----- encode.c | 5 ----- map.c | 5 ----- outline.c | 5 ----- path.c | 14 -------------- 8 files changed, 53 deletions(-) diff --git a/_imagingcms.c b/_imagingcms.c index 65958f33a..6b215cabb 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -28,11 +28,6 @@ http://www.cazabon.com\n\ #include "Imaging.h" #include "py3.h" -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - #if LCMS_VERSION < 117 #define LCMSBOOL BOOL #endif diff --git a/_imagingft.c b/_imagingft.c index 864d74033..f54e38553 100644 --- a/_imagingft.c +++ b/_imagingft.c @@ -35,15 +35,6 @@ #include #endif -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - -#if !defined(Py_RETURN_NONE) -#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None -#endif - #define KEEP_PY_UNICODE #include "py3.h" diff --git a/decode.c b/decode.c index 0b2be8dfd..9ecfee346 100644 --- a/decode.c +++ b/decode.c @@ -31,11 +31,6 @@ #include "Python.h" -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - #include "Imaging.h" #include "py3.h" diff --git a/display.c b/display.c index 1ce537c64..3751b0a2b 100644 --- a/display.c +++ b/display.c @@ -25,11 +25,6 @@ #include "Python.h" -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - #include "Imaging.h" #include "py3.h" diff --git a/encode.c b/encode.c index 670bd7dc9..479da7d51 100644 --- a/encode.c +++ b/encode.c @@ -24,11 +24,6 @@ #include "Python.h" -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - #include "Imaging.h" #include "py3.h" #include "Gif.h" diff --git a/map.c b/map.c index cd3a8e43d..c9ad10914 100644 --- a/map.c +++ b/map.c @@ -20,11 +20,6 @@ #include "Python.h" -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - #include "Imaging.h" #ifdef WIN32 diff --git a/outline.c b/outline.c index 46db7c938..25e63aeaf 100644 --- a/outline.c +++ b/outline.c @@ -19,11 +19,6 @@ #include "Python.h" -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - #include "Imaging.h" diff --git a/path.c b/path.c index a27b59156..622edb10a 100644 --- a/path.c +++ b/path.c @@ -30,20 +30,6 @@ #include -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - -#if PY_VERSION_HEX < 0x02050000 -#define Py_ssize_t int -#define lenfunc inquiry -#define ssizeargfunc intargfunc -#define ssizessizeargfunc intintargfunc -#define ssizeobjargproc intobjargproc -#define ssizessizeobjargproc intintobjargproc -#endif - #include "py3.h" /* compatibility wrappers (defined in _imaging.c) */ From 254f0cf674e150e3ce79af84977dbfc937f745c3 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Tue, 23 Oct 2012 21:56:54 -0500 Subject: [PATCH 42/61] py3k: FIX: Bad upside-down check in BmpImagePlugin --- PIL/BmpImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index 465904e77..cbfd82ed8 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -91,7 +91,7 @@ class BmpImageFile(ImageFile.ImageFile): lutsize = 4 colors = i32(s[32:]) direction = -1 - if s[11] == '\xff': + if i8(s[11]) == 0xff: # upside-down storage self.size = self.size[0], 2**32 - self.size[1] direction = 0 From 8035b1a76a3947fd84572fb1d0f12ba5991f3d97 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Tue, 23 Oct 2012 21:58:15 -0500 Subject: [PATCH 43/61] py3k: FIX: TGA missing o8 declaration Plus, TGA was eligible for a round-trip test in test_imagefile. It has one now. --- PIL/TgaImagePlugin.py | 1 + Tests/test_imagefile.py | 1 + 2 files changed, 2 insertions(+) diff --git a/PIL/TgaImagePlugin.py b/PIL/TgaImagePlugin.py index 82d469db3..b30d8dcb0 100644 --- a/PIL/TgaImagePlugin.py +++ b/PIL/TgaImagePlugin.py @@ -133,6 +133,7 @@ class TgaImageFile(ImageFile.ImageFile): # -------------------------------------------------------------------- # Write TGA file +o8 = _binary.o8 o16 = _binary.o16le o32 = _binary.o32le diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index e260a24dd..4b8c84299 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -40,6 +40,7 @@ def test_parser(): assert_image_equal(*roundtrip("PPM")) assert_image_equal(*roundtrip("TIFF")) assert_image_equal(*roundtrip("XBM")) + assert_image_equal(*roundtrip("TGA")) im1, im2 = roundtrip("JPEG") # lossy compression assert_image(im1, im2.mode, im2.size) From 63be4a13345c82e72714d01aa3d033f165c5c1ae Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Tue, 23 Oct 2012 22:07:14 -0500 Subject: [PATCH 44/61] py3k: Get rid of _ParserFile; io.BytesIO does its job --- PIL/ImageFile.py | 50 ++---------------------------------------------- 1 file changed, 2 insertions(+), 48 deletions(-) diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index 1a5a8f11e..eb4caef24 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -278,52 +278,6 @@ class StubImageFile(ImageFile): "StubImageFile subclass must implement _load" ) -## -# (Internal) Support class for the Parser file. - -class _ParserFile: - # parser support class. - - def __init__(self, data): - self.data = data - self.offset = 0 - - def close(self): - self.data = self.offset = None - - def tell(self): - return self.offset - - def seek(self, offset, whence=0): - if whence == 0: - self.offset = offset - elif whence == 1: - self.offset = self.offset + offset - else: - # force error in Image.open - raise IOError("illegal argument to seek") - - def read(self, bytes=0): - pos = self.offset - if bytes: - data = self.data[pos:pos+bytes] - else: - data = self.data[pos:] - self.offset = pos + len(data) - return data - - def readline(self): - # FIXME: this is slow! - s = b"" - while True: - c = self.read(1) - if not c: - break - s = s + c - if c == b"\n": - break - return s - ## # Incremental image parser. This class implements the standard # feed/close consumer interface. @@ -399,7 +353,7 @@ class Parser: # attempt to open this file try: try: - fp = _ParserFile(self.data) + fp = io.BytesIO(self.data) im = Image.open(fp) finally: fp.close() # explicitly close the virtual file @@ -449,7 +403,7 @@ class Parser: # incremental parsing not possible; reopen the file # not that we have all data try: - fp = _ParserFile(self.data) + fp = io.BytesIO(self.data) self.image = Image.open(fp) finally: self.image.load() From 4f7d784a7137c4a0f7c75891a63c5f0cd9170dd0 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Tue, 23 Oct 2012 22:21:19 -0500 Subject: [PATCH 45/61] py3k: Actually fix the EPS encoder The EPS encoder wasn't part of Gohlke's test suite, so the previous "fixes" there were only expected syntactic ones. This gives a cleaner fix to the encoder. The decoder doesn't work in round-trip due to a missing eps_decoder method on the core module, but it's clear it worked at some point. --- PIL/EpsImagePlugin.py | 66 +++++++++++++++++++++++++---------------- Tests/test_imagefile.py | 1 + 2 files changed, 42 insertions(+), 25 deletions(-) diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py index b1df67fd6..9d30bc40f 100644 --- a/PIL/EpsImagePlugin.py +++ b/PIL/EpsImagePlugin.py @@ -21,6 +21,7 @@ __version__ = "0.5" import re +import io from . import Image, ImageFile, _binary # @@ -90,6 +91,8 @@ class PSFile: def seek(self, offset, whence=0): self.char = None self.fp.seek(offset, whence) + def read(self, count): + return self.fp.read(count).decode('latin-1') def tell(self): pos = self.fp.tell() if self.char: @@ -109,7 +112,7 @@ class PSFile: self.char = self.fp.read(1) if self.char == b"\n": self.char = None - return s + b"\n" + return s.decode('latin-1') + "\n" def _accept(prefix): @@ -193,13 +196,14 @@ class EpsImageFile(ImageFile.ImageFile): if m: k = m.group(1) + if k == "EndComments": break if k[:8] == "PS-Adobe": self.info[k[:8]] = k[9:] else: self.info[k] = "" - elif s[0] == '%': + elif s[0:1] == '%': # handle non-DSC Postscript comments that some # tools mistakenly put in the Comments section pass @@ -228,7 +232,7 @@ class EpsImageFile(ImageFile.ImageFile): if s[:11] == "%ImageData:": [x, y, bi, mo, z3, z4, en, id] =\ - s[11:].split(maxsplit=7) + s[11:].split(None, 7) x = int(x); y = int(y) @@ -299,42 +303,54 @@ def _save(im, fp, filename, eps=1): # # determine postscript image mode if im.mode == "L": - operator = (8, 1, b"image") + operator = (8, 1, "image") elif im.mode == "RGB": - operator = (8, 3, b"false 3 colorimage") + operator = (8, 3, "false 3 colorimage") elif im.mode == "CMYK": - operator = (8, 4, b"false 4 colorimage") + operator = (8, 4, "false 4 colorimage") else: raise ValueError("image mode is not supported") + class NoCloseStream: + def __init__(self, fp): + self.fp = fp + def __getattr__(self, name): + return getattr(self.fp, name) + def close(self): + pass + + base_fp = fp + fp = io.TextIOWrapper(NoCloseStream(fp), encoding='latin-1') + if eps: # # write EPS header - fp.write(b"%!PS-Adobe-3.0 EPSF-3.0\n") - fp.write(b"%%Creator: PIL 0.1 EpsEncode\n") + fp.write("%!PS-Adobe-3.0 EPSF-3.0\n") + fp.write("%%Creator: PIL 0.1 EpsEncode\n") #fp.write("%%CreationDate: %s"...) - fp.write(("%%%%BoundingBox: 0 0 %d %d\n" % im.size).encode('ascii')) - fp.write(b"%%Pages: 1\n") - fp.write(b"%%EndComments\n") - fp.write(b"%%Page: 1 1\n") - fp.write(("%%ImageData: %d %d " % im.size).encode('ascii')) - fp.write(("%d %d 0 1 1 \"%s\"\n" % operator).encode('ascii')) + fp.write("%%%%BoundingBox: 0 0 %d %d\n" % im.size) + fp.write("%%Pages: 1\n") + fp.write("%%EndComments\n") + fp.write("%%Page: 1 1\n") + fp.write("%%ImageData: %d %d " % im.size) + fp.write("%d %d 0 1 1 \"%s\"\n" % operator) # # image header - fp.write(b"gsave\n") - fp.write(b"10 dict begin\n") - fp.write(("/buf %d string def\n" % (im.size[0] * operator[1])).encode('ascii')) - fp.write(("%d %d scale\n" % im.size).encode('ascii')) - fp.write(("%d %d 8\n" % im.size).encode('ascii')) # <= bits - fp.write(("[%d 0 0 -%d 0 %d]\n" % (im.size[0], im.size[1], im.size[1])).encode('ascii')) - fp.write(b"{ currentfile buf readhexstring pop } bind\n") - fp.write(operator[2] + b"\n") + fp.write("gsave\n") + fp.write("10 dict begin\n") + fp.write("/buf %d string def\n" % (im.size[0] * operator[1])) + fp.write("%d %d scale\n" % im.size) + fp.write("%d %d 8\n" % im.size) # <= bits + fp.write("[%d 0 0 -%d 0 %d]\n" % (im.size[0], im.size[1], im.size[1])) + fp.write("{ currentfile buf readhexstring pop } bind\n") + fp.write(operator[2] + "\n") + fp.flush() - ImageFile._save(im, fp, [("eps", (0,0)+im.size, 0, None)]) + ImageFile._save(im, base_fp, [("eps", (0,0)+im.size, 0, None)]) - fp.write(b"\n%%%%EndBinary\n") - fp.write(b"grestore end\n") + fp.write("\n%%%%EndBinary\n") + fp.write("grestore end\n") fp.flush() # diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index 4b8c84299..485208c36 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -40,6 +40,7 @@ def test_parser(): assert_image_equal(*roundtrip("PPM")) assert_image_equal(*roundtrip("TIFF")) assert_image_equal(*roundtrip("XBM")) + #assert_image_equal(*roundtrip("EPS")) #no eps_decoder assert_image_equal(*roundtrip("TGA")) im1, im2 = roundtrip("JPEG") # lossy compression From 2972b2178ec4b1b71c587154231f9b9b38a45bb2 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Tue, 23 Oct 2012 22:22:38 -0500 Subject: [PATCH 46/61] py3k: Add PCX to roundtrip test For some reason, the PCX codec round-trips now. --- Tests/test_imagefile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index 485208c36..95ed0ee30 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -42,11 +42,11 @@ def test_parser(): assert_image_equal(*roundtrip("XBM")) #assert_image_equal(*roundtrip("EPS")) #no eps_decoder assert_image_equal(*roundtrip("TGA")) + assert_image_equal(*roundtrip("PCX")) im1, im2 = roundtrip("JPEG") # lossy compression assert_image(im1, im2.mode, im2.size) - assert_exception(IOError, lambda: roundtrip("PCX")) assert_exception(IOError, lambda: roundtrip("PDF")) From 9921d838e4daab0e8801eb1411a1e39a64b97305 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Wed, 24 Oct 2012 08:24:26 -0500 Subject: [PATCH 47/61] py3k: Add Photoshop sanity check --- Images/lena.psd | Bin 0 -> 101308 bytes Tests/test_file_psd.py | 14 ++++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 Images/lena.psd create mode 100644 Tests/test_file_psd.py diff --git a/Images/lena.psd b/Images/lena.psd new file mode 100644 index 0000000000000000000000000000000000000000..c7b19402a9c41acd1c0f2e3e13ebbc79b438faeb GIT binary patch literal 101308 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 z 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 t}7mE;c2o{SUY1&?^7{ literal 0 HcmV?d00001 diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py new file mode 100644 index 000000000..ef2d40594 --- /dev/null +++ b/Tests/test_file_psd.py @@ -0,0 +1,14 @@ +from tester import * + +from PIL import Image + +# sample ppm stream +file = "Images/lena.psd" +data = open(file, "rb").read() + +def test_sanity(): + im = Image.open(file) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "PSD") From 6765b8e026d71b3156842361d350fb7bb2d63c9c Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Wed, 24 Oct 2012 08:25:00 -0500 Subject: [PATCH 48/61] py3k: Add .FLI sanity check, and fix palette bytes bug --- Images/lena.fli | Bin 0 -> 16371 bytes PIL/FliImagePlugin.py | 2 +- Tests/test_file_fli.py | 14 ++++++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 Images/lena.fli create mode 100644 Tests/test_file_fli.py diff --git a/Images/lena.fli b/Images/lena.fli new file mode 100644 index 0000000000000000000000000000000000000000..2c7367e229f04610bc7790b1d6f5d147b7b4b922 GIT binary patch literal 16371 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 Date: Wed, 24 Oct 2012 08:37:27 -0500 Subject: [PATCH 49/61] py3k: Rewrite PdfImagePlugin into something more legible After adding all the encode() calls, the PDF plugin (and a few others) became much harder to read. This should be much easier on the eyes. --- PIL/PdfImagePlugin.py | 99 +++++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 46 deletions(-) diff --git a/PIL/PdfImagePlugin.py b/PIL/PdfImagePlugin.py index b83e2d41c..462627bd0 100644 --- a/PIL/PdfImagePlugin.py +++ b/PIL/PdfImagePlugin.py @@ -38,19 +38,16 @@ import io # 5. page contents def _obj(fp, obj, **dict): - fp.write(("%d 0 obj\n" % obj).encode('ascii')) + fp.write("%d 0 obj\n" % obj) if dict: - fp.write(b"<<\n") + fp.write("<<\n") for k, v in dict.items(): if v is not None: - if not isinstance(v, bytes): - v = str(v).encode('ascii') - - fp.write(b"/" + k.encode('ascii') + b" " + v + b"\n") - fp.write(b">>\n") + fp.write("/%s %s\n" % (k, v)) + fp.write(">>\n") def _endobj(fp): - fp.write(b"endobj\n") + fp.write("endobj\n") ## # (Internal) Image save plugin for the PDF format. @@ -64,8 +61,18 @@ def _save(im, fp, filename): xref = [0]*(5+1) # placeholders - fp.write(b"%PDF-1.2\n") - fp.write(b"% created by PIL PDF driver " + __version__.encode('ascii') + b"\n") + class TextWriter: + def __init__(self, fp): + self.fp = fp + def __getattr__(self, name): + return getattr(self.fp, name) + def write(self, value): + self.fp.write(value.encode('latin-1')) + + fp = TextWriter(fp) + + fp.write("%PDF-1.2\n") + fp.write("% created by PIL PDF driver " + __version__ + "\n") # # Get image characteristics @@ -80,33 +87,33 @@ def _save(im, fp, filename): params = None if im.mode == "1": - filter = b"/ASCIIHexDecode" - colorspace = b"/DeviceGray" + filter = "/ASCIIHexDecode" + colorspace = "/DeviceGray" procset = "/ImageB" # grayscale bits = 1 elif im.mode == "L": - filter = b"/DCTDecode" + filter = "/DCTDecode" # params = "<< /Predictor 15 /Columns %d >>" % (width-2) - colorspace = b"/DeviceGray" + colorspace = "/DeviceGray" procset = "/ImageB" # grayscale elif im.mode == "P": - filter = b"/ASCIIHexDecode" - colorspace = b"[ /Indexed /DeviceRGB 255 <" + filter = "/ASCIIHexDecode" + colorspace = "[ /Indexed /DeviceRGB 255 <" palette = im.im.getpalette("RGB") for i in range(256): r = i8(palette[i*3]) g = i8(palette[i*3+1]) b = i8(palette[i*3+2]) - colorspace = colorspace + ("%02x%02x%02x " % (r, g, b)).encode('ascii') + colorspace = colorspace + "%02x%02x%02x " % (r, g, b) colorspace = colorspace + b"> ]" procset = "/ImageI" # indexed color elif im.mode == "RGB": - filter = b"/DCTDecode" - colorspace = b"/DeviceRGB" + filter = "/DCTDecode" + colorspace = "/DeviceRGB" procset = "/ImageC" # color images elif im.mode == "CMYK": - filter = b"/DCTDecode" - colorspace = b"/DeviceCMYK" + filter = "/DCTDecode" + colorspace = "/DeviceCMYK" procset = "/ImageC" # color images else: raise ValueError("cannot save mode %s" % im.mode) @@ -115,17 +122,17 @@ def _save(im, fp, filename): # catalogue xref[1] = fp.tell() - _obj(fp, 1, Type = b"/Catalog", - Pages = b"2 0 R") + _obj(fp, 1, Type = "/Catalog", + Pages = "2 0 R") _endobj(fp) # # pages xref[2] = fp.tell() - _obj(fp, 2, Type = b"/Pages", + _obj(fp, 2, Type = "/Pages", Count = 1, - Kids = b"[4 0 R]") + Kids = "[4 0 R]") _endobj(fp) # @@ -133,7 +140,7 @@ def _save(im, fp, filename): op = io.BytesIO() - if filter == b"/ASCIIHexDecode": + if filter == "/ASCIIHexDecode": if bits == 1: # FIXME: the hex encoder doesn't support packed 1-bit # images; do things the hard way... @@ -141,18 +148,18 @@ def _save(im, fp, filename): im = Image.new("L", (len(data), 1), None) im.putdata(data) ImageFile._save(im, op, [("hex", (0,0)+im.size, 0, im.mode)]) - elif filter == b"/DCTDecode": + elif filter == "/DCTDecode": ImageFile._save(im, op, [("jpeg", (0,0)+im.size, 0, im.mode)]) - elif filter == b"/FlateDecode": + elif filter == "/FlateDecode": ImageFile._save(im, op, [("zip", (0,0)+im.size, 0, im.mode)]) - elif filter == b"/RunLengthDecode": + elif filter == "/RunLengthDecode": ImageFile._save(im, op, [("packbits", (0,0)+im.size, 0, im.mode)]) else: raise ValueError("unsupported PDF filter (%s)" % filter) xref[3] = fp.tell() - _obj(fp, 3, Type = b"/XObject", - Subtype = b"/Image", + _obj(fp, 3, Type = "/XObject", + Subtype = "/Image", Width = width, # * 72.0 / resolution, Height = height, # * 72.0 / resolution, Length = len(op.getvalue()), @@ -161,9 +168,9 @@ def _save(im, fp, filename): DecodeParams = params, ColorSpace = colorspace) - fp.write(b"stream\n") - fp.write(op.getvalue()) - fp.write(b"\nendstream\n") + fp.write("stream\n") + fp.fp.write(op.getvalue()) + fp.write("\nendstream\n") _endobj(fp) @@ -172,37 +179,37 @@ def _save(im, fp, filename): xref[4] = fp.tell() _obj(fp, 4) - fp.write(("<<\n/Type /Page\n/Parent 2 0 R\n"\ + fp.write("<<\n/Type /Page\n/Parent 2 0 R\n"\ "/Resources <<\n/ProcSet [ /PDF %s ]\n"\ "/XObject << /image 3 0 R >>\n>>\n"\ "/MediaBox [ 0 0 %d %d ]\n/Contents 5 0 R\n>>\n" %\ - (procset, int(width * 72.0 /resolution) , int(height * 72.0 / resolution))).encode('ascii')) + (procset, int(width * 72.0 /resolution) , int(height * 72.0 / resolution))) _endobj(fp) # # page contents - op = io.BytesIO() + op = TextWriter(io.BytesIO()) - op.write(("q %d 0 0 %d 0 0 cm /image Do Q\n" % (int(width * 72.0 / resolution), int(height * 72.0 / resolution))).encode('ascii')) + op.write("q %d 0 0 %d 0 0 cm /image Do Q\n" % (int(width * 72.0 / resolution), int(height * 72.0 / resolution))) xref[5] = fp.tell() - _obj(fp, 5, Length = len(op.getvalue())) + _obj(fp, 5, Length = len(op.fp.getvalue())) - fp.write(b"stream\n") - fp.write(op.getvalue()) - fp.write(b"\nendstream\n") + fp.write("stream\n") + fp.fp.write(op.fp.getvalue()) + fp.write("\nendstream\n") _endobj(fp) # # trailer startxref = fp.tell() - fp.write(("xref\n0 %d\n0000000000 65535 f \n" % len(xref)).encode('ascii')) + fp.write("xref\n0 %d\n0000000000 65535 f \n" % len(xref)) for x in xref[1:]: - fp.write(("%010d 00000 n \n" % x).encode('ascii')) - fp.write(("trailer\n<<\n/Size %d\n/Root 1 0 R\n>>\n" % len(xref)).encode('ascii')) - fp.write(("startxref\n%d\n%%%%EOF\n" % startxref).encode('ascii')) + fp.write("%010d 00000 n \n" % x) + fp.write("trailer\n<<\n/Size %d\n/Root 1 0 R\n>>\n" % len(xref)) + fp.write("startxref\n%d\n%%%%EOF\n" % startxref) fp.flush() # From d6a0dec15b0ba848f2cca087189ea245449df180 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Thu, 25 Oct 2012 07:57:39 -0500 Subject: [PATCH 50/61] py3k: Add TarIO test Not too convinced of the size fix. While it works against my file, I'm not sure someone would have accidentally been an index off and not noticed. --- Images/lena.tar | Bin 0 -> 51200 bytes PIL/TarIO.py | 4 ++-- Tests/test_file_tar.py | 22 ++++++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 Images/lena.tar create mode 100644 Tests/test_file_tar.py diff --git a/Images/lena.tar b/Images/lena.tar new file mode 100644 index 0000000000000000000000000000000000000000..3afb1f542405347b093062d8e9e96cae6fce3be8 GIT binary patch literal 51200 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 0: name = name[:i] - size = int(s[124:136], 8) + size = int(s[124:135], 8) if file == name: break diff --git a/Tests/test_file_tar.py b/Tests/test_file_tar.py new file mode 100644 index 000000000..0f87ea2c0 --- /dev/null +++ b/Tests/test_file_tar.py @@ -0,0 +1,22 @@ +from tester import * + +from PIL import Image, TarIO + +# sample ppm stream +tarfile = "Images/lena.tar" + +def test_sanity(): + tar = TarIO.TarIO(tarfile, 'lena.png') + im = Image.open(tar) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "PNG") + + tar = TarIO.TarIO(tarfile, 'lena.jpg') + im = Image.open(tar) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "JPEG") + From 276cc421f71de8a5ad79ebd2d4eab231e701e4bc Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Thu, 25 Oct 2012 08:12:13 -0500 Subject: [PATCH 51/61] py3k: Add XPM read test This test includes an XPM file with transparency. --- Images/lena.xpm | 175 +++++++++++++++++++++++++++++++++++++++++ PIL/XpmImagePlugin.py | 4 +- Tests/test_file_xpm.py | 14 ++++ 3 files changed, 191 insertions(+), 2 deletions(-) create mode 100644 Images/lena.xpm create mode 100644 Tests/test_file_xpm.py diff --git a/Images/lena.xpm b/Images/lena.xpm new file mode 100644 index 000000000..925fd03cb --- /dev/null +++ b/Images/lena.xpm @@ -0,0 +1,175 @@ +/* XPM */ +static char * lena_xpm[] = { +"128 128 44 1", +" c None", +". c #CC9966", +"+ c #663333", +"@ c #996666", +"# c #CC6666", +"$ c #CC9999", +"% c #FFCC99", +"& c #996633", +"* c #FF9966", +"= c #663366", +"- c #FFCCCC", +"; c #CCCC99", +"> c #FF9999", +", c #993333", +"' c #333333", +") c #CCCCCC", +"! c #996699", +"~ c #CC6633", +"{ c #666666", +"] c #999999", +"^ c #666699", +"/ c #330000", +"( c #663300", +"_ c #330033", +": c #999966", +"< c #FFFFCC", +"[ c #333366", +"} c #660033", +"| c #CC99CC", +"1 c #9999CC", +"2 c #660000", +"3 c #FFFFFF", +"4 c #CCCC66", +"5 c #FFCC66", +"6 c #666633", +"7 c #993366", +"8 c #FFFF99", +"9 c #993300", +"0 c #333300", +"a c #FF99CC", +"b c #CCCCFF", +"c c #CC9933", +"d c #CC6699", +"e c #000000", +".....*...*.%*..#&@#&@&#&#&#.&#.#.#.#.#.#..#.*.#...#..#.#.#.#..#..#..#..#..#.#.#@#.>..>...*..*...*....%%%%.&&.@.#.@.#..&#.~.#&#$.", +">%*..*.*..*%.*.&#@&#@#&#&#&#*&.#.#.#.#.*#..#&.~.#.#.#.&.#.#.#.#.#.#&.#..#&.@.@&#&#.>.*.*...*..*..*...%%%%%&&~@#&#.@&##.&#.#.@.>@", +"...>...*.*.>.%*&#,&#&#&@&#.&#.#.#.#.~.#.#.~.>.#.#..~.#*.~.&*.#.*&*.#..#&.#.##.#@&#.$.>.>.*..*..*.....%%;%%@&@.#.@.#..&#..&#.#@+(", +".*...*...%*.*.>&&@@#@&##&#.#&#.~.#.#.#.#.##.#&*.~.#..#.#.#.#.#.#.#.#~.#.#.#&.#&@#&#.>.....*..*..*.*...%%%%%&.&@.#&#.#.#.#$.$&+++", +"..>.*.*.*.%.>.~&#&#&#.@&#&#&.#.#.#..#.~.~.*.~>&>.#.#*.#.#.~.~.#.#.~.#.*&*.#.#.#.#&#..>.>*..>..*.......%%<%%$&#.#&$&#.&#.&>@,++2+", +".*.%...%*.*..*.#&@#@&##&#&#&#&#.&.#*.#.#.#.#*.~*#.~.#~.~*#.#*#.*.#.#.#.#&.#.#.#&@.##.*..%*..*..*>.*.....%%%%&&&.#~.#.#.#.$&+++++", +"..>.*.*.%*.*..*#&,&#@&#&#&#.#.~#.~.&>.~.~#*&#~#.~>#.~$~$&.~..#.#~.~.~.#.#.#.#&$#&#&$.>.>..%*.>....*.....%%%-.@#&.#&#.#.$$,++++++", +"..*.*%.*%.*.>..#&@#&##&#&#&#&.#.#.#.~.#.*.#.*.~.~.#.#.~.##&#.~.#.##.#*&*~.#.#&#.@#&>.>%.*>.%..*.>..*.*..%%%%%&&&.#.&#&#.@++++'=+", +".*...*.*.*.*.*.~#@&#@&#&#&.#~.#&*&*&#.~#.~*#~#.#*#.~.#&>..#.#.#...#.#.#.#.#..#.#&#.#..>...*.*..%*..>...>.%<%-%@.&#.#.$@@++++++=+", +".%.*%.%*.%.*.*.&&,&#&#@&#&#.#&.#&*#.~.*.#..#.*~.#.##..#&..&*&#.##.~.#~.#~.#~#.#.@#@.#..>.>..*.*..>.*.*....%%%%.&&#.#&$,++++=+='+", +"*>.*.*..**..*>.#,@#@#&#&##&#&#*&>&*#.#~.#~.#.#.#*#.&#&.#@#.@..#..@.#..#.#~..#.#&#&#.>.>...*.$..*.*...*.*.>%%%-%&&@&$#@++/++'++=+", +"..%*.%*...*.*.*&,&#&@#&#&&#.#.#.#&.~#.#.#.~.#.#..#$#$.$...$&@.@.#.@.##.#.##.##.#.@#.#..*.>.%*.>...>.#.#....%%<-.&#.@&+++=+=+=+=+", +"*.*.*.*.*%*.*.*&#,@#&##&##&~#&*&*#*&*&*&*@.#.~.#$..$.$.-$%$-%$.@.@.&..#*#.#*&#&##&#.#.>...*....*>.*.*.*.~..%%%%%@@.@++++++=+=++=", +"%..%*..*.*.*..*&&&,@#&##&#.#.#.#.#.#.#.#.#.#.#..@>@.$$$;$%$.$-$%$.#@.#..#.##.##.#@&#$.>.>.>.*>.....*.~.*....%<%<.$+++/+=+=++=/++", +"*.*..~.~.%*.*.*# #&,&#&#&#~.#~.~#.#*&*#..@.#.$.$$.$.$;$;$%$%$-.$.&@#.#~.##.#&##.#.#....*...*.>*.#..#.~....<%%-&+++++'=+[++=++", +".%*.*.&*.*%*.*. @#&#&#.#&#.##.~.#.#..>.#.$&#$@.$$:$$;$;$;$;-;-$.@&#$#.#&###&@.#..#.>..>.>..~..*#*.*.~...%%<-@+++=++=++=++=+", +"*.%*&,&*.*.*.* @#&##&#.~&*&>~.#.>@#&.@#.@$.@$.$.$.$;$;%-;.)%-%$&@.##.#.@.##&#.>.*.#.#.*&*.*.*.#.#..*...-%-+++/+{++_++=+}+", +"*.>.~&~..%*.*. &#&#&#&#.##&.#~#&*&##.@.#&$#.@.$.$;$.%).;-;$;$)%$&@&@#.##,@@.#..>.>..>.>.~.*.#.#>.>.#.*.-$+/+=+=+=++=+=+_+", +".*.&#(#.**.%*. @#&#&##.#&*#.#..##.#&#@.$.$@>$$.$.$%]%;$;;-;)%)%--&@&@&#&#&##.#..*.#.#..>..~.*..#.*...#..++='+++=++=+'+++@", +"%.#&~&~..*c*.* #@&#&#&#.#&*&#*#.&#.#.#@##.&.@.$$.$%$$;$%]%)$;$;-<-%@&@$&##@&#.>.>.#.#&#.*.*.*.>*.~.*..>++++=+=++'=+++=++$", +"*.~&,&#.*%.*.*. &#@#&#.#&#.#.#.#&#&#&@.#..@$#.#.@.$.]%;-;-;$;$;-;)--<$@#&,@&##.*.*.>.#&#&$..*.#.*.#.*$.$&++/+++='=+}+=++@$$", +".#@&,&#.*.*%*.*& @&#&#@&#.#.#.~&*.#&#.##&@@.#.@.#.$.$.$.$%$;$)-3))-;;-;-&@@@#@#&#.>.*.#.@(,$#...*#.**.*..@++++!'+}+=+=+@+@@$.", +".&,&#&&>.*..*.#&#@,&#@&#&#&#&#&#.#.##&#&#.#.#&$.#.$.@.$.$$-$;$$%)%-;-;3;)<-)&@&@&,#.~.*>.>#&,+.@**.*.~>.#.>.+++'+++=+=+=++@@@.$.", +"&#&#&,#..*%*.*.,&,@@#@#&##&#.#&#&#.&~#.##&#&#.#$.#.>.#.$.;.$$;-;-)-);)-)<)%3-++@@&@#.*.*..>@,+@@..#*.*~**.>+++_+='++=+=+=++@.$..", +"&,@,&@&>.*.%.>.#&+&#&@&#&#&#&#.#.#.#&#~.#.#.$&#.#..#.$.$.$$.-;--)%)););););-<-&+,@~@*.**>.#&+2+@>*.*#.*.#>&2+++=++=++_+++@@$$...", +"&#&@,#&>.%**.*.&,@,&@#&@@#&#.#&#.~#.#&#&#&#&#.@.#.$.@.@.$.$;-$-;-;-)-;-);-);-)-$+@&#.*..*.>@++++@.#.**.>*$,+/=+=++'=+'+=+@$$.$..", +"@,#&@&#.*..%.>.#&@@,@##&#&##&#.#.#&#&#&#.##.#.$.$.#..#.@$.--;%)$-)%));)<))<)-<)%@+@.#.**.>.#++++#@.>&#.>.@++++=+'+++=+++@$.@..-.", +"@&@,&,&>..**.*.#&,&#&@&@#&#&#.#&>&#&#.#&#.#.#.#.#.>@.@.$%).-$;-;$-;-%)-)%)-;)%)-$+@&.*..*.#@+++++.@...$.#++}=+=+=+=+++++$$$.$...", +",#@,@&#..*...>&#@+&@,@#&#&#.#&#&%#&.#&##.#.#.#.#...#&$.-.-;$;$-;)-))-;-;-;)-;3;3-@@&$.*.*.>&++++/@.-%%$$+++++++=+'+=+++$.$..$.$.", +"@,&#@,&#%.%*.*.&,@,&#&#&#&#&#.#.>&#&#.#&#&#.#.@.#@@..$%$%$.-;$;-;-;-;--)--;)-;);-$(#..*.>.>@++/++--;-<<)/'+[+=+++=++'+@$&$.$%.$.", +"@,@&,&..*.*%..>&,&@@,@#@#&#&#&#%>&#&#.#.#.#.#.#..&@.$%$%]%).-;$;-;$)%$;-%)-;))<)3-@@...*..#@++=@-)%)%)-<{+[++{+=+='}+@:$.$..$.>.", +"@&#@#,&#.%.*.*.&,@,@&&#&@#&#.#&%>&#&#&#&#.#.#.@>&#.$%$.-.$;$;-;$;$;-)--)-;-)%);)))%@..*.*..@++]-]$<);<)-]'='=++++=@++$.$..$..$.$", +"#&,&#&#.**%.%*#&,&&##&#&#&##&&#%.#&#&#&>&#&.#..#&-*$.$.-$-.)$.-$;$-.;-;.-)%;);-;-)).$...*>&&+$)$))--;)%)$+'+=+=++'}'$$$.$.%$....", +"#&@#,~&*...*..*&@,@&&#&##&#.#&.%*&#&#.#.#.#.&#.&.>%.$.$.;-$%$).-$;-$;--);$);-);)-))-$.>..~@.%;;-;));-;3-{+'+++=+=++@@$$..$...$$.", +"@&#&##&*.*5.%*.#,&@,&#&#&##&#&>%#&#~@#&#@.##.@.>..#.$%$-$.).$;$;%$;-.;-%)%$;-;-;);-;%$....%--$3)-;-));-)++=+[+/++/+@$.$$..$%>..>", +"~@#&,&#*.%*.*.*&@,&#&#&#&#&#@&%.#&#&~.#&#.&#.#..>.$.%$.%$.-;$.$$%$.-;$.$;$;$;-;-)$)-%$.$-%-;%;-;-)-;-3%)'+'++=+=++@$$$.%$..$..$.", +"@&###&#.*.5.%.>&,@@&#&#,&#@~&#%..~#&#&#&#$&#..>..#.>$.$.$%$.-;%.-.$%$%)%$;-)%$);%).]%-%%$.-;-);-)%)%;-3-+'=+_++++@$$.$.$..$.>..$", +"~@&#~#&*..%**.*.,(#,&#&##&#&#&%.>.*&#&#.#.#.#.#..#...$.-.$.$..$.%;$.%$.-;$%)$;$$;$;-;$)%-;-$%)-;-;-)-;-]++'+'=+++@.$$.$.>...$.>.", +"#&#&,&#.*..%.*.#@@&@&#&#&##&#&-%..#.#&#&#.#.$..#.$.>.$.$.-;%$%.-..$;.).$%$-.-$;$;$;$;$;-);-;)-;-)%$%)%-{+_=+=+'+@$$..$.%$.>..$.$", +",@&##,~*.5*%*.*&,@,##&,&#&#@&~%.**.~&#&#&#.#.#..>.$.$%$.$.$.$.$.$%$.$%$%$;$:-$-.$%$;-;-;$)-)%)-;-$:$.)-+'+'=+++@@.$.$.$...$.>.>.", +"&#,&#&#.*..%.*.#,&@&#&#&#&#&#&%..*&*#&#&>$.#$.#..$.#.$.-$.-.-..-..$%$.-$;-$$%]$;-;%)%))););$)%)%-.$@;$$++=++=++@$.$.$..$%.$..$.$", +"@&#&#&#..*.*.*.&@,@#&#&,#&#&&#%.*.*.&#.#.#.#&$.#.>.$.$.$.$$.$$$.$$$%$$%$.$;$$.$$;$%$;)%)-);-;-;-.).$$-'+'+='+'@@.$..-.>.$...$...", +"&#@,#~#*.%5%.%*#+,&@,#&#&##&#.%*.*.*~&#.>@#.>..#&$.#$$$$$.$$@@$$$.$$$$:$$-.$;-;-%;)%)$;);-)-;--$;@.@;-++=+=+++@$.$.$..$..>$..$>.", +",&#&#&&*.*.*.*.~@@,&#&#&#&#&,.%%..*..#.#.#.#.>.#.$#..$&@@@@@$$@$-$$]-$$+.{$%-$;-%$;-;-;-)-;--;-.$@:@-$/++_+=++@$.$..$.$.$..$..$.", +"@&#,##~.*.%.%*>&,@@,&#,&#&#&,.*.*.**.&#.#$#$.#.@*@.$>@$$@@@+$)@!]+@@@++{+$;$:-%]%)%$;-);;--;-%$@&+@%-+'+=++++@$$.$.>.%.$.$..$..$", +"&#&&#&#*..5*..*&,@,@&#&#,#&#&&%%%*.*.#.#&#.#.$.#.$#.@$@@@{!@!@!+=$=@'+++@$$-$%-;)-;).)%-%)%$.@&@&$%)+++'+==++.$.$%$.$$.$.%$.$...", +"#&#@~#..*.*%%*.#@+#@#&#&&#&,&~%%5*.~.~&#.$#$&#..#.$#$@$@!@={=!!@[@[@=e+)$;-;);$%;$-;-);$<$&@6+@&]$-(++++=++++$$.$.$%.$..$....$..", +"&#&#&##*.%..*..~#+&#&,###&#&#(%%%...>&@#.#.#.#.@>.@#$@@+@+=+!{=!!=+!++)@-]%$%)%$;)$;$%)%@]&@&@.$.)+++'++=+=+@$.$%.$.$.-.$.$$..$.", +"@#&##&#.**%.%*.#&,@,@&#&,#&#&#.%5....#@>#$&#.$#.$$@$@=!==+=]!_+=@[+{+$$)%$);$;;-$-;---@@@@.:%.-$-++++=+=+++@$.#.$.%.$.$.$.$.$$.$", +"&&#&#~#...5*%*.&,@,@#@#&#&#&&,&%%%%..@.$.,.#@&$@,$@+}@='=/={+=]!_!/!$]%$;).-$;$;)-$;.@{@@$-$..%%+/'++=+'=++@$$.%..$.%..$%$.$.$.$", +"#&#&#&#**.*..**#@,&@,@#@#&##.#&%%%..#$##&#.&$.$$+@+!@=+!={=+={=@@_]$@-;$%$;$;$;$;-.$(+@${@:%.--+++++=+=++++@..$.$%.$.$%$.$.$.$$.", +"#&###&#.%.%*%...,@,#&#&,#&,&#&#&%%%.&..#$#.>@#@@@@=@=+=!+_'+=+='!!{@)$--;$;$;$%;$)%]&@@@$@$$%.(+++++'+_+=+@$.$%..$%.$..$.%$.$..$", +"#&@&#,&**.%.*%*&,@&@,#@&#&#&#&&9%%.>.#@..@.@.@@@@@@=_=+=@_+=@@!+!]@$).;$.-.$;$;-;-%-.@@@@@:-@++++++=@+'+++@@@.$.%$..$..$.$.$.$.$", +"#&##&###..*%.*.~@,+#@&###~#&#.#&.%%.&.,.#&#$@$@+=+@=+=^@[+++=++!@]@)$%).).)$.).-;$;%.+@@@$@.6+/++++++=+=+@$&$%..$.$.-.$..$..$...", +"&#&#&,&*.%.%*%*#&,@&#,@&@&#&#~&#&-%#.#&#&>.@@@}+=+={!==@!+'+'+@+$1$)$%$.$$.-;-%;-%-%$@++=@]$+/+=+{=+=/+++@$$.$.%.%..$.%$.%$..$.$", +"#&@##&#.*%*.....,@,@@&##,#&#&>&#&%%&#&.@.@>@@+!+='+==+![$!+++@@$]$-$;$;$%$.$;$%-;-%%%$++@+$]+++=+++++'+++$@$.$.$.$$%.$.$.$%.$...", +"&#&#&,&#..%.%*>&,@(##@#&#&#&#.#&#->.&#&>&@++!+=+=+=+[=+=@++/@$$$$]$).$..$;$-%-;%-%;%.$@+}{$$+'+=+={+=+++@@$.$..$...$..%.$.$..$.$", +"&#&##&#*.*%*...#&@,@&#@#&##.#~&#..-&#..#$@@++++^+==+^=@!'=/@$$$-$$$-;-.$.$%$%$%-%--%%#@+}@]$@++=++=+++++$@.$.$..$.$.$.$;..;.$...", +"@&#@#,&...%.%*.#&@,@#&#&#&#~$~#.##%#.@.$@++!_=@={+=+=='=+@{@.$;$)&$%$.$.-$%$%-;%)%%%-%@+++@${+={=++=+++@$$.$..:.$.$:$.$.$$.$.$$.", +"&@&#&,#*.*%*%*.&,@@&@#@##&#&#.&#&$@.@.#@+!++@'+=+'=+=+=@'@$:$.$%-:-$.$.%>%$-.-%%-;-%%>@@+/@$++=+={++'++@@$$.$%$.;..$..$..$.$:..$", +"#&#@#&@....%.%>&#(@#@&#&#&#&##*#@&#.#$@@+@_!_=+{=+={==+/+@@$.$;$$$..$%>.$>%$%-;.-%%)%-$+}+@)@++={+=++++$..$;$...$$..$:@.$:$.$$$.", +"@&#&#~##..*%.*.&@@,&#@#@##~#.&#.>++&$.@+=+=@=+=++++='+^+]@-;-..-%$@&&#.>%>-.$%-;-)%%%$#++++-@+@+=+[+++@$@.$..$;..$.$.$.@.$$.@.$$", +"&#@,@,&*.%..%%>~@(@@&#&#&@&##.*.#+2$@@@+!=^!'={+_+=@!@_+@:-.).;-.$.$.,(#.>$%$%%%$;-%.@@++++%@+=+{+=+++@$$.$....$;.$:.$.$.$.$$@.@", +"@&&#&#@#.*%.*..&#@,@#&#&#~#&#&>.$@(@@@+@++=!+=+'@/=/@+'$@)$%$.-&@$$$.$&,&#.>.$;%%$@@&@#@+++]$++=+=+'++.@.$%$.$..$%.$.$.$:$$$:$@$", +"##&#@9&..*%*%%*.,+#&#&#&#&@&>..>.$@++!=^!!/^+^+=1'+=++@:-.;-$%@@@+++++$.#.>.%%-;-$@+++@++++$@@++=+=++@$@.$.$..$..$$.$.$.$.$.@$:$", +"&#&#&#,>...%.*..&,@,&#&&#&#..$..$+@+=@!@^]=@=+'+_!'+0+$.$;-%-+(+,++@@2+#.#*>.%-;@+++++}+++{$@@+++'+++@..$.$.$..$...$.$.$.$$$$$@$", +"&,@,@(&.**%*.%*&,@,@&#,.,&#-.$.@$++^+^_=!@_'+==/^+_++@:.-.-%+@,++@++--(@~.#.>%%$++@@a+++=++$]@=+==+++.@$.$.$.$.$.>$.$.$$.@.:@@.@", +"#&#&#,,...*%.*..@,&##&#&#@+,$$$(+/@=+=!+^1+[+'+='=+'+@$$.%-&@&,&@+@@$-#@#&#..)%6@+@@$7++++@@$@@++=+++$..$.$.$..$..$.$.$.$.@.$:$.", +"@&#@,&@#.%.%*%.~#,&#&@.,+&@--%+++{!'=!=^+!+==+=+=+'+$..$;-&&$$@@&$.--%.$~#*.>%-@.$#$$,@+++!{$@@+=+'+@:$.$.$.$.$.$..$.$.@$:$.@..;", +"#@#&@,&#.*5.%*..&@#&##.+#$$$@++@@'}'={=!!!'/'=+'=+++@-$%-&@...*$#$@&@$-.#.#.$%;$.$.@,+@}++@@|{++++++@$.$.$..$....$.@.$.@.@.;.%%%", +"#&@,@,&#.*%*%.*.,,&#&#$#$$+@=+'+1$!==^={$1+=++=++/+$]$.-&&*#>.$.$.$.-.#.>.#*$-%-$.##@#@++++@$@+=+=+@.@...$.$.$.$.$.$.@.$.$.$%;.%", +",@#&,+,.>.%*%.*.&#&#.@&$@!+!+==@!{^'==!!!!{+='/='+@@$--#&#...>.>.$.$.$%#*#.*$%-.$.$.#$,@+++@$@@++++@$.-$..$.$.$....$.:@&$%%%%%.%", +"@&+@,&+...%.*%.#,#,@##$#$$|={={^{!+_=]={@$!+++=+++@$$%$&@.#*>.>%$%%$%$..>.#.>%--.*>##@@+}+{+|$+++++@....$..$...$.$.@.@.;%.;%.;5%", +"@,&,@+,#.*%*%.*.&,&#.$@$+=+='=!+!{[='==!^/@!'++/+@]$--(,#.~.*..>.>.%$%>.*#*#$%-%$...>##+++++$]+++++$@.$..$.$$.$.@:$...$.-%.%%.;%", +"@,@+,&+...*%*%.~#&,@@#$+!@^=!'!1'_+==+=+++]@+=+++$$$)++&#&>~>.>%%%$%..$.*.#..---..>.#$#+++++-$++++@.$...$...$.@...@.@.%;%%;%%%%%", +"@+@@,+,@>%..%*..,#&#&@@$!{=!==!{=='+[+{=++$@@'/+$&--@+/@~#*~.>.$>%.%>.>.>*#~$%%-.>.#.#@,++++$$++++@$.$..$.$.$.:$.$.:..%%.%;.;%;%", +",@(+,+&#..*%.%*&,@#@@$$+@!]={^@!{+=='_++++$@$@+/@$-)+/@@#.~**.>..>%$%..*#.~.#$-%...>$#++7+++1$++++@.$.%..$.@.$.@.$&$.;%%4%%%%;%%", +"+,@@+,+&>.%*.*..&@&#$$+!@^===!1]|'+=+}{@+@$$+++]@%$+++/&###~#..>...>.>..#*#..%--.>..>@,+++++$$++++$.$.$...$.$.:$.@...%.%.%4;%%;-", +"@@+,+,+..*.%*%*#,,@@@$@+!{={1{=$=/'+=+@+]@+@/'/$@%@'++@@~*~**>.$*>%..$*.~.#*@.-<..$#.@+++!+/$$@++@$....$...$.$.@.@.$%%454%%%;-%)", +",@+@,++#$%*%.%..,@.#@@@=@!==!=^][+='/=@$@.++++$@:$+/+/@+@#*#*.*$..>.>..##*.#..--...>@,}++@+/$$$+(@@$.$..$.$.$.$.$@..%%%%4%;%;%)%", +"@,@++(,....*%*..#,@@@$+@!{=@^+!!{|+={++=+@+/+@;$-@+'++@,#&*#.#.>*>.%>*#.*#.#.$%-.>.@$+++=++++$$/+.$.$.$..$.$:$...@.%%.4%%%;%%;%;", +",@,@,@+.>.%..%*&&&,@@@@!@=_!='!!]1+={}'+!++++$-$]+/+++@@&#*~*>.>.>.>.$*.#&#&#&>.*..#@+=++++++$$+&$..$.$.$.$.@.$.$..;%%%4;%%;%-%%", +"@,@,+&@...%*%*.>#+#@$@+!+=]=!=]!|!'+!@/++/@@@$6-++=+/+#@~#.#*~>*.#*.*.>.*..#.$%$.>$@+}+=@++++-@+@.$.>.$..@..$:@.@..%%4%%%;%;%;;%", +"@+@@+,@.$*.%.%..&+&#@$+{!@=_{=!!{!!'@^++!@$$@@$+'+++++#@$~#*#.#.>.>>.>.#.>.%%-%$..#@++++!+@+($$@#.$.$..$.$.$..$..$.%%%;%;%%;-;-;", +",@,+@+#..%.*%*.##,@@$@!+!{==={={=1@={@/+]$$$@@@+++=@++@&#.~*~**.#...*..>..>-.%>.>$.++=++@+++/$.@...$.$..$..@.$.@..%%.%5%;%;-%<%;", +"++@+,+@.>.%.*%..&@@#@$+@!+!'==!!^!1+@'+@@$-@@]+++@++++#+##.#.#*.*.>.>.*.>..%$%%$.$@/+=+7@@++($@@$.$...$.@.:$.:.@..%%%;%<%%<;%-;%", +"+,@+@,&$..*%.*.#&,@@@!+!+[=@^@[+^={|++.)$-+@$++{+=+!+++&#.~***#.#*.~.#..*$*>%$.$#@+++++=+@,++@:....$.$..$.$.@.$.@.%%%5;%;;%;%;-<", +"++,++@,.>%.%%%.#&@#@$@=+!'===!{=^!!{@)@-%@@@+/+@}++++++@##.#~.**.#&#.~#.#&.#.,&@>$++++++}@++(@@@.$....$...$..@....%%%%%%%%;-<%;%", +"+++@+@&$..*%*%*.@$,@@@@=+={^'|!{!!{+@$&3$&]$++=+++=+=+,+&#~.#*#.*>*~#.~~>&#.>$.$$}+}'+=+@@+@+&$.$..$..$.@.$:$.]@&%%;%4%;%;%<-;%<", +"@++++@&>..%*.%.#&$+$@@'=+_=@=]=!^!={]$$;@$$++'/=+++@+++@@&#~.~.#.#.*#*#.*>%-*#$>@+++++++}@2@+&$.$..$..$:.$..$..$&%%%%%<%<%;%%;%%", +"++7+,+@..>.%%*..#+@@d+@='='=^@[@^^@!$]$$@-+++++++=++=+@,,@~@~#*~.*>.~.#*#.>.>.>@+_+=+++@+@+@2+$..$..$..$.$:@:$@:@;%;8%;%;%-;%;%;", +"=++++@&#.%.*%>..,@$@@+!+=$[@b{!{=^!]@$$$-+@+}{=+++++@++@,@#&#.#.#.#*#*#.#~##$#$,+=++{++@7@}@++.$..$..$.$.$.$.$.@.%%%%;%<%;%)%%%%", +"+++++#@..*%.%*..@,$@@+!'_=='|^@1!+1@@{$-@++=++++++=+{+@++&#&#&>.#*.>..#.>..>.>@@/=+++++@+@+@++.$...$..$.$.$@:$@:@;%<%%<%%<%%;%;%", +"+=++@@@#..*%.%>.@+@@+@@='@[=$^!]!^+1$+]$+++++=+=++++=+++++,#&##.#.#.>*.>.>$%$.++=+[+{++@+#+#++$.$..$.$.$:$.$$$.@&;<%;%;;%-<%%%%%", +"+++++@..>.%*%..#@,@@=+^+=^+^]='1|]=]$@@+=+=++'+++++=+'+++&+,,&#.#....>.>%$.>$#(++=+++++@@+7@@(-.$.$..@.$.$@:@$@].-%;<%<%;%%)%;%%", +"+=}++@@...%.*%*.@+@+++!+'='_!]={!!]!!{!+'=+=+='@+++++=@+++(+(,#&#.#>..%.>%$.$@+++'={'++@+@+@@+.$..$:$.@$.$.$.@:@:%<%;%%<%;%%%%..", +"+++++#@#.*.%*%.#+@$@=@==+='=]|={!^|{|$1@_+'}{@}@_+'+=+++@+#+,+,(#&&#.#>.%$.$.@++=++++++@!@+#++%$&@.@.$.]$$@$]@$&.;%%4%;%%%-$$+++", +"++++++&$>.>..*..@-+$+'@={=+{!]=={!^!]!{)!'=++=+@+=+++=+=++++&#.#.#.>...$*.$%$.@+'='@{=+@+!+@@($.@.@&$@.@.@.$.@]@%%<%%%%;%;.(++++", +"++++}@,&$%*%*$*.-++===+=+=+_{|]=[!$^@^!+]@'=@++=+++=+'+@++,@#@#$#.#.>.*.$..$%$;-@+'++++!@@@+@($.&@.@.@@@.]@@.@&$;%%4%8%%%.@++++@", +"@@+++++#.>.%.%>.+@]++_{='!{+[@^@|]^!^@^|]!++='+@=+'=++=++++@@#.#.#.#.#..>.$.-%----]/'++++7@+@+.$.$&$&@&@@&@@@@@&%;%%%%%;$&(++@@@", +"@!@@+@+&..**%*..@++='+=@_+^@='|^!!{^@1@^$+1+@+=+++++=++=@++,@#.#.#.>..>...%.-.-;-)--+++@+@@+,@.@.$.@.$@.@@&@&$&@%;%;%;%.&+(@,+@,", +"$]@@@++,.$%..%>.$@++_+|{=]+^+={={==!{!]!{={{=++=+=+'=++++@+@&#$##.#.#.~.>.$.%%$%%)%3;@'+++@++@$.@.$.$.@.@@@&@&@&%4%%%%$(+(@&@,@@", +"@$$$@@2@.*.*%*..$@+=+_+=@_@'!{=$={'!^$!]=$_$+'/@=+=++'=++@+,@.#..#.*.>..*.%%$%.;$%;-%-$+{+@++.$.$.@.@.@$&$@&$.@&%%<;%%@(@@@+&@&@", +"{$]$@@+@$%.%*.>.@)/'={=+='=+=+[{==!{!{1@1+)=+=+{+'+=@+++@@++#.##.#.#..#..>...$%%;%-;%<)@++@++$$.$.:$.$.$@.@@&$&%;%%%;.@(@&#@#@,@", +"+@|$$@(,..>%*%..@@+/=+='=+{='=+={[+=!!]|]!+{$+/=+=++=++@@@#(@#&$*.#.*#.*..%.%.%-%;%;-%--{++++$.$.$@.@$@.$#.@.&$%%%;%%@(@@,@.@&&&", +"'@]$$@++...%.%*.$@+=/!+=+=@=+_{==@^{+^!@|{=|)=++{+=+{+}+@@@(,.#.#.*>.*.>.>..-..%$%-;%<;--+@++.@$..:$..$.@.@.#..%%%%%.&,@&@#@,@#@", +"++]$$$+&.*%*%*.$$+'=+[='_'$'='_@^{!!!{!]|'=@-+/!+=++=+++#@#2@.#.#.#.~>.*..>..%$%;%;%-;-%)++++$..$..@$@.@$.@&..%%%<%%$&@&#@@&@&&@", +"''$]$@++$%.%.%*.$++'=+=+=@!'+=+='=+{!'!1!@={|'+@{=+++++@@@&2&#.#*~.*..#.>.%$..%.%$;%%;-<-)++($.@.$...$@.@.@.$%%%%%%.&&,@&##@#@@@", +"++!$$$(+..*%*...@+_@_+[+[@@_{='=+[={!=@^$^@!{+_+++=++++#&#@(#.#..#*#.*.*.>..%$.-.%-;-%)-;3/0@@...@.$@.$@.@..%%%;%%%$&#&#@&@,@@#&", +"''])$@(+..%.%*..@'+'+[+=@[++=+=+='+=]=!]|]!@^++=++@++++@#.#,~.*#*..#*#.#..>.$%.%$;%-%;-<--@+@.$..$...@.@$&@%<%%%%%%&&&#&#@,@&@@@", +"/+@]$$+($.%*%%*.@@==/='=+]='+'='=+^='!{|=]|$@{++=+++},@#.@(&##..~**..#*.>.>.>.$%.-;%)%--;-)(]..@..@$.$.@.@:%;%4%%%$&#&&#.&#@@@+@", +"''!)$]+,..%..*..$++'+=+[+!@_!+_+='!+=@!1$_'[+=]$@=+++@@#.>,&&*#.#.~*#.*#.>.$.%$.-.-;-;-;-<$+&]..$..@.@:@.@.%%%%<%*.&&#&#@,&@,@@@", +"++{$$$@($.*%*%.>@'+=+'=+'='+'=+'=+^'^^@^@1+=+++@+@@++&#@.#(##.#*.*$*.#.*#*.*$..%.%%%-;-;-;-(@.$..@.$:$$.@...%%8%%.#.&&#&@@#@@@,@", +"_/!]$]@+.>%.%*..$++=++{=++=+=+=+{=+^+^]1+^@'/={++++2+#&#@#+&#.*.#.~.**#*.>.*..$..$;%%-<-%)-:$.@.$..$@.@:@...%.%%%.~#&#&@,@@@+@@@", +"+'+$]$+&&.*%*%.>$@+'+=+'={={+=+=+[+^='!^+^@|'++=++++@&#..#&#.#*.*.#*#.*.#*.>.%.%.%.-;%);-<--:@.@&$.$$].$&.....%%%.&#&@,&@,@#@@+@", +"_+{$$]@+$..%.*..$@}'++='+=+='='+!'_$!'={[@^@=++/++++#@##@2&#.#*#.*.*.~>~.*..>..$..%$%-%-)%-<@&@&@&@.@$@.$....4%%.#&,&@,@&@@+@@@+", +"+/=]$$&&&>%*.%..@@+=+'}=+{=++=+_=@!{!={=={!+'_{+=+++#&$*@(#.#.>.*#.#.*.#.#*...%.%.%.;-;-;-;-.@&+(@@@@@@$...&.%%%.@&@,&@@@@@&@@++", +"+'+]$)@+...*%*..@++'=+'='=+[+='+'_+^$|{!]='=++=+/++@#.##2&#.#.*#*.*.*#.**.*.>*.$.$%$%;-%<--<$6@&@&+({(@+&$..%%%..,+&@,@@,@@@@@++", +"+++$$).(..%*%.%.@=+++=++='={+=+==+=={|!^@=+^/=+/+++#@#&,(#.#.*#.*#.*#*.#.#*...*.%..%$%;-;-%--&@@@@@++++(+%%.%%%.@(@+@&@@@+@@@+++", +"{@{]$]$+&..*%*.&$@_'+'='+=+='=++='=+_{$^!{='=/+=++@#$#&2#.#.>.~*.*.*.#*#*.#.>.>..$%.%-%-;3;-%{&@&@@@@++(,%>.%<.&++,@+@@(@@@@@+@+", +"@@@$$;$&@%%.%%.&$+++=+=+='=@=+[/=+='=!_@!{_++[+++@#@~,(,.#.#.*.~*#.**..*.*.*...*..$%.;-;-%-;-$@&@@&@@++(+.%-.>&(+@+@@@@+@@@@@@++", +"+{@]$]$&&.*.*%%&@=+=+'='+='='=@='!+=+^@^+='=+/+++@@#(2,~#.#.#*#.*.~.#*~.#*#.#*.$*.$%$.%-;)%-<-6@@@@6@@+@&>%%$&+(+@+@@+@+@@@@{+@+", +"@@@$$.]&@.%.%...@++'=+=+=+=++={}+!'!'=@1+=++++}+@(,(#~..~#.*.**~*.**.*>.*.~.*.>..>.%.-;%-;-%--+@&@@@@&@&#.>.&,&+@+@@@@+@@&@@++++", +"+@{@)$$&..*%*%..{++=+{='='=^+!@='!+=]={!{='=++++,(#&&#~#.*##.#*.#.#*.*.~.#*.#.**...$%.$;%)%);-:@@@&@@@&@&..,@(@&+@@@@++@@@@@@+++", +"+'@@$)$&@..%%%.#++_+=+=+!+=@[+=@={!_+={!@++++/+,@&.#*.*~#..~.#****.~.#*.*..*.*#.>>.%$%%$%)%----+6@@@&@@.##&~@#@+@@@+@+@@]@@++{++", +"+++{$-)$..%*.%.@++++'+{='='@={=$=+!'^$_+^+_+++@$&#&~&~*.~#*#*.~.~.*.*.#*.#*#.*.*..>..$;%;-;;-;-+@&@@&$&#&&#&#&@@@@+@+@@.@@@+++++", +"++'@)-.$&$.%%%%&+'=+==+=+=={=@={@[@='={!$@+++,@,.&.#*&~~.*&.#*#*#*~.*.*.*..*>.*>.>.%.%$;%)%--;-@&@&@@.@.#&#.#&@@+@+@@@@@@@++++!@", +"++++-))$...>.%.@++_++{={'=@_+=+={=]+_@[/^@+@+@#@#.~.~*.~*#*#*.~.*.*.**.*.#*.*#.*.>.$..$;$%);--)$6@&@&#.@*&#.#@,@+@@+@@$@@@++++@@"}; diff --git a/PIL/XpmImagePlugin.py b/PIL/XpmImagePlugin.py index c2a52f9b2..304ba33b7 100644 --- a/PIL/XpmImagePlugin.py +++ b/PIL/XpmImagePlugin.py @@ -77,13 +77,13 @@ class XpmImageFile(ImageFile.ImageFile): for i in range(0, len(s), 2): - if s[i] == "c": + if s[i] == b"c": # process colour key rgb = s[i+1] if rgb == b"None": self.info["transparency"] = c - elif rgb[0] == b"#": + elif rgb[0:1] == b"#": # FIXME: handle colour names (see ImagePalette.py) rgb = int(rgb[1:], 16) palette[c] = o8((rgb >> 16) & 255) +\ diff --git a/Tests/test_file_xpm.py b/Tests/test_file_xpm.py new file mode 100644 index 000000000..44135d028 --- /dev/null +++ b/Tests/test_file_xpm.py @@ -0,0 +1,14 @@ +from tester import * + +from PIL import Image + +# sample ppm stream +file = "Images/lena.xpm" +data = open(file, "rb").read() + +def test_sanity(): + im = Image.open(file) + im.load() + assert_equal(im.mode, "P") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "XPM") From 848579af9bc05e85f814d270a4885f6aac0840f7 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Thu, 25 Oct 2012 17:00:19 -0500 Subject: [PATCH 52/61] py3k: Add Windows icon sanity test --- Images/lena.ico | Bin 0 -> 1406 bytes PIL/IcoImagePlugin.py | 2 +- Tests/test_file_ico.py | 14 ++++++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 Images/lena.ico create mode 100644 Tests/test_file_ico.py diff --git a/Images/lena.ico b/Images/lena.ico new file mode 100644 index 0000000000000000000000000000000000000000..6fadbe29edcd3b4b4986b2a1cb020443a1515099 GIT binary patch literal 1406 zcma)*`A-uD6vsaf)RpT3Uo$q7_hzoI$aOKj#4 zQsXq*tYH$f1_{m_Cn~oG^_f9Zb9&I78bh8nP2`zTlvz_4@;i~Ann9m8Om5*#lm)}Y z7MOiGza!0_C!ufv_1O_3^QH(bctTMAEV^P7DWwC16-{xx#DunG{3wew`m5| zh1aAv&7*C4P2R<)BwhH6=$5y{x4fb9$^^QLZ^^zqOX1ZC^q2l7x$PNc*TyMspQf^7 zlIo6G&bKeq*f~ks%_WSTFRAOErm4%U>t3Su-V8VHO>u7M6~)8L6q?>r^WY7A<4ZJ* z|3lxS1zI2e`>wZsZ$4t+!*ySNBFe6+?43R0w{x@Yh{YPq&$k3u)jl6~ed1_k`)Kv2 zmNu)@MT5&R`L}C?o5$8$dat*zKG%C}zuxubcbg}_wTxQ3*172HN~_D6OUqlv4t?{3 z|Hs+2JzKXn<0S$LQ$n z!u%^@E(uz9OG1nV3yZJQell6C>X6;qD~=64^LSBjjIU4lVB@W8g5c!zf@VXUv9*3~ c--;VIofPRO)6-9A&$ZvJNj&`ChkA$bKR(j;ssI20 literal 0 HcmV?d00001 diff --git a/PIL/IcoImagePlugin.py b/PIL/IcoImagePlugin.py index 0068db47f..c484a6774 100644 --- a/PIL/IcoImagePlugin.py +++ b/PIL/IcoImagePlugin.py @@ -49,7 +49,7 @@ class IcoImageFile(BmpImagePlugin.BmpImageFile): raise SyntaxError("not an ICO file") # pick the largest icon in the file - m = "" + m = b"" for i in range(i16(s[4:])): s = self.fp.read(16) if not m: diff --git a/Tests/test_file_ico.py b/Tests/test_file_ico.py new file mode 100644 index 000000000..f741cd663 --- /dev/null +++ b/Tests/test_file_ico.py @@ -0,0 +1,14 @@ +from tester import * + +from PIL import Image + +# sample ppm stream +file = "Images/lena.ico" +data = open(file, "rb").read() + +def test_sanity(): + im = Image.open(file) + im.load() + assert_equal(im.mode, "P") + assert_equal(im.size, (16, 16)) + assert_equal(im.format, "ICO") From ec961de303a4c9aefef7c40f148ff0808b9c28b8 Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Sun, 28 Oct 2012 10:33:58 -0500 Subject: [PATCH 53/61] py3k: Remove use_2to3 from setup.py This is unnecessary now. --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 5608b0dd0..22c67f620 100644 --- a/setup.py +++ b/setup.py @@ -454,7 +454,6 @@ setup( author='Alex Clark (fork author)', author_email='aclark@aclark.net', url='http://github.com/python-imaging/Pillow', - use_2to3=True, classifiers=[ "Development Status :: 6 - Mature", "Topic :: Multimedia :: Graphics", From a1c16545944ed20112b046b77b98243c8787bddb Mon Sep 17 00:00:00 2001 From: "Brian J. Crowell" Date: Tue, 30 Oct 2012 17:38:05 -0500 Subject: [PATCH 54/61] py3k: inquiry is now lenfunc This fixes a build warning on 64-bit machines. --- _imaging.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_imaging.c b/_imaging.c index 59b045abf..986f221e4 100644 --- a/_imaging.c +++ b/_imaging.c @@ -3033,7 +3033,7 @@ image_item(ImagingObject *self, Py_ssize_t i) } static PySequenceMethods image_as_sequence = { - (inquiry) image_length, /*sq_length*/ + (lenfunc) image_length, /*sq_length*/ (binaryfunc) NULL, /*sq_concat*/ (ssizeargfunc) NULL, /*sq_repeat*/ (ssizeargfunc) image_item, /*sq_item*/ @@ -3152,7 +3152,7 @@ static PyTypeObject ImagingDraw_Type = { #endif static PyMappingMethods pixel_access_as_mapping = { - (inquiry) NULL, /*mp_length*/ + (lenfunc) NULL, /*mp_length*/ (binaryfunc) pixel_access_getitem, /*mp_subscript*/ (objobjargproc) pixel_access_setitem, /*mp_ass_subscript*/ }; From c952134e0069cdf61edcc212a21450a1730240a3 Mon Sep 17 00:00:00 2001 From: "Brian J. Crowell" Date: Thu, 8 Nov 2012 22:59:38 -0600 Subject: [PATCH 55/61] py3k: Issue warnings when using the old fromstring/tostring ...although, you have to turn on deprecation warnings specifically in order to get them. --- PIL/Image.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 2f2eac3b4..b2b5d2493 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -540,7 +540,9 @@ class Image: if bytes is str: # Declare tostring as alias to tobytes - tostring = tobytes + def tostring(self, *args, **kw): + warnings.warn('tostring() is deprecated. Please call tobytes() instead.', DeprecationWarning) + return self.tobytes(*args, **kw) ## # Returns the image converted to an X11 bitmap. This method @@ -591,7 +593,9 @@ class Image: if bytes is str: # Declare fromstring as alias to frombytes - fromstring = frombytes + def fromstring(self, *args, **kw): + warnings.warn('fromstring() is deprecated. Please call frombytes() instead.', DeprecationWarning) + return self.frombytes(*args, **kw) ## # Allocates storage for the image and loads the pixel data. In @@ -1804,7 +1808,9 @@ def frombytes(mode, size, data, decoder_name="raw", *args): if bytes is str: # Declare fromstring as an alias for frombytes - fromstring = frombytes + def fromstring(*args, **kw): + warnings.warn('fromstring() is deprecated. Please call frombytes() instead.', DeprecationWarning) + return frombytes(*args, **kw) ## # (New in 1.1.4) Creates an image memory referencing pixel data in a From decabcf96a9314b9a75259a2f3420970ad001195 Mon Sep 17 00:00:00 2001 From: Mikhail Korobov Date: Tue, 30 Oct 2012 00:50:20 +0600 Subject: [PATCH 56/61] Fix tox test running: port selftest.py to Python 3; make it clear Python 2.5 is no longer supported (its support is broken in many ways in this branch); remove bundled doctest.py module (it is in stdlib since forever); remove extra stuff from tox.ini --- selftest.py | 48 ++++++++++++++++++++++++------------------------ setup.py | 6 ++++++ tox.ini | 13 +------------ 3 files changed, 31 insertions(+), 36 deletions(-) diff --git a/selftest.py b/selftest.py index 3102b6f0d..b6d7a300c 100644 --- a/selftest.py +++ b/selftest.py @@ -1,5 +1,5 @@ # minimal sanity check - +from __future__ import print_function ROOT = "." import os, sys @@ -13,7 +13,7 @@ from PIL import ImageMath try: Image.core.ping except ImportError as v: - print "***", v + print("***", v) sys.exit() except AttributeError: pass @@ -49,19 +49,19 @@ def testimage(): ('PPM', 'RGB', (128, 128)) >>> try: ... _info(Image.open(os.path.join(ROOT, "Images/lena.jpg"))) - ... except IOError, v: - ... print v + ... except IOError as v: + ... print(v) ('JPEG', 'RGB', (128, 128)) PIL doesn't actually load the image data until it's needed, or you call the "load" method: >>> im = Image.open(os.path.join(ROOT, "Images/lena.ppm")) - >>> print im.im # internal image attribute + >>> print(im.im) # internal image attribute None >>> a = im.load() - >>> type(im.im) - + >>> type(im.im) # doctest: +ELLIPSIS + <... '...ImagingCore'> You can apply many different operations on images. Most operations return a new image: @@ -89,17 +89,17 @@ def testimage(): 2 >>> len(im.histogram()) 768 - >>> _info(im.point(range(256)*3)) + >>> _info(im.point(list(range(256))*3)) (None, 'RGB', (128, 128)) >>> _info(im.resize((64, 64))) (None, 'RGB', (64, 64)) >>> _info(im.rotate(45)) (None, 'RGB', (128, 128)) - >>> map(_info, im.split()) + >>> [_info(ch) for ch in im.split()] [(None, 'L', (128, 128)), (None, 'L', (128, 128)), (None, 'L', (128, 128))] >>> len(im.convert("1").tobitmap()) 10456 - >>> len(im.tostring()) + >>> len(im.tobytes()) 49152 >>> _info(im.transform((512, 512), Image.AFFINE, (1,0,0,0,1,0))) (None, 'RGB', (512, 512)) @@ -159,15 +159,15 @@ def check_module(feature, module): try: __import__("PIL." + module) except ImportError: - print "***", feature, "support not installed" + print("***", feature, "support not installed") else: - print "---", feature, "support ok" + print("---", feature, "support ok") def check_codec(feature, codec): if codec + "_encoder" not in dir(Image.core): - print "***", feature, "support not installed" + print("***", feature, "support not installed") else: - print "---", feature, "support ok" + print("---", feature, "support ok") if __name__ == "__main__": @@ -175,28 +175,28 @@ if __name__ == "__main__": exit_status = 0 - print "-"*68 - print "PIL", Image.VERSION, "TEST SUMMARY " - print "-"*68 - print "Python modules loaded from", os.path.dirname(Image.__file__) - print "Binary modules loaded from", os.path.dirname(Image.core.__file__) - print "-"*68 + print("-"*68) + print("PIL", Image.VERSION, "TEST SUMMARY ") + print("-"*68) + print("Python modules loaded from", os.path.dirname(Image.__file__)) + print("Binary modules loaded from", os.path.dirname(Image.core.__file__)) + print("-"*68) check_module("PIL CORE", "_imaging") check_module("TKINTER", "_imagingtk") check_codec("JPEG", "jpeg") check_codec("ZLIB (PNG/ZIP)", "zip") check_module("FREETYPE2", "_imagingft") check_module("LITTLECMS", "_imagingcms") - print "-"*68 + print("-"*68) # use doctest to make sure the test program behaves as documented! import doctest, selftest - print "Running selftest:" + print("Running selftest:") status = doctest.testmod(selftest) if status[0]: - print "*** %s tests of %d failed." % status + print("*** %s tests of %d failed." % status) exit_status = 1 else: - print "--- %s tests passed." % status[1] + print("--- %s tests passed." % status[1]) sys.exit(exit_status) diff --git a/setup.py b/setup.py index 22c67f620..239b4676c 100644 --- a/setup.py +++ b/setup.py @@ -462,6 +462,12 @@ setup( "Topic :: Multimedia :: Graphics :: Capture :: Screen Capture", "Topic :: Multimedia :: Graphics :: Graphics Conversion", "Topic :: Multimedia :: Graphics :: Viewers", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.6", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.2", + "Programming Language :: Python :: 3.3", ], cmdclass={"build_ext": pil_build_ext}, ext_modules=[Extension("_imaging", ["_imaging.c"])], diff --git a/tox.ini b/tox.ini index 6123e8613..338c94d6b 100644 --- a/tox.ini +++ b/tox.ini @@ -4,19 +4,8 @@ # and then run "tox" from this directory. [tox] -envlist = py25, py26, py27, py32, pypy +envlist = py26, py27, py32, py33, pypy [testenv] commands = - {envpython} setup.py clean - rm -rf build dist - {envpython} setup.py install {envpython} selftest.py - -[testenv:py32] -commands = - {envpython} setup.py clean - rm -rf build dist - {envpython} setup.py install - 2to3 -w -n -o . --add-suffix=3 selftest.py - {envpython} selftest.py3 From 67a131a9e1e4aec8748276c60926f50470bdd9e8 Mon Sep 17 00:00:00 2001 From: "Brian J. Crowell" Date: Sat, 10 Nov 2012 11:01:14 -0600 Subject: [PATCH 57/61] Update the manifest to include a lot of missing files --- MANIFEST.in | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/MANIFEST.in b/MANIFEST.in index d0cf89c6b..3807bdd32 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -8,6 +8,10 @@ include *.rst include *.c include *.h include selftest.py tox.ini +recursive-include Tests *.py *.txt +graft Tests/fonts +graft Tests/icc +graft Tests/images recursive-include Scripts *.py README graft Images recursive-include docs *.txt *.html *.rst *.css *.py README CHANGES CONTENTS Makefile make.bat From e84cc9203637e3135ebcdaa6d4d7543da129f08d Mon Sep 17 00:00:00 2001 From: Sandro Mani Date: Sun, 23 Dec 2012 19:04:46 +0100 Subject: [PATCH 58/61] Make pysane Python3-compatible --- Sane/_sane.c | 58 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/Sane/_sane.c b/Sane/_sane.c index a4f4377ac..2939371a5 100644 --- a/Sane/_sane.c +++ b/Sane/_sane.c @@ -28,6 +28,12 @@ PERFORMANCE OF THIS SOFTWARE. #include +#if PY_MAJOR_VERSION >= 3 + #define PyInt_AsLong PyLong_AsLong + #define PyInt_FromLong PyLong_FromLong + #define PyInt_Check PyLong_Check +#endif + static PyObject *ErrorObject; typedef struct { @@ -237,7 +243,7 @@ SaneDev_get_options(SaneDevObject *self, PyObject *args) constraint=PyList_New(0); for(j=0; d->constraint.string_list[j]!=NULL; j++) PyList_Append(constraint, - PyString_FromString(d->constraint.string_list[j])); + PyBytes_FromString(d->constraint.string_list[j])); break; } value=Py_BuildValue("isssiiiiO", i, d->name, d->title, d->desc, @@ -349,13 +355,13 @@ SaneDev_set_option(SaneDevObject *self, PyObject *args) *( (SANE_Fixed*)v) = SANE_FIX(PyFloat_AsDouble(value)); break; case(SANE_TYPE_STRING): - if (!PyString_Check(value)) + if (!PyBytes_Check(value)) { PyErr_SetString(PyExc_TypeError, "SANE_STRING requires a string"); free(v); return NULL; } - strncpy(v, PyString_AsString(value), d->size-1); + strncpy(v, PyBytes_AsString(value), d->size-1); ((char*)v)[d->size-1] = 0; break; case(SANE_TYPE_BUTTON): @@ -1165,8 +1171,8 @@ PySane_exit(PyObject *self, PyObject *args) static PyObject * PySane_get_devices(PyObject *self, PyObject *args) { - SANE_Device **devlist; - SANE_Device *dev; + const SANE_Device **devlist; + const SANE_Device *dev; SANE_Status st; PyObject *list; int local_only, i; @@ -1176,7 +1182,9 @@ PySane_get_devices(PyObject *self, PyObject *args) return NULL; } + Py_BEGIN_ALLOW_THREADS st=sane_get_devices(&devlist, local_only); + Py_END_ALLOW_THREADS if (st) return PySane_Error(st); if (!(list = PyList_New(0))) return NULL; @@ -1204,7 +1212,9 @@ PySane_open(PyObject *self, PyObject *args) rv = newSaneDevObject(); if ( rv == NULL ) return NULL; + Py_BEGIN_ALLOW_THREADS st = sane_open(name, &(rv->h)); + Py_END_ALLOW_THREADS if (st) { Py_DECREF(rv); @@ -1261,17 +1271,40 @@ insint(PyObject *d, char *name, int value) Py_DECREF(v); } +#if PY_MAJOR_VERSION >= 3 +static struct PyModuleDef PySane_moduledef = { + PyModuleDef_HEAD_INIT, + "_sane", + NULL, + 0, + PySane_methods, + NULL, + NULL, + NULL, + NULL +}; + +PyMODINIT_FUNC +PyInit__sane(void) +{ + /* Create the module and add the functions */ + PyObject *m = PyModule_Create(&PySane_moduledef); + if(!m) + return NULL; +#else /* if PY_MAJOR_VERSION < 3 */ + PyMODINIT_FUNC init_sane(void) { - PyObject *m, *d; - - /* Create the module and add the functions */ - m = Py_InitModule("_sane", PySane_methods); + /* Create the module and add the functions */ + PyObject *m = Py_InitModule("_sane", PySane_methods); + if(!m) + return; +#endif /* Add some symbolic constants to the module */ - d = PyModule_GetDict(m); - ErrorObject = PyString_FromString("_sane.error"); + PyObject *d = PyModule_GetDict(m); + ErrorObject = PyBytes_FromString("_sane.error"); PyDict_SetItemString(d, "error", ErrorObject); insint(d, "INFO_INEXACT", SANE_INFO_INEXACT); @@ -1335,4 +1368,7 @@ init_sane(void) NUMARRAY_IMPORTED = 1; #endif /* WITH_NUMARRAY */ +#if PY_MAJOR_VERSION >= 3 + return m; +#endif } From fb2ccb15a7136215816d6203fd8482aad73e5101 Mon Sep 17 00:00:00 2001 From: "Brian J. Crowell" Date: Wed, 9 Jan 2013 23:45:17 -0600 Subject: [PATCH 59/61] Set up Sane error object as exception instead of string String exceptions don't work anymore. --- Sane/_sane.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sane/_sane.c b/Sane/_sane.c index 2939371a5..d45e6a346 100644 --- a/Sane/_sane.c +++ b/Sane/_sane.c @@ -1304,7 +1304,7 @@ init_sane(void) /* Add some symbolic constants to the module */ PyObject *d = PyModule_GetDict(m); - ErrorObject = PyBytes_FromString("_sane.error"); + ErrorObject = PyErr_NewException("_sane.error", NULL, NULL); PyDict_SetItemString(d, "error", ErrorObject); insint(d, "INFO_INEXACT", SANE_INFO_INEXACT); From e49cba9b32955d94b5a6469243ae0990679127e5 Mon Sep 17 00:00:00 2001 From: "Brian J. Crowell" Date: Wed, 9 Jan 2013 23:46:45 -0600 Subject: [PATCH 60/61] Handle Sane option strings as Unicode strings The Sane documentation seems to imply that these option strings contain Latin-1 text, not byte data, so we decode it and present it to the user that way. --- Sane/_sane.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/Sane/_sane.c b/Sane/_sane.c index d45e6a346..c94a120a0 100644 --- a/Sane/_sane.c +++ b/Sane/_sane.c @@ -243,7 +243,11 @@ SaneDev_get_options(SaneDevObject *self, PyObject *args) constraint=PyList_New(0); for(j=0; d->constraint.string_list[j]!=NULL; j++) PyList_Append(constraint, - PyBytes_FromString(d->constraint.string_list[j])); +#if PY_MAJOR_VERSION >= 3 + PyUnicode_DecodeLatin1(d->constraint.string_list[j], strlen(d->constraint.string_list[j]), NULL)); +#else + PyString_FromString(d->constraint.string_list[j])); +#endif break; } value=Py_BuildValue("isssiiiiO", i, d->name, d->title, d->desc, @@ -294,7 +298,11 @@ SaneDev_get_option(SaneDevObject *self, PyObject *args) value=Py_BuildValue("d", SANE_UNFIX((*((SANE_Fixed*)v))) ); break; case(SANE_TYPE_STRING): +#if PY_MAJOR_VERSION >= 3 + value=PyUnicode_DecodeLatin1((const char *) v, strlen((const char *) v), NULL); +#else value=Py_BuildValue("s", v); +#endif break; case(SANE_TYPE_BUTTON): case(SANE_TYPE_GROUP): @@ -355,14 +363,33 @@ SaneDev_set_option(SaneDevObject *self, PyObject *args) *( (SANE_Fixed*)v) = SANE_FIX(PyFloat_AsDouble(value)); break; case(SANE_TYPE_STRING): - if (!PyBytes_Check(value)) +#if PY_MAJOR_VERSION >= 3 + if (!PyUnicode_Check(value)) { PyErr_SetString(PyExc_TypeError, "SANE_STRING requires a string"); free(v); return NULL; } - strncpy(v, PyBytes_AsString(value), d->size-1); + { + PyObject *encoded = PyUnicode_AsLatin1String(value); + + if (!encoded) + return NULL; + + strncpy(v, PyBytes_AsString(encoded), d->size-1); + ((char*)v)[d->size-1] = 0; + Py_DECREF(encoded); + } +#else + if (!PyString_Check(value)) + { + PyErr_SetString(PyExc_TypeError, "SANE_STRING requires a string"); + free(v); + return NULL; + } + strncpy(v, PyString_AsString(value), d->size-1); ((char*)v)[d->size-1] = 0; +#endif break; case(SANE_TYPE_BUTTON): case(SANE_TYPE_GROUP): From 9f82f025dc4c03a40259cae0e4d557c5dae10ac9 Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Wed, 9 Jan 2013 21:30:33 -0800 Subject: [PATCH 61/61] Modified map.c to fix some MSVC10 compilation errors. --- map.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/map.c b/map.c index c9ad10914..5bd601ba3 100644 --- a/map.c +++ b/map.c @@ -378,10 +378,10 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args) /* setup file pointers */ if (ystep > 0) for (y = 0; y < ysize; y++) - im->image[y] = view.buf + offset + y * stride; + im->image[y] = (char*)view.buf + offset + y * stride; else for (y = 0; y < ysize; y++) - im->image[ysize-y-1] = view.buf + offset + y * stride; + im->image[ysize-y-1] = (char*)view.buf + offset + y * stride; im->destroy = mapping_destroy_buffer;