diff --git a/.travis.yml b/.travis.yml index 34ffcfe1a..36dae5b7a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,15 @@ language: python -# for python-qt4 -virtualenv: - system_site_packages: true - notifications: irc: "chat.freenode.net#pil" python: + - "pypy" - 2.6 - 2.7 - 3.2 - 3.3 - - "pypy" + - 3.4 install: - "sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-qt4 ghostscript libffi-dev cmake" @@ -29,8 +26,14 @@ script: - coverage erase - python setup.py clean - python setup.py build_ext --inplace - - coverage run --append --include=PIL/* selftest.py - - python Tests/run.py --coverage + + # Don't cover PyPy: it fails intermittently and is x5.8 slower (#640) + - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then python selftest.py; fi + - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then python Tests/run.py; fi + + # Cover the others + - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then coverage run --append --include=PIL/* selftest.py; fi + - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then python Tests/run.py --coverage; fi after_success: - coverage report @@ -38,7 +41,5 @@ after_success: - pip install pep8 pyflakes - pep8 PIL/*.py - pyflakes PIL/*.py - -matrix: - allow_failures: - - python: "pypy" + - pep8 Tests/*.py + - pyflakes Tests/*.py diff --git a/CHANGES.rst b/CHANGES.rst index c774a6ddf..3964b798d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,27 @@ Changelog (Pillow) 2.5.0 (unreleased) ------------------ +- Rename variables not to use built-in function names + [hugovk] + +- Ignore junk JPEG markers + [hugovk] + +- Change default interpolation for Image.thumbnail to Image.ANTIALIAS + [hugovk] + +- Add tests and fixes for saving PDFs + [hugovk] + +- Remove transparency resource after P->RGBA conversion + [hugovk] + +- Clean up preprocessor cruft for Windows + [CounterPillow] + +- Adjust Homebrew freetype detection logic + [jacknagel] + - Added Image.close, context manager support. [wiredfool] diff --git a/PIL/Image.py b/PIL/Image.py index 9424f4bd2..887ceabc1 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -92,8 +92,10 @@ except ImportError: builtins = __builtin__ from PIL import ImageMode -from PIL._binary import i8, o8 -from PIL._util import isPath, isStringType, deferred_error +from PIL._binary import i8 +from PIL._util import isPath +from PIL._util import isStringType +from PIL._util import deferred_error import os import sys @@ -531,7 +533,7 @@ class Image: self.fp.close() except Exception as msg: if Image.DEBUG: - print("Error closing: %s" % msg) + print ("Error closing: %s" % msg) # Instead of simply setting to None, we're setting up a # deferred error that will better explain that the core image @@ -545,8 +547,8 @@ class Image: self.readonly = 0 def _dump(self, file=None, format=None): - import tempfile import os + import tempfile suffix = '' if format: suffix = '.'+format @@ -836,9 +838,8 @@ class Image: t = self.info['transparency'] if isinstance(t, bytes): # Dragons. This can't be represented by a single color - warnings.warn( - 'Palette images with Transparency expressed ' + - ' in bytes should be converted to RGBA images') + warnings.warn('Palette images with Transparency expressed ' + + ' in bytes should be converted to RGBA images') delete_trns = True else: # get the new transparency color. @@ -854,7 +855,10 @@ class Image: # can't just retrieve the palette number, got to do it # after quantization. trns_im = trns_im.convert('RGB') - trns = trns_im.getpixel((0, 0)) + trns = trns_im.getpixel((0,0)) + + elif self.mode == 'P' and mode == 'RGBA': + delete_trns = True if mode == "P" and palette == ADAPTIVE: im = self.im.quantize(colors) @@ -1546,6 +1550,7 @@ class Image: math.cos(angle), math.sin(angle), 0.0, -math.sin(angle), math.cos(angle), 0.0 ] + def transform(x, y, matrix=matrix): (a, b, c, d, e, f) = matrix @@ -1722,7 +1727,7 @@ class Image: """ return 0 - def thumbnail(self, size, resample=NEAREST): + def thumbnail(self, size, resample=ANTIALIAS): """ Make this image into a thumbnail. This method modifies the image to contain a thumbnail version of itself, no larger than @@ -1738,22 +1743,19 @@ class Image: Also note that this function modifies the :py:class:`~PIL.Image.Image` object in place. If you need to use the full resolution image as well, - apply this method to a :py:meth:`~PIL.Image.Image.copy` of the - original image. + apply this method to a :py:meth:`~PIL.Image.Image.copy` of the original + image. :param size: Requested size. :param resample: Optional resampling filter. This can be one of :py:attr:`PIL.Image.NEAREST`, :py:attr:`PIL.Image.BILINEAR`, :py:attr:`PIL.Image.BICUBIC`, or :py:attr:`PIL.Image.ANTIALIAS` (best quality). If omitted, it defaults to - :py:attr:`PIL.Image.NEAREST` (this will be changed to ANTIALIAS in a - future version). + :py:attr:`PIL.Image.ANTIALIAS`. (was :py:attr:`PIL.Image.NEAREST` + prior to version 2.5.0) :returns: None """ - # FIXME: the default resampling filter will be changed - # to ANTIALIAS in future versions - # preserve aspect ratio x, y = self.size if x > size[0]: diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index da52006ca..7b40d5d4f 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -34,7 +34,8 @@ __version__ = "0.6" -import array, struct +import array +import struct from PIL import Image, ImageFile, _binary from PIL.JpegPresets import presets from PIL._util import isStringType @@ -44,6 +45,7 @@ o8 = _binary.o8 i16 = _binary.i16be i32 = _binary.i32be + # # Parser @@ -51,6 +53,7 @@ def Skip(self, marker): n = i16(self.fp.read(2))-2 ImageFile._safe_read(self.fp, n) + def APP(self, marker): # # Application marker. Store these in the APP dictionary. @@ -59,14 +62,14 @@ def APP(self, marker): n = i16(self.fp.read(2))-2 s = ImageFile._safe_read(self.fp, n) - app = "APP%d" % (marker&15) + app = "APP%d" % (marker & 15) - self.app[app] = s # compatibility + self.app[app] = s # compatibility self.applist.append((app, s)) if marker == 0xFFE0 and s[:4] == b"JFIF": # extract JFIF information - self.info["jfif"] = version = i16(s, 5) # version + self.info["jfif"] = version = i16(s, 5) # version self.info["jfif_version"] = divmod(version, 256) # extract JFIF properties try: @@ -81,10 +84,10 @@ def APP(self, marker): self.info["jfif_density"] = jfif_density elif marker == 0xFFE1 and s[:5] == b"Exif\0": # extract Exif information (incomplete) - self.info["exif"] = s # FIXME: value will change + self.info["exif"] = s # FIXME: value will change elif marker == 0xFFE2 and s[:5] == b"FPXR\0": # extract FlashPix information (incomplete) - self.info["flashpix"] = s # FIXME: value will change + self.info["flashpix"] = s # FIXME: value will change 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 @@ -108,16 +111,17 @@ def APP(self, marker): else: self.info["adobe_transform"] = adobe_transform + def COM(self, marker): # # Comment marker. Store these in the APP dictionary. - n = i16(self.fp.read(2))-2 s = ImageFile._safe_read(self.fp, n) - self.app["COM"] = s # compatibility + self.app["COM"] = s # compatibility self.applist.append(("COM", s)) + def SOF(self, marker): # # Start of frame marker. Defines the size and mode of the @@ -149,21 +153,22 @@ def SOF(self, marker): if self.icclist: # fixup icc profile - self.icclist.sort() # sort by sequence number + self.icclist.sort() # sort by sequence number if i8(self.icclist[0][13]) == len(self.icclist): profile = [] for p in self.icclist: profile.append(p[14:]) icc_profile = b"".join(profile) else: - icc_profile = None # wrong number of fragments + icc_profile = None # wrong number of fragments self.info["icc_profile"] = icc_profile self.icclist = None for i in range(6, len(s), 3): t = s[i:i+3] # 4-tuples: id, vsamp, hsamp, qtable - self.layer.append((t[0], i8(t[1])//16, i8(t[1])&15, i8(t[2]))) + self.layer.append((t[0], i8(t[1])//16, i8(t[1]) & 15, i8(t[2]))) + def DQT(self, marker): # @@ -181,10 +186,10 @@ def DQT(self, marker): raise SyntaxError("bad quantization table marker") v = i8(s[0]) if v//16 == 0: - self.quantization[v&15] = array.array("b", s[1:65]) + self.quantization[v & 15] = array.array("b", s[1:65]) s = s[65:] else: - return # FIXME: add code to read 16-bit tables! + return # FIXME: add code to read 16-bit tables! # raise SyntaxError, "bad quantization table element size" @@ -261,6 +266,7 @@ MARKER = { def _accept(prefix): return prefix[0:1] == b"\377" + ## # Image plugin for JPEG and JFIF images. @@ -284,32 +290,37 @@ class JpegImageFile(ImageFile.ImageFile): self.huffman_dc = {} self.huffman_ac = {} self.quantization = {} - self.app = {} # compatibility + self.app = {} # compatibility self.applist = [] self.icclist = [] while True: - s = s + self.fp.read(1) - - i = i16(s) + i = i8(s) + if i == 0xFF: + s = s + self.fp.read(1) + i = i16(s) + else: + # Skip non-0xFF junk + s = b"\xff" + continue if i in MARKER: name, description, handler = MARKER[i] # print hex(i), name, description if handler is not None: handler(self, i) - if i == 0xFFDA: # start of scan + if i == 0xFFDA: # start of scan rawmode = self.mode if self.mode == "CMYK": - rawmode = "CMYK;I" # assume adobe conventions - self.tile = [("jpeg", (0,0) + self.size, 0, (rawmode, ""))] + rawmode = "CMYK;I" # assume adobe conventions + self.tile = [("jpeg", (0, 0) + self.size, 0, (rawmode, ""))] # self.__offset = self.fp.tell() break s = self.fp.read(1) - elif i == 0 or i == 65535: + elif i == 0 or i == 0xFFFF: # padded marker or junk; move on - s = "\xff" + s = b"\xff" else: raise SyntaxError("no marker found") @@ -343,7 +354,8 @@ class JpegImageFile(ImageFile.ImageFile): # ALTERNATIVE: handle JPEGs via the IJG command line utilities - import tempfile, os + import tempfile + import os f, path = tempfile.mkstemp() os.close(f) if os.path.exists(self.filename): @@ -354,8 +366,10 @@ class JpegImageFile(ImageFile.ImageFile): try: self.im = Image.core.open_ppm(path) finally: - try: os.unlink(path) - except: pass + try: + os.unlink(path) + except: + pass self.mode = self.im.mode self.size = self.im.size @@ -372,6 +386,7 @@ def _getexif(self): # version. from PIL import TiffImagePlugin import io + def fixup(value): if len(value) == 1: return value[0] @@ -422,7 +437,7 @@ RAWMODE = { "RGB": "RGB", "RGBA": "RGB", "RGBX": "RGB", - "CMYK": "CMYK;I", # assume adobe conventions + "CMYK": "CMYK;I", # assume adobe conventions "YCbCr": "YCbCr", } @@ -441,16 +456,19 @@ samplings = { (2, 2, 1, 1, 1, 1): 2, } + def convert_dict_qtables(qtables): qtables = [qtables[key] for key in range(len(qtables)) if key in qtables] for idx, table in enumerate(qtables): qtables[idx] = [table[i] for i in zigzag_index] return qtables + def get_sampling(im): sampling = im.layer[0][1:3] + im.layer[1][1:3] + im.layer[2][1:3] return samplings.get(sampling, -1) + def _save(im, fp, filename): try: @@ -563,12 +581,11 @@ def _save(im, fp, filename): info.get("exif", b"") ) - - # if we optimize, libjpeg needs a buffer big enough to hold the whole image in a shot. - # Guessing on the size, at im.size bytes. (raw pizel size is channels*size, this - # is a value that's been used in a django patch. + # if we optimize, libjpeg needs a buffer big enough to hold the whole image + # in a shot. Guessing on the size, at im.size bytes. (raw pizel size is + # channels*size, this is a value that's been used in a django patch. # https://github.com/jdriscoll/django-imagekit/issues/50 - bufsize=0 + bufsize = 0 if "optimize" in info or "progressive" in info or "progression" in info: if quality >= 95: bufsize = 2 * im.size[0] * im.size[1] @@ -577,17 +594,20 @@ def _save(im, fp, filename): # The exif info needs to be written as one block, + APP1, + one spare byte. # Ensure that our buffer is big enough - bufsize = max(ImageFile.MAXBLOCK, bufsize, len(info.get("exif",b"")) + 5 ) + bufsize = max(ImageFile.MAXBLOCK, bufsize, len(info.get("exif", b"")) + 5) + + ImageFile._save(im, fp, [("jpeg", (0, 0)+im.size, 0, rawmode)], bufsize) - ImageFile._save(im, fp, [("jpeg", (0,0)+im.size, 0, rawmode)], bufsize) def _save_cjpeg(im, fp, filename): # ALTERNATIVE: handle JPEGs via the IJG command line utilities. import os file = im._dump() os.system("cjpeg %s >%s" % (file, filename)) - try: os.unlink(file) - except: pass + try: + os.unlink(file) + except: + pass # -------------------------------------------------------------------q- # Registry stuff diff --git a/PIL/PdfImagePlugin.py b/PIL/PdfImagePlugin.py index 725f22ecf..fcc841438 100644 --- a/PIL/PdfImagePlugin.py +++ b/PIL/PdfImagePlugin.py @@ -46,9 +46,11 @@ def _obj(fp, obj, **dict): fp.write("/%s %s\n" % (k, v)) fp.write(">>\n") + def _endobj(fp): fp.write("endobj\n") + ## # (Internal) Image save plugin for the PDF format. @@ -59,13 +61,15 @@ def _save(im, fp, filename): # make sure image data is available im.load() - xref = [0]*(5+1) # placeholders + xref = [0]*(5+1) # placeholders class TextWriter: def __init__(self, fp): self.fp = fp + def __getattr__(self, name): return getattr(self.fp, name) + def write(self, value): self.fp.write(value.encode('latin-1')) @@ -89,13 +93,13 @@ def _save(im, fp, filename): if im.mode == "1": filter = "/ASCIIHexDecode" colorspace = "/DeviceGray" - procset = "/ImageB" # grayscale + procset = "/ImageB" # grayscale bits = 1 elif im.mode == "L": filter = "/DCTDecode" # params = "<< /Predictor 15 /Columns %d >>" % (width-2) colorspace = "/DeviceGray" - procset = "/ImageB" # grayscale + procset = "/ImageB" # grayscale elif im.mode == "P": filter = "/ASCIIHexDecode" colorspace = "[ /Indexed /DeviceRGB 255 <" @@ -105,16 +109,16 @@ def _save(im, fp, filename): g = i8(palette[i*3+1]) b = i8(palette[i*3+2]) colorspace = colorspace + "%02x%02x%02x " % (r, g, b) - colorspace = colorspace + b"> ]" - procset = "/ImageI" # indexed color + colorspace = colorspace + "> ]" + procset = "/ImageI" # indexed color elif im.mode == "RGB": filter = "/DCTDecode" colorspace = "/DeviceRGB" - procset = "/ImageC" # color images + procset = "/ImageC" # color images elif im.mode == "CMYK": filter = "/DCTDecode" colorspace = "/DeviceCMYK" - procset = "/ImageC" # color images + procset = "/ImageC" # color images else: raise ValueError("cannot save mode %s" % im.mode) @@ -122,17 +126,21 @@ def _save(im, fp, filename): # catalogue xref[1] = fp.tell() - _obj(fp, 1, Type = "/Catalog", - Pages = "2 0 R") + _obj( + fp, 1, + Type="/Catalog", + Pages="2 0 R") _endobj(fp) # # pages xref[2] = fp.tell() - _obj(fp, 2, Type = "/Pages", - Count = 1, - Kids = "[4 0 R]") + _obj( + fp, 2, + Type="/Pages", + Count=1, + Kids="[4 0 R]") _endobj(fp) # @@ -144,29 +152,31 @@ def _save(im, fp, filename): if bits == 1: # FIXME: the hex encoder doesn't support packed 1-bit # images; do things the hard way... - data = im.tostring("raw", "1") + data = im.tobytes("raw", "1") im = Image.new("L", (len(data), 1), None) im.putdata(data) - ImageFile._save(im, op, [("hex", (0,0)+im.size, 0, im.mode)]) + ImageFile._save(im, op, [("hex", (0, 0)+im.size, 0, im.mode)]) elif filter == "/DCTDecode": Image.SAVE["JPEG"](im, op, filename) elif filter == "/FlateDecode": - ImageFile._save(im, op, [("zip", (0,0)+im.size, 0, im.mode)]) + ImageFile._save(im, op, [("zip", (0, 0)+im.size, 0, im.mode)]) elif filter == "/RunLengthDecode": - ImageFile._save(im, op, [("packbits", (0,0)+im.size, 0, im.mode)]) + ImageFile._save(im, op, [("packbits", (0, 0)+im.size, 0, im.mode)]) else: raise ValueError("unsupported PDF filter (%s)" % filter) xref[3] = fp.tell() - _obj(fp, 3, Type = "/XObject", - Subtype = "/Image", - Width = width, # * 72.0 / resolution, - Height = height, # * 72.0 / resolution, - Length = len(op.getvalue()), - Filter = filter, - BitsPerComponent = bits, - DecodeParams = params, - ColorSpace = colorspace) + _obj( + fp, 3, + Type="/XObject", + Subtype="/Image", + Width=width, # * 72.0 / resolution, + Height=height, # * 72.0 / resolution, + Length=len(op.getvalue()), + Filter=filter, + BitsPerComponent=bits, + DecodeParams=params, + ColorSpace=colorspace) fp.write("stream\n") fp.fp.write(op.getvalue()) @@ -179,11 +189,14 @@ def _save(im, fp, filename): xref[4] = fp.tell() _obj(fp, 4) - fp.write("<<\n/Type /Page\n/Parent 2 0 R\n"\ - "/Resources <<\n/ProcSet [ /PDF %s ]\n"\ - "/XObject << /image 3 0 R >>\n>>\n"\ - "/MediaBox [ 0 0 %d %d ]\n/Contents 5 0 R\n>>\n" %\ - (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))) _endobj(fp) # @@ -191,10 +204,13 @@ def _save(im, fp, filename): op = TextWriter(io.BytesIO()) - op.write("q %d 0 0 %d 0 0 cm /image Do Q\n" % (int(width * 72.0 / resolution), int(height * 72.0 / resolution))) + op.write( + "q %d 0 0 %d 0 0 cm /image Do Q\n" % ( + int(width * 72.0 / resolution), + int(height * 72.0 / resolution))) xref[5] = fp.tell() - _obj(fp, 5, Length = len(op.fp.getvalue())) + _obj(fp, 5, Length=len(op.fp.getvalue())) fp.write("stream\n") fp.fp.write(op.fp.getvalue()) diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index 2bdf74608..e794ef702 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -89,33 +89,33 @@ class ChunkStream: "Fetch a new chunk. Returns header information." if self.queue: - cid, pos, len = self.queue[-1] + cid, pos, length = self.queue[-1] del self.queue[-1] self.fp.seek(pos) else: s = self.fp.read(8) cid = s[4:] pos = self.fp.tell() - len = i32(s) + length = i32(s) if not is_cid(cid): raise SyntaxError("broken PNG file (chunk %s)" % repr(cid)) - return cid, pos, len + return cid, pos, length def close(self): self.queue = self.crc = self.fp = None - def push(self, cid, pos, len): + def push(self, cid, pos, length): - self.queue.append((cid, pos, len)) + self.queue.append((cid, pos, length)) - def call(self, cid, pos, len): + def call(self, cid, pos, length): "Call the appropriate chunk handler" if Image.DEBUG: - print("STREAM", cid, pos, len) - return getattr(self, "chunk_" + cid.decode('ascii'))(pos, len) + print("STREAM", cid, pos, length) + return getattr(self, "chunk_" + cid.decode('ascii'))(pos, length) def crc(self, cid, data): "Read and verify checksum" @@ -139,10 +139,10 @@ class ChunkStream: cids = [] while True: - cid, pos, len = self.read() + cid, pos, length = self.read() if cid == endchunk: break - self.crc(cid, ImageFile._safe_read(self.fp, len)) + self.crc(cid, ImageFile._safe_read(self.fp, length)) cids.append(cid) return cids @@ -190,10 +190,10 @@ class PngStream(ChunkStream): self.im_tile = None self.im_palette = None - def chunk_iCCP(self, pos, len): + def chunk_iCCP(self, pos, length): # ICC profile - s = ImageFile._safe_read(self.fp, len) + s = ImageFile._safe_read(self.fp, length) # according to PNG spec, the iCCP chunk contains: # Profile name 1-79 bytes (character string) # Null separator 1 byte (null character) @@ -213,10 +213,10 @@ class PngStream(ChunkStream): self.im_info["icc_profile"] = icc_profile return s - def chunk_IHDR(self, pos, len): + def chunk_IHDR(self, pos, length): # image header - s = ImageFile._safe_read(self.fp, len) + s = ImageFile._safe_read(self.fp, length) self.im_size = i32(s), i32(s[4:]) try: self.im_mode, self.im_rawmode = _MODES[(i8(s[8]), i8(s[9]))] @@ -228,30 +228,30 @@ class PngStream(ChunkStream): raise SyntaxError("unknown filter category") return s - def chunk_IDAT(self, pos, len): + def chunk_IDAT(self, pos, length): # image data self.im_tile = [("zip", (0,0)+self.im_size, pos, self.im_rawmode)] - self.im_idat = len + self.im_idat = length raise EOFError - def chunk_IEND(self, pos, len): + def chunk_IEND(self, pos, length): # end of PNG image raise EOFError - def chunk_PLTE(self, pos, len): + def chunk_PLTE(self, pos, length): # palette - s = ImageFile._safe_read(self.fp, len) + s = ImageFile._safe_read(self.fp, length) if self.im_mode == "P": self.im_palette = "RGB", s return s - def chunk_tRNS(self, pos, len): + def chunk_tRNS(self, pos, length): # transparency - s = ImageFile._safe_read(self.fp, len) + s = ImageFile._safe_read(self.fp, length) if self.im_mode == "P": if _simple_palette.match(s): i = s.find(b"\0") @@ -265,17 +265,17 @@ class PngStream(ChunkStream): self.im_info["transparency"] = i16(s), i16(s[2:]), i16(s[4:]) return s - def chunk_gAMA(self, pos, len): + def chunk_gAMA(self, pos, length): # gamma setting - s = ImageFile._safe_read(self.fp, len) + s = ImageFile._safe_read(self.fp, length) self.im_info["gamma"] = i32(s) / 100000.0 return s - def chunk_pHYs(self, pos, len): + def chunk_pHYs(self, pos, length): # pixels per unit - s = ImageFile._safe_read(self.fp, len) + s = ImageFile._safe_read(self.fp, length) px, py = i32(s), i32(s[4:]) unit = i8(s[8]) if unit == 1: # meter @@ -285,10 +285,10 @@ class PngStream(ChunkStream): self.im_info["aspect"] = px, py return s - def chunk_tEXt(self, pos, len): + def chunk_tEXt(self, pos, length): # text - s = ImageFile._safe_read(self.fp, len) + s = ImageFile._safe_read(self.fp, length) try: k, v = s.split(b"\0", 1) except ValueError: @@ -301,10 +301,10 @@ class PngStream(ChunkStream): self.im_info[k] = self.im_text[k] = v return s - def chunk_zTXt(self, pos, len): + def chunk_zTXt(self, pos, length): # compressed text - s = ImageFile._safe_read(self.fp, len) + s = ImageFile._safe_read(self.fp, length) try: k, v = s.split(b"\0", 1) except ValueError: @@ -358,16 +358,16 @@ class PngImageFile(ImageFile.ImageFile): # # get next chunk - cid, pos, len = self.png.read() + cid, pos, length = self.png.read() try: - s = self.png.call(cid, pos, len) + s = self.png.call(cid, pos, length) except EOFError: break except AttributeError: if Image.DEBUG: - print(cid, pos, len, "(unknown)") - s = ImageFile._safe_read(self.fp, len) + print(cid, pos, length, "(unknown)") + s = ImageFile._safe_read(self.fp, length) self.png.crc(cid, s) @@ -388,7 +388,7 @@ class PngImageFile(ImageFile.ImageFile): rawmode, data = self.png.im_palette self.palette = ImagePalette.raw(rawmode, data) - self.__idat = len # used by load_read() + self.__idat = length # used by load_read() def verify(self): @@ -413,7 +413,7 @@ class PngImageFile(ImageFile.ImageFile): ImageFile.ImageFile.load_prepare(self) - def load_read(self, bytes): + def load_read(self, read_bytes): "internal: read more image data" while self.__idat == 0: @@ -421,23 +421,23 @@ class PngImageFile(ImageFile.ImageFile): self.fp.read(4) # CRC - cid, pos, len = self.png.read() + cid, pos, length = self.png.read() if cid not in [b"IDAT", b"DDAT"]: - self.png.push(cid, pos, len) + self.png.push(cid, pos, length) return b"" - self.__idat = len # empty chunks are allowed + self.__idat = length # empty chunks are allowed # read more data from this chunk - if bytes <= 0: - bytes = self.__idat + if read_bytes <= 0: + read_bytes = self.__idat else: - bytes = min(bytes, self.__idat) + read_bytes = min(read_bytes, self.__idat) - self.__idat = self.__idat - bytes + self.__idat = self.__idat - read_bytes - return self.fp.read(bytes) + return self.fp.read(read_bytes) def load_end(self): @@ -560,7 +560,7 @@ def _save(im, fp, filename, chunk=putchunk, check=0): chunk(fp, b"PLTE", palette_bytes) transparency = im.encoderinfo.get('transparency',im.info.get('transparency', None)) - + if transparency or transparency == 0: if im.mode == "P": # limit to actual palette size @@ -580,7 +580,7 @@ def _save(im, fp, filename, chunk=putchunk, check=0): else: if "transparency" in im.encoderinfo: # don't bother with transparency if it's an RGBA - # and it's in the info dict. It's probably just stale. + # and it's in the info dict. It's probably just stale. raise IOError("cannot use transparency for this mode") else: if im.mode == "P" and im.im.getpalettemode() == "RGBA": diff --git a/Tests/images/junk_jpeg_header.jpg b/Tests/images/junk_jpeg_header.jpg new file mode 100644 index 000000000..564eb3199 Binary files /dev/null and b/Tests/images/junk_jpeg_header.jpg differ diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 095cad359..4871c3fbf 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -12,17 +12,19 @@ if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs: test_file = "Images/lena.jpg" + def roundtrip(im, **options): out = BytesIO() im.save(out, "JPEG", **options) bytes = out.tell() out.seek(0) im = Image.open(out) - im.bytes = bytes # for testing only + im.bytes = bytes # for testing only return im # -------------------------------------------------------------------- + def test_sanity(): # internal version number @@ -34,6 +36,7 @@ def test_sanity(): assert_equal(im.size, (128, 128)) assert_equal(im.format, "JPEG") + # -------------------------------------------------------------------- def test_app(): @@ -44,6 +47,7 @@ def test_app(): assert_equal(im.applist[1], ("COM", b"Python Imaging Library")) assert_equal(len(im.applist), 2) + def test_cmyk(): # Test CMYK handling. Thanks to Tim and Charlie for test data, # Michael for getting me to look one more time. @@ -62,6 +66,7 @@ def test_cmyk(): c, m, y, k = [x / 255.0 for x in im.getpixel((im.size[0]-1, im.size[1]-1))] assert_true(k > 0.9) + def test_dpi(): def test(xdpi, ydpi=None): im = Image.open(test_file) @@ -70,7 +75,8 @@ def test_dpi(): assert_equal(test(72), (72, 72)) assert_equal(test(300), (300, 300)) assert_equal(test(100, 200), (100, 200)) - assert_equal(test(0), None) # square pixels + assert_equal(test(0), None) # square pixels + def test_icc(): # Test ICC support @@ -89,6 +95,7 @@ def test_icc(): assert_false(im1.info.get("icc_profile")) assert_true(im2.info.get("icc_profile")) + def test_icc_big(): # Make sure that the "extra" support handles large blocks def test(n): @@ -96,16 +103,20 @@ def test_icc_big(): # using a 4-byte test code should allow us to detect out of # order issues. icc_profile = (b"Test"*int(n/4+1))[:n] - assert len(icc_profile) == n # sanity + assert len(icc_profile) == n # sanity im1 = roundtrip(lena(), icc_profile=icc_profile) assert_equal(im1.info.get("icc_profile"), icc_profile or None) - test(0); test(1) - test(3); test(4); test(5) - test(65533-14) # full JPEG marker block - test(65533-14+1) # full block plus one byte - test(ImageFile.MAXBLOCK) # full buffer block - test(ImageFile.MAXBLOCK+1) # full buffer block plus one byte - test(ImageFile.MAXBLOCK*4+3) # large block + test(0) + test(1) + test(3) + test(4) + test(5) + test(65533-14) # full JPEG marker block + test(65533-14+1) # full block plus one byte + test(ImageFile.MAXBLOCK) # full buffer block + test(ImageFile.MAXBLOCK+1) # full buffer block plus one byte + test(ImageFile.MAXBLOCK*4+3) # large block + def test_optimize(): im1 = roundtrip(lena()) @@ -113,25 +124,29 @@ def test_optimize(): assert_image_equal(im1, im2) assert_true(im1.bytes >= im2.bytes) + def test_optimize_large_buffer(): - #https://github.com/python-imaging/Pillow/issues/148 + # https://github.com/python-imaging/Pillow/issues/148 f = tempfile('temp.jpg') # this requires ~ 1.5x Image.MAXBLOCK - im = Image.new("RGB", (4096,4096), 0xff3333) + im = Image.new("RGB", (4096, 4096), 0xff3333) im.save(f, format="JPEG", optimize=True) + def test_progressive(): im1 = roundtrip(lena()) im2 = roundtrip(lena(), progressive=True) assert_image_equal(im1, im2) assert_true(im1.bytes >= im2.bytes) + def test_progressive_large_buffer(): f = tempfile('temp.jpg') # this requires ~ 1.5x Image.MAXBLOCK - im = Image.new("RGB", (4096,4096), 0xff3333) + im = Image.new("RGB", (4096, 4096), 0xff3333) im.save(f, format="JPEG", progressive=True) + def test_progressive_large_buffer_highest_quality(): f = tempfile('temp.jpg') if py3: @@ -142,16 +157,18 @@ def test_progressive_large_buffer_highest_quality(): # this requires more bytes than pixels in the image im.save(f, format="JPEG", progressive=True, quality=100) + def test_large_exif(): - #https://github.com/python-imaging/Pillow/issues/148 + # https://github.com/python-imaging/Pillow/issues/148 f = tempfile('temp.jpg') im = lena() - im.save(f,'JPEG', quality=90, exif=b"1"*65532) + im.save(f, 'JPEG', quality=90, exif=b"1"*65532) + def test_progressive_compat(): im1 = roundtrip(lena()) im2 = roundtrip(lena(), progressive=1) - im3 = roundtrip(lena(), progression=1) # compatibility + im3 = roundtrip(lena(), progression=1) # compatibility assert_image_equal(im1, im2) assert_image_equal(im1, im3) assert_false(im1.info.get("progressive")) @@ -161,31 +178,34 @@ def test_progressive_compat(): assert_true(im3.info.get("progressive")) assert_true(im3.info.get("progression")) + def test_quality(): im1 = roundtrip(lena()) im2 = roundtrip(lena(), quality=50) assert_image(im1, im2.mode, im2.size) assert_true(im1.bytes >= im2.bytes) + def test_smooth(): im1 = roundtrip(lena()) im2 = roundtrip(lena(), smooth=100) assert_image(im1, im2.mode, im2.size) + def test_subsampling(): def getsampling(im): layer = im.layer return layer[0][1:3] + layer[1][1:3] + layer[2][1:3] # experimental API - im = roundtrip(lena(), subsampling=-1) # default + im = roundtrip(lena(), subsampling=-1) # default assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1)) - im = roundtrip(lena(), subsampling=0) # 4:4:4 + im = roundtrip(lena(), subsampling=0) # 4:4:4 assert_equal(getsampling(im), (1, 1, 1, 1, 1, 1)) - im = roundtrip(lena(), subsampling=1) # 4:2:2 + im = roundtrip(lena(), subsampling=1) # 4:2:2 assert_equal(getsampling(im), (2, 1, 1, 1, 1, 1)) - im = roundtrip(lena(), subsampling=2) # 4:1:1 + im = roundtrip(lena(), subsampling=2) # 4:1:1 assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1)) - im = roundtrip(lena(), subsampling=3) # default (undefined) + im = roundtrip(lena(), subsampling=3) # default (undefined) assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1)) im = roundtrip(lena(), subsampling="4:4:4") @@ -197,6 +217,7 @@ def test_subsampling(): assert_exception(TypeError, lambda: roundtrip(lena(), subsampling="1:1:1")) + def test_exif(): im = Image.open("Tests/images/pil_sample_rgb.jpg") info = im._getexif() @@ -207,3 +228,11 @@ def test_quality_keep(): im = Image.open("Images/lena.jpg") f = tempfile('temp.jpg') assert_no_exception(lambda: im.save(f, quality='keep')) + + +def test_junk_jpeg_header(): + # https://github.com/python-imaging/Pillow/issues/630 + filename = "Tests/images/junk_jpeg_header.jpg" + assert_no_exception(lambda: Image.open(filename)) + +# End of file diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py new file mode 100644 index 000000000..e99f22db1 --- /dev/null +++ b/Tests/test_file_pdf.py @@ -0,0 +1,58 @@ +from tester import * +import os.path + + +def helper_save_as_pdf(mode): + # Arrange + im = lena(mode) + outfile = tempfile("temp_" + mode + ".pdf") + + # Act + im.save(outfile) + + # Assert + assert_true(os.path.isfile(outfile)) + assert_greater(os.path.getsize(outfile), 0) + + +def test_monochrome(): + # Arrange + mode = "1" + + # Act / Assert + helper_save_as_pdf(mode) + + +def test_greyscale(): + # Arrange + mode = "L" + + # Act / Assert + helper_save_as_pdf(mode) + + +def test_rgb(): + # Arrange + mode = "RGB" + + # Act / Assert + helper_save_as_pdf(mode) + + +def test_p_mode(): + # Arrange + mode = "P" + + # Act / Assert + helper_save_as_pdf(mode) + + +def test_cmyk_mode(): + # Arrange + mode = "CMYK" + + # Act / Assert + helper_save_as_pdf(mode) + + +# End of file diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index 4d40e43b2..6a39b0e3b 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -2,6 +2,7 @@ from tester import * from PIL import Image + def test_sanity(): def convert(im, mode): @@ -16,6 +17,7 @@ def test_sanity(): for mode in modes: yield_test(convert, im, mode) + def test_default(): im = lena("P") @@ -26,26 +28,29 @@ def test_default(): assert_image(im, "RGB", im.size) - # ref https://github.com/python-imaging/Pillow/issues/274 def _test_float_conversion(im): - orig = im.getpixel((5,5)) - converted = im.convert('F').getpixel((5,5)) + orig = im.getpixel((5, 5)) + converted = im.convert('F').getpixel((5, 5)) assert_equal(orig, converted) + def test_8bit(): im = Image.open('Images/lena.jpg') _test_float_conversion(im.convert('L')) + def test_16bit(): im = Image.open('Tests/images/16bit.cropped.tif') _test_float_conversion(im) + def test_16bit_workaround(): im = Image.open('Tests/images/16bit.cropped.tif') _test_float_conversion(im.convert('I')) - + + def test_rgba_p(): im = lena('RGBA') im.putalpha(lena('L')) @@ -54,30 +59,45 @@ def test_rgba_p(): comparable = converted.convert('RGBA') assert_image_similar(im, comparable, 20) - -def test_trns_p(): + + +def test_trns_p(): im = lena('P') - im.info['transparency']=0 + im.info['transparency'] = 0 f = tempfile('temp.png') l = im.convert('L') - assert_equal(l.info['transparency'], 0) # undone + assert_equal(l.info['transparency'], 0) # undone assert_no_exception(lambda: l.save(f)) - rgb = im.convert('RGB') - assert_equal(rgb.info['transparency'], (0,0,0)) # undone + assert_equal(rgb.info['transparency'], (0, 0, 0)) # undone assert_no_exception(lambda: rgb.save(f)) - + + +# ref https://github.com/python-imaging/Pillow/issues/664 + +def test_trns_p_rgba(): + # Arrange + im = lena('P') + im.info['transparency'] = 128 + + # Act + rgba = im.convert('RGBA') + + # Assert + assert_false('transparency' in rgba.info) + + def test_trns_l(): im = lena('L') im.info['transparency'] = 128 f = tempfile('temp.png') - + rgb = im.convert('RGB') - assert_equal(rgb.info['transparency'], (128,128,128)) # undone + assert_equal(rgb.info['transparency'], (128, 128, 128)) # undone assert_no_exception(lambda: rgb.save(f)) p = im.convert('P') @@ -85,28 +105,26 @@ def test_trns_l(): assert_no_exception(lambda: p.save(f)) p = assert_warning(UserWarning, - lambda: im.convert('P', palette = Image.ADAPTIVE)) + lambda: im.convert('P', palette=Image.ADAPTIVE)) assert_false('transparency' in p.info) assert_no_exception(lambda: p.save(f)) - + def test_trns_RGB(): im = lena('RGB') - im.info['transparency'] = im.getpixel((0,0)) + im.info['transparency'] = im.getpixel((0, 0)) f = tempfile('temp.png') - + l = im.convert('L') - assert_equal(l.info['transparency'], l.getpixel((0,0))) # undone + assert_equal(l.info['transparency'], l.getpixel((0, 0))) # undone assert_no_exception(lambda: l.save(f)) p = im.convert('P') assert_true('transparency' in p.info) assert_no_exception(lambda: p.save(f)) - + p = assert_warning(UserWarning, - lambda: im.convert('P', palette = Image.ADAPTIVE)) + lambda: im.convert('P', palette=Image.ADAPTIVE)) assert_false('transparency' in p.info) assert_no_exception(lambda: p.save(f)) - - diff --git a/Tests/tester.py b/Tests/tester.py index c1e8404d7..4476aced1 100644 --- a/Tests/tester.py +++ b/Tests/tester.py @@ -351,7 +351,10 @@ def _setup(): import sys if "--coverage" in sys.argv: - import coverage + # Temporary: ignore PendingDeprecationWarning from Coverage (Py3.4) + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + import coverage cov = coverage.coverage(auto_data=True, include="PIL/*") cov.start() diff --git a/_imaging.c b/_imaging.c index c47868b81..4f14b7cbd 100644 --- a/_imaging.c +++ b/_imaging.c @@ -3350,7 +3350,7 @@ extern PyObject* PyImaging_ZipEncoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args); /* Display support etc (in display.c) */ -#ifdef WIN32 +#ifdef _WIN32 extern PyObject* PyImaging_CreateWindowWin32(PyObject* self, PyObject* args); extern PyObject* PyImaging_DisplayWin32(PyObject* self, PyObject* args); extern PyObject* PyImaging_DisplayModeWin32(PyObject* self, PyObject* args); @@ -3423,14 +3423,14 @@ static PyMethodDef functions[] = { /* Memory mapping */ #ifdef WITH_MAPPING -#ifdef WIN32 +#ifdef _WIN32 {"map", (PyCFunction)PyImaging_Mapper, 1}, #endif {"map_buffer", (PyCFunction)PyImaging_MapBuffer, 1}, #endif /* Display support */ -#ifdef WIN32 +#ifdef _WIN32 {"display", (PyCFunction)PyImaging_DisplayWin32, 1}, {"display_mode", (PyCFunction)PyImaging_DisplayModeWin32, 1}, {"grabscreen", (PyCFunction)PyImaging_GrabScreenWin32, 1}, diff --git a/_imagingcms.c b/_imagingcms.c index 99da647ae..df26e1a2d 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -28,12 +28,6 @@ http://www.cazabon.com\n\ #include "Imaging.h" #include "py3.h" -#ifdef WIN32 -#include -#include -#include -#endif - #define PYCMSVERSION "1.0.0 pil" /* version history */ @@ -450,7 +444,7 @@ cms_profile_is_intent_supported(CmsProfileObject *self, PyObject *args) return PyInt_FromLong(result != 0); } -#ifdef WIN32 +#ifdef _WIN32 static PyObject * cms_get_display_profile_win32(PyObject* self, PyObject* args) { @@ -496,7 +490,7 @@ static PyMethodDef pyCMSdll_methods[] = { {"createProfile", createProfile, 1}, /* platform specific tools */ -#ifdef WIN32 +#ifdef _WIN32 {"get_display_profile_win32", cms_get_display_profile_win32, 1}, #endif diff --git a/decode.c b/decode.c index 77038cc2c..33367dfe3 100644 --- a/decode.c +++ b/decode.c @@ -433,9 +433,6 @@ PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args) #include "TiffDecode.h" #include -#ifdef __WIN32__ -#define strcasecmp(s1, s2) stricmp(s1, s2) -#endif PyObject* PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args) diff --git a/display.c b/display.c index 9cc1ae3ad..e10d72c0d 100644 --- a/display.c +++ b/display.c @@ -31,7 +31,7 @@ /* -------------------------------------------------------------------- */ /* Windows DIB support */ -#ifdef WIN32 +#ifdef _WIN32 #include "ImDib.h" @@ -864,4 +864,4 @@ error: return buffer; } -#endif /* WIN32 */ +#endif /* _WIN32 */ diff --git a/encode.c b/encode.c index d403ab072..b14942376 100644 --- a/encode.c +++ b/encode.c @@ -670,9 +670,6 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args) #include "TiffDecode.h" #include -#ifdef __WIN32__ -#define strcasecmp(s1, s2) stricmp(s1, s2) -#endif PyObject* PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) diff --git a/libImaging/Dib.c b/libImaging/Dib.c index 6db8c076e..8e138bd6b 100644 --- a/libImaging/Dib.c +++ b/libImaging/Dib.c @@ -22,7 +22,7 @@ #include "Imaging.h" -#ifdef WIN32 +#ifdef _WIN32 #include "ImDib.h" @@ -308,4 +308,4 @@ ImagingDeleteDIB(ImagingDIB dib) free(dib->info); } -#endif /* WIN32 */ +#endif /* _WIN32 */ diff --git a/libImaging/Gif.h b/libImaging/Gif.h index 85d428b0d..2cb95efd2 100644 --- a/libImaging/Gif.h +++ b/libImaging/Gif.h @@ -59,7 +59,7 @@ typedef struct { unsigned char buffer[GIFTABLE]; /* Symbol table */ - unsigned INT16 link[GIFTABLE]; + UINT16 link[GIFTABLE]; unsigned char data[GIFTABLE]; int next; diff --git a/libImaging/ImDib.h b/libImaging/ImDib.h index 23c660488..55ecb35aa 100644 --- a/libImaging/ImDib.h +++ b/libImaging/ImDib.h @@ -10,20 +10,9 @@ * See the README file for information on usage and redistribution. */ -#ifdef WIN32 +#ifdef _WIN32 -#if (defined(_MSC_VER) && _MSC_VER >= 1200) || (defined __GNUC__) -/* already defined in basetsd.h */ -#undef INT8 -#undef UINT8 -#undef INT16 -#undef UINT16 -#undef INT32 -#undef INT64 -#undef UINT32 -#endif - -#include +#include "ImPlatform.h" #if defined(__cplusplus) extern "C" { diff --git a/libImaging/ImPlatform.h b/libImaging/ImPlatform.h index 7fc5cbdca..92c126a10 100644 --- a/libImaging/ImPlatform.h +++ b/libImaging/ImPlatform.h @@ -17,26 +17,22 @@ #error Sorry, this library requires ANSI header files. #endif -#if defined(_MSC_VER) -#ifndef WIN32 -#define WIN32 -#endif -/* VC++ 4.0 is a bit annoying when it comes to precision issues (like - claiming that "float a = 0.0;" would lead to loss of precision). I - don't like to see warnings from my code, but since I still want to - keep it readable, I simply switch off a few warnings instead of adding - the tons of casts that VC++ seem to require. This code is compiled - with numerous other compilers as well, so any real errors are likely - to be catched anyway. */ -#pragma warning(disable: 4244) /* conversion from 'float' to 'int' */ +#if defined(_MSC_VER) && !defined(__GNUC__) +#define inline __inline #endif -#if defined(_MSC_VER) -#define inline __inline -#elif !defined(USE_INLINE) -#define inline +#if !defined(PIL_USE_INLINE) +#define inline #endif +#ifdef _WIN32 + +#define WIN32_LEAN_AND_MEAN +#include + +#else +/* For System that are not Windows, we'll need to define these. */ + #if SIZEOF_SHORT == 2 #define INT16 short #elif SIZEOF_INT == 2 @@ -61,12 +57,16 @@ #define INT64 long #endif -/* assume IEEE; tweak if necessary (patches are welcome) */ -#define FLOAT32 float -#define FLOAT64 double - #define INT8 signed char #define UINT8 unsigned char #define UINT16 unsigned INT16 #define UINT32 unsigned INT32 + +#endif + +/* assume IEEE; tweak if necessary (patches are welcome) */ +#define FLOAT32 float +#define FLOAT64 double + + diff --git a/libImaging/Incremental.c b/libImaging/Incremental.c index 9e7fb38ec..206c8130b 100644 --- a/libImaging/Incremental.c +++ b/libImaging/Incremental.c @@ -41,7 +41,6 @@ two cases. */ #ifdef _WIN32 -#include #include #else #include diff --git a/libImaging/Lzw.h b/libImaging/Lzw.h index 7d087ac47..8fc30bd7f 100644 --- a/libImaging/Lzw.h +++ b/libImaging/Lzw.h @@ -45,7 +45,7 @@ typedef struct { unsigned char buffer[LZWTABLE]; /* Symbol table */ - unsigned INT16 link[LZWTABLE]; + UINT16 link[LZWTABLE]; unsigned char data[LZWTABLE]; int next; diff --git a/map.c b/map.c index 95d5d1d35..dc9ead0aa 100644 --- a/map.c +++ b/map.c @@ -22,18 +22,6 @@ #include "Imaging.h" -#ifdef WIN32 -#define WIN32_LEAN_AND_MEAN -#undef INT8 -#undef UINT8 -#undef INT16 -#undef UINT16 -#undef INT32 -#undef INT64 -#undef UINT32 -#include "windows.h" -#endif - #include "py3.h" /* compatibility wrappers (defined in _imaging.c) */ @@ -48,7 +36,7 @@ typedef struct { char* base; int size; int offset; -#ifdef WIN32 +#ifdef _WIN32 HANDLE hFile; HANDLE hMap; #endif @@ -71,7 +59,7 @@ PyImaging_MapperNew(const char* filename, int readonly) mapper->base = NULL; mapper->size = mapper->offset = 0; -#ifdef WIN32 +#ifdef _WIN32 mapper->hFile = (HANDLE)-1; mapper->hMap = (HANDLE)-1; @@ -114,7 +102,7 @@ PyImaging_MapperNew(const char* filename, int readonly) static void mapping_dealloc(ImagingMapperObject* mapper) { -#ifdef WIN32 +#ifdef _WIN32 if (mapper->base != 0) UnmapViewOfFile(mapper->base); if (mapper->hMap != (HANDLE)-1) diff --git a/setup.py b/setup.py index 93918debd..50ca985e3 100644 --- a/setup.py +++ b/setup.py @@ -205,25 +205,31 @@ class pil_build_ext(build_ext): # darwin ports installation directories _add_directory(library_dirs, "/opt/local/lib") _add_directory(include_dirs, "/opt/local/include") - - # if homebrew is installed, use its lib and include directories + + # if Homebrew is installed, use its lib and include directories import subprocess try: - prefix = subprocess.check_output(['brew', '--prefix']) - if prefix: - prefix = prefix.strip() - _add_directory(library_dirs, os.path.join(prefix, 'lib')) - _add_directory(include_dirs, os.path.join(prefix, 'include')) - - # freetype2 is a key-only brew under opt/ - _add_directory(library_dirs, os.path.join(prefix, 'opt', 'freetype', 'lib')) - _add_directory(include_dirs, os.path.join(prefix, 'opt', 'freetype', 'include')) + prefix = subprocess.check_output(['brew', '--prefix']).strip() except: - pass # homebrew not installed - - # freetype2 ships with X11 (after homebrew, so that homebrew freetype is preferred) - _add_directory(library_dirs, "/usr/X11/lib") - _add_directory(include_dirs, "/usr/X11/include") + # Homebrew not installed + prefix = None + + ft_prefix = None + + if prefix: + # add Homebrew's include and lib directories + _add_directory(library_dirs, os.path.join(prefix, 'lib')) + _add_directory(include_dirs, os.path.join(prefix, 'include')) + ft_prefix = os.path.join(prefix, 'opt', 'freetype') + + if ft_prefix and os.path.isdir(ft_prefix): + # freetype might not be linked into Homebrew's prefix + _add_directory(library_dirs, os.path.join(ft_prefix, 'lib')) + _add_directory(include_dirs, os.path.join(ft_prefix, 'include')) + else: + # fall back to freetype from XQuartz if Homebrew's freetype is missing + _add_directory(library_dirs, "/usr/X11/lib") + _add_directory(include_dirs, "/usr/X11/include") elif sys.platform.startswith("linux"): arch_tp = (plat.processor(), plat.architecture()[0])