From 6f797c58516bb80859abaea1def4827a7540e34d Mon Sep 17 00:00:00 2001 From: Brian Crowell Date: Sat, 20 Oct 2012 16:01:53 -0500 Subject: [PATCH] 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: